Использование Pragma в теле пакета Oracle
Вопрос
Я хотел бы создать пакет Oracle и две функции в нем:Общественная функция ( function_public
) и частный ( function_private
).Публичная функция использует частную функцию в операторе sql.
Без прагмы код не компилируется(PLS-00231: function 'FUNCTION_PRIVATE' may not be used in SQL
)
CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;
CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
return 'z';
END;
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
ret VARCHAR2(100);
BEGIN
SELECT 'x' || function_private(x) INTO ret FROM dual;
return ret;
END;
END PRAGMA_TEST;
Код компилируется, если я добавляю WNDS, WNPS
Прагма для function_private
.Мне кажется, что прагму можно использовать только в объявлении пакета, а не в теле пакета, поэтому я должен объявить function_private
в упаковке также:
CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2;
PRAGMA RESTRICT_REFERENCES( function_private, WNDS, WNPS);
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;
CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
return 'z';
END;
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
ret VARCHAR2(100);
BEGIN
SELECT 'x' || function_private(x) INTO ret FROM dual;
return ret;
END;
END PRAGMA_TEST;
Это решение делает мой function_private
общественность тоже.Есть ли решение добавить прагму к функции, которую можно найти только в теле пакета?
ОБНОВЛЯТЬ: Заменил псевдокод на рабочий (упрощённый) пример.
ОБНОВЛЕНИЕ2:Исправления в коде, предложенные Робом ван Вейком.
Решение
Ваша проблема не имеет ничего общего с прагами. Как говорит Роб, современные версии Oracle обрабатывают большую часть этого автоматически.
Проблема в том, что вы не можете назвать частные функции из оператора SQL, Даже из них встроен в другую подпрограмму в том же пакете. Когда PL / SQL выполняет SQL, он передан на SQL Engine для выполнения, и что по существу приведет вас за пределами объема пакета, поэтому он не имеет доступа к частным членам.
Это компилирует хорошо - нет прагма, но делает «частную» функцию общественности:
CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;
CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
return 'z';
END;
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
ret VARCHAR2(30);
BEGIN
SELECT 'x' || function_private(x) INTO ret FROM dual;
RETURN ret;
END;
END PRAGMA_TEST;
Если вы хотите сохранить функцию частным, вам нужно посмотреть, сможете ли вы переписать публичную функцию таким образом, чтобы вызов к личной функции выполнен за пределы оператора SQL:
CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;
CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
return 'z';
END;
FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
ret VARCHAR2(30);
BEGIN
ret := function_private(x);
SELECT 'x' || ret INTO ret FROM dual;
RETURN ret;
END;
END PRAGMA_TEST;
Другие советы
Твой function_private
объявляется только в теле пакета, поэтому его область действия ограничена только другими процедурами в вашем пакете.Следовательно, он должен будет соответствовать уровню чистоты вызывающих процедур, иначе компилятор выдаст исключение.
Сравните это безопасное заявление (заметьте, я расширил чистоту function_public
) ...
SQL> CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
2 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
3 PRAGMA RESTRICT_REFERENCES( function_public, WNDS, WNPS, RNDS);
4 END PRAGMA_TEST;
5 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
2 FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
3 BEGIN
4 return 'no harm done';
5 END;
6
7 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
8 BEGIN
9 return function_private(x);
10 END;
11 END PRAGMA_TEST;
12 /
Package body created.
SQL>
...с этим небезопасным...
SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
2 FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
3 rv varchar2(1);
4 BEGIN
5 select dummy into rv from dual;
6 return rv;
7 END;
8
9 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
10 BEGIN
11 return function_private(x);
12 END;
13 END PRAGMA_TEST;
14 /
Warning: Package Body created with compilation errors.
SQL> sho err
Errors for PACKAGE BODY PRAGMA_TEST:
LINE/COL ERROR
-------- -----------------------------------------------------------------
9/3 PLS-00452: Subprogram 'FUNCTION_PUBLIC' violates its associated
pragma
SQL>
Суть прагмы RESTRICTS_REFERENCES заключается в том, что процедуры, объявленные в спецификации пакета, могут использоваться другими пакетами, даже операторами SQL, принадлежащими или выполняемыми другими пользователями (схемами), которые могут не иметь доступа к источнику тела нашего пакета.Прагма — это метод, с помощью которого мы даем им гарантии о последствиях включения нашего кода в их код.Вот почему прагма должна быть объявлена в спецификации, потому что это единственная часть кода, которая отображается, когда мы предоставляем EXECUTE для пакета другому пользователю.
редактировать
Ах, теперь, увидев ваш исправленный пример кода, я понимаю, что вы пытаетесь сделать.Это не работает, не будет, не может работать.Нам разрешено использовать только упакованные функции, которые были объявлены в спецификации = общедоступные функции - в SQL.Не имеет значения, написан ли SQL в SQL*Plus или закодирован в другой упакованной процедуре.Причина вполне ясна в стеке ошибок:
SQL> CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
2 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
3 PRAGMA RESTRICT_REFERENCES( function_public, WNDS, WNPS);
4 END PRAGMA_TEST;
5 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
2 FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
3 rv varchar2(1);
4 BEGIN
5 select dummy into rv from dual;
6 return rv;
7 END;
8
9 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
10 rv varchar2(1);
11 BEGIN
12 select function_private(x) into rv from dual;
13 return rv;
14 END;
15 END PRAGMA_TEST;
16 /
Warning: Package Body created with compilation errors.
SQL> sho err
Errors for PACKAGE BODY PRAGMA_TEST:
LINE/COL ERROR
-------- -----------------------------------------------------------------
12/6 PL/SQL: SQL Statement ignored
12/13 PL/SQL: ORA-00904: : invalid identifier
12/13 PLS-00231: function 'FUNCTION_PRIVATE' may not be used in SQL
SQL>
Компилятор выбрасывает ORA-00904: invalid identifier
потому что функция не объявлена в спецификации;это не имеет ничего общего с уровнем чистоты,
примечание о сфере применения
PL/SQL не полностью соответствует требованиям его правила области видимости:мы можем использовать частные переменные в нашем упакованном операторе SQL:
SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
2
3 gv constant varchar2(8) := 'global';
4
5 FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
6 rv varchar2(1);
7 BEGIN
8 select dummy into rv from dual;
9 return rv;
10 END;
11
12 FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
13 rv varchar2(10);
14 BEGIN
15 select gv||'+'||dummy into rv from dual;
16 return rv;
17 END;
18 END PRAGMA_TEST;
19 /
Package body created.
SQL>
Это просто функции и типы, которые wm должен объявить в спецификации, если мы хотим использовать их в операторах SQL.
Вы пишете «Я хотел бы добавить WNDS, WNPPS Pragma ...». Почему тебе это нравится? С версии 9 (я думаю) Oracle делает этот чек для вас. Единственная причина, по которой вы можете самостоятельно добавить прагму, это когда:
Вы знаете, где в операторе SQL вы хотите использовать функцию и
Вы знаете, какие уровни чистоты требуются для этого использования и
Вы хотите найти нарушения во время компиляции, а не время выполнения
Самый простой вариант - просто пропустить все декларации в прагме.
Сказав это, вы можете опустить restrict_References Pragma для функции_Private, если вы добавляете ключевое слово доверия к RESTRICT_REFERCES PRAGMA FUNCTION_PUBLEABLE.
http://download.orcle.com/docs/cd/b10501_01/Appdev.920/a96590/adg10pck.htm#21958.
С уважением, Роб.
Oracle делает эту проверку.
Следующий код не скомпилируется, поскольку function_public
Имеет прагма RNDS
, и это называет function_private
который читает стол.
PLS-00452: Подпрограмма «Функция_Public» нарушает связанную с ними прагма
Удалить SELECT
от function_private
И это работает.
CREATE OR REPLACE PACKAGE pragma_test AS
FUNCTION function_public RETURN VARCHAR2;
PRAGMA RESTRICT_REFERENCES( function_public, RNDS );
END pragma_test;
CREATE OR REPLACE PACKAGE BODY pragma_test AS
FUNCTION function_private RETURN VARCHAR2 IS
v_return dual.dummy%TYPE;
BEGIN
SELECT dummy INTO v_return FROM dual;
RETURN v_return;
END;
--
FUNCTION function_public RETURN VARCHAR2 IS
v_return dual.dummy%TYPE;
BEGIN
RETURN function_private;
END;
END pragma_test;