目前在Oracle中的包中执行过程名称

时间:2018-05-25 20:19:16

标签: oracle plsql oracle11g

有没有办法在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;

4 个答案:

答案 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_backtracedbms_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;

如您所见,我获得当前程序包名称的方式与之相同: