简体   繁体   中英

Is there a way to make Delphi's FireDAC recognize PostgreSQL positional parameters that FireDAC generated?

I am executing queries with named parameters from FireDAC to PostgreSQL 11 using the native FireDAC Postgres driver. During the prepare statement FireDAC converts the named parameters to positional parameters, which is correct. However, if I then attempt to assign values to those parameters FireDAC throws an "Argument out of range" exception. It appears that FireDAC does not recognize the positional parameters that it generated. For example, if the original SQL text looked something like this:

SELECT * FROM account WHERE accountid = :accid;

upon calling the Prepare method of the FDQuery, FireDAC converted this query into this:

SELECT * FROM account WHERE accountid = $1;

But when I try to assign the value to the parameter, I get the error. The assignment looks something like this:

FDQuery1.Params[0].AsString = strID;

where strID is a string value and accountid is a text field. Furthermore, if I use something like the following, it returns 0.

ShowMessage( IntToStr( FDQuery1.Params.Count ) );

I've simplified this code significantly, but the issues are the same. How do I get FireDAC to recognize the positional parameters that it generated?


Update : As I mentioned, the above code is greatly simplified. What is actually happening is that in our framework we have one set of routines that assign values to FireDAC macros, and then we generate the SQL statement by preparing the query and then reading the FDQuery's Text property. That SQL statement then gets assigned to the SQL.Text property of another FDQuery (also dynamically created), and it is there that the query fails. So, here is a very simple example of what is happening internally in the code:

var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create( nil );
  Query.Connection := PGConnection;
  // In reality, the SQL statement below was generated earlier,
  // from a function call where the SQL was created by the FireDAC
  // SQL preprocessor, as opposed to being a literal expression
  Query.SQL.Text := 'SELECT * FROM tablename WHERE field2 = $1;';
  Query.Params[0].AsString := '4'; // BANG! Argument out of range

I thought it might be due to FireDAC macro expansion, so I added the following two lines after instantiating the FDQuery:

Query.ResourceOptions.MacroCreate := False;
Query.ResourceOptions.MacroExpand := False;

Nope. That did help either. I am guessing that FireDAC simply doesn't recognize that $1 is a valid positional parameter in PostgreSQL.

You need to put the symbolic parameter identifier (in your case :accid ) in the SQL.Text string, not the positional code ( $1 )

I just tested these two variations. The first works, the second doesn't.

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = :lname;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

The next tries to use positional. Of course, FireDac doesn't see a colon, so doesn't know there is a parameter to create

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = $1;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

In the first, 11 records in the person table are returned with my last name; in the second, the Argument Out Of Range error is generated, since there is no parameter specified in the SQL text. Note: I am accessing a MySQL database, but the issue here is Delphi & FireDac preprocessing of the code to send to the db server.

Ok, there may be a way to fix this issue using FireDAC properties, but I haven't found it yet. However, for this particular situation, where the SQL is prepared in one method, and then assigned to a different FDQuery from within another method, I have found an answer. Since PostgreSQL permits named parameters to use numerals, such as :1, what I did was to replace the $ characters with : characters. As in

var
  SQLStmt: String;
  FDQuery: TFDQuery;
begin
  SQLStmt :=  'SELECT * FROM tablename WHERE field2 = $1;';
  FDQuery := TFDQuery.Create( nil );
  FDQuery.Connection := FDConnection1;
  FDQuery.SQL.Text := SQLStmt.Replace( '$', ':' );
  FDQuery.Params[0].AsString := 'SomeValue'); // Works!
  ...

And, yes, if your query includes more than one instance of a named parameter, FireDAC replaces it with the same numeral.

If I find another solution I will post it. If someone else comes up with a solution using FireDAC properties, I will select that answer as the correct answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM