FireDAC FDSCript error while executing last script - delphi

When I execute the following code I get an error when executing the last script.
The error is:
"Cannot open the file "c:\FiredacDemo\las.sql"
I cannot understand why it executes fine named scripts #fdrop_dables, #first, #second and only for the very last it tries to executes instead an sql file.
procedure TForm1.btnMasterScriptClick(Sender: TObject);
begin
with FDScript.SQLScripts do begin
Clear;
with Add do begin
Name := 'root';
SQL.Add('#drop_tables');
SQL.Add('#first');
SQL.Add('#second');
SQL.Add('#last');
end;
with Add do begin
Name := 'drop_tables';
SQL.Add('drop TABLE T1;');
SQL.Add('drop TABLE T2;');
SQL.Add('drop TABLE T3;');
SQL.Add('drop TABLE T4;');
end;
with Add do begin
Name := 'first';
SQL.Add('CREATE TABLE T1 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
SQL.Add('CREATE TABLE T2 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
end;
with Add do begin
Name := 'second';
SQL.Add('CREATE TABLE T3 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
SQL.Add('CREATE TABLE T4 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
end;
with Add do begin
Name := 'last';
SQL.Add(format('INSERT INTO T1 (ID, FNAME, CREATED) VALUES(%d, %s, current_timestamp);', [ 1, 'fabio', now]));
SQL.Add('commit;');
end;
end;
FDScript.ValidateAll;
FDScript.ExecuteAll;
end;
EDIT: This one works!
procedure TForm1.btnMasterScriptClick(Sender: TObject);
begin
FDScript.SQLScriptFileName := '';
FDScript.Arguments.Clear;
with FDScript.SQLScripts do begin
Clear;
with Add do begin
Name := 'root';
SQL.Add('#drop_tables');
SQL.Add('#first_script');
SQL.Add('#second_script');
SQL.Add('#third_script');
SQL.Add('#last_script');
SQL.Add('#'); // Added this -> see below
end;
with Add do begin
Name := 'drop_tables';
SQL.Add('drop TABLE T1;');
SQL.Add('drop TABLE T2;');
SQL.Add('drop TABLE T3;');
SQL.Add('drop TABLE T4;');
end;
with Add do begin
Name := 'first_script';
SQL.Add('CREATE TABLE T1 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
SQL.Add('CREATE TABLE T2 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
end;
with Add do begin
Name := 'second_script';
SQL.Add('CREATE TABLE T3 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
SQL.Add('CREATE TABLE T4 (ID INTEGER NOT NULL, NAME VARCHAR(50), CREATED TIMESTAMP);');
end;
with Add do begin
Name := 'third_script';
SQL.Add(Format('INSERT INTO T1 (ID, NAME) VALUES(%d, %s);', [ 1, '''fabio''']));
end;
with Add do begin
Name := 'last_script';
SQL.Add(Format('INSERT INTO T1 (ID, NAME) VALUES(%d, %s);', [ 2, '''stefano''']));
end;
with Add do begin // addes this (see above)
Name := ''; // please note the empty '' Name
end; //
end;
FDScript.ValidateAll;
FDScript.ExecuteAll;
end;

Related

generate a number increases with a specific format ex 'PRT-00000'

I want to create auto numbering to my access database in delphi
example :
I have a database with part names , i want to create an auto id that counts number of these records and generates a name with number as this 'PRT-00000' and increase it with one each time i add a record and keeps this format of five digits , like this 'PRT-00001'
help me please and thanks a lot .
sorry for my poor english
Let's assume your Access table is named 'Parts' and has an AutoNumber
field named 'ID' and a Short Text field named 'PartNumber'. One way of generating the
PartNumber value would be to get Access to calculate it for you, but since you have asked about Delphi, I'm going to explain a way to do it in Delphi.
Please start a new, very simple project with just the following items on the main form:
A TAdoConnection configured to connect to your database;
A TAdoQuery configured to use the TAdoConnection with its SQL.Text property set
to 'select * from Parts'
A TDataSource and TDBGrid configured to display the contents of the TAdoQuery.
A TButton
Then, add the following code to the form's unit:
procedure TForm2.Button1Click(Sender: TObject);
begin
NewPart;
end;
procedure TForm2.NewPart;
const
sSelect = 'select * from Parts';
sPrefix = 'PRT-';
iDigits = 5;
var
PartNumber : String;
ID : Integer;
begin
qryParts.Insert;
try
// First, set the new record's PartNumber field to a temporary value
qryParts.FieldByName('PartNumber').AsString := 'xxxx';
// save the record so that we can then read the ID value Access has allocated to the record
qryParts.Post;
// read the ID value
ID := qryParts.FieldByName('ID').AsInteger;
// next, construct the desired value for the PartNumber field based on the ID
PartNumber := qryParts.FieldByName('ID').AsString;
// left-pad the PartNumber with zeroes
while Length(PartNumber) < iDigits do
PartNumber := '0' + PartNumber;
// pre-pend the PRT- prefix
PartNumber := sPrefix + PartNumber;
// put qryParts into its dsEdit state
qryParts.Edit;
qryParts.FieldByName('PartNumber').AsString := PartNumber;
finally
// post the record back to the Parts table
qryParts.Post;
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
qryParts.Open;
end;
Update I've managed to get the new Part's autonumber ID in advance of the new Part being posted to the db. To use this, add the NewAutoNumber function and modify the NewPart method as shown below.
function TForm2.NewAutoNumber(ATable, AColumn: String): Integer;
var
vCat : OleVariant;
vTable : OleVariant;
vColumn : OleVariant;
begin
vCat := CreateOleObject('ADOX.Catalog');
vCat.ActiveConnection := AdoConnection1.ConnectionString;
vTable := vCat.Tables[ATable];
vColumn := vTable.Columns[AColumn];
Result := vColumn.Properties['Seed'].Value;
end;
procedure TForm2.NewPart;
const
sSelect = 'select * from Parts';
sPrefix = 'PRT-';
iDigits = 5;
var
PrvSql : String;
PartNumber : String;
ID : Integer;
begin
ID := NewAutoNumber('Parts', 'ID');
try
qryParts.Insert;
qryParts.FieldByName('PartNumber').AsString := 'xxxx';
qryParts.Post;
if not qryParts.Locate('ID', ID, []) then begin
raise exception.CreateFmt('Failed to create new Parts record with ID = %d', [ID]);
end;
PartNumber := qryParts.FieldByName('ID').AsString;
while Length(PartNumber) < iDigits do
PartNumber := '0' + PartNumber;
PartNumber := sPrefix + PartNumber;
qryParts.Edit;
qryParts.FieldByName('PartNumber').AsString := PartNumber;
finally
qryParts.Post;
end;
end;
Update #2 As an alternative to getting the ID value for a newly-added Parts record using
either of the methods above, it can be obtained by using the 'select ##identity' method. The simplest
way to do this is to add another TAdoQuery, qryAutoNumber to the form and to add this function to
get the AutoNumber value:
function TForm2.NewAutoNumberFromIdentity : Integer;
begin
if qryAutoNumber.Active then
qryAutoNumber.Close;
qryAutoNumber.SQL.Text := 'select ##identity';
qryAutoNumber.Open;
Result := qryAutoNumber.Fields[0].AsInteger;
end;
Note that to obtain the correct ID value, this function should be called immediately after calling qryParts.Post. However, I have included this
only for completeness but as far as I can see, it is largely pointless
because once the new Parts record has been posted, the ID AutoNumber value
can be read directly from the ID field of qryParts.

how to set a default value for a column in cxGrid?

How to set a default value for a boolean column in cxGrid? I mean i would like that by default all new rows have the value "False" for my boolean column
Assuming this is a data grid (containing e.g. a cxGridDBTableView), you should set any defaults in the dataset's OnNewRecord event, like this
procedure TForm1.ClientDataSet1NewRecord(DataSet: TDataSet);
var
Field : TField;
i : Integer;
begin
// set all Boolean data fields to False
for i := 0 to DataSet.FieldCount - 1 do begin
Field := DataSet.Fields[i];
if (Field.DataType = ftBoolean) and (Field.FieldKind = fkData) then
// the test on Field.FieldKind is to avoid disturbing any fkCalculated or fkInternalCalc fields
Field.AsBoolean := False;
end;
end;
If you do that (or set any other field values in the OnNewRecord event), the values will automatically be transferred to the cxGrid.
Update The following shows how to set an initial False value for a boolean column of an
unbound cxGridTableView. Note: The code creates the TableView so there is no need
to add it or a cxGrid to the form.
// form fields (of Form1)
cxGrid1 : TcxGrid;
cxLevel : TcxGridLevel;
TableView : TcxGridTableView;
Col1,
Col2,
Col3 : TcxGridColumn;
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
cxGrid1 := TcxGrid.Create(Self);
cxGrid1.Parent := Self;
cxLevel := cxGrid1.Levels.Add;
cxLevel.Name := 'Firstlevel';
TableView := TcxGridTableView.Create(Self);
TableView := cxGrid1.CreateView(TcxGridTableView) as TcxGridTableView;
TableView.Name := 'ATableView';
TableView.Navigator.Visible := True;
Col1 := TableView.CreateColumn;
Col1.DataBinding.ValueType := 'Integer';
Col1.Caption := 'RowID';
Col2 := TableView.CreateColumn;
Col2.DataBinding.ValueType := 'String';
Col2.Caption := 'RowName';
Col3 := TableView.CreateColumn;
Col3.DataBinding.ValueType := 'Boolean';
Col3.Caption := 'RowChecked';
cxLevel.GridView := TableView;
TableView.DataController.OnNewRecord := cxGridTableViewDataControllerNewRecord;
end;
procedure TForm1.cxGridTableViewDataControllerNewRecord(
ADataController: TcxCustomDataController; ARecordIndex: Integer);
begin
Caption := IntToStr(ARecordIndex);
ADataController.Values[ARecordIndex, 2] := False; // The 2 is the index of Col3
end;

Delphi query multi Parameters

I'm trying to make a query on dataBase Table with ADOQuery by this code
begin
adoquery1.close;
adoquery1.sql.clear;
adoquery1.sql.add(SELECT * FROM Table WHERE name=:name and tel=:tel);
adoquery1.Parameters.ParamByName('name').Value:= edtName.text;
adoquery1.Parameters.ParamByName('tel').Value:= edtTel.text;
adoquery1.open;
end;
Now, if I set edtName and edtTel the query is return a result
But what if edtName or edtTel is empty
the query will return null
what should I do, to make the query returning the result if one of them or both has a value??
Thanks.
You should check if textboxes are empty;
begin
adoquery1.close;
adoquery1.sql.clear;
adoquery1.sql.add(SELECT * FROM Table WHERE 1=1 );
if edtName.text <> '' then
Begin
adoquery1.sql.add(' And name=:name ');
adoquery1.Parameters.ParamByName('name').Value:= edtName.text;
End;
if edtTel.text <> '' then
Begin
adoquery1.sql.add(' And tel=:tel ');
adoquery1.Parameters.ParamByName('tel').Value:= edtTel.text;
End;
adoquery1.open;
end;
note: if both of them are empty result will show all records.
Build the SQL dynamically based on whether the Edit boxes are empty or not, eg:
var
hasName, hasTel: Boolean;
whereClause: string;
begin
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('SELECT * FROM Table');
hasName := edtName.GetTextLen > 0;
hasTel := edtTel.GetTextLen > 0;
if hasName or hasTel then
begin
whereClause := 'WHERE ';
if hasName then
whereClause := whereClause + 'name=:name';
if hasTel then
begin
if hasName then
whereClause := whereClause + ' and ';
whereClause := whereClause + 'tel=:tel';
end;
ADOQuery1.SQL.Add(whereClause);
if hasName then
ADOQuery1.Parameters.ParamByName('name').Value := edtName.Text;
if hasTel then
ADOQuery1.Parameters.ParamByName('tel').Value := edtTel.Text;
end;
ADOQuery1.Open;
end;
Or, more generically:
var
params: TStringList;
I: Integer;
begin
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
params := TStringList.Create;
try
if edtName.GetTextLen > 0 then
params.Add('name=' + edtName.Text);
if edtTel.GetTextLen > 0 then
params.Add('tel=' + edtTel.Text);
// other parameters as needed ...
ADOQuery1.SQL.Add('SELECT * FROM Table');
if params.Count > 0 then
begin
ADOQuery1.SQL.Add('WHERE ' + params.Names[0] + '=:' + params.Names[0]);
for I := 1 to params.Count-1 do
ADOQuery1.SQL.Add('AND ' + params.Names[I] + '=:' + params.Names[I]);
for I := 0 to params.Count-1 do
ADOQuery1.Parameters.ParamByName(params.Names[I]).Value := params.ValueFromIndex[I];
end;
finally
params.Free;
end;
ADOQuery1.Open;
end;
You could do what sddk was suggesting (building your SQL in response of the TEdits value) or
you could assign NULL value to your parameters like that
begin
adoquery1.close;
adoquery1.sql.clear;
adoquery1.sql.add('SELECT * FROM Table WHERE name=:name and tel=:tel');
if edtName.text <> '' then begin
adoquery1.Parameters.ParamByName('name').Value:= edtName.text;
end else begin
adoquery1.Parameters.ParamByName('name').Value:= Null;
end;
if edtTel.text <> '' then begin
adoquery1.Parameters.ParamByName('tel').Value:= edtTel.text;
end else begin
adoquery1.Parameters.ParamByName('tel').Value:= Null;
end;
adoquery1.open;
end;
Don't forget to add System.Variants in your uses list (for the Null use)
You need to beware of testing for Null, as others have warned, but there is a bit more to it
than merely setting the parameter value to Null.
Suppose you have a Sql Server table with a name column and a number of rows have null
entries for that column. Then consider this code:
procedure TForm1.Button1Click(Sender: TObject);
begin
if cbUseNullParam.Checked then begin
AdoQuery1.SQL.Text := 'select * from MATable1 where name = :name';
AdoQuery1.Parameters.ParamByName('name').Value := Null;
end
else
AdoQuery1.SQL.Text := 'select * from MATable1 where name is Null';
AdoQuery1.Open;
end;
In other words, if the cbUseNullParam checkbox is checked, set the name param
to Nll, otherwise use SQL which explicitly specifies that the name column is Null.
Open Sql Server Management Studio's Profiler and observe what happens, both in what is sent to the server, and what a DBGrid connected to AdoQuery1 displays, which is this.
When cbUseNullParam is checked, the query fails to return the rows having
a Null name. When it is not checked, the correct rows are returned.
In other words, using ADO against MS Sql Server at any rate, if you want to find rows that have a Null state for a given column, you need to use Sql that explicitly states that the column is Null, rather than relying of setting an AdoQuery parameter for that column to Null. So, if your case, you actually need four versions of your SQL (one each for the two columns being Null, one for them both being Null and one for neither being Null).

FireDAC Image BLOB as FDStoredProc parameter [duplicate]

I want to store images in a database using sql but cant seem to get it to work:
qry.SQL.Clear;
qry.Sql.Add('update tbl set pic = :blobVal where id = :idVal');
qry.Parameters.ParamByName('idVal')._?:=1;
.Parameters has no .asinteger like .Param has but .Param isn't compatible with a TADOquery - to workaround I tried:
a_TParameter:=qry.Parameters.CreateParameter('blobval',ftBlob,pdinput,SizeOf(TBlobField),Null);
a_TParam.Assign(a_TParameter);
a_TParam.asblob:=a_Tblob;
qry.ExecSql;
This also doesnt work:
qry.SQL.Clear;
qry.Sql.Add('update tbl set pic = :blobVal where id = 1')
qry.Parameters.ParamByName('blobVal').LoadFromStream(img as a_TFileStream,ftGraphic);//ftblob
//or
qry.Parameters.ParamByName('blobVal').LoadFromFile('c:\sample.jpg',ftgrafic);//ftblob
qry.ExecSql;
Should be something like:
qry.Parameters.Clear;
qry.Parameters.AddParameter.Name := 'blobVal';
qry.Parameters.ParamByName('blobVal').LoadFromFile('c:\sample.jpg', ftBlob);
// or load from stream:
// qry.Parameters.ParamByName('blobVal').LoadFromStream(MyStream, ftBlob);
qry.Parameters.AddParameter.Name := 'idVal';
qry.Parameters.ParamByName('idVal').Value := 1;
qry.SQL.Text := 'update tbl set pic = :blobVal where id = :idVal';
qry.ExecSQL;
To read the BLOB back from the DB:
qry.SQL.Text := 'select id, pic from tbl where id = 1';
qry.Open;
TBlobField(qry.FieldByName('pic')).SaveToFile('c:\sample_2.jpg');
I'm using Lazarus, not Delphi, but I guess its usually the same syntax. If so, here's a slight improvement on kobiks suggestion:
Parameters are added automatically if the SQL.Text is assigned before trying to assign values to the parameters. Like this:
qry.Parameters.Clear;
qry.SQL.Text := 'update tbl set pic = :blobVal where id = :idVal';
qry.Parameters.ParamByName('blobVal').LoadFromFile('c:\sample.jpg', ftBlob);
qry.Parameters.ParamByName('idVal').Value := 1;
qry.ExecSQL;
I wrote this as an answer to this q,
Delphi save packed record as blob in a sql database
which is currently flagged as a duplicate, possibly incorrectly because the technique
used by the OP as described in comments appears to be correct. So, the cause of the problem may lie elsewhere.
If the Duplicate flag gets removed, I'll re-post this answer there.
The following code works fine for me against a Sql Server table defined as shown below.
The data from Rec1 is saved into the table and correctly read back into Rec2.
(* MS Sql Server DDL
CREATE TABLE [blobs] (
[id] [int] NOT NULL ,
[blob] [image] NULL ,
CONSTRAINT [PK_blobs] PRIMARY KEY CLUSTERED
(
[id]
) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
*)
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
qBlobInsert: TADOQuery;
qBlobRead: TADOQuery;
Button1: TButton;
procedure Button1Click(Sender: TObject);
[...]
type
TMyRecord = packed record
FontName: string[30];
FontSize: word;
FontColor: integer;
FontStyle: word;
Attachement: string[255];
URL: string[255];
end;
const
scInsert = 'insert into blobs(id, blob) values(:id, :blob)';
scSelect = 'select * from blobs where id = %d';
procedure TForm1.Button1Click(Sender: TObject);
begin
TestInsert;
end;
procedure TForm1.TestInsert;
var
Rec1,
Rec2 : TMyRecord;
MS : TMemoryStream;
begin
FillChar(Rec1, SizeOf(Rec1), #0);
FillChar(Rec2, SizeOf(Rec2), #0);
Rec1.FontName := 'AName';
Rec1.URL := 'AUrl';
MS := TMemoryStream.Create;
try
// Save Rec1 using an INSERT statement
MS.Write(Rec1, SizeOf(Rec1));
MS.Seek(0, soFromBeginning);
qBlobInsert.Parameters[0].Value := 1;
qBlobInsert.Parameters[1].LoadFromStream(MS, ftBlob);
qBlobInsert.SQL.Text := scInsert;
qBlobInsert.ExecSQL;
// Read saved data back into Rec2
qBlobRead.SQL.Text := Format(scSelect, [1]);
qBlobRead.Open;
MS.Clear;
TBlobField(qBlobRead.FieldByName('blob')).SaveToStream(MS);
MS.Seek(0, soFromBeginning);
MS.Read(Rec2, MS.Size - 1);
Caption := Rec2.FontName + ':' + Rec2.URL;
finally
MS.Free;
end;
end;
Extract from DFM
object qBlobInsert: TADOQuery
Connection = ADOConnection1
Parameters = <
item
Name = 'id'
DataType = ftInteger
Value = Null
end
item
Name = 'blob'
DataType = ftBlob
Value = Null
end>
Left = 56
Top = 32
end

Paradox error on CreateTable

I was creating table during execution, however at the creation of the table, (see my code)
procedure CreateTempTable(pDataBaseName,pSessionName:String);
begin
//-------create "TempTable"
TempTable:=TTable.Create(application);
With TempTable Do
begin
//-------set false in "Active"
Active := False;
//-------name of Session
SessionName:=pSessionName;
//-------name of DataBase.
DatabaseName :=pDataBaseName;
Filtered := True;
//-------name of table
TableName := 'TempTabl.DB';
//-------paradox type
TableType := ttParadox;
//-------if the table is already exists
if TempTable.Exists then
//-------delete the table
TempTable.DeleteTable;
//-------create 2 fields "Field1" & "Field2"
with FieldDefs do
begin
Clear;
with AddFieldDef do
begin
DataType := ftFloat;
Name := 'Field1';
end;
with AddFieldDef do
begin
DataType := ftFloat;
Name:='Field2';
end;
end;
//-------Create table
CreateTable; // Here where the exception bursts off
end;
end;
an exception is raised, which is: "Table is open, Table does not exist".
so what is exactly the problem, is it open or doesnot exist?
This is the exception:
(inside With TempTable Do) There is no need to delete the TempTabl.DB explicit. Is overwritten by CreateTable anyway.
To test if the table is not being used by other components in the IDE, you can try to delete the file TempTabl.DB.
CreateTempTable is now a function CreateTempTable(pDataBaseName,pSessionName:String):Boolean;
So you can better handle errors.
Tested with delphi 5 and RAD Studio 2007.
function CreateTempTable(pDataBaseName,pSessionName:String):Boolean;
begin
result:=false;
// assume pDataBaseName=directory, or change it
if FileExists(pDataBaseName+'TempTabl.DB') then begin
if NOT DeleteFile(pDataBaseName+'TempTabl.DB') then begin
showMessage('Table opened by another part of the IDE');
exit;
end;
end;
TempTable:=TTable.Create(application);
With TempTable Do
begin
Active := False;
SessionName:=pSessionName;
DatabaseName :=pDataBaseName;
//Filtered := True;
TableName := 'TempTabl.DB';
TableType := ttParadox;
with FieldDefs do
begin
Clear;
with AddFieldDef do
begin
DataType := ftFloat;
Name := 'Field1';
end;
with AddFieldDef do
begin
DataType := ftFloat;
Name:='Field2';
end;
end;
CreateTable;
result:=true;
end;
end;

Resources