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 在触发器中检索存储过程名称的主要内容,如果未能解决你的问题,请参考以下文章
使用 PL\SQL Developer 7.0.2 调试 Oracle 触发器?