Это ошибка PL/SQL?

Здесь выдержка из некоторого кода PL/SQL, который, как мне кажется, демонстрирует ошибку PL/SQL:

if guid_ is null then
   dbms_output.put_line('guid_ is null: ' || guid_);
end if;

Когда эти строки выполняются, он печатает

guid_ is null: 07D242FCC55000FCE0530A30D4928A21

Я нахожусь на Oracle 11R2

select * from v$version;

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

Я могу воспроизвести это со следующими типами и анонимным блоком. Извините за длину, но я считаю, что больше не могу ее укоротить:

create type tq84_t as table of varchar2(32);
/

create type tq84_o as object (
  dummy number(1),

  not final member procedure clear

) not final;
/

show errors

create type tq84_d under tq84_o (

  g varchar2(32),
  constructor function tq84_d return self as result,

  overriding member procedure clear


);
/
show errors


create package tq84_h as

    t tq84_t;

end tq84_h;
/
show errors



create package body tq84_h as
begin

  t := tq84_t();
end;
/
show errors

create type body tq84_o as

   member procedure clear is begin
      null;
   end clear;

end;
/

create type body tq84_d as

  constructor function tq84_d return self as result is
  begin

      g := sys_guid;
      return;

  end tq84_d;

  overriding member procedure clear is begin

      tq84_h.t.extend;
      tq84_h.t(tq84_h.t.count) := g;

      g := null;

  end clear;

end;
/
show errors


declare

  b  tq84_o;  -- Change to tq84_d ...

  guid_ varchar2(32);

begin

  b := new tq84_d;

  guid_ := treat(b as tq84_d).g;

  b.clear;

  if guid_ is null then
     dbms_output.put_line('guid_ is null: ' || guid_);
  end if;


end;
/

drop type tq84_t;
drop type tq84_d;
drop type tq84_o;
drop package tq84_h;

Обратите также внимание, что при изменении b tq84_o на b tq84_d ошибка больше не возникает.

Может ли кто-нибудь проверить, происходит ли это в других системах?

Ответ 1

Для меня это ошибка. В IF переменная guid_ не обрабатывается так же, как в конкатенации строк для put_line. То, что я нахожу странным, заключается в том, что перед выражением b.clear работает is null:

declare
  b  tq84_o;  -- Change to tq84_d ...
  guid_ varchar2(32);
begin
  b := new tq84_d;
  guid_ := treat(b as tq84_d).g;

  if guid_ is null then
     dbms_output.put_line('before clear: guid_ is  null: ' || guid_);
  end if;

  b.clear;

  if guid_ is null then
     dbms_output.put_line('after clear: guid_ is null: ' || guid_);
  end if;
end;
/

Вывод:

after clear: guid_ is null: 07D43ACB728A2173E054A0481C66CF28

Я обход проблемы при возврате GUID из функции:

declare
  b  tq84_o;  -- Change to tq84_d ...
  guid_ varchar2(32);
  function get_guid 
  return varchar2 is 
  begin 
    return treat(b as tq84_d).g;
  end;  
begin
  b := new tq84_d;
  guid_ := get_guid; -- treat(b as tq84_d).g;

  if guid_ is null then
     dbms_output.put_line('before clear: guid_ is  null: ' || guid_);
  end if;

  b.clear;

  if guid_ is null then
     dbms_output.put_line('after clear: guid_ is null: ' || guid_);
  end if;
end;
/

Вышеприведенный код не входит ни в один из if guid_ is null. Поэтому мне это доказывает:

Это ошибка.

Ответ 2

Он выглядит как ошибка Oracle. Я вижу то же самое в нашем экземпляре Oracle Enterprise 11.2.0.3.0 (64 бит).

После небольшой игры довольно ясно, что ошибка находится в состоянии if: guid_ is null оценивает значение true, даже если оно содержит значение.


Мой предыдущий комментарий относительно else больше не кажется истинным. Либо я случайно поменял тест script каким-то образом, либо ошибка несколько непредсказуема.


Кажется, я уверен, что выполнение сценария else должно выполняться следующим образом:

  • Измените if на:

if guid_ is null then dbms_output.put_line('guid_ is null: ' || guid_); else dbms_output.put_line('guid_ is not null: ' || guid_); end if;

  1. Запустите script; вывод возвращает нулевое сообщение.
  2. Измените объявление с tq84_o на tq84_d.
  3. Запустите script; вывод возвращает не пустое сообщение.
  4. Измените объявление на tq84_o.
  5. Запустите script; вывод возвращает не пустое сообщение.

Мой вывод из этого процесса:

guid_ is null: 07D41C8BCE696EA3E0539014190A7DA0
guid_ is not null: 07D41C8BCE7D6EA3E0539014190A7DA0
guid_ is not null: 07D41C8BCE916EA3E0539014190A7DA0

Похоже, что исправленное поведение не имеет ничего общего с else. Если я просто возьму исходный script и запустил его дважды в том же сеансе, он работает неправильно в первый раз и правильно во второй раз.


Как заметил @hol, эта ошибка также исчезает, если код находится в функции или хранимой процедуре. В моем случае я изменил весь анонимный блок на процедуру, затем выполнил процедуру, и ошибка не возникла.