Error when using parameter in ADOQuery - delphi

I have this simple code to check if a record exists in a table, but it always returns a runtime error :
Arguments are of the wrong type, are out of acceptable range, or are
in conflict with one another.
my code is this :
function TDataModuleMain.BarCodeExists(barCode: string): boolean;
begin
if ADOQuerySql.Active then
ADOQuerySql.Close;
ADOQuerySql.SQL.Clear;
ADOQuerySql.SQL.Text := 'select count(1) from Card where BarCode = (:TestBarcode)';
ADOQuerySql.Parameters.ParamByName('TestBarcode').Value := barCode;
ADOQuerySql.Open; // HERE THE RUNTIME ERROR APPEARS
Result := ADOQuerySql.Fields[0].AsInteger = 1;
ADOQuerySql.Close;
ADOQuerySql.Parameters.Clear;
end;
The field BarCode in table Card is of type nvarchar(100)
In debug I see that the parameter is created, and gets populated with the correct value.
Running the query in sql server management studio also works.
I also found this How to pass string parameters to an TADOQuery? and checked my code with the code in the answer but I don't see any problems here.
Also this AdoQuery Error using parameters did not help me.
It will no doubt be something very simple that I have missed but I just dont see it now.
EDIT : things I tried from suggestions in the comments:
.ParamCheck := True (default)
.Parameters.ParamByName('TestBarcode').DataType := ftString
.Parameters.ParamByName('TestBarcode').DataType := ftWideString
None of these worked however.
What did help was using a non-shared AdoQuery for this, and that one did the job without any errors. I am using that now as the solution but I am still looking at the shared AdoQuery out of curiousity what the exact problem is.
EDIT: the source of the problem is found.
I used the function provided by MartinA to examine both the dynamic created query and the shared AdoQuery and I found one difference.
The shared AdoQuery had the this property filled :
ExecuteOption := [eoExecuteNoRecords]
and the dynamic created query does not.
Since this property is not set in designtime I did not see it.
After clearing the property to [] the shared AdoQuery worked again.
I am going to switch to using non shared AdoQuery for this kind of work as been suggested.
Thanks everyone for your assistance.

The following isn't intended to be a complete answer to your q, but to follow up my comment that "all you have to do is to inspect your form's DFM and compare the properties of your original ADoQuery with the unshared one. The answer should lie in the difference(s)" and your reply that the unshared query is created dynamically.
There is no "voodoo" involved in the difference in behaviour between your two ADOQuerys. It's just a question of capturing what the differences actually are.
So, what you need, to debug the problem yourself, is some code to compare the properties of two components, even if one or both of them is created dynamically. Using the following routine on both components will enable you to do exactly that:
function TForm1.ComponentToString(AComponent : TComponent) : String;
var
SS : TStringStream;
MS : TMemoryStream;
Writer : TWriter;
begin
// Note: There may be a more direct way of doing the following, without
// needing the intermediary TMemoryStream, MS
SS := TStringStream.Create('');
MS := TMemoryStream.Create;
Writer := TWriter.Create(MS, 4096);
try
Writer.Root := Self;
Writer.WriteSignature;
Writer.WriteComponent(AComponent);
Writer.FlushBuffer;
MS.Position := 0;
ObjectBinaryToText(MS, SS);
Result := SS.DataString;
finally
Writer.Free;
MS.Free;
SS.Free;
end;
end;
Over to you ...

Related

How can I remove a PCB Object in Altium PCB Library using Altium scripting systems?

