database - 如何在 Delphi XE5 中使用 Firedac FDConnection 组件调用具有 void 返回的函数?
问题描述
最近开始在Delphi XE5中使用FDConnection组件的[ExecSQLScalar]
1和[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()');
第一次运行时,我收到以下错误:
项目 TFDConnectionDEMO.exe 引发异常类 EPgNativeException,消息为“[FireDAC][Phys][PG][libpq] 错误:失败”。
在第二次运行时,我收到一个 Violation Access 错误:
项目 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 函数调用银行 raise_exception 函数。所以我尝试使用并且正如我想象的那样,如果参数中有子句,FDConnection.ExecSQL
则不能使用它。SELECT
有没有更好的方法来使用 FDConnection.ExecSQL 调用具有 void 返回的函数?BUG 会在组件中吗?还是打这种电话不正确?
解决方案
在这种情况下使用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 版本中完成的。
推荐阅读
- java - 将一个占位符替换为 Java 字符串中的另一个占位符
- java - 从数据类型列表中选择最合适的数据类型
- csv - 删除 CSV 文件中的最后一行
- python - 为什么poetry安装windows只依赖macOS和linux?
- javascript - 代理 ??= new https.Agent({ ...this.client.options.http.agent, keepAlive: true });
- c# - C# 模式匹配与属性的操作值
- r - 如何将所有因子水平折叠成R中的新水平
- sql - SQL - 仅将日期时间转换为月份(全名)年份
- react-native-android - 错误:错误:无法解析模块操作系统
- c# - 具有 Name 属性的 HttpCookies 在 MVC 中不起作用