USING - ключевое слово PL/SQL в версии 9i

Источник: ln

Эта статья посвящена двум особенностям работы PL/SQL-машины, которая в версиях 9.x объединена с SQL-машиной. Интересные особенности, которые могут всплыть при переносе программного обеспечения на новую версию сервера Oracle... По мотивам ответа Тома Кайта на вопросы, заданные 15 июня 2003 года.

USING - ключевое слово PL/SQL!

Том,

Я хотел бы задать два вопроса, которые меня сильно сбивают с толку.
Первый вопрос: Один из разработчиков написал следующий код в Oracle 8.1.7.3:

create table auxtab as select from dual;
create or replace procedure p_insert_auxtab as
begin
  insert into auxtab using (select * from dual);
  commit;
end;
/

Procedure created.

SQL> show errors

No errors.

При попытке выполнения тех же действий в версии 9.2.0.3.0 получаем сообщение об ошибке:

create table auxtab as select from dual;
create or replace procedure p_insert_auxtab as
begin
  insert into auxtab using (select * from dual);
  commit;
end;
/

Warning: Procedure created with compilation errors.

SQL> show errors

Errors for PROCEDURE P_INSERT_AUXTAB:

LINE/COL ERROR
-------- ----------------------------------------------------
3/1      PL/SQL: SQL Statement ignored
3/31     PL/SQL: ORA-00926: missing VALUES keyword

Но если сделать так:

SQL> create or replace procedure p_insert_auxtab as
  2  begin
  3  insert into auxtab using select * from dual;
  4  commit;
  5* end;
SQL> /

Procedure created.

SQL> show errors

No errors.

Я никогда не думал, что слово "using" может использоваться так. Я использовал слово using в операторах типа execute immediate или при открытии курсора. Мой вопрос: правильно ли использовать слово using в операторе "insert into..."? Почему разработчик смог скомпилировать и выполнить процедуру в версии 8i со скобками, а в версии 9i это уже не прошло?

Второй вопрос: Один из разработчиков сделал следующее в версии 8.1.7.3

SQL> create table test2
  2  (colnum number)
  3  ;

Table created.

SQL> create or replace procedure p_test2 as
  2  x number:=0;
  3  begin
  4    select colnum into x from test2 where colnum mod 4 =0;
  5    dbms_output.put_line(to_char(x));
  6* end;
SQL> /

Procedure created.

SQL> show error

No errors.

В версии 9i я получаю:

SQL> create table test2
  2  (colnum number)
  3  ;

Table created.

SQL> create or replace procedure p_test2 as
  2  x number:=0;
  3  begin
  4    select colnum into x from test2 where colnum mod 4 =0;
  5    dbms_output.put_line(to_char(x));
  6* end;
  7  /

Warning: Procedure created with compilation errors.

SQL> show errors

Errors for PROCEDURE P_TEST2:

LINE/COL ERROR
-------- -----------------------------------------------------------------
4/1      PL/SQL: SQL Statement ignored
4/59     PL/SQL: ORA-00920: invalid relational operator

Почему версия 8i позволяла использовать функцию mod как оператор? Не должна ли былап выдаваться ошибка и в версии 8i? Я просмотрел документацию Oracle и не нашел ни одного примера, где mod используется как оператор. Можно ли использовать mod как оператор? Почему процедура перестала успешно компилироваться в версии 9i?

Ответ Тома Кайта

Первый вопрос:

insert into T 
(select * from dual);

Такой оператор вас не удивляет, перавда? Так что:

insert into T T1
(select * from dual);

тоже не удивит. А дальше:

insert into T "using"
(select * from dual);

Это ведь аналогичная конструкция, как и:

insert into T using
(select * from dual);

USING - всего лишь "корреляционное имя", ПСЕВДОНИМ таблицы T, а не ключевое слово. Но в версии 9i появилось ключевое слово USING в языке PL/SQL, что усложнило ситуацию:

ops$tkyte@ORA920> create table t as select * from dual where 1=0;

Table created.

ops$tkyte@ORA920> begin
  2          insert into t using (select * from dual);
  3  end;
  4  /

end;
   *
ERROR at line 3:
ORA-06550: line 2, column 16:
PL/SQL: ORA-00926: missing VALUES keyword
ORA-06550: line 2, column 2:
PL/SQL: SQL Statement ignored

ops$tkyte@ORA920> begin
  2          insert into t using select * from dual;
  3  end;
  4  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> begin
  2          insert into t "using" (select * from dual);
  3  end;
  4  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> begin
  2          insert into t "using" select * from dual;
  3  end;
  4  /

PL/SQL procedure successfully completed.

Но если коротоко -- USING интерпретировалось как псевдоним, а не как ключевое слово. Его просто не нужно указывать.

Второй вопрос:

Уникальная ситуация - я с такой не сталкивался. Проблема связана с тем, что в версиях до 8i включительно PL/SQL-машина использовала отдельный анализатор SQL. В PL/SQL все операторы типа =, < и т.п. определялись как "функции", они переписывались. Если включить sql_trace и посмотреть на SQL, сгенерированный из PL/SQL, можно увидеть следующее:

SELECT COLNUM
FROM
 T  WHERE MOD (COLNUM,4) = 0

Хотя в исходном коде и было написано:

ops$tkyte@ORA817DEV> create table t ( colnum number );

Table created.

ops$tkyte@ORA817DEV> insert into t select rownum-1 from all_users where rownum <=4;

4 rows created.

ops$tkyte@ORA817DEV> create or replace procedure p_test2 as
  2     x number:=0;
  3  begin
  4     select colnum into x from t where colnum mod 4 =0;
  5     dbms_output.put_line(to_char(x));
  6  end;
  7  /

Procedure created.

ops$tkyte@ORA817DEV> exec p_test2;

0

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV> select colnum from t where colnum mod 4 = 0;
select colnum from t where colnum mod 4 = 0
                                  *
ERROR at line 1:
ORA-00920: invalid relational operator

Это нигде не описано в документации. Работало в 8i "случайно". Вот такие "странные, но интересные" особенности реализации...


Страница сайта http://www.interface.ru
Оригинал находится по адресу http://www.interface.ru/home.asp?artId=23782