Oracle DBMS_SESSION SET_CONTEXT 不存储值
Oracle DBMS_SESSION SET_CONTEXT does not store a value
我正在尝试在 Oracle 中生成一个系统,其中上下文存储一个值;如果在任何会话中更新 table (update/insert/delete),则应增加该值。我遇到的问题是,即使我确定我已经正确设置了它,它似乎也不起作用——上下文似乎并没有真正存储值。我正在使用 Oracle 11.2.0.1.0。
最小可能的例子:
我有一个上下文(应该使用 ACCESSED GLOBALLY 子句,以便所有 Oracle 会话都可以访问这些值,这正是我想要的):
CREATE OR REPLACE CONTEXT MM_CONTEXT USING PCKG_TESTGLOBALS ACCESSED GLOBALLY;
我有调试table:
CREATE TABLE DATALOG (
DATALOG_SEQ NUMBER,
AT_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
MESSAGE VARCHAR2(4000)
);
我有序列支持 DATALOG
table:
CREATE SEQUENCE SQ_DATALOG;
现在需要 PCKG_TESTGLOBALS 包,它可以访问 MM_CONTEXT
上下文并可以更新其中的元素:
CREATE OR REPLACE PACKAGE PCKG_TESTGLOBALS IS
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2);
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2);
FUNCTION GetTABLEID RETURN NUMBER;
END PCKG_TESTGLOBALS;
/
CREATE OR REPLACE PACKAGE BODY PCKG_TESTGLOBALS IS
CONTEXT_NAME CONSTANT VARCHAR2(100) := 'MM_CONTEXT';
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO DATALOG(DATALOG_SEQ, MESSAGE) VALUES (SQ_DATALOG.NEXTVAL, FunctionName || ':' || Msg);
COMMIT;
END;
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2) IS
ActualValue VARCHAR2(10000);
BEGIN
Log('SetParameter', 'ENTERED');
Log('SetParameter', 'SETTING "' || p_name || '" TO "' || p_value || '"');
DBMS_SESSION.SET_CONTEXT(CONTEXT_NAME, p_name, p_value);
ActualValue := SYS_CONTEXT(CONTEXT_NAME, p_name);
Log('SetParameter', 'READ "' || p_name || '" AS "' || ActualValue || '"');
Log('SetParameter', 'EXITED');
END;
PROCEDURE Initialise IS
iTmp NUMBER;
BEGIN
Log('Initialise', 'ENTERED');
IF SYS_CONTEXT(CONTEXT_NAME, 'LOWNID') IS NULL THEN
iTmp := DBMS_RANDOM.RANDOM;
Log('Initialise', '"LOWNID" has no value, writing "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
END IF;
Log('Initialise', 'EXITED');
END;
FUNCTION GetTABLEID RETURN NUMBER IS
ReadValue VARCHAR2(32767);
BEGIN
Log('GetTABLEID', 'ENTERED');
ReadValue := SYS_CONTEXT(CONTEXT_NAME, 'LOWNID');
Log('GetTABLEID', 'READ VALUE OF "LOWNID" AS "' || ReadValue || '"');
Log('GetTABLEID', 'EXITED');
RETURN TO_NUMBER(ReadValue);
END;
BEGIN
Initialise;
END PCKG_TESTGLOBALS;
/
所以解释一下PCKG_TESTGLOBALS
中的函数:
Log
- 出于调试原因记录一条消息。
SetParameter
- 使用 name/value 对并使用 DBMS_SESSION.SET_CONTEXT
将其存储在 MM_CONTEXT
中
Initialise
- 对于会话变量 LOWNID
,它检查变量是否为 null,如果是,则使用 SetParameter
将其设置为随机值。 Initialise
在包首次在会话中使用时调用。
GetTABLEID
- 这个 returns 存储在会话变量中的值 LOWNID
.
最后在 table LOWN
上有一个名为 TR_ONDML_TL_LOWN
的触发器 - 这里的结构无关紧要,任何 table 都可以 -并在任何 DML、INSERT 或 UPDATE 或 DELETE 之后触发。
CREATE OR REPLACE TRIGGER TR_ONDML_TL_LOWN
AFTER INSERT OR UPDATE OR DELETE ON LOWN
DECLARE
iTmp NUMBER;
BEGIN
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'ENTERED');
iTmp := PCKG_TESTGLOBALS.GetTABLEID;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Read Value "' || iTmp || '"');
iTmp := NVL(iTmp, 1) + 1;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Updated Value "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'EXITED');
END TR_ONDML_TL_LOWN;
/
这个触发器的目的是这样的:每当 table LOWN
更新时,会话变量 LOWNID
的值被读回,并向其添加 1,并写回。
如果在新会话中我对 LOWN
table 进行了几次连续的更新,我会在调试中得到这些结果 table (SELECT MESSAGE FROM DATALOG ORDER BY DATALOG_SEQ
)
Initialise:ENTERED
Initialise:"LOWNID" has no value, writing "805223597"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "805223597"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
Initialise:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
从示例中可以看出,它正确地将值传递给DBMS_SESSION.SET_CONTEXT
,但它似乎根本没有存储该值。我究竟做错了什么?谢谢。
要让会话查看全局可访问上下文的值,其 client_identifier
(根据 SYS_CONTEXT('userenv','client_identifier')
)必须与调用 client_id
参数匹配=13=].
如果您的 set_context
调用没有设置 client_id
它默认为 NULL;在这种情况下,如果会话 client_identifier
也为 NULL,会话将仅看到新值。
如果会话为 client_identifier
使用任何特定值,您必须在对 set_context
.
的调用中使用相同的值
在您的情况下,您需要一个所有会话都可以访问的全局变量;如果您的会话正在获取 client_identifier
的随机值,您可能需要在 运行 您的代码之前将其设置为 NULL,然后(可能)在将控制权返回给调用者之前恢复其值。
我正在尝试在 Oracle 中生成一个系统,其中上下文存储一个值;如果在任何会话中更新 table (update/insert/delete),则应增加该值。我遇到的问题是,即使我确定我已经正确设置了它,它似乎也不起作用——上下文似乎并没有真正存储值。我正在使用 Oracle 11.2.0.1.0。
最小可能的例子:
我有一个上下文(应该使用 ACCESSED GLOBALLY 子句,以便所有 Oracle 会话都可以访问这些值,这正是我想要的):
CREATE OR REPLACE CONTEXT MM_CONTEXT USING PCKG_TESTGLOBALS ACCESSED GLOBALLY;
我有调试table:
CREATE TABLE DATALOG (
DATALOG_SEQ NUMBER,
AT_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
MESSAGE VARCHAR2(4000)
);
我有序列支持 DATALOG
table:
CREATE SEQUENCE SQ_DATALOG;
现在需要 PCKG_TESTGLOBALS 包,它可以访问 MM_CONTEXT
上下文并可以更新其中的元素:
CREATE OR REPLACE PACKAGE PCKG_TESTGLOBALS IS
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2);
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2);
FUNCTION GetTABLEID RETURN NUMBER;
END PCKG_TESTGLOBALS;
/
CREATE OR REPLACE PACKAGE BODY PCKG_TESTGLOBALS IS
CONTEXT_NAME CONSTANT VARCHAR2(100) := 'MM_CONTEXT';
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO DATALOG(DATALOG_SEQ, MESSAGE) VALUES (SQ_DATALOG.NEXTVAL, FunctionName || ':' || Msg);
COMMIT;
END;
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2) IS
ActualValue VARCHAR2(10000);
BEGIN
Log('SetParameter', 'ENTERED');
Log('SetParameter', 'SETTING "' || p_name || '" TO "' || p_value || '"');
DBMS_SESSION.SET_CONTEXT(CONTEXT_NAME, p_name, p_value);
ActualValue := SYS_CONTEXT(CONTEXT_NAME, p_name);
Log('SetParameter', 'READ "' || p_name || '" AS "' || ActualValue || '"');
Log('SetParameter', 'EXITED');
END;
PROCEDURE Initialise IS
iTmp NUMBER;
BEGIN
Log('Initialise', 'ENTERED');
IF SYS_CONTEXT(CONTEXT_NAME, 'LOWNID') IS NULL THEN
iTmp := DBMS_RANDOM.RANDOM;
Log('Initialise', '"LOWNID" has no value, writing "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
END IF;
Log('Initialise', 'EXITED');
END;
FUNCTION GetTABLEID RETURN NUMBER IS
ReadValue VARCHAR2(32767);
BEGIN
Log('GetTABLEID', 'ENTERED');
ReadValue := SYS_CONTEXT(CONTEXT_NAME, 'LOWNID');
Log('GetTABLEID', 'READ VALUE OF "LOWNID" AS "' || ReadValue || '"');
Log('GetTABLEID', 'EXITED');
RETURN TO_NUMBER(ReadValue);
END;
BEGIN
Initialise;
END PCKG_TESTGLOBALS;
/
所以解释一下PCKG_TESTGLOBALS
中的函数:
Log
- 出于调试原因记录一条消息。
SetParameter
- 使用 name/value 对并使用 DBMS_SESSION.SET_CONTEXT
将其存储在 MM_CONTEXT
Initialise
- 对于会话变量 LOWNID
,它检查变量是否为 null,如果是,则使用 SetParameter
将其设置为随机值。 Initialise
在包首次在会话中使用时调用。
GetTABLEID
- 这个 returns 存储在会话变量中的值 LOWNID
.
最后在 table LOWN
上有一个名为 TR_ONDML_TL_LOWN
的触发器 - 这里的结构无关紧要,任何 table 都可以 -并在任何 DML、INSERT 或 UPDATE 或 DELETE 之后触发。
CREATE OR REPLACE TRIGGER TR_ONDML_TL_LOWN
AFTER INSERT OR UPDATE OR DELETE ON LOWN
DECLARE
iTmp NUMBER;
BEGIN
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'ENTERED');
iTmp := PCKG_TESTGLOBALS.GetTABLEID;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Read Value "' || iTmp || '"');
iTmp := NVL(iTmp, 1) + 1;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Updated Value "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'EXITED');
END TR_ONDML_TL_LOWN;
/
这个触发器的目的是这样的:每当 table LOWN
更新时,会话变量 LOWNID
的值被读回,并向其添加 1,并写回。
如果在新会话中我对 LOWN
table 进行了几次连续的更新,我会在调试中得到这些结果 table (SELECT MESSAGE FROM DATALOG ORDER BY DATALOG_SEQ
)
Initialise:ENTERED
Initialise:"LOWNID" has no value, writing "805223597"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "805223597"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
Initialise:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
从示例中可以看出,它正确地将值传递给DBMS_SESSION.SET_CONTEXT
,但它似乎根本没有存储该值。我究竟做错了什么?谢谢。
要让会话查看全局可访问上下文的值,其 client_identifier
(根据 SYS_CONTEXT('userenv','client_identifier')
)必须与调用 client_id
参数匹配=13=].
如果您的 set_context
调用没有设置 client_id
它默认为 NULL;在这种情况下,如果会话 client_identifier
也为 NULL,会话将仅看到新值。
如果会话为 client_identifier
使用任何特定值,您必须在对 set_context
.
在您的情况下,您需要一个所有会话都可以访问的全局变量;如果您的会话正在获取 client_identifier
的随机值,您可能需要在 运行 您的代码之前将其设置为 NULL,然后(可能)在将控制权返回给调用者之前恢复其值。