30 дек. 2013 г.

Oracle Database 12c R1: ACCESSIBLE BY Clause in PL/SQL

Как Вы хорошо знаете, в 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 - инкапсулирует функционал группировки объектов в группы, тоже напрямую может работать с фабрикой объектов.
В всех других программных единицах PL/SQL, обращение к типу TObjectRepository запрещено.

Итак, при создании спецификации типа TObjectRepository указываем фразу ACCESSIBLE BY

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 является полезной возможностью, которая позволяет структурировать Ваш код и защищает его от логических ошибок связанных с неверным местом вызова.

2 комментария:

  1. А еще с помощью такой штуки вендер может защитить свой код "от несанкционированного использования" клиентом.

    ОтветитьУдалить
  2. Да , Серега , удобная вещь :) я о такой штуке мечтал на 8i

    ОтветитьУдалить