Oracle 12 PL/SQL 在触发器中检索存储过程名称

Posted

技术标签:

【中文标题】Oracle 12 PL/SQL 在触发器中检索存储过程名称【英文标题】:Oracle 12 PL/SQL retrieving a stored procedure name within a trigger 【发布时间】:2020-10-09 15:04:08 【问题描述】:

我有一个使用触发器的情况,我想记录存储过程的名称,该存储过程启动了堆栈存储过程调用,这些调用最终执行 INSERT 到触发器正在监视的表中。此触发器仅用于调试目的,其中有多个存储过程将在触发器正在监视的表中插入记录,并记录调用存储过程的名称将有助于识别发生错误情况的上下文。

约束:我们不希望(不允许)更改触发器正在监视的包装存储过程,因为这些是由第三方供应商提供的,如果这些存储过程被解包或更改,他们将不支持这些存储过程。

PL/SQL 中是否存在可在触发器中检查的可访问调用堆栈?如果是这样,我如何访问调用堆栈以获取所需的信息?


编辑:

在William Robertson 的评论提示下,我发现UTL_CALL_STACK 显然会给我一个初始过程的名称,调用如下:

UTL_CALL_STACK.concatenate_subprogram (
    UTL_CALL_STACK.subprogram (
        UTL_CALL_STACK.dynamic_depth
    )
)

这是我找到的描述UTL_CALL_STACK的链接:

https://oracle-base.com/articles/12c/utl-call-stack-12cr1

【问题讨论】:

这能回答你的问题吗? Get the name of the calling procedure or function in Oracle PL/SQL 这与我正在寻找的内容很接近,但它似乎在获取包中的过程名称方面存在限制,而且看起来我必须解析调用堆栈的输出 -我会尝试一下,看看我是否可以使这种方法起作用。谢谢 不幸的是,解析调用堆栈是唯一的选择:我不知道有任何简单的函数可以返回调用者的名称。 这有帮助吗? ***.com/a/50541599/230471 @WilliamRobertson - 我刚刚编辑了我的问题,以反映我根据您的评论发现的内容 - 谢谢! 【参考方案1】:

正如其他人在您的 cmets 中所说,唯一的选择是解析调用堆栈。我在下面创建了一个示例,说明如何通过触发器做到这一点。

演示表

CREATE TABLE test_ids
(
    id    NUMBER
);

CREATE TABLE insert_log
(
    insert_date          DATE,
    insert_value         NUMBER,
    call_stack           VARCHAR2 (4000),
    calling_procedure    VARCHAR2 (4000)
);

演示触发器

下面的触发器会将每个插入记录到insert_log 表中的test_ids 表中。您可以存储DBMS_UTILITY.format_call_stack 的完整堆栈以帮助您进行故障排除,也可以仅存储调用过程。在我搭建的demo中,insert的调用过程会一直保存在第5行(第4行是触发器),从第23行开始。

CREATE OR REPLACE TRIGGER test_ids_after_insert
    AFTER INSERT
    ON test_ids
    FOR EACH ROW
DECLARE
    l_stack   VARCHAR2 (4000) := DBMS_UTILITY.format_call_stack;
BEGIN
    INSERT INTO insert_log (insert_date,
                            insert_value,
                            call_stack,
                            calling_procedure)
         VALUES (SYSDATE,
                 :NEW.id,
                 l_stack,
                 SUBSTR (l_stack,
                           INSTR (l_stack,
                                  CHR (10),
                                  1,
                                  4)
                         + 23,
                           INSTR (l_stack,
                                  CHR (10),
                                  1,
                                  5)
                         - (  INSTR (l_stack,
                                     CHR (10),
                                     1,
                                     4)
                            + 23)));
END;
/

测试程序

只是为了演示触发器的一些不同输出

CREATE OR REPLACE PROCEDURE test_insert (p_num NUMBER)
IS
BEGIN
    INSERT INTO test_ids
         VALUES (p_num);
END;
/

测试插入

INSERT INTO test_ids
     VALUES (1);

DECLARE
    PROCEDURE insert_function (p_num NUMBER)
    IS
    BEGIN
        INSERT INTO test_ids
             VALUES (p_num);
    END;
BEGIN
    INSERT INTO test_ids
         VALUES (2);

    insert_function (3);
    test_insert (4);
END;
/

演示结果

这就是insert_log 的结果。如果插入语句自己执行,则不会有调用过程,匿名块被记录为匿名块,如果从过程/函数/包中调用,则将存储该对象的名称。

   INSERT_DATE    INSERT_VALUE                                          CALL_STACK                CALLING_PROCEDURE
______________ _______________ ___________________________________________________ ________________________________
09-OCT-20                    1 ----- PL/SQL Call Stack -----
                               object      line  object
                               handle    number  name
                               0x6ab98970         2  EJSTEST.TEST_IDS_AFTER_INSERT

09-OCT-20                    2 ----- PL/SQL Call Stack -----                       anonymous block
                               object      line  object
                               handle    number  name
                               0x6ab98970         2  EJSTEST.TEST_IDS_AFTER_INSERT
                               0x6727e250         9  anonymous block

09-OCT-20                    3 ----- PL/SQL Call Stack -----                       anonymous block
                               object      line  object
                               handle    number  name
                               0x6ab98970         2  EJSTEST.TEST_IDS_AFTER_INSERT
                               0x6727e250         5  anonymous block
                               0x6727e250        12  anonymous block

09-OCT-20                    4 ----- PL/SQL Call Stack -----                       procedure EJSTEST.TEST_INSERT
                               object      line  object
                               handle    number  name
                               0x6ab98970         2  EJSTEST.TEST_IDS_AFTER_INSERT
                               0x615b3ce0         4  procedure EJSTEST.TEST_INSERT
                               0x6727e250        13  anonymous block

【讨论】:

感谢您的建议 - 我根据在此链接上对我的问题的评论找到了 UTL_CALL_STACK:oracle-base.com/articles/12c/utl-call-stack-12cr1 - 您喜欢 UTL_CALL_STACK 的方式吗? 我没有使用 UTL_CALL_STACK 的经验,但它看起来确实是您正在寻找的可行解决方案。

以上是关于Oracle 12 PL/SQL 在触发器中检索存储过程名称的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE PL/SQL 触发器的编程问题

使用 PL\SQL Developer 7.0.2 调试 Oracle 触发器?

触发器中的 Oracle PL/SQL 游标

为啥在 PL/SQL Oracle 中尝试创建 INSTEAD OF 触发器时出现“错误的绑定变量”错误?

PL/SQL Oracle 错误处理

Oracle笔记4-pl/sql-分支/循环/游标/异常/存储/调用/触发器