I am writing a Delphi Altium script to remove all tracks in TopOverLay inside the PCB Library.
When running the script though, nothing happens (the tracks are not removed).
I don't know why. Can you please help me?
Here below is my code :
procedure RemoveTrackObject;
Var
MyComponent : IPCB_LibComponent;
MyTrack : IPCB_Track;
Iterator : IPCB_GroupIterator;
DeleteList : TInterfaceList;
TrackTemp : IPCB_Track;
i : Integer;
begin
MyComponent := PCBServer.GetCurrentPCBLibrary.CurrentComponent;
//////////////////////////////////////////////////////////////////////
Iterator := MyComponent.GroupIterator_Create;
Iterator.AddFilter_ObjectSet(Mkset(eTrackObject));
Iterator.AddFilter_LayerSet(Mkset(eTopOverLay));
DeleteList := TInterfaceList.Create;
try
MyTrack := Iterator.FirstPCBObject;
While MyTrack <> nil do
begin
DeleteList.Add(MyTrack);
MyTrack := Iterator.NextPCBObject;
end;
finally
MyComponent.GroupIterator_Destroy(Iterator);
end;
try
PCBServer.PreProcess;
for i := 0 to DeleteList.Count - 1 do
begin
TrackTemp := DeleteList.Items[i];
MyComponent.RemovePCBObject(TrackTemp);
end;
finally
PCBServer.PostProcess;
DeleteList.Free;
end;
Client.SendMessage('PCB:Zoom', 'Action=Redraw' , 255, Client.CurrentView);
end;
AFAIU In Altium dephiscript API InterfaceList have specific uses: holding non-PCB objects & passing to external dll functions & letting the receiving fn destroy the list.
You don't really need one here.
The PcbLib does have some strange behaviour around deleting from selected/focused footprint etc.
I think the problem is caused by the Pcb Editor not allowing objects to be deleted from the current focused component/footprint.
The history around this issue points to solutions involving moving focus away from the required component..
You can't complete the delete process while the List still contains the object reference.
Use a While loop, after RemovePCBObject(), remove object ref from the List (remove the List Item). Then when the While loop terminates you have zero items in List.
Might help refresh or look & feel to use some of these fn calls:
CurrentLib.Board.GraphicallyInvalidate;
CurrentLib.Navigate_FirstComponent;
CurrentLib.Board.ViewManager_FullUpdate;
CurrentLib.Board.GraphicalView_ZoomRedraw;
CurrentLib.RefreshView;

Delphi - mORMot Can not access data through client using full memory model and TSQLRestClientURI

