【问题标题】:Setting Oracle CLOB field to null fails with ORA 22275将 Oracle CLOB 字段设置为 null 失败并出现 ORA 22275
【发布时间】:2019-02-03 07:07:09
【问题描述】:

我们有一个(!)客户将 Oracle CLOB 字段设置为 NULL 失败,原因是

[FireDAC][Phys][Ora] ORA 22275 - Invalid LOB locator specified

发送到数据库的查询*

update tt_hrs set
 TT_INFO = ?
where
 TT_HRS_ID = ?

Params:
0 -  : <NULL>
1 -  : 276727

通过 FireDAC 查询数据集显示 lDataset.Fields[i].DataType 字段 TT_HRSftWideMemo

我在互联网上找到的许多东西都与更新您使用的 CLOBS 的“旧方式”(Oracle 8.0.5 IIRC)有关

UPDATE ClobTable
SET
  Value = EMPTY_CLOB()
WHERE
  Id = :Id
RETURNING
  Value
INTO
  :Value

但 AFAIK 不再需要此类语句。

在 SQLPLUS 中,我可以在我们自己的 Oracle 12c 数据库上毫无问题地执行这些操作,因此 the difference between EMPTY_CLOB() and NULL 似乎无关紧要:

update tt_hrs set tt_info='test' where tt_hrs_id=276727;
update tt_hrs set tt_info=NULL where tt_hrs_id=276727;
update tt_hrs set tt_info=empty_clob() where tt_hrs_id=276727;
  • 如错误消息所示,我们在 Delphi Tokyo 10.2.2 中使用 FireDAC 32 位 Windows 应用。
  • 该字段没有 NOT NULL 约束,它不在索引中, 没有触发器。
  • 客户端使用 OracleDB12 Release 1。
  • 我们的更新代码由 FireDAC 从 TClientDataSet 生成 连接到用户编辑的网格。

问题

Oracle 设置中有什么可以解释这种行为吗?
也许他们设置了一些“兼容模式”来支持旧应用程序之类的……我对 Oracle 还不够熟悉。

注意:这绝不会与 2 字节字符 I reported earlier 的问题有关吗?

在这里抓着稻草......

* 我们可以记录这一点,因为我们有一个 TDataSetProvider 后代记录正在被覆盖的 DoBeforeExecute 中发送的内容。

【问题讨论】:

    标签: oracle delphi clob firedac delphi-10.2-tokyo


    【解决方案1】:

    我们一直无法找到造成这种情况的确切原因,但找到了解决方法。

    受影响的代码都通过TClientDataSet 运行,我们已经使用TDataSetProvider 后代和覆盖DoBeforeExecute 来记录发送到数据库的实际SQL(用于调试目的)。这在我们的代码中调用了一个日志记录过程,我们在其中添加了以下内容:

    // Force parameter to ftOraClob when NULL:
    if TFDQuery(TDataSetProvider(Sender).Dataset).Connection.Params.DriverID = S_FD_OraId then
      for i := 0 to params.count-1 do
        if (params[i].DataType = ftMemo) and (VarIsNull(params[i].Value) or VarIsClear(params[i].Value)) then
           params[i].DataType := ftOraClob;
    

    这会强制Datasnap.Provider.pas 中的TSQLResolver.GenUpdateSQL 中的AddField 子例程遵循路径:

    else if UseFieldInUpdate(Field) then
    begin
      Result := True;
      if (Field.DataType = ftOraClob) and (not InformixLob) then
      begin
        NoParam := True;
        if InObject then
          SQL.Add(string.Format(' %s.%s = EMPTY_CLOB(),', [Alias, QuoteFullName(Field.FullName, PSQLInfo(Tree.Data).QuoteChar),   { Do not localize }
            Field.FullName]))
    

    因此写入= EMPTY_CLOB() 而不是= NULL,Oracle DB 很乐意接受。

    注意:我们测试 ftMemo 是因为我们有一个映射规则处于活动状态

    with AConnection.FormatOptions.MapRules.Add do 
    begin
      SourceDataType := dtWideHMemo;
      TargetDataType := dtMemo;
    end;
    

    (见https://stackoverflow.com/a/47904988/512728
    如果您没有这个,则需要对ftWideMemo 进行测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      • 2012-05-04
      • 2016-12-04
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      相关资源
      最近更新 更多