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

Database 如何在Delphi XE5中使用Firedac FDConnection组件调用具有void返回的函数?,database,postgresql,delphi,firedac,Database,Postgresql,Delphi,Firedac,我最近开始在Delphi XE5中使用FDConnection组件的[ExecSQLScalar]和[ExecSQL]方法。不需要构建数据集对象(如仅用于简单查询或执行的FDQuery)非常方便。 然而,当执行具有void return的函数时,我遇到了一个奇怪的问题,该函数具有内部验证,可以在其中生成异常。我正在使用Postgres数据库 CREATE FUNCTION can_be_exception() RETURNS void AS $$ BEGIN RAISE EXCEPT

我最近开始在Delphi XE5中使用FDConnection组件的
[ExecSQLScalar]
[ExecSQL]
方法。不需要构建数据集对象(如仅用于简单查询或执行的FDQuery)非常方便。 然而,当执行具有void return的函数时,我遇到了一个奇怪的问题,该函数具有内部验证,可以在其中生成异常。我正在使用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错误: 失败'

在第二次运行时,我得到一个违规访问错误:

Project TFDConnectionDEMO.exe引发异常类$C0000005 消息“0x00000000处的访问冲突:读取地址0x00000000”

显然,错误发生在unit
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_异常函数。因此,我尝试使用
FDConnection.ExecSQL
,正如我想象的那样,如果参数中有
SELECT
子句,则不能使用此选项

是否有更好的方法使用FDConnection.ExecSQL调用void return函数?组件中会有BUG吗?还是说打这种电话是不对的?

在这种情况下,使用就可以了。这当然是一个bug(至少在Delphi10.2.3中已经修复)。正如您正确指出的,问题在于使用过程释放由FExecSQLTab字段持有的表存储对象实例

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

如果BasePrepareSQL(oCmd、ASQL、AParams、ATypes)或(FExecSQLTab=nil),则
开始
FDFree(FExecSQLTab);{ ← 如果对象不是nil},则直接调用析构函数
FExecSQLTab:=oCmd.Define;{ ← 如果命令执行引发异常,则不进行赋值}
结束;
问题是,当SQL命令执行在存储表定义阶段(oCmd.Define)引发异常时,对以前销毁的存储表对象实例(by)的引用仍然存储在FExecSQLTab字段中(作为悬挂指针)

然后,当以这种方式执行另一个命令时,只为那个悬空的指针调用该过程。因此存在访问冲突

解决此问题的方法是更换管路,例如:

FDFree(FExecSQLTab);
作者:

FDFreeAndNil(FExecSQLTab);

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

我无法在东京用PostgreSQL 10.1重现这一点。但我想我知道为什么。在
FDFree(FExecSQLTab)
之后似乎仍然有一个悬空指针。东京有
FDFreeAndNil(FExecSQLTab)
。太棒了!我复制了一份文件,并按照你说的做了更改。。。它按预期工作。非常感谢你。显然,embarcadero对以前版本的IDE中应用程序的稳定性不感兴趣。欢迎使用!对于EBMT来说,不关心修复程序向旧版本的传播是很典型的。@Victoria:捕捉得好!为什么不把它贴出来作为答案?@Victoria:很高兴你有时间这么做+1附注;这并不像人们所期望的那样明显,但PostgreSQL的函数returning
void
只返回一个字段的元组,该字段的值为
void
(有点
NULL
),因此我说
ExecSQLScalar
很好。