Использование Pragma в теле пакета Oracle - PullRequest
4 голосов
/ 10 мая 2010

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

ОБНОВЛЕНИЕ: Заменен псевдокод на рабочий (упрощенный) пример.

UPDATE2 : исправлены ошибки в коде, предложенные Робом ван Вейк.

Ответы [ 4 ]

8 голосов
/ 10 мая 2010

Ваша проблема не имеет ничего общего с PRAGMA.Как говорит Роб, современные версии Oracle обрабатывают большую часть этого автоматически.

Проблема в том, что вы не можете вызывать закрытые функции из оператора SQL, даже те, которые встроены в другую подпрограмму в том же пакете. Когда PL / SQL выполняет SQL, он передается в механизм SQL для выполнения, и это, по сути, выводит вас за пределы пакета, поэтому у него нет доступа к закрытым членам.

Это прекрасно компилируется -- никаких прагм, но сделав «приватную» функцию общедоступной:

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;
2 голосов
/ 10 мая 2010

Ваш 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 для пакета другому пользователю.

редактировать

Ах, теперь, увидев ваш исправленный пример кода, я понимаю, что вы пытаетесь сделать. Это не, не будет, не может работать. Нам разрешено использовать только упакованные функции, которые были объявлены в spec = public functions - в 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.

1 голос
/ 10 мая 2010

Вы пишете "Я хотел бы добавить WNDS, WNPS pragma ...". Почему тебе это нравится? Начиная с версии 9 (я думаю) Oracle делает эту проверку за вас. Единственная причина, по которой вы можете добавить прагму самостоятельно, это когда:

  • вы знаете, где в операторе SQL вы хотите использовать функцию AND

  • Вы знаете, какие уровни чистоты требуются для этого использования И

  • вы хотите найти нарушения во время компиляции, а не во время выполнения

Самый простой вариант - просто пропустить все объявления прагмы.

Сказав это, вы можете опустить прагму restrict_references для function_private, если добавите ключевое слово TRUST в прагму restrict_references для function_public.

http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96590/adg10pck.htm#21958

С уважением, Роб.

0 голосов
/ 10 мая 2010

Oracle делает эту проверку.

Следующий код не компилируется, поскольку function_public имеет прагму RNDS и вызывает function_private, которая читает таблицу.

PLS-00452: подпрограмма 'FUNCTION_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;
...