Как Вы хорошо знаете, в PL/SQL нет возможности ограничить область видимости подпрограмм. Если создана PL/SQL-процедура (пакет, функция или объектный тип), то она видима в любом другом PL/SQL-объекте и в анонимном блоке, то есть может вызываться в любом месте!
До Oracle Database версии 12c R1 стандартными средствами СУБД, невозможно реализовать декларативную проверку на возможность вызова PL/SQL-процедур только в определенном месте. Например, было нельзя деклативно реализовать такую проверку: процедуры отвечающие за интерфейс пользователя, имеют право вызывать только процедуры бизнес-логики, и не могут напрямую обращаться к слою доступа к данным, иначе это может привести нарушению логической целостности данных.
В версии 12с эта проблема была устранена: при создании программной единицы PL/SQL можно указывать фразу ACCESSIBLE BY, после которой следует список PL/SQL-объектов, в которых разрешен вызов данной программной единицы.
По умолчанию, отсутствие фразы ACCESSIBLE BY подразумевает возможность вызова PL/SQL-программной единицы в произвольном месте, то есть поведение будет аналогичным как для предыдущих версий СУБД Oracle Database.
Рассмотрим уже знакомый Вам пример.
В приложении, работу с репозитарием объектов инкапсулирует объектный тип TObjectRepository. Этот объектный тип могут использовать только следующих три объектных типа:
- TPersistent - абстрактный тип, его наследники обладают свойством сохранять и читать свое состояние из БД;
- TDictionary - инкапсулирует работу с справочниками, в связи с особой ролью системных справочников, может напрямую обращаться к API поддержки репозитория;
- TGroup - инкапсулирует функционал группировки объектов в группы, тоже напрямую может работать с фабрикой объектов.
SQL> create or replace type TObjectRepository force
accessible by (TPersistent,TDictionary,TGroup) under TObject
(
static procedure registerObject(v_pObject in out nocopy TObject),
static procedure unregisterObject(v_pObject in out nocopy TObject)
)
not final;
/
Type created.
SQL>
Стоит отметить, что в вышеуказанном примере, типов TPersistent,TDictionary и TGroup еще не существует!
Попробуем обратится к типу TObjectRepository в неразрешенной процедуре:
SQL> create or replace procedure test_accb1 is
v_xObject TObjectRepository;
begin
null;
end;
/
Warning: Procedure created with compilation errors.
SQL> show errors;
Errors for PROCEDURE TEST_ACCB1:
LINE/COL ERROR
-------- -----------------------------------------------------------------
2/13 PL/SQL: Item ignored
2/13 PLS-00904: insufficient privilege to access object
TOBJECTREPOSITORY
SQL>
Как видите, проверка производится уже на этапе компиляции.
Следует отметить важное замечание: после того как для PL/SQL-программой единицы указана фраза ACCESSIBLE BY, становится невозможеным обращение к ней в анонимном блоке:
SQL> declare
v_xObject TObjectRepository;
begin
null;
end;
/
v_xObject TObjectRepository;
*
ERROR at line 2:
ORA-06550: line 2, column 13:
PLS-00904: insufficient privilege to access object TOBJECTREPOSITORY
ORA-06550: line 2, column 13:
PL/SQL: Item ignored
Поэтому, не следует применять ограничение видимости для PL/SQL-программных единиц которые применяются в вызовах верхнего уровня (top calls).
Как обстоит дело насчет проверки в runtime, то есть в время выполнения PL/SQL?
Давай-те попробуем "обмануть" компилятор и сделаем обращение к типу TObjectRepository в динамическом PL/SQL:
create or replace type body TPersistent is
member procedure save(self in out nocopy TPersistent) is
begin
execute immediate 'declare v_xObject TObjectRepository; begin null; end;';
end;
... ... ...
При этом обращение к типу TObjectRepository происходит внутри разрешенного типа TPersistent!
Однако проверка видимости все равно срабатывает:
SQL> declare
v_xObject TPersistent;
begin
v_xObject := new TPersistent(1);
v_xObject.save();
end;
/
declare
*
ERROR at line 1:
ORA-06550: line 1, column 19:
PLS-00904: insufficient privilege to access object TOBJECTREPOSITORY
ORA-06550: line 1, column 19:
PL/SQL: Item ignored
ORA-06512: at "SCOTT.TPERSISTENT", line 5
ORA-06512: at line 5
Из вышеприведенного примера следует второй важный вывод: проверка на разрешение видимости осуществляется только для текущего уровня вложенности вызов, и не учитывает более высокие уровни вызовов.
Ну и наконец, пробуем сделать обращение к типу TObjectRepository разрешенное внутри типа TPersistent:
SQL> create or replace type body TPersistent is
member procedure save(self in out nocopy TPersistent) is
v_xObject TObjectRepository;
begin
null;
end;
... ... ...
/
Type body created.
Появившаяся в Oracle Database 12c технология ограничения видимости программных единиц PL/SQL является полезной возможностью, которая позволяет структурировать Ваш код и защищает его от логических ошибок связанных с неверным местом вызова.