Detect if a table exists - delphi

In SQL Server you can write SQL to check if a table exists. How can I do that for ADS?
I have a need to write some Delphi code to say if table exists do this else this...

The system procedure sp_GetTables can tell you what tables exist in the directory that you connected to:
EXECUTE PROCEDURE sp_GetTables( NULL, NULL, NULL, 'TABLE' )
A non-SQL solution would be to use the AdsCheckExistence API.

I'm not ADS user, so I can't answer in detail.
See http://devzone.advantagedatabase.com/dz/webhelp/Advantage10.1/index.html
The're is system.tables view with information about tables.
I suppose you also can write SQL query to check a table.

I like Peter's answer, but depending on what it is you need to do, you might be looking for a TRY, CATCH, FINALLY statement.
TRY
// Try to do something with the table
select top 1 'file exists' from "non_existing_table";
CATCH ADS_SCRIPT_EXCEPTION
// If a "7041 - File does not exist" error ocurrs
IF __errcode = 7041 THEN
// Do something else
select 'file does not exist' from system.iota;
ELSE
// re-raise the other exception
RAISE;
END IF;
END TRY;

Delphi code:
function TableExists(AConnection: TADOConnection; const TableName: string): boolean;
var
R: _Recordset;
begin
if AConnection.Connected then
try
R := AConnection.Execute('Select case when OBJECT_ID(''' + TableName + ''',''U'') > 0 then 1 else 0 end as [Result]', cmdText, []);
if R.RecordCount > 0 then
Result := (R.Fields.Items['Result'].Value = 1);
except on E:exception do Result := false;
end;
this simple function use existing TADOConnection
end;

Related

FireDAC DBMS Identifiers for conditional statement

I'm trying to use the FireDAC DBMS Identifiers for generating a database specific query.
I'm connecting to a MySQL-Server currently (DriverID = MySQL). I want to query either from mysql or mssql with a limit / top query.
My current statement looks like this:
SELECT
{IF MSSQL} TOP(1) {fi} `tr`.`TaxRate_Primkey`
FROM
`tbl_taxrates` AS `tr`
WHERE
`tr`.`TaxRate_TaxCodeId` = `tc`.`TaxCode_Primkey`
AND
`tr`.`TaxRate_ValidSince` <= :DATE
ORDER BY `tr`.`TaxRate_ValidSince` DESC
{IF MySQL} LIMIT 1 {fi}
Of course I'm aware, that the escaping will not be correct for mssql, but that's a different story.
When I inspect the FireDAC Monitor the preprocessed query looks like this:
SELECT
TOP(1) `tr`.`TaxRate_Primkey`
FROM
`tbl_taxrates` AS `tr`
WHERE
`tr`.`TaxRate_TaxCodeId` = `tc`.`TaxCode_Primkey`
AND
`tr`.`TaxRate_ValidSince` <= ?
ORDER BY
`tr`.`TaxRate_ValidSince` DESC
LIMIT 1
Of course this will result in error
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.`TaxRate_Primkey` FROM `tbl_taxrates` AS `tr` WHERE `tr`.`TaxRate_TaxCodeId` ' at line 1 [errno=1064, sqlstate="42000"]
since it should only add the LIMIT 1 and omit the TOP(1).
Since I only have the Delphi Community Edition 10.4, I don't have access to the MSSQL-Driver. I thought, that could cause this error and I tried with others. But also with {if FIREBIRD} and {if ADS} it was the same.
The constructor looks like this:
constructor TFireDACTenantRepository.Create(
ADBName: string;
ADBServer: string;
APort: Integer;
AUserName: string;
APassword: string;
ALogger: TLogger
);
var
oParams: TStrings;
begin
inherited Create;
FLogger := ALogger;
Self.MonitorLink := nil;
Self.MonitorBy := mbRemote;
Self.Tracing := True;
FConnection := TFDConnection.Create(nil);
oParams := TStringList.Create;
try
oParams.Add('Server=' + ADBServer);
oParams.Add('Port=' + IntToStr(APort));
oParams.Add('Database=' + ADBName);
oParams.Add('User_Name=' + AUserName);
oParams.Add('Password=' + APassword);
oParams.Add('OSAuthent=No');
FDManager.AddConnectionDef('MySQLConnectionTenant', 'MySQL', oParams);
FConnection.Params.MonitorBy := Self.MonitorBy;
FConnection.ConnectionDefName := 'MySQLConnectionTenant';
FConnection.ResourceOptions.ParamCreate := True;
FConnection.ResourceOptions.MacroCreate := True;
FConnection.ResourceOptions.ParamExpand := True;
FConnection.ResourceOptions.MacroExpand := True;
FConnection.ResourceOptions.PreprocessCmdText := True;
FConnection.ResourceOptions.EscapeExpand := True;
finally
oParams.Free;
end;
FConnection.AfterConnect := DoAfterConnect;
FConnection.AfterDisconnect := DoAfterDisconnect;
end;
My question looks a bit related to this question here
How do I use FireDAC DBMS identifiers to conditionally change SQL text
Thanks
I found the solution. If you want to use the conditional escape sequence, you also have to use the FireDAC.Phys.XXX unit for all involved databases.
Since I want to use MSSQL, I have to add FireDAC.Phys.MSSQL to the use-clause.
The units are listed here:
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Databases_(FireDAC)
Maybe this answer helps others.

How can I use the Locate Method of a TADODataSet with non-ASCII data in Delphi 10.3

I have an MS SQL Server 2019 table that has a column named "Char" and it is defined as nvarchar(4).
In my sample Delphi 10.3.3 code, I have a line that says:
found := ADODataSet1.Locate('Char', '⓪', []);
There is no record in the table with such a value, but when I execute the code, Locate returns True and positions to the first record in the result set. If I add a record with that value, Locate still returns True, but positions to the first record in the result set, not the record with the desired character.
With an ASCII character, the code works as expected.
Update:
The first record in my table has "0" in the Char column. If I delete that, then Locating "⓪" returns False. If I add "⓪" to the table then Locate finds that one, but if both "⓪" and "0" are included, then it finds the ASCII digit. If I try to locate "②", it returns the record with "2" in it. The Char column, by the way is a unique index on the table.
Steps to recreate problem.
SQL Server 2019, default US installation
Create table
CREATE TABLE [dbo].[Things](
[Thing] nvarchar NOT NULL
) ON [PRIMARY]
I created a VCL application with a TADODataSet, a TMemo and a TButton
Here is the code for the button:
procedure TForm2.Button1Click(Sender: TObject);
{} procedure Add2Table(aString: string);
begin
with ADODataSet1 do begin
Insert;
FieldByName('Thing').AsString := aString;
Post;
Memo1.Lines.Add('Added: ' + aString);
end;
end;
const
cTarget = '①';
begin
with ADODataSet1 do begin
Close;
CommandText := 'Select * from Things';
Open;
Memo1.Clear;
Memo1.Lines.Add('RecordCount ' + IntToStr(RecordCount));
Add2Table('0');
Add2Table('1');
Add2Table('2');
Add2Table('⓪');
Add2Table(cTarget);
Add2Table('②');
Close; Open;
Memo1.Lines.Add('Trying to locate: ' + cTarget);
if Locate('Thing', cTarget, []) then
Memo1.Lines.Add(Format('Found %s in record %d', [
FieldByName('Thing').AsString, Recno]))
else
Memo1.Lines.Add('Not found');
end;
end;
When the program runs, instead of finding the target character, "①", it finds '1'.

How can I check if a stored procedure exists in PERVASIVE database before creating it?

I want to create a stored procedure in a Pervasive database, but only if it does not yet exist.
I found something for SQL Server:
IF NOT EXISTS (SELECT *
FROM sys.objects
WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO
I found that code on this link How to check if a stored procedure exists before creating it.
So I want something really similar for Pervasive.
There's no way to execute the IF NOT EXISTS syntax outside of a Stored Procedure in Pervasive. You could create a procedure taking a procedure name and drop it if it exist. Something like:
CREATE PROCEDURE DropProcIfExists(in :procname char(20))
AS
BEGIN
IF( EXISTS (SELECT xp$Name FROM X$proc WHERE Xp$Name = :procname) ) THEN
Exec('DROP Procedure "' + :procname + '"') ;
END IF;
End#
call DropProcIfExists ('myProc')#
So your SQL would be something like:
call DropProcIfExists('MyNewProc')#
Create Procedure MyNewProc()
AS
BEGIN...

Delphi Postgres stored procedure

I am working on Delphi application which deals with stored procedures.I created stored procedure for insert statement and it works fine for my application.
Now,I wanted to create the same for select statement.
CREATE TYPE list_all_firstname AS ( first_name character varying);
CREATE FUNCTION select_sp()
RETURNS SETOF list_all_firstname AS
$$
DECLARE
rec record;
BEGIN
FOR rec IN (SELECT first_name FROM person) LOOP
RETURN NEXT rec;
END LOOP;
END;
$$ LANGUAGE plpgsql;
The call is:
SELECT * FROM select_sp();
Till this everything is fine in postgres.I wanted to access this stored procedure in my delphi application.
My code is:
with StoredProc2 do begin
StoredProcName :='select_sp';
ExecProc;
Edit5.Text:=ParamByName('list_all_firstname').AsString ;
end;
But i gets the error saying "Could not find object".How do i access return values of stored procedure in delphi??
I got the answer..could not find object is BDE error...
Next thing is accessing values,no need to use stored procedure component.We can use TQuery as below...:
Query1.SQL.Clear;
Query1.SQL.Add('SELECT * FROM select_sp()');
Query1.Active := True;
for i:=0 to Query1.RowsAffected-1 do
begin
sval:=Query1.FieldByName('first_name').AsString;
ShowMessage(sval);
Query1.Next;
end;

Delphi 5 & Crystal XI Rel. 2 (RDC) how to?

I'm trying to work with the class from JosephStyons but I do get an "Invalid Index" Error on the line where the "User ID" should get set.
FRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] := edUserName.Text;
Here's my environment:
WinXP Sp3, Crystal Reports Developer XI Rel.2 SP4, Delphi 5 Update Pack 1
Any help or ideas greatly appreciated!
Thx,
Reinhard
Your value for [i] could be the culprit...I can't remember for sure but I believe the first table will be Table[1] instead of Table[0] as one would expect.
I altered my loop to use:
CrTables := CrDatabase.Tables;
for crTableObj in crTables do
You might try stepping through the table using a for loop as shown above or by starting with 1 instead of 0.
I hope this helps.
Put a break point on that line and use Evaluate/Modify.
It will return an error if you try something invalid.
Examine FRpt.Database.Tables[i] and see if it's valid for what you think are the min and max values for i.
If Tables is an array, one way to avoid that is to use ...Low(Tables) to High(Tables)
If you get your Table Ok, examine FRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] and see if it's valid.
It might be that the Item getter does not like the space embedded in "User ID". Some products need either to surround by special characters like "[User ID]", other to replace by an underscore like "User_ID"
Are you also setting the password, server name and database name?
procedure TReports.LogonToDBTables(cReport:
CrystalDecisions.CrystalReports.Engine.ReportDocument;
ConnInfo: ConnectionInfo);
var
CrDataBase: Database;
CrTables: Tables;
CrTableObj: TObject;
CrTable: Table;
CrTableLogonInfo: TableLogonInfo;
iSubReportIndex: smallint;
begin
CrDataBase := CReport.Database;
CrTables := CrDatabase.Tables;
cReport.DataSourceConnections[0].IntegratedSecurity := False;
for crTableObj in crTables do
begin
crTable := CrystalDecisions.CrystalReports.Engine.Table(crTableObj);
crTableLogonInfo := crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo := ConnInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
end;
end;
function TReports.GetConnectionInfo(): ConnectionInfo;
var
cTemp: ConnectionInfo;
begin
cTemp := ConnectionInfo.Create();
cTemp.AllowCustomConnection := True;
cTemp.ServerName := GetServerName();
cTemp.DatabaseName := GetDBName();
cTemp.UserID := GetDBUserID();
cTemp.Password := GetDBPassword();
Result := cTemp;
end;

Resources