如何在Delphi XE5中使用Firedac FDConnection组件调用具有无效返回的函数?

时间:2018-07-14 13:30:19

标签: database postgresql delphi firedac

我最近开始在Delphi XE5中使用FDConnection组件的[ExecSQLScalar] 1[ExecSQL] 2方法。无需仅为简单查询或执行而构建FDQuery之类的Dataset对象,就非常方便。 但是,在执行带有void返回的函数时,我有一个奇怪的问题,该函数具有内部验证,可以在其中生成异常。我正在使用Postgres数据库。

CREATE FUNCTION can_be_exception()
  RETURNS void AS
$$
BEGIN
    RAISE EXCEPTION E'fail';
END;
$$
  LANGUAGE plpgsql STABLE;

在delphi中,我调用ExecSQLScalar函数...

FDConnection1.ExecSQLScalar('select 1');
FDConnection1.ExecSQLScalar('select can_be_exception()');

第一次运行时,出现以下错误:

  

项目TFDConnectionDEMO.exe引发异常类   带有消息“ [FireDAC] [Phys] [PG] [libpq]的EPgNativeException错误:   失败”。

第二次运行时,我收到“违规访问”错误:

  

项目TFDConnectionDEMO.exe引发了带有$ C0000005的异常类   消息“访问冲突在0x00000000:读取地址0x00000000”。

显然,该错误发生在单元FireDAC.Comp.Client的下面一行中

function TFDCustomConnection.ExecSQLScalar(const ASQL: String;
  const AParams: array of Variant; const ATypes: array of TFieldType): Variant;
var
  oCmd: IFDPhysCommand;
begin
  oCmd := BaseCreateSQL;
  try
    if BasePrepareSQL(oCmd, ASQL, AParams, ATypes) or (FExecSQLTab = nil) then begin
      FDFree(FExecSQLTab);

...

忽略上一个错误,然后重试,则显示另一个错误...

  

项目TZConnectionDEMO.exe引发了带有以下内容的异常类EFDException   消息'[FireDAC] [DatS] -24。行未嵌套”。

在搜索中,我没有找到对此错误的回应。我发现我的错误是使用FDConnection组件的ExecSQLScalar函数调用bank raise_exception函数。因此,我尝试使用FDConnection.ExecSQL,但正如我想象的那样,如果参数中有SELECT子句,则无法使用它。

是否有更好的方法使用FDConnection.ExecSQL调用带有空返回的函数? BUG会出现在组件中吗?还是打这样的电话不正确?

1 个答案:

答案 0 :(得分:4)

在这种情况下,使用ExecSQLScalar很好。这肯定是一个错误(至少在Delphi 10.2.3中已得到修复)。正如您已经正确指出的那样,问题在于使用FDFree过程释放由 FExecSQLTab 字段保存的表存储对象实例。

我没有Delphi XE5源代码,但也许您可以在内部看到类似的内容(关于发生的事情的评论由我添加):

if BasePrepareSQL(oCmd, ASQL, AParams, ATypes) or (FExecSQLTab = nil) then
begin
  FDFree(FExecSQLTab); { ← directly calls destructor if object is not nil }
  FExecSQLTab := oCmd.Define; { ← no assignment if command execution raises exception }
end;

问题在于,当在存储表定义阶段( oCmd.Define )内执行SQL命令引发异常时,对先前销毁的存储表对象实例(由FDFree的引用)仍然存储在 FExecSQLTab 字段(悬空指针)。

然后,当以这种方式执行其他命令时,仅针对该悬空指针调用了FDFree过程。因此出现访问冲突。

更正此问题的方法是替换行,例如通过:

FDFree(FExecSQLTab);

作者:

FDFreeAndNil(FExecSQLTab);

这是在后来的Delphi版本中完成的。