有没有办法在Oracle中的包中获取当前正在执行的过程名称?
create or replace package test_pkg
as
procedure proc1;
end test_pkg;
create or replace package body test_pkg
as
procedure proc1
is
-- // Get the Procedure Name here?? //
end proc1;
end test_pkg;
答案 0 :(得分:5)
在12c中,当前子程序名称只是:
utl_call_stack.subprogram(1)(2);
当前包也可以从
获得utl_call_stack.subprogram(1)(1);
但通常更容易使用$$plsql_unit
。您还可以获取限定名称(package.procedure
):
utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
但是,我想不出任何过程或函数(或对象方法)需要自己名字的情况。此功能在日志记录过程中非常有用,在这种情况下,'谁叫我?'代码应该在记录器中,而不是在每个调用它的东西中重复。因此,我强烈建议在程序中避免任何'我是谁?'逻辑。相反,在记录器中放置这样的东西(需要12.1或更高版本):
create or replace procedure logdemo
as
k_calling_package constant varchar2(30) := utl_call_stack.subprogram(2)(1);
k_calling_subprog constant varchar2(30) := utl_call_stack.subprogram(2)(2);
begin
dbms_output.put_line
( $$plsql_unit ||
' called from package '||k_calling_package||', subprogram '||k_calling_subprog );
end logdemo;
不幸的是,由于你必须解析dbms_utility.format_call_stack
,因此在11g中有点复杂,因为这只会给你包名和行号(在换行符分隔的文本字符串中),你必须查询{ {1}}找到子程序名称。
如果您澄清它的用途,我可以发布一些11g代码。在我的11g记录器中,我发现根据all_source
等捕获dbms_utility.format_error_backtrace
和dbms_utility.format_call_stack
很有用,因此有一堆特定于日志记录的逻辑,您可能不需要如果您只是想以其他原因捕获当前的过程名称。
答案 1 :(得分:1)
如果它是一个独立的程序(即不是包中的程序),你会得到相当简单的答案:
SQL> create or replace procedure p_test is
2 begin
3 dbms_output.put_line('I am ' || $$plsql_unit);
4 null;
5 end;
6 /
Procedure created.
SQL> exec p_test
I am P_TEST
PL/SQL procedure successfully completed.
SQL>
对于包装来说,事情并不那么简单。上面的选项(使用$$plsql_unit
)返回包体名称,而不是过程的名称:
SQL> create or replace package pkg_test as
2 procedure p_test_in_pkg;
3 end;
4 /
Package created.
SQL> create or replace package body pkg_test as
2 procedure p_test_in_pkg is
3 begin
4 dbms_output.put_line('Packaged procedure whose name is ' || $$plsql_unit);
5 null;
6 end;
7 end;
8 /
Package body created.
SQL> exec pkg_test.p_test_in_pkg;
Packaged procedure whose name is PKG_TEST --> should have been "p_test_in_pkg"
PL/SQL procedure successfully completed.
以下功能似乎有效。作者是 garbuya ,来源是OTN论坛的讨论Efficient WHO_AM_I and WHO_CALLED_ME讨论(日期为2010年)。
create or replace
FUNCTION FN_WHO_AM_I ( p_lvl NUMBER DEFAULT 0) RETURN VARCHAR2
IS
/***********************************************************************************************
FN_WHO_AM_I returns the full ORACLE name of your object including schema and package names
--
FN_WHO_AM_I(0) - returns the name of your object
FN_WHO_AM_I(1) - returns the name of calling object
FN_WHO_AM_I(2) - returns the name of object, who called calling object
etc., etc., etc.... Up to to he highest level
-------------------------------------------------------------------------------------------------
Copyrigth GARBUYA 2010
*************************************************************************************************/
TYPE str_varr_t IS VARRAY(2) OF CHAR(1);
TYPE str_table_t IS TABLE OF VARCHAR2(256);
TYPE num_table_t IS TABLE OF NUMBER;
v_stack VARCHAR2(2048) DEFAULT UPPER(dbms_utility.format_call_stack);
v_tmp_1 VARCHAR2(1024);
v_tmp_2 VARCHAR2(1024);
v_pkg_name VARCHAR2(32);
v_obj_type VARCHAR2(32);
v_owner VARCHAR2(32);
v_idx NUMBER := 0;
v_pos1 NUMBER := 0;
v_pos2 NUMBER := 0;
v_line_nbr NUMBER := 0;
v_blk_cnt NUMBER := 0;
v_str_len NUMBER := 0;
v_bgn_cnt NUMBER := 0;
v_end_cnt NUMBER := 0;
it_is_comment BOOLEAN := FALSE;
it_is_literal BOOLEAN := FALSE;
v_literal_arr str_varr_t := str_varr_t ('''', '"');
v_blk_bgn_tbl str_table_t := str_table_t (' IF ' , ' LOOP ' , ' CASE ', ' BEGIN ');
v_tbl str_table_t := str_table_t();
v_blk_bgn_len_tbl num_table_t := num_table_t();
BEGIN
v_stack := SUBSTR(v_stack,INSTR(v_stack,CHR(10),INSTR(v_stack,'FN_WHO_AM_I'))+1)||'ORACLE'; -- skip myself
FOR v_pos2 in 1 .. p_lvl LOOP -- advance to the input level
v_pos1 := INSTR(v_stack, CHR(10));
v_stack := SUBSTR(v_stack, INSTR(v_stack, CHR(10)) + 1);
END LOOP;
v_pos1 := INSTR(v_stack, CHR(10));
IF v_pos1 = 0 THEN
RETURN (v_stack);
END IF;
v_stack := SUBSTR(v_stack, 1, v_pos1 - 1); -- get only current level
v_stack := TRIM(SUBSTR(v_stack, instr(v_stack, ' '))); -- cut object handle
v_line_nbr := TO_NUMBER(SUBSTR(v_stack, 1, instr(v_stack, ' ') - 1)); -- get line number
v_stack := TRIM(SUBSTR(v_stack, instr(v_stack, ' '))); -- cut line number
v_pos1 := INSTR(v_stack, ' BODY');
IF v_pos1 = 0 THEN
RETURN (v_stack);
END IF;
v_pos1 := INSTR(v_stack, ' ', v_pos1 + 2); -- find end of object type
v_obj_type := SUBSTR(v_stack, 1, v_pos1 - 1); -- get object type
v_stack := TRIM(SUBSTR(v_stack, v_pos1 + 1)); -- get package name
v_pos1 := INSTR(v_stack, '.');
v_owner := SUBSTR(v_stack, 1, v_pos1 - 1); -- get owner
v_pkg_name := SUBSTR(v_stack, v_pos1 + 1); -- get package name
v_blk_cnt := 0;
it_is_literal := FALSE;
--
FOR v_idx in v_blk_bgn_tbl.FIRST .. v_blk_bgn_tbl.LAST
LOOP
v_blk_bgn_len_tbl.EXTEND(1);
v_blk_bgn_len_tbl (v_blk_bgn_len_tbl.last) := LENGTH(v_blk_bgn_tbl(v_idx));
END LOOP;
--
FOR src
IN ( SELECT ' '||REPLACE(TRANSLATE(UPPER(text), ';('||CHR(10), ' '),'''''',' ') ||' ' text
FROM all_source
where owner = v_owner
and name = v_pkg_name
and type = v_obj_type
and line < v_line_nbr
ORDER BY line
)
LOOP
v_stack := src.text;
IF it_is_comment THEN
v_pos1 := INSTR (v_stack, '*/');
IF v_pos1 > 0 THEN
v_stack := SUBSTR (v_stack, v_pos1 + 2);
it_is_comment := FALSE;
ELSE
v_stack := ' ';
END IF;
END IF;
--
IF v_stack != ' ' THEN
--
v_pos1 := INSTR (v_stack, '/*');
WHILE v_pos1 > 0 LOOP
v_tmp_1 := SUBSTR (v_stack, 1, v_pos1 - 1);
v_pos2 := INSTR (v_stack, '*/');
IF v_pos2 > 0 THEN
v_tmp_2 := SUBSTR (v_stack, v_pos2 + 2);
v_stack := v_tmp_1||v_tmp_2;
ELSE
v_stack := v_tmp_1;
it_is_comment := TRUE;
END IF;
v_pos1 := INSTR (v_stack, '/*');
END LOOP;
--
IF v_stack != ' ' THEN
v_pos1 := INSTR (v_stack, '--');
IF v_pos1 > 0 THEN
v_stack := SUBSTR (v_stack, 1, v_pos1 - 1);
END IF;
--
IF v_stack != ' ' THEN
FOR v_idx in v_literal_arr.FIRST .. v_literal_arr.LAST
LOOP
v_pos1 := INSTR (v_stack, v_literal_arr (v_idx) );
WHILE v_pos1 > 0 LOOP
v_pos2 := INSTR (v_stack, v_literal_arr (v_idx), v_pos1 + 1);
IF v_pos2 > 0 THEN
v_tmp_1 := SUBSTR (v_stack, 1, v_pos1 - 1);
v_tmp_2 := SUBSTR (v_stack, v_pos2 + 1);
v_stack := v_tmp_1||v_tmp_2;
ELSE
IF it_is_literal THEN
v_stack := SUBSTR (v_stack, v_pos1 + 1);
it_is_literal := FALSE;
ELSE
v_stack := SUBSTR (v_stack, 1, v_pos1 - 1);
it_is_literal := TRUE;
END IF;
END IF;
v_pos1 := INSTR (v_stack, v_literal_arr (v_idx) );
END LOOP;
END LOOP;
--
IF v_stack != ' ' THEN
WHILE INSTR (v_stack, ' ') > 0
LOOP
v_stack := REPLACE(v_stack, ' ', ' ');
END LOOP;
v_stack := REPLACE(v_stack, ' END IF ', ' END ');
v_stack := REPLACE(v_stack, ' END LOOP ', ' END ');
--
IF v_stack != ' ' THEN
v_stack := ' '||v_stack;
v_pos1 := INSTR(v_stack, ' FUNCTION ') + INSTR(v_stack, ' PROCEDURE ');
IF v_pos1 > 0 THEN
v_obj_type := TRIM(SUBSTR(v_stack, v_pos1 + 1, 9)); -- get object type
v_stack := TRIM(SUBSTR(v_stack, v_pos1 + 10))||' '; -- cut object type
v_stack := SUBSTR(v_stack, 1, INSTR(v_stack, ' ') - 1 ); -- get object name
v_tbl.EXTEND(1);
v_tbl (v_tbl.last) := v_obj_type||' '||v_owner||'.'||v_pkg_name||'.'||v_stack;
END IF;
--
v_pos1 := 0;
v_pos2 := 0;
v_tmp_1 := v_stack;
v_tmp_2 := v_stack;
FOR v_idx in v_blk_bgn_tbl.FIRST .. v_blk_bgn_tbl.LAST
LOOP
v_str_len := NVL(LENGTH(v_tmp_1),0);
v_tmp_1 := REPLACE(v_tmp_1,v_blk_bgn_tbl(v_idx), NULL);
v_bgn_cnt := NVL(LENGTH(v_tmp_1), 0);
v_pos1 := v_pos1 + (v_str_len - v_bgn_cnt)/v_blk_bgn_len_tbl(v_idx);
v_str_len := NVL(LENGTH(v_tmp_2),0);
v_tmp_2 := REPLACE(v_tmp_2,' END ', NULL);
v_end_cnt := NVL(LENGTH(v_tmp_2), 0);
v_pos2 := v_pos2 + (v_str_len - v_end_cnt)/5; --- 5 is the length(' END ')
END LOOP;
IF v_pos1 > v_pos2 THEN
v_blk_cnt := v_blk_cnt + 1;
ELSIF v_pos1 < v_pos2 THEN
v_blk_cnt := v_blk_cnt - 1;
IF v_blk_cnt = 0 AND v_tbl.COUNT > 0 THEN
v_tbl.DELETE(v_tbl.last);
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END LOOP;
RETURN CASE v_tbl.COUNT WHEN 0 THEN 'UNKNOWN' ELSE v_tbl(v_tbl.LAST) END;
END;
/
测试:
SQL> create or replace package body pkg_test as
2 procedure p_test_in_pkg is
3 begin
4 dbms_output.put_line('Packaged procedure, using $$plsql_unit = ' || $$plsql_unit);
5 dbms_output.put_line('FN_WHO_AM_I = ' || fn_who_am_i);
6 null;
7 end;
8 end;
9 /
Package body created.
SQL> exec pkg_test.p_test_in_pkg;
Packaged procedure, using $$plsql_unit = PKG_TEST --> this one is wrong (package body name) ...
FN_WHO_AM_I = PROCEDURE SCOTT.PKG_TEST.P_TEST_IN_PKG --> ... but this one is OK
PL/SQL procedure successfully completed.
SQL>
答案 2 :(得分:1)
使用从这里借来的想法的方法:http://www.orafaq.com/forum/t/173023/
注意:这仅适用于Oracle 12c。对于11g,它只给出包名。
创建一个名为show_caller
的过程。这使用OWA_UTIL.WHO_CALLED_ME
程序。
create or replace procedure show_caller
as
l_owner varchar2(200);
l_name varchar2(200);
l_lineno number;
l_caller varchar2(200);
begin
OWA_UTIL.WHO_CALLED_ME (l_owner, l_name,l_lineno,l_caller);
dbms_output.put_line('Inside '||l_name);
end;
/
现在,您可以将包写为:
CREATE OR replace PACKAGE test_pkg
AS
PROCEDURE proc1;
END test_pkg;
/
CREATE OR replace PACKAGE BODY test_pkg
AS
PROCEDURE Proc1
AS
BEGIN
show_caller; -- proc1 calls show_caller
END proc1;
END test_pkg;
/
执行。
SET SERVEROUTPUT ON
BEGIN
Test_pkg.Proc1;
END;
/
Inside TEST_PKG.PROC1
请注意,这将打印过程的名称。如果要将其用作变量,请将l_name
(如果需要,还有其他人)作为OUT
变量从show_caller
传递
Live SQL Demo(需要免费的OTN帐户)
另一个选择是在程序中使用OWA_UTIL.GET_PROCEDURE
函数:但在这种情况下,它似乎对我不起作用。我很乐意从专家那里了解更多。
答案 3 :(得分:0)
获取包中当前方法名称的最简单方法是将其从调用堆栈获取为本地变量,
CurrentMethodName varchar2(128) := regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',1,1,'i',1);
您还可以使用相同的方法将包名称获取到包级私有变量:
CurrentPackageName varchar2(128) := regexp_substr(
dbms_utility.format_call_stack,
'package\sbody\s(((\w)+[.])?(\w+))',1,1,'i',4);
此方法在所有包含regexp函数的Oracle DB版本中都能正常工作
这是一个很小的样本:
create or replace package body MyPackage as
CurrentPackageName varchar2(128) := regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',1,1,'i',1);
procedure MyMethod as
CurrentMethodName varchar2(128) := regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',1,1,'i',1);
begin
<...>
end;
end;
如您所见,我获得当前程序包名称的方式与之相同: