简体   繁体   English

有没有办法让 Delphi 的 FireDAC 识别 FireDAC 生成的 PostgreSQL 位置参数?

[英]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.我正在使用本机 FireDAC Postgres 驱动程序从 FireDAC 到 PostgreSQL 11 执行带有命名参数的查询。 During the prepare statement FireDAC converts the named parameters to positional parameters, which is correct.在准备语句期间,FireDAC 将命名参数转换为位置参数,这是正确的。 However, if I then attempt to assign values to those parameters FireDAC throws an "Argument out of range" exception.但是,如果我随后尝试为这些参数赋值,FireDAC 会抛出“参数超出范围”异常。 It appears that FireDAC does not recognize the positional parameters that it generated. FireDAC 似乎无法识别它生成的位置参数。 For example, if the original SQL text looked something like this:例如,如果原始 SQL 文本如下所示:

SELECT * FROM account WHERE accountid = :accid;

upon calling the Prepare method of the FDQuery, FireDAC converted this query into this:在调用 FDQuery 的 Prepare 方法后,FireDAC 将此查询转换为:

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.其中 strID 是一个字符串值,而 accountid 是一个文本字段。 Furthermore, if I use something like the following, it returns 0.此外,如果我使用类似下面的内容,它会返回 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?如何让 FireDAC 识别它生成的位置参数?


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.实际发生的是,在我们的框架中,我们有一组例程将值分配给 FireDAC 宏,然后我们通过准备查询并读取 FDQuery 的 Text 属性来生成 SQL 语句。 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.然后该 SQL 语句被分配给另一个 FDQuery(也是动态创建的)的 SQL.Text 属性,并且在那里查询失败。 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:我认为可能是由于 FireDAC 宏扩展,所以我在实例化 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.我猜 FireDAC 根本无法识别 $1 是 PostgreSQL 中的有效位置参数。

You need to put the symbolic parameter identifier (in your case :accid ) in the SQL.Text string, not the positional code ( $1 )您需要将符号参数标识符(在您的情况下:accid )放在 SQL.Text 字符串中,而不是位置代码( $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当然,FireDac 没有看到冒号,所以不知道有参数可以创建

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;首先,person 表中的 11 条记录返回了我的姓氏; in the second, the Argument Out Of Range error is generated, since there is no parameter specified in the SQL text.在第二个中,会生成 Argument Out Of Range 错误,因为 SQL 文本中没有指定参数。 Note: I am accessing a MySQL database, but the issue here is Delphi & FireDac preprocessing of the code to send to the db server.注意:我正在访问 MySQL 数据库,但这里的问题是 Delphi 和 FireDac 预处理要发送到数据库服务器的代码。

Ok, there may be a way to fix this issue using FireDAC properties, but I haven't found it yet.好的,可能有一种方法可以使用 FireDAC 属性来解决此问题,但我还没有找到。 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.但是,对于这种特殊情况,SQL 在一种方法中准备好,然后从另一种方法中分配给不同的 FDQuery,我找到了答案。 Since PostgreSQL permits named parameters to use numerals, such as :1, what I did was to replace the $ characters with : characters.由于 PostgreSQL 允许命名参数使用数字,例如 :1,我所做的是将 $ 字符替换为 : 字符。 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.而且,是的,如果您的查询包含多个命名参数实例,FireDAC 会用相同的数字替换它。

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.如果其他人提出了使用 FireDAC 属性的解决方案,我会选择该答案作为正确答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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