А дядюшка, ракетчик-офицер,
Расскажет про язык военный Ada.
«От C к C++»
Из каких-то соображений Oracle хранит молчание про появление и развитие
языка PL/SQL. Редкие источники, в которых можно найти что-то
вразумительное про технологическую историю компании, обходят этот вопрос
стороной. Особенно это огорчает на фоне неплохо документированной истории языка SQL.
Достоверно известно немногое. Язык впервые появился в версии 6
(1988 год); в версии 7 (1992 год) с реализацией хранимых процедур его
поддержка стала полноценной:
Ken Jacobs: In Oracle 6, we dealt with stored procedures. The kernel, the engine, would execute a PL/SQL procedure, but it didn’t have the ability to store it. So, you would have client side programs that would create these packages of multiple statements with procedure logic around them and submit it to the database as an executable unit. And, so stored procedures came in version 7, but PL/SQL was in version 6.
RDBMS
workshop: Oracle, p. 26
Прообразом для PL/SQL послужила Ада — «официальный» язык МО США. Почему? Возможно, таково было пожелание госзаказчиков, однако точной информации на этот счет нет. Сам факт родственных связей очевиден и из простого сравнения двух языков, и заявлен в PL/SQL Language Reference, Appendix C:
PL/SQL is
based on the programming language Ada. As a result, PL/SQL uses a variant
of Descriptive Intermediate Attributed Notation for Ada (DIANA), a tree-structured intermediate language. It is defined using a metanotation called Interface Definition Language (IDL). DIANA is used internally by compilers and other tools.
Как видим, языки похоже не только внешне: транслятор PL/SQL переводит
код в промежуточное представление DIANA — атрибутированное
синтаксическое дерево, — которое было разработано в начале
1980-х для использования компиляторами Ады. Это наводит на мысль, что
в основу реализации PL/SQL был положен уже готовый компилятор Ады.
Другой артефакт, позволяющий строить догадки, хорошо спрятан на самом
видном месте — пакет standard. Кажется, первым на него обратил внимание Пит
Финниган: он заметил, что булевский тип объявлен в standard как
type BOOLEAN is (FALSE, TRUE);
В PL/SQL такой конструкции нет, зато она есть в Аде. Пит сделал вывод о том, что Oracle реализовал перечислимые типы «невзаправду»:
Why do Oracle use syntax available to them only in the STANDARD package and not available to us? — well, my educated guess would be that they have only implemented this syntax in a very narrow way, i. e. to fulfill a
particular case and not much more. They must have made sure it compiles the BOOLEAN correctly but not tested or implemented much else hence we cannot use it.
Undocumented
Oracle — Using ENUM's in PL/SQL
На мой взгляд, Пит ошибается. На самом деле это еще один аргумент в пользу того, что разработчики PL/SQL имели на руках готовый компилятор Ады: какой смысл в поддержке перечислимых типов ради одного только boolean? Хватило бы числового типа и немного хардкода.
А в том, что произвольные перечислимые типы без проблем работают, легко убедиться
самостоятельно, ну например:
type color is (red, green, blue);
В обычном пакете получим PLS-00505: User Defined Types may only be
defined as PLSQL Tables or Records, а внутри standard это
компилируется и потом прекрасно работает, включая (как и полагается в Аде) функции сравнения и вхождения:
SQL> set serveroutput on
SQL> declare
2 c color;
3 begin
4 c := red;
5 dbms_output.put_line(case when c < green then 'yes' else 'no' end);
6 dbms_output.put_line(case when c in (blue,green) then 'yes' else 'no' end);
7 end;
8 /
yes
no
PL/SQL procedure successfully completed.
К слову, скомпилировать standard просто так не получится: полезут
блокировки и внутренние ошибки, в результате чего база загнется чуть
менее, чем насмерть. Для 9i (и, по слухам, 10g) можно поднять базу в режиме migrate (это отключает триггеры), скомпилировать пакет и перекомпилировать инвалидные объекты:
$ sqlplus / as sysdba
SQL> shutdown abort
SQL> startup migrate
SQL> @stdspec
SQL> @?/rdbms/admin/utlrp
Для 11g это уже не прокатило, как обходиться с ней — не знаю.
Итак, зачем Ораклу поддержка перечислимых типов? Напомню, что в версии 6 была переписана примерно половина СУБД:
Jacobs:
Version 5 was pretty successful but it had some serious problems. It still
had table-level locking. It had no real scalability. You didnt need
it with table locking. You couldnt do much anyway. So we set out in
about 1986 and made a fundamental decision to rewrite half of the product.
We threw away, and literally deleted the directories for the lower half of
the database. We kept the SQL layer but re-architected the process model,
the storage format, the logging, the locking, the multi-threadedness.
RDBMS
workshop: Oracle, p. 19
На фоне этих изменений появление PL/SQL несколько меркнет. Были ли у компании силы делать в это время лишнюю работу? Сомнительно. Скорее, сил как раз не хватало, ведь хранимых процедур пришлось ждать еще четыре года.
Ну хорошо, реализация перечислимых типов не выглядит очень сложной,
могли и сделать. Однако заглянем внутрь standard чуть глубже:
type VARCHAR2 is NEW CHAR_BASE;
Что такое new? В PL/SQL такого нет, он будет ругаться PLS-00504:
type CHAR_BASE may not be used outside of package STANDARD, а если
заменить тип, скажем, на number, то PLS-00103: Encountered the symbol
"NUMBER" when expecting one of the following...
Зато такая конструкция имеется в Аде. Она означает создание
производного типа, наследующего атрибуты базового, но несовместимого с ним (в отличие от подтипов, которые ограничивают
диапазон значений базового типа, но сохраняют совместимость).
Если эта возможность достается даром (вместе с компилятором Ады), то
почему бы ей не воспользоваться? Но реализовывать концепцию производных
типов с нуля, если они не нужны в языке? Не верю.
Кстати, попытка объявить аналогичный тип varchar3 внутри standard не увенчалась успехом. Standard компилируется без проблем, но использовать новый тип невозможно:
SQL> declare
2 v3 varchar3(100);
3 begin
4 null;
5 end;
6 /
v3 varchar3(100);
*
ERROR at line 2:
ORA-06550: line 2, column 6:
PLS-00566: type name "VARCHAR3" cannot be constrained
Если же не указывать ограничение, то код компилируется, но с грохотом
падает при выполнении:
SQL> declare
2 v3 varchar3;
3 begin
4 null;
5 end;
6 /
declare
*
ERROR at line 1:
ORA-06550: line 0, column 0:
PLS-00801: internal error [74402]
Самый убойный аргумент нашелся почти случайно в форуме. В 9i (а судя по написанному, и в 10g) без ошибок компилируется такой код:
SQL> create or replace package test as
2 procedure set_n(x number);
3 private
4 n number;
5 end;
6 /
Package created.
Что такое private в спецификации? В Аде это слово позволяет описать
данные, к которым нельзя обратиться непосредственно — это механизм
для создания абстрактных типов. А вот в PL/SQL развесистую систему
типов Ады существенно упростили и в нем слово private лишено всякого
смысла.
Вообще система типов — тема для отдельного поста, но откуда
private попал в PL/SQL, как не из компилятора Ады? Если бы язык писали с нуля, в него не стали бы добавлять абсолютно ненужную конструкцию.
Еще интересный момент: приведенный выше код компилируется, но не работает. Если добавить тело и попробовать вызвать процедуру (или обратиться напрямую к переменной), получим:
SQL> create or replace package body test as
2 procedure set_n(x number)
3 is
4 begin
5 n := x;
6 end;
7 end;
8 /
Package body created.
SQL> begin
2 test.set_n(42);
3 end;
4 /
begin
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
Возможно, внутренняя ошибка возникает из-за конфликта старой и новой модели сокрытия данных, а из парсера private просто забыли убрать (в 11g это уже подчистили).
Итак, мое мнение состоит в том, что для PL/SQL изначально был взят код
компилятора Ады. Часть возможностей вырубили топором, часть присыпали
листвой (из-под которой, как мы видели, кое-что просвечивает), и уже на этом готовом фундаменте вырастили новый транслятор.
P. S. для пытливых умов. С Адой у Oracle также связаны такие продукты, как Ada*Precompiler (в седьмой версии) и SQL*Module (в восьмой). Оба они, по всей видимости, успешно загнулись, однако идея использовать Аду для доступа к данным не умирает.