使用 SQLDirect 组件时 TClientDataset.ApplyUpdates 失败并显示 'SQL not supported'

TClientDataset.ApplyUpdates fails with 'SQL not supported' when using SQLDirect components

从 DelphiXE2 更新到 Delphi Seattle 10 Update 1 后,我们在使用 SQLDirect components 版本 6.4 时执行 TClientDataSet ApplyUpdates 调用时遇到问题。 5

我做了一个小测试应用程序。
组件:TDBGrid -> TDataSource -> TClientDataSet -> TDataSetProvider -> TSDQuery -> TSDDatabase
查询是 select * from tt_plan_task,提供者有 UpdateMode=upWhereAll,客户端数据集有 IndexFieldName=tt_plan_task_id
我们修改字段 tt_plan_task.tt_prj.

的一个值

当执行ApplyUpdates(0)时,代码追踪到TSQLResolver.InternalDoUpdate in DataSnap.Provider (for UpdateKind=ukModify).
在那里,生成的 SQL 和参数看起来符合预期。
然后代码跳转到

procedure TSQLResolver.DoExecSQL(SQL: TStringList; Params: TParams);
var
  RowsAffected: Integer;
begin
  RowsAffected := (Provider.DataSet as IProviderSupportNG).PSExecuteStatement(SQL.Text, Params);

此语句崩溃并出现错误 SQL not supported(Data.DBConsts 中的 SProviderSQLNotSupported)

但由于这是一个接口,我无法进一步追踪。
我不知道如何从这里着手解决这个问题。 有什么建议如何做,或者会发生什么?

附加信息:

[注意:自我回答这个问题是因为我花了很长时间才弄明白。可能对其他人有帮助.]

Googling the error message I stumbled upon this post SProviderSQLNotSupported on DOA with Delphi XE3 用户遇到与 Oracle Direct Access 类似的问题时。
它建议:

1) 以牺牲性能为代价设置TDataSetProvider.ResolveToDataSet=true。这适用于测试应用程序。

2) PSExecuteStatement 可能未在第 3 方软件中实现(覆盖),其来自 data.db 的基本过程开始:

function TDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams): Integer;
begin
  Result := 0;
  DatabaseError(SProviderSQLNotSupported, Self);
end;

第二种情况就是这样。 SQLDirect 代码确实有 TSDDataSet 方法的覆盖

function PSExecuteStatement(const ASQL: string; AParams: TParams; {$IFDEF SD_CLR} var ResultSet: TObject {$ELSE} {$IFDEF SD_VCL17} var ResultSet: TDataSet {$ELSE} ResultSet: TSDPtr = nil {$ENDIF} {$ENDIF}): Integer; overload; override;

在 Delphi 西雅图 resolves/compiles 下覆盖:

function PSExecuteStatement(const ASQL: string; AParams: TParams; var ResultSet: TDataSet): Integer; overload; override;

但是

有 none
function TDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams): Integer;

解决方法是加一个:

在SDEngine.pas中TSDDateSet的保护方法中,更新如下:

function PSExecuteStatement(const ASQL: string; AParams: TParams): Integer; overload; override; // New override
function PSExecuteStatement(const ASQL: string; AParams: TParams; {$IFDEF SD_CLR} var ResultSet: TObject {$ELSE} {$IFDEF SD_VCL17} var ResultSet: TDataSet {$ELSE} ResultSet: TSDPtr = nil {$ENDIF} {$ENDIF}): Integer; overload; override;

实施:

function TSDDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams): Integer;  // JD 20-4-2016
var
  ds: TDataSet;
begin
  ds := nil;
  Result := InternalPSExecuteStatement( ASQL, AParams, false, ds );
end;

我在 TAsaDataSet + TDataSetProvider + TClientDataSet 从 Delphi 2009 年迁移到西雅图 10 时遇到了类似的问题 我们解决了更改一些属性的问题:

AsaDataSet.readyOnly := true;
DataSetProvider.ResolveToDataSet := True;

然后在我们代码的某个点,我们确保 TAsaDataSet 能够 table。

TAsaDataSet .Close;
TAsaDataSet .Session  := DmConection.AsaConnection;
TAsaDataSet .SQL.Text := 'SELECT * FROM dba.table_name';
TAsaDataSet .Open;

我们还注意到从 2009 年到西雅图的方法签名 OnGetTableName 来自: (Sender: TObject; DataSet: TDataSet; var TableName: WideString); 至: (Sender: TObject; DataSet: TDataSet; var TableName: string);

希望对你有所帮助。