Сбой установки поля Oracle CLOB в ноль с ORA 22275 - PullRequest
0 голосов
/ 28 августа 2018

У нас есть один (!) Клиент, для которого установка поля 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_HRS равно ftWideMemo.

Многие вещи, которые я нахожу в Интернете, связаны со «старым способом» (Oracle 8.0.5 IIRC) обновления CLOBS, где вы использовали

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

но AFAIK такого рода заявления больше не требуются.

В SQLPLUS я могу выполнить их без проблем в нашей собственной базе данных Oracle 12c, поэтому разница между EMPTY_CLOB() и 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;
  • Как показывает сообщение об ошибке, мы используем FireDAC в Delphi Tokyo 10.2.2. 32-разрядное приложение для Windows.
  • На поле нет ограничения NOT NULL, оно отсутствует в индексе, триггеров нет.
  • Клиент использует OracleDB12 Release 1.
  • Наш код обновления генерируется FireDAC из TClientDataSet подключен к сетке, которую пользователь редактирует.

Вопрос

Есть ли в настройках Oracle что-нибудь, что могло бы объяснить такое поведение?
Может быть, они установили какой-то «режим совместимости» для поддержки старых приложений или что-то ... Я недостаточно знаком с Oracle.

Примечание: это не было бы случайно связано с проблемой с 2-байтовыми символами Я сообщал ранее ?

Сжимая здесь соломинку ...

* Мы можем регистрировать это, потому что у нас есть потомок TDataSetProvider, который регистрирует то, что отправляется в переопределенном DoBeforeExecute.

1 Ответ

0 голосов
/ 24 сентября 2018

Мы никогда не могли найти точную причину этого, но нашли обходной путь.

Весь уязвимый код работает через 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;

Это заставляет подпрограмму AddField в TSQLResolver.GenUpdateSQL в Datasnap.Provider.pas следовать по пути:

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.

Примечание: мы проверяем ftMemo, потому что у нас есть активное правило отображения, которое говорит

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

(см. https://stackoverflow.com/a/47904988/512728)
Если у вас этого нет, потребуется тестирование на ftWideMemo.

...