I've been trying to start a new project using mORMOt the DDD-way and have created a few classes and begun to test one of them in an easy/simple way.
I used the regression test code from your DDD-sample about TUser and modified it to suit my class .
I have tried to minimize the code and hopefully, it could contain some clues for you to help me understand what's wrong here.
I found that when only using the server, everything works ok but when using client the ORMselection won't find the data.
I stripped down the code as much as possible and marked with some comments where it works and where it not works.
class procedure TInfraRepoPackageFactory.RegressionTestsPackage(test: TSynTestCase);
procedure TestOne(Rest: TSQLRest);
var cmd: IDomPackageCommand;
qry: IDomPackageQuery;
package: TPackage;
begin
test.Check(Rest.Services.Resolve(IDomPackageCommand,cmd));
package := TPackage.Create;
try
package.articleNo := 10000;
test.check(cmd.Add(package)=cqrsSuccess);
end;
test.check(cmd.Commit=cqrsSuccess);
finally
package.Free;
end;
package := TPackage.Create;
try
test.Check(Rest.Services.Resolve(IDompackageQuery,qry));
test.Check(qry.SelectByArticleNo(10000,false)=cqrsSuccess); // <<-- Debugging shows that it will not find anything when using client.
test.Check(qry.GetCount=1); // <<-- getCount returns zero when using client.
end;
finally
package.Free;
end;
end;
var RestServer: TSQLRestServerFullMemory;
RestClient: TSQLRestClientURI;
begin
RestServer := TSQLRestServerFullMemory.CreateWithOwnModel([TSQLRecordPackage]);
try // first try directly on server side
RestServer.ServiceContainer.InjectResolver([TInfraRepoPackageFactory.Create(RestServer)],true);
TestOne(RestServer); // sub function will ensure that all I*Command are released // <<=== Works
finally
RestServer.Free;
end;
RestServer := TSQLRestServerFullMemory.CreateWithOwnModel([TSQLRecordPackage]);
try // then try from a client-server process
RestServer.ServiceContainer.InjectResolver([TInfraRepoPackageFactory.Create(RestServer)],true);
RestServer.ServiceDefine(TInfraRepoPackage,[IDomPackageCommand,IDomPackageQuery],sicClientDriven);
test.Check(RestServer.ExportServer);
RestClient := TSQLRestClientURIDll.Create(TSQLModel.Create([TSQLRecordPackage]),#URIRequest);
try
RestClient.Model.Owner := RestClient;
RestClient.ServiceDefine([IDomPackageCommand],sicClientDriven);
TestOne(RestServer); // <<=== Works
RestServer.DropDatabase;
USEFASTMM4ALLOC := true; // for slightly faster process
TestOne(RestClient); // <<=== DO NOT Work !!!!
finally
RestClient.Free;
end;
finally
RestServer.Free;
end;
end;
I also tried to put this question on the mORMot forum but the mailer can not reach the site.
Got this message :
An error was encountered
Error: Unable to send email. Please contact the forum administrator with the following error message reported by the SMTP server: "450 4.1.2: Recipient address rejected: Domain not found ".
I finally found what's wrong.
In the agregate, class TPackage, I had set the property of packageNo to "stored AS_UNIQUE" - that resulted in that the commit just stored zero in that field and then the SELECT('packageNo=?,[10001]) couldn't find anything.
I didn't realize that because the package object contained just 10001 and could never think of the possibility that the commit should store a 0.
But when I tested with TSQLHttpServer and TSQLHttpClient and a real database I could see all records containing zeroes in the packageNo field.
Then I understood that it must be something with this field that's wrong. When I looked up TPackage I found my mistake.
I should have set the "STORED AS_UNIQUE" in the TSQLRecordPackage class instead, the one that's used by ORM.
The moral of the story... "OPEN YOUR EYES.. and you will see" ;-)

Delphi - Can you close all of the database tables?

Can you close all database tables except some? Can you then reopen them? I use an absolute database that is similar to BDE. If this is possible, how can I do so many?
Yes, of course you can. You could iterate the Components property of your form/datamodule, use the is operator to check whether each is an instance of your table type and use a cast to call Open or Close on it.
The following closes all TABSDataSet tables on your form except one called Table1.
procedure TForm1.ProcessTables;
var
ATable : TABSDataSet; // used to access a particular TABSDataSet found on the form
i : Integer;
begin
for i := 0 to ComponentCount - 1 do begin
if Components[i] is TABSDataSet then begin
ATable := TABSDataSet(Components[i]);
// Now that you have a reference to a dataset in ATable, you can
// do whatever you like with it. For example
if ATable.Active and (ATable <> Table1) then
ATable.Close;
end;
end;
end;
I've seen from the code you've posted in comments and your answer that you
are obviously having trouble applying my code example to your situation. You
may find the following code easier to use:
procedure ProcessTables(AContainer : TComponent);
var
ATable : TABSTable;
i : Integer;
begin
for i := 0 to AContainer.ComponentCount - 1 do begin
if AContainer.Components[i] is TABSTable then begin
ATable := TABSTable(AContainer.Components[i]);
// Now that you have a reference to a dataset in ACDS, you can
// do whatever you like with it. For example
if ATable.Active then
ATable.Close;
end;
end;
end;
Note that this is a stand-alone procedure, not a procedure of a particular
form or datamodule. Instead, when you use this procedure, you call it passing
whatever form or datamodule contains the TABSTables you want to work with as the
AContainer parameter, like so
if Assigned(DataModule1) then
ProcessTables(DataModule1);
or
if Assigned(Form1) then
ProcessTables(Form1);
However, the downside of doing it this was is that it is trickier to specify which tables, if any, to leave open, because AContainer, being a TComponent, will not have any member tables.
Btw, your task would probably be easier if you could iterate through the tables in a TABSDatabase. However I've looked at its online documentation but can't see an obvious way to do this; I've asked the publishers, ComponentAce, about this but haven't had a reply yet.

Delphi ADO : Locate with dataset filter on bug

I have as simple query, which returns following rows:
Name Value
Peter 1
Peter 2
Peter 3
John 1
John 2
Applying filter:
ADO.Filter := 'Name="John"';
ADO.Filtered := True; // Now, its only 2 rows in dataset
ADO.Locate('Value', 2);
Cursor should point to "John 2", but it points to "Peter 2" (which being filtered out by filter). And locate returns True.
Tried on Delphi 7, Rad studio XE 6. It seems that this error is living there for the last 15 years
Any solution ?
The problem with TCustomADODataSet.Locate is that it's internally using Recordset.Clone and trying to locate a record in the cloned recordset, without setting the filter to the cloned recordset (see ADODB TCustomADODataSet.LocateRecord).
From the Remarks in the docs:
The Filter property of the original Recordset, if any, will not be
applied to the clone. Set the Filter property of the new Recordset
to filter the results. The simplest way to copy any existing Filter
value is to assign it directly, as follows. rsNew.Filter =
rsOriginal.Filter The current record of a newly created clone is set
to the first record.
I have been using my own simple Locate function for filtered ADO DataSets: Basically, storing the current bookmark, moving to the first record and iterating the DataSet until it found a match. If no match found restore the previous bookmark.
Bellow is a really limited implementation that worked for me (a lot of todo tho for a perfect solution):
class function TData.Locate(DataSet: TDataSet; const KeyFields: string;
const KeyValues: Variant; Options: TLocateOptions): Boolean;
{ a very simple Locate function - todo: TLocateOptions & multiple KeyFields/KeyValues }
var
BM: TBookmarkStr;
begin
Result := False;
if DataSet.IsEmpty then Exit;
BM := DataSet.Bookmark;
DataSet.DisableControls;
try
DataSet.First;
while not DataSet.Eof do
begin
if DataSet.FieldByName(KeyFields).Value = KeyValues then
begin
Result := True;
Break;
end;
DataSet.Next;
end;
if not Result then DataSet.Bookmark := BM;
finally
DataSet.EnableControls;
end;
end;
Another option is to patch ADODB.pas TCustomADODataSet.LocateRecord and set the FLookupCursor.Filter to match the current dataset filter. This option is acceptable as long as you patch ADODB.pas as a new copy placed in your project folder.
Yet another option is to use TCustomADODataSet.Recordset.Find method (See also: How to use a RecordSet.Find with TADOQuery?).
Try this:
ADO.Filter := 'Name=' + QuotedStr('John');
ADO.Filtered := True; // Now, its only 2 rows in dataset
ADO.Locate('Value',2,[]);
The third parameter of the Locate function is for locate options, which I've left empty in the example above. You can read up on Locate here
http://docwiki.embarcadero.com/RADStudio/XE7/en/Using_Locate and here http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TDataSet.Locate

How to read a BLOB(Text) field using TpFIBQuery components from FibPlus

I am using Delphi 7 with the FibPlus components . One of them being TpFIBQuery.
I am loading data from a table with the generic
select * from TableName where Key = 1
One of the fields returned is of type BLOB(Text).
I can't seem to get the value into a string list informatie using either of the following 3 ways :
Informatie.Text := FieldByName('Informatie').AsString // Returns the string 'BLOB'
Informatie.Text := BlobAsString('Informatie') // Returns ''
BlobToStrings('Informatie',Informatie) // Returns ''
I have confirmed using Database Workbench that the field in the table indeed contains the saved text.
Anyone ?
usualy, i do like this
var
sl: TStrings; // blob IS NOT string!
ms: TMemoryStream;
begin
sl := TStringList.Create;
ms := TMemoryStream.Create;
try
q.FieldByName('x').SaveToStream(ms);
ms.Position := 0;
sl.LoadFromStream(ms);
// do what ever you want with sl here
// and here too
finally
sl.Free;
ms.Free;
end; // try..finally
end;
note that q is your TpFibQuery object.
also
select * from table is bad bad practice.
that habit eventually will lead you continuous headache.
After trying the solution of #jiang, which produced the same error, I finally have found the culprit.
Turns out it was an error due to my part ( it usually is, you just have to find it ).
Turns out I had set the read transaction to False sometime during the processing/reading of the fields of the original query.
I perform a lookup in another table to get the description of a integer value in the query.
This lookup query uses the same read transaction , and sets this transaction to False after looking up the description.
AFter returning to the original query, reading integer and string fields pose no problem (although the read transaction has been set to False), but reading a BLOB field into a string with the ...AsString method produces an error or returns 'BLOB'.
Obviously I need to set the read transaction to True at the start of the read actions and set it to False after ALL read transactions. This is the major change when you convert a Paradox BDE application to a Firebird of Sqlserver application.
Anyway, I am glad I have found the solution. Hopefully it will help others too.

Resources