Вячеслав Любомудров
Member
Откуда: Владивосток
Сообщений: 11564
|
Я когда-то рисовал статью по этому поводу для наших программеров | Помимо обычного (приватного) контекста, начиная с версии 9.0.1 в Oracle появился глобальный контекст приложения. Он позволяет "разделять" (share) данные контекста между сессиями. Более того, значения в глобальном контексте сохраняются до перезагрузки БД или явной очистки.
Для разграничения доступа используются дополнительные атрибуты:
- USERNAME -- имя пользователя Oracle, под которым аутентифицирована сессия. Если это имя не указано (NULL) -- доступ к содержимому контекста получают все пользователи с соответствующим CL_IDENTIFIER
- CL_IDENTIFIER -- идентификатор клиента -- произвольное текстовое значение (до 64 байт), используемое для идентификации группы сессий, получающих доступ к данным контекста. Только при совпадении CL_IDENTIFIER у сессии, устанавливающей содержимое контекста и читающей это содержимое возможен доступ. При этом NULL=NULL.
Указывать значение USERNAME может только сессия, устанавлмвающая данные контекста. Для читающей сессии это значение есть значение функции USER.
Значение CL_IDENTIFIER могут устанавливать как пишущая сессия (dbms_session.set_context), указывая тем самым группу сессий, которые будут иметь доступ к этим данным, так и читающая (dbms_session.set_identifier), подключаясь к известной группе.
Например, можно передавать данные отчетам через глобальный контекст, установив нужный CL_IDENTIFIER в отчете, который передается как параметр отчета при запуске (USERNAME у них будет одинаковый).
В отношения чтения/записи все сессии равноправные -- если сессия имеет доступ к данным определенного глобального контекста (USERNAME, CL_IDENTIFIER), то она так же может менять/удалять/добавлять данные.
Возможные грабли
Данные в глобальном контексте не удаляются сами по себе и остаются болтаться до перезагрузки экземпляра или явной очистки. При этом все содержимое располагается в Shared pool в SGA - глобальной памяти для экземпляра БД. Размер ее, естественно, ограничен, поэтому при распухании места под глобальный контекст остальные структуры в Shared pool (кешированные SQL операторы, пакеты и другие PL/SQL блоки, а также записи и определения словаря данных) могут быть вытеснены из памяти, что приведет к резкому ухудшению производительности и в конце концов исчерпанию памяти. Поэтому основное правило при работе с глобальными контекстами: "ПОЕЛ - УБЕРИ ЗА СОБОЙ" (добавлял данные в глобальный контекст - очисти их с помощью операции clear).
Так же не забываем, что в глобальном контексте могут находиться одинаковые "переменные" (атрибуты) с разными значениями для разных CL_IDENTIFIER (даже для одинаковых USERNAME), что может внести некоторую путаницу. И что CL_IDENTIFIER равный NULL представляет собой обычное значение, как и любое другое, например 'PUPKIN'. Что тоже ясности не добавляет.
Ну и проблемы с секурностью - любой, кто видит данные контекста - может их изменить. Более того, изменение данных пользователем с определенным CL_IDENTIFIER (возможно NULL) для определенного атрибута удалит значение этого атрибута для ВСЕХ USERNAME для соответствующего CL_IDENTIFIER
И опять секурность - при очистке контекста (операция clear), для соответствующего CL_IDENTIFIER очищаются данные для ВСЕХ USERNAME
| Небольшой примерчик:tst> connect u1/u1@tst
Connected.
tst> create context gctx using gctx_set accessed globally;
Context created.
tst> create procedure gctx_set(field in varchar2, value in varchar2, cl_ident varchar2, usr varchar2 default user) as
2 begin dbms_session.set_context('GCTX', field, value, usr, cl_ident);
3 end;
4 /
Procedure created.
tst> exec u1.gctx_set('V1', 'User + Identifier', 'Id1', user)
PL/SQL procedure successfully completed.
tst> exec u1.gctx_set('V2', 'User with NULL Id', null, user)
PL/SQL procedure successfully completed.
tst> exec u1.gctx_set('V3', 'Any User + ID', 'Id2', null)
PL/SQL procedure successfully completed.
tst> exec u1.gctx_set('V4', 'Any User with NULL Id', null, null);
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 /* не видим, т.к. текущий CL_IDENTIFIER не установлен (NULL) */
V2 User with NULL Id
V3 /* аналогично */
V4 Any User with NULL I
tst> exec dbms_session.set_identifier('Id1');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 User + Identifier
V2 /* остальное не видим, т.к. больше с таким CL_IDENTIFIER ничего нет) */
V3
V4
tst> connect system/manager@tst
Connected.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 /* USER не совпадает */
V2 /* ничего не совпадает */
V3 /* USER по барабану, но CL_IDENTIFIER не совпадает */
V4 Any User with NULL I
tst> exec dbms_session.set_identifier('Id1');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1
V2 /* CL_IDENTIFIER совпадает, но USER нет */
V3
V4
tst> exec dbms_session.set_identifier('Id2');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1
V2
V3 Any User + ID
V4
tst> exec u1.gctx_set('V1', 'Updated', 'Id1', user)
/* для другого USER (SYSTEM) завели с тем же CL_IDENTIFIER одинаковый с U1 аттрибут */
PL/SQL procedure successfully completed.
tst> exec u1.gctx_set('V3', 'Updated', 'Id2', null)
/* изменили аттрибут для всех USER с тем же CL_IDENTIFIER */
PL/SQL procedure successfully completed.
tst> exec dbms_session.set_identifier('Id1');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 Updated /* свое видим */
V2
V3
V4
tst> exec dbms_session.set_identifier('Id2');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1
V2
V3 Updated
V4
tst> connect u1/u1@tst
Connected.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 /* не назначен CL_IDENTIFIER */
V2 User with NULL Id /* старое все осталось */
V3
V4 Any User with NULL I
tst> exec dbms_session.set_identifier('Id1');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1 /* !!! назначив такой же аттрибут другому USER с одинаковым CL_IDENTIFIER, мы затерли значения у всех остальных этой группы !!! */
V2
V3
V4
tst> exec dbms_session.set_identifier('Id2');
PL/SQL procedure successfully completed.
tst> select 'V1', substr(sys_context('GCTX', 'V1'), 1, 20) from dual union all
2 select 'V2', substr(sys_context('GCTX', 'V2'), 1, 20) from dual union all
3 select 'V3', substr(sys_context('GCTX', 'V3'), 1, 20) from dual union all
4 select 'V4', substr(sys_context('GCTX', 'V4'), 1, 20) from dual;
'V SUBSTR(SYS_CONTEXT('
-- --------------------
V1
V2
V3 Updated
V4
|