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,然后(可能)在将控制权返回给调用者之前恢复其值。