如何在 Delphi XE5 中使用 Firedac FDConnection 组件调用带有 void return 的函数?
How to call a function with void return using Firedac FDConnection Component in Delphi XE5?
我最近开始使用 Delphi XE5 中 FDConnection 组件的 [ExecSQLScalar]
1 and [ExecSQL]
2 方法。不需要构建 Dataset 对象非常方便,例如 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()');
首先 运行,我收到以下错误:
Project TFDConnectionDEMO.exe raised exception class
EPgNativeException with message '[FireDAC][Phys][PG][libpq] ERROR:
fail'.
在第二个 运行 上,我收到违规访问错误:
Project TFDConnectionDEMO.exe raised exception class $C0000005 with
message 'access violation at 0x00000000: read of address 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);
...
忽略之前的错误并重试,显示另一个错误...
Project TZConnectionDEMO.exe raised exception class EFDException with
message '[FireDAC][DatS]-24. Row is not nested'.
搜索,我没有找到对此错误的响应。我认为我的错误是使用 FDConnection 组件的 ExecSQLScalar 函数调用银行 raise_exception 函数。所以我尝试使用 FDConnection.ExecSQL
并且正如我想象的那样,如果参数中有 SELECT
子句,则不能使用它。
有没有更好的方法用FDConnection.ExecSQL调用void return函数? BUG会在组件中吗?还是说这样的电话不对?
使用 ExecSQLScalar is fine in this case. This is certainly a bug (which was already fixed, at least in Delphi 10.2.3). As you've correctly pointed out, the problem is in releasing a table storage object instance held by the FExecSQLTab field by using FDFree 程序。
我没有 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;
问题是,当 SQL 命令执行在存储 table 定义阶段(oCmd.Define)引发异常时,引用之前销毁的存储 table 对象实例(通过 FDFree)仍然存储在 FExecSQLTab 字段中(作为悬挂指针)。
然后,当以这种方式执行不同的命令时,FDFree 过程仅为该悬挂指针调用。因此访问冲突。
更正此问题的方法是更换行,例如通过:
FDFree(FExecSQLTab);
作者:
FDFreeAndNil(FExecSQLTab);
这是在后来的 Delphi 版本中完成的。
我最近开始使用 Delphi XE5 中 FDConnection 组件的 [ExecSQLScalar]
1 and [ExecSQL]
2 方法。不需要构建 Dataset 对象非常方便,例如 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()');
首先 运行,我收到以下错误:
Project TFDConnectionDEMO.exe raised exception class EPgNativeException with message '[FireDAC][Phys][PG][libpq] ERROR: fail'.
在第二个 运行 上,我收到违规访问错误:
Project TFDConnectionDEMO.exe raised exception class $C0000005 with message 'access violation at 0x00000000: read of address 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);
...
忽略之前的错误并重试,显示另一个错误...
Project TZConnectionDEMO.exe raised exception class EFDException with message '[FireDAC][DatS]-24. Row is not nested'.
搜索,我没有找到对此错误的响应。我认为我的错误是使用 FDConnection 组件的 ExecSQLScalar 函数调用银行 raise_exception 函数。所以我尝试使用 FDConnection.ExecSQL
并且正如我想象的那样,如果参数中有 SELECT
子句,则不能使用它。
有没有更好的方法用FDConnection.ExecSQL调用void return函数? BUG会在组件中吗?还是说这样的电话不对?
使用 ExecSQLScalar is fine in this case. This is certainly a bug (which was already fixed, at least in Delphi 10.2.3). As you've correctly pointed out, the problem is in releasing a table storage object instance held by the FExecSQLTab field by using FDFree 程序。
我没有 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;
问题是,当 SQL 命令执行在存储 table 定义阶段(oCmd.Define)引发异常时,引用之前销毁的存储 table 对象实例(通过 FDFree)仍然存储在 FExecSQLTab 字段中(作为悬挂指针)。
然后,当以这种方式执行不同的命令时,FDFree 过程仅为该悬挂指针调用。因此访问冲突。
更正此问题的方法是更换行,例如通过:
FDFree(FExecSQLTab);
作者:
FDFreeAndNil(FExecSQLTab);
这是在后来的 Delphi 版本中完成的。