Про ініціаторів викликів і вихідних параметрах в Oracle., Інші СУБД, Бази даних, статті

Краще з практичного досвіду використання прав ініціатора виклику і функцій.

Питання: Останнім часом я багато разів використовував оператор AUTHID CURRENT_USER (права ініціатора виклику Invoker rights). Я часто пишу утиліти для інших розробників моєї команди і визначаю їх в центральній схемою SHARED_CODE. Коли я визначаю програму як виконувану в правах викликає, все розробники можуть викликати цю програму, і вона буде автоматично виконувати бажані операції над тими об’єктами, які знаходяться у власній схемі розробника. Але іноді я стикаюся з проблемами, пов’язаними з правами ініціатора. У черговому проекті нам доводиться багато працювати з файлами операційної системи через UTL_FILE. Зокрема, ми записуємо текст у файли, і цей текст має бути відформатовано в різних видах. Тому я вирішив написати ядро ​​”запис в файл”, в якому будуть виконуватися всі операції з UTL_FILE, і дати користувачам можливість специфікувати форматування за допомогою їх власних функцій. Таким чином, у них буде максимум гнучкості для виконання форматування. Але це, здається, не працює. Я написав програму “запис в файл” з правами ініціатора виклику, але вона відмовляється викликати програму форматування ініціатора. У чому ж помилка?

Відповідь: Ніякої помилки немає. Просто ви натрапили на обмеження в роботі прав ініціатора дзвінка. На щастя, я можу допомогти обійти це обмеження і досягти бажаного.

Зазвичай все, що потрібно, це написати основну програму, яка повертає керування підпрограмі, назва якої ще невідомо при написанні програми. Таким чином, програма повинна під час виклику розуміти, яка програма її викликала.

Один із способів використання механізму повернення, це застосування динамічного поліморфізму і об’єктних типів Oracle. Це найбільш зручний спосіб, який необхідно буде розглянути іншим разом. А в цей раз я покажу, як ще можна використовувати повернення і права ініціатора виклику, щоб отримати бажаний результат.

Давайте почнемо з деяких принципів прав ініціатора виклику, які застосовуються до всіх SQL-пропозицій (як статичним, так і динамічним), виконуваних в PL / SQL-блоці. При застосуванні прав ініціатора виклику, область імен та обчислень відповідних привілеїв виникає в процесі виклику (пізніше розглянемо ролі, розпізнавані і використовувані для обчислення цих привілеїв). Права ініціатора виклику застосовуються до наступних пропозицій:


Припустимо, я включу оператор AUTHID CURRENT_USER в заголовок моєї програми. Коли я компілюю цю програму, поточної схемою все ще потрібні безпосередньо видані привілеї на всі об’єкти, до яких звертається програма.

Потім я передаю авторизацію іншими схемами для виконання моєї програми. Коли користувач, що приєднався до однієї з цих схем, викликає мою програму, залежно від об’єктів бази даних у кожному з перерахованих вище пропозицій будуть дозволені відповідно до привілеями (прямими або наданими через ролі) поточної схеми.

Один з очевидних висновків, які можна зробити з цього, це те, що оператор AUTHID CURRENT_USER не буде застосовний до статичних викликам інших підпрограм, що викликаються з програм з правами ініціатора виклику.

Статичний виклик програми, це коли програма визначена на момент компіляції. Наприклад, в наступному фрагменті коду, proc1 статично викликає proc2.

PROCEDURE proc1
IS
BEGIN
proc2 ();
END proc1;

Під час компіляції proc1 в схемі ABC, Oracle Database дозволяє посилання на proc2 стосовно привілеїв цієї схеми. Тепер припустимо, що база даних шукає процедуру proc2 на рівні схеми ABC. Видаємо привілей EXECUTE для ABC.proc1 іншою схемою, такий як DEF, в якій є своя власна процедура proc2. Коли користувач з’єднується як DEF і виконує ABC.proc1, цей користувач завжди буде викликати ABC.proc2, а не DEF.proc2.

Чи означає це, що неможливо створити працюючу програму “запис в файл” з правами ініціатора виклику? Не зовсім. Можна насправді змусити Oracle динамічно вибирати, яку програму він повинен викликати, але для цього необхідно застосувати можливості динамічного PL / SQL.

Розглянемо два прості демонстраційних прикладу про вплив прав ініціатора виклику, а потім я продемонструю динамічний PL/SQL- “обхід”

Спочатку розглянемо AUTHID CURRENT USER по відношенню до об’єктів для зберігання даних. У лістингу 1 у схемі HR створюється процедура з правами ініціатора виклику show_my_data, яка показує кількість рядків у таблиці my_data table (10). Видаємо привілей EXECUTE користувачеві SCOTT для виклику цієї програми. У схемі SCOTT теж є таблиця my_data, в якій 1 рядок. Потім SCOTT викликає HR.show_my_data, і програма відображає 1, а не 10 рядків.

