Аннотация
В каждом сеансе работы с СУБД можно использовать так называемые контексты, формально представляющие собой именованный набор пар «параметр/значение». Контексты сеансов обладают рядом интересных свойств, существенно повышающих «внутренние» возможности Oracle по созданию приложений. В статье рассматриваются возможности проектирования локальных и глобальных контекстов сеанса и некоторые их свойства.
Как построить контекст сеанса своими руками
Для самостоятельного создания контекста служит специальная команда CREATE CONTEXT. Выдать ее (то есть создать контекст) может сеанс, имеющий полномочие CREATE ANY CONTEXT. Слово ANY в названии полномочия (привилегии) свидетельствует о том, что контекст - внесхемный объект в БД Oracle, такой, например, как роль, и отличный, например, от таблицы.
Для каждого контекста требуется указать специальную «доверительную» программную единицу: процедуру, функцию или пакет. Именно из тела этой программной единицы Oracle разрешит обращаться к процедуре DBMS_SESSION.SET_CONTEXT. Принято такое неочевидное решение во имя безопасности, так как доступ к хранимым программным единицам регулируется уже готовым механизмом привилегий.
Пример создания контекста
Положим, доверительной программной единицей должна быть процедура SET_MYCONTEXT_VALUE:
CONNECT / as sysdba
CREATE OR REPLACE CONTEXT mycontext USING set_mycontext_value;
Обратите внимание, что процедура не обязана существовать в момент создания контекста. Но в конце концов ее-таки потребуется создать:
CREATE OR REPLACE PROCEDURE set_mycontext_value (
par IN VARCHAR2
, val IN VARCHAR2
)
AS
BEGIN DBMS_SESSION.SET_CONTEXT ( 'mycontext', par, val );
END;
/
GRANT EXECUTE ON set_mycontext_value TO scott;
Проверка:
SQL> CONNECT scott/tiger
Connected.
SQL> SELECT SYS_CONTEXT ( 'mycontext', 'sesame' ) FROM dual;
SYS_CONTEXT('MYCONTEXT','SESAME')
------------------------------------------------------------
SQL> EXECUTE sys.set_mycontext_value (
'sesame', '123' )
PL/SQL procedure successfully completed.
SQL> SELECT SYS_CONTEXT ( 'mycontext', 'sesame' ) FROM dual;
SYS_CONTEXT('MYCONTEXT','SESAME')
------------------------------------------------------------
123
Выше серым фоном выделена пустая строка.
С помощью контекста MYCONTEXT и доступной ему процедуры пользователь SCOTT завел значение, которое сможет читать и переустанавливать в собственном сеансе вплоть до завершения. Другой сеанс пользователя SCOTT создаст и будет использовать с помощью этого же контекста свои значения, то есть значения контекста являются собственностью сеанса.
Значения атрибутов контекста живут не долее пределов сеанса и защищены от доступа из других сеансов. В течение сеанса значения переменных пакета могут пропасть («сброс» пакета, хотя пользователи и нечасто прибегают к нему), и значения атрибутов контекста тоже (с помощью пакета DBMS_SESSION). Этим атрибуты схожи с переменными пакета. Но есть и отличия:
- в пакете набор переменных фиксирован, а набор атрибутов контекста произволен
- переменные пакета могут быть разнообразны по структуре, а значения атрибутов контекста - всего лишь строки текста
- атрибутам контекста можно устанавливать до сеанса и делать доступными в другом сеансе.
Вот еще пример использования нашего контекста:
SQL> EXECUTE set_mycontext_value -
> ( 'start work', TO_CHAR ( SYSDATE, 'hh24:mi:ss' ) )
PL/SQL procedure successfully completed.
SQL> REMARK выполняем работу, после чего смотрим когда начинали ...
SQL> SELECT SYS_CONTEXT ( 'mycontext', 'start work' ) FROM dual;
SYS_CONTEXT('MYCONTEXT','STARTWORK')
--------------------------------------------------------------------
13:58:06
Вариация в технологии: использование доверительного пакета
Если запросить структуру справочной таблицы доступных контекстов, то видно, что поле для доверительной программной единицы названо PACKAGE:
SQL> SELECT * FROM all_context;
NAMESPACE SCHEMA PACKAGE
---------------------- ---------------------- ----------------------
MYCONTEXT SYS SET_CONTEXT_VALUE
Это не случайно: на практике часто более технологично установку контекста выполнять с помощью доверительного пакета, а не самостоятельной процедуры или функции. Ведь в состав пакета можно включить и прочие программные элементы, моделирующие логику предметной области.
Пример:
CONNECT / as sysdba
CREATE OR REPLACE CONTEXT mycontext USING mycontext_pckg;
CREATE OR REPLACE PACKAGE mycontext_pckg IS
PROCEDURE set_value ( par VARCHAR2, val VARCHAR2 );
FUNCTION get_value ( par VARCHAR2 ) RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY mycontext_pckg IS
PROCEDURE set_value ( par VARCHAR2, val VARCHAR2 )
IS
BEGIN
DBMS_SESSION.SET_CONTEXT ( 'mycontext', par, val );
END;
FUNCTION get_value ( par VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN SYS_CONTEXT ( 'mycontext', par );
END;
END;
/
GRANT EXECUTE ON mycontext_pckg TO scott;
Проверка:
SQL> CONNECT scott/tiger
Connected.
SQL> SELECT sys.mycontext_pckg.get_value ( 'sesame' ) FROM dual;
SYS.MYCONTEXT_PCKG.GET_VALUE('SESAME')
---------------------------------------------------------------------
SQL> EXECUTE sys.mycontext_pckg
.set_value (
'sesame', '123' )
PL/SQL procedure successfully completed.
SQL> SELECT sys.mycontext_pckg.get_value ( 'sesame' ) FROM dual;
SYS.MYCONTEXT_PCKG.GET_VALUE('SESAME')
---------------------------------------------------------------------
123
Пакет можно спроектировать и иначе, закрыв, например, для пользователя имя атрибута или даже контекста. Можно запрограммировать все, что требует логика предметной области.
Начальные значения атрибутов контекста
Выше было показано, как атрибуты контекстов выставляются в процессе сеанса связи с СУБД. Однако по подобию с CLIENTCONTEXT создаваемый контекст тоже можно обеспечить начальными значениями требуемых атрибутов из программы, открывающий сеанс. Это можно сделать
- средствами OCI (вызовы OCIAttrSet и OCISessionBegin) и
- через сервер имен (каталог) LDAP.
Возможность таких начальных присвоений обеспечивается соответствующими указаниями при создании контекста:
CREATE CONTEXT ... INITIALIZED EXTERNALLY и
CREATE CONTEXT ... INITIALIZED EXTERNALLY
Начальные значения атрибутов контекста для сеансов - очень сильное средство, позволяющее наложить на относительно примитивный механизм внутренних пользователей БД инфраструктуру внешних пользователей, часто более проработанную и универсальную. Например, сервер приложений может поддерживать очень много пользователей web-страниц, обладающих собственными свойствами. Моделировать их отдельными локальными для Oracle пользователями может оказаться крайне неэффективно, и в этом случае возможность предварительного установления атрибутов перед обращением к БД будет давать единственный ключ к построению решения.
Глобальный контекст сеанса («контекст приложения»)
«Обычный» контекст сеанса имеет своею областью действия отдельный сеанс. Иногда этого разработчику приложения вполне достаточно, а иногда хочется большего. Можно ли, например, запретить сеансу самостоятельно выставлять значение атрибута и предоставить ему только чтение, а значение задавать из другого сеанса? Такую возможность обеспечивает
глобальный контекст сеанса, называемый еще иногда
контекстом приложения. Пример его использования показан ниже:
CONNECT / AS SYSDBA
CREATE OR REPLACE CONTEXT globalcontext
USING globalcontext_pckg
ACCESSED GLOBALLY
/
CREATE OR REPLACE PACKAGE globalcontext_pckg AS
PROCEDURE set_value (
par VARCHAR2
, val VARCHAR2
, usr VARCHAR2
, usrid VARCHAR2
);
END;
/
CREATE OR REPLACE PACKAGE BODY globalcontext_pckg AS
PROCEDURE set_value (
par VARCHAR2
, val VARCHAR2
, usr VARCHAR2
, usrid VARCHAR2
)
AS
BEGIN
DBMS_SESSION.SET_CONTEXT (
'globalcontext'
, par
, val
, usr
, usrid
);
END;
END;
/
EXECUTE globalcontext_pckg.set_value -
( 'sesame' , '123', 'SCOTT', 'XYZ32A6' )
Проверка:
SQL> CONNECT scott/tiger
Connected.
SQL> SELECT SYS_CONTEXT ( 'globalcontext', 'sesame' ) FROM dual;
SYS_CONTEXT('GLOBALCONTEXT','SESAME')
--------------------------------------------------------------------
SQL> EXECUTE DBMS_SESSION.
SET_IDENTIFIER (
'XYZ32A6' );
PL/SQL procedure successfully completed.
SQL> SELECT SYS_CONTEXT ( 'globalcontext', 'sesame' ) FROM dual;
SYS_CONTEXT('GLOBALCONTEXT','SESAME')
--------------------------------------------------------------------
123
SQL> EXECUTE DBMS_SESSION.SET_IDENTIFIER ( 'XYZ32A6ZZZ' );
PL/SQL procedure successfully completed.
SQL> SELECT SYS_CONTEXT ( 'globalcontext', 'sesame' ) FROM dual;
SYS_CONTEXT('GLOBALCONTEXT','SESAME')
----------------------------------------------------------------------
Тут есть сразу несколько интересных новшеств.
- То, что контекст глобальный, было указано словами ACCESSED GLOBALLY при его создании.
- В процедуре DBMS_SESSION.SET_CONTEXT именно для глобального контекста существуют два дополнительных параметра. Первый сообщает, сеансам чьего пользователя будет доступен этот контекст (для каждого такого пользователя нужно будет выполнить отдельный вызов SET_CONTEXT), а второй - условное значение, которое необходимо будет сообщить для возможности прочитать установленное другим сеансом значения атрибута, своего рода пароль.
- Сообщение этого условного значения выполняется специальной процедурой DBMS_SESSION.SET_IDENTIFIER.
Таким образом, мало войти в СУБД под «правильным» пользователем; для того, чтобы получить в сеансе значение желаемого атрибута (глобального контекста), нужно будет еще сообщить условную строку. Излишне напоминать, что очевидным кандидатом на такую строку является cookie сеанса общения с web. И только благодаря этому, а также механизму избирательного доступа к частям таблицы в Oracle («виртуальные частные базы данных», VPD/FGAC) и возможности сервера приложений автоматически выдавать SET_IDENTIFIER при обращении к БД, многочисленные пользователи web, формально подключаемые к СУБД под одними и теми же именами пользователей Oracle, смогут увидеть в базе каждый собственные данные.