Лістинг 1: Створення і виклик show_my_data

SQL> CONNECT hr/hr
Connected.
SQL> CREATE TABLE my_data (VALUE NUMBER)
2 /
Table created.
SQL> BEGIN
2 FOR indx IN 1.. 10
3 LOOP
4 INSERT INTO my_data
5 VALUES (indx);
6 END LOOP;
7
8 COMMIT;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> CREATE OR REPLACE PROCEDURE show_my_data
2 AUTHID CURRENT_USER
3 IS
4 num PLS_INTEGER;
5 BEGIN
6 SELECT COUNT (*)
7 INTO num
8 FROM my_data;
9
10 DBMS_OUTPUT.put_line (“Count of my_data = ” // num);
11 END show_my_data;
12 /
Procedure created.
SQL> GRANT EXECUTE ON show_my_data TO scott
2 /
Grant succeeded.
SQL> CONNECT scott/tiger
Connected.
SQL> CREATE TABLE my_data (VALUE NUMBER)
2 /
Table created.
SQL> BEGIN
2 INSERT INTO my_data
3 VALUES (1);
4
5 COMMIT;
6 END;
7 /
PL/SQL procedure successfully completed.
SQL> CALL hr.show_my_data();
Count of my_data = 1

Як бачите, навіть незважаючи на те, що я запустив процедуру show_my_data користувача HR, ця програма показує мені кількість з таблиці my_data користувача SCOTT.

Однак тепер давайте спробуємо створити програму “запис в файл”. Спочатку створимо директорію бази даних у користувача HR і функцію форматування “за умовчанням” (текст перекладається в верхній регістр):

CONNECT hr/hr
CREATE DIRECTORY temp AS “c: emp”
/
GRANT WRITE ON DIRECTORY temp
TO scott;
/
CREATE OR REPLACE FUNCTION
format_line (line_in IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
RETURN UPPER (line_in);
END format_line;
/

Потім створимо дуже просту програму, показану в Лістингу 2, яка виконує “запис в файл”, і надамо користувачеві SCOTT можливість викликати її.

Лістинг 2: Створення write_to_file

CREATE OR REPLACE PROCEDURE write_to_file (
dir_in IN VARCHAR2
, file_name_in IN VARCHAR2
, lines_in IN DBMS_SQL.varchar2s
)
AUTHID CURRENT_USER
IS
l_file UTL_FILE.file_type;
BEGIN
l_file :=
UTL_FILE.fopen (LOCATION => dir_in
, filename => file_name_in
, open_mode => “W”
, max_linesize => 32767
);
FOR indx IN 1.. lines_in.COUNT
LOOP
UTL_FILE.put_line (l_file, format_line (lines_in (indx)));
END LOOP;
UTL_FILE.fclose (l_file);
END write_to_file;
/
GRANT EXECUTE ON write_to_file TO scott
/

Потім приєднаємося як SCOTT, створимо форматують функцію, яка переводить текст в нижній регістр, і викличемо write_to_file як показано в лістингу 3.

Лістинг 3: Створення format_line (і виклик write_to_file)

CREATE OR REPLACE FUNCTION format_line (line_in IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
RETURN LOWER (line_in);
END format_line;
/
DECLARE
l_lines DBMS_SQL.varchar2s;
BEGIN
l_lines (1) := “steven feuerstein”;
l_lines (2) := “is obsessed with PL/SQL.”;
hr.write_to_file (“TEMP”, “myfile.txt”, l_lines);
END;
/

На жаль, я отримав наступний текст у вихідному файлі myfile.txt:

STEVEN FEUERSTEIN
IS OBSESSED WITH PL/SQL.

Отже, не дуже схоже, що ми змогли використати права ініціатора при виклику програми? Добре, може бути, спробуємо інший спосіб: використання динамічного SQL.

Я переписав вихідну програму write_to_file у схемі HR, як показано в лістингу 4.

Лістинг 4: Створення зміненої write_to_file

CREATE OR REPLACE PROCEDURE write_to_file (
dir_in IN VARCHAR2
, file_name_in IN VARCHAR2
, lines_in IN DBMS_SQL.varchar2s
)
AUTHID CURRENT_USER
IS
l_file UTL_FILE.file_type;
l_newline VARCHAR2 (32767);
BEGIN
l_file :=
UTL_FILE.fopen (LOCATION => dir_in
, filename => file_name_in
, open_mode => “W”
, max_linesize => 32767
);
FOR indx IN 1.. lines_in.COUNT
LOOP
EXECUTE IMMEDIATE
“BEGIN :new_line := format_line (:old_line); END;”
USING OUT l_newline, IN lines_in (indx);
UTL_FILE.put_line (l_file, l_newline);
END LOOP;
UTL_FILE.fclose (l_file);
END write_to_file;
/

Зауважте, що я перемістив виклик функції format_line в пропозицію EXECUTE IMMEDIATE. Таким чином, я викликав функцію з динамічного PL / SQL-блоку. А тепер я приєднаюся як SCOTT і викликом write_to_file:

DECLARE
l_lines DBMS_SQL.varchar2s;
BEGIN
l_lines (1) := “steven feuerstein”;
l_lines (2) := “is obsessed
with PL/SQL.”;
hr.write_to_file (“TEMP”,
“myfile.txt”,
l_lines);
END;
/

І ось бачимо наступне вміст у вихідному файлі myfile.txt:

steven feuerstein
is obsessed with pl/sql.

О, працює! Невже це можливо?

Перемістивши виклик програми в динамічно виконується PL / SQL-блок, виклик став виконуватися з пропозиції, до якого можуть бути застосовані права ініціатора виклику: EXECUTE IMMEDIATE. Таким чином, блок тепер виконується в правах користувача SCOTT “, тому що викликається версія format_line користувача SCOTT.

Тому саме його права як ініціатора виклику можуть застосовуватися для виконання стека викликів при використанні динамічного PL / SQL.

Зверніть увагу, будь ласка, що динамічне виконання PL / SQL-підпрограми повільніше, ніж статична. Для довго виконуються програм ці витрати будуть несуттєві, однак майте це на увазі при створення програм, які завершуються дуже швидко.

Я порівняв продуктивність статичного і динамічного запуску програми, яка викликає функцію USER і зберігає це значення в локальній змінній. Запуск програми 100,000 раз з використанням статичного виклику виконується за 2.83 секунд. Динамічний запуск виконується за 6.66 секунд.

Я перейняв цю технологію у Алекса Ньюжтена (Alex Nuijten) з AMIS (technology.amis.nl / blog), який нещодавно брав участь у минулій конференції з Oracle PL / SQL Programming 2007 в Сан Матео, Каліфорнія (www.odtugopp07.com) і був нагороджений Oracle Development Tools User Group PL / SQL Innovation і Community Award від імені групи AMIS Oracle.

І останнє зауваження: альтернативний спосіб досягти такого ж ефекту (вибір програми для виконання під час роботи), це використання динамічного поліморфізму. Ця техніка вбудована в об’єктні типи в PL / SQL-а саме за допомогою установки ієрархії об’єктних типів з ключовим словом UNDER. Більш докладно про об’єктних типах і динамічному поліморфізм в PL / SQL, вивчайте Главу 12, “Using PL / SQL With Object Types “в Oracle Database PL/SQL User”s Guide and Reference .

Ніякого виходу

Питання: Я вважаю, що функція повинна повертати дані тільки через оператор return. Чому PL / SQL дозволяє використовувати OUT-параметри у функцій? Може, у використанні цієї можливості є якась специфіка?

Відповідь: Я сформував деякі рекомендації по структурі функції:


Чому Oracle дозволяє нам застосовувати OUT-аргументи в списку параметрів функції? Ймовірно, тому що він розпізнає цей світ не як чорно-білий, а створює безліч відтінків сірого. Програмісти мають велику свободу вибору, з безліччю різних стилів написання коду.

Я дуже радий, що Oracle не відмовляється від практики використання OUT-параметрів функцій (яка не є, між іншим, універсально прийнятої або поширеною практикою у світі програмування) при його реалізації PL / SQL.

Мій досвід знайомства зі світом програмування на C, наприклад, показує, що розробники зазвичай пишуть функції, які повертають код повернення, позначає, чи успішно завершена функція. Потім вони використовують OUT-аргументи в списку параметрів для повернення інформації з функції.

Мені не подобається цей підхід, особливо в PL / SQL, однак я відчуваю, що потрібно більше гнучкості мови, а не менше. І необхідно розробляти власні індивідуальні технології, щоб вибирати, які можливості PL / SQL використовувати, а які ні.

Звичайно, можуть бути ситуації, в яких функція з OUT-параметром корисна. Розглянемо наступний блок. Функція source_code.more_data повертає значення Boolean, яке використовується для виходу з циклу, і вона ж повертає як OUT-аргумент структуру даних, яка потім обробляється в циклі.

BEGIN
source_code.init (…);
WHILE source_code.more_data
(output_structure)
LOOP
process (output_structure);
END LOOP;
source_code.TERMINATE ();
END;

Так, я, звичайно, можу переписати цей код, так щоб він використовував функцію без OUT-параметра або процедуру, однак це зробить код більш нескладним і складним для читання.

Тому ми явно не хочемо, щоб Oracle робив функції з OUT або IN OUT параметрами неприпустимими, проте я думаю, що такий список параметрів стає корисним тільки в специфічних ситуаціях.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*