【问题标题】:Currently Executing Procedure Name within the Package包中当前正在执行的过程名称
【发布时间】:2018-11-05 06:34:15
【问题描述】:

有没有办法在包中获取当前正在执行的过程名称?

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;

【问题讨论】:

    标签: oracle plsql oracle11g


    【解决方案1】:

    在 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(128) := utl_call_stack.subprogram(2)(1);
        k_calling_subprog constant varchar2(128) := 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;
    

    不幸的是,它在 11g 中稍微复杂一点,因为您必须解析 dbms_utility.format_call_stack,并且因为这只会为您提供包名称和行号(在换行符分隔的文本字符串中),然后您必须查询 all_source找到子程序名称。

    如果你澄清它的用途,我可以发布一些 11g 代码。在我的 11g 记录器中,我发现捕获 dbms_utility.format_error_backtracedbms_utility.format_call_stack 取决于 sqlcode 等很有用,所以有一堆特定于日志记录的逻辑,如果你只想捕获这些逻辑,你可能不需要出于其他原因的当前过程名称。

    【讨论】:

    • 我试过 utl_call_stack.subprogram(1)(2);使用 Oracle 12c,它简单地没有输出任何东西——但也没有引发错误。是否有某种机制需要在输出之前打开?像 DBMS_OUTPUT.Enable;
    • 其他dbms_output 调用是否产生输出?捕获结果的其他方法是否有效?
    • 我试过了:L_Text := Utl_Call_Stack.Subprogram(1)(1) || '。' || Utl_Call_Stack.Subprogram(1)(2);并将其写入记录器文件。这没有用。以其他方式写入 Logger 确实有效。我禁用了 DBMS_OUTPUT。
    • 在告诉我我的代码不起作用之前检查您的dbms_output 设置应该很简单。
    【解决方案2】:

    如果它是一个独立的过程(即不是包中的那个),你会很容易得到答案:

    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>
    

    【讨论】:

    • 很高兴看到您在 2011 年回答这个问题,在我在答案中提到的链接中:-)
    • 男孩,2011 年……那时我年轻英俊,@Kaushik。现在我很帅:)
    【解决方案3】:

    使用从这里借来的想法的方法: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 函数:但在这种情况下它似乎对我不起作用。我很高兴能从专家那里了解更多信息。

    【讨论】:

    • 嘿,这将返回包名 Test_pkg 而不是 Test_pkg.Proc1;
    • @sqlpractice :看起来这在 Oracle 12c 中有效,但在 11g 中无效。
    【解决方案4】:

    我的快速示例:

    create or replace package body MyPackage as
    
        procedure PrintMethodName is
            ThisMethod#FullName varchar2(128) := regexp_substr(
                dbms_utility.format_call_stack, 
                'package\s+body\s+([^[:space:]]+)',
                1,1,'i',1); -- schema.package.method
            ThisMethod#CurrentSchemaName varchar2(128) := regexp_substr(
                regexp_substr(
                    dbms_utility.format_call_stack, 
                    'package\s+body\s+([^[:space:]]+)',
                    1,1,'i',1), 
                '[^.]+[.][^.]+$'); -- package.method
            ThisMethod#MethodName varchar2(128) := regexp_substr(
                regexp_substr(
                    dbms_utility.format_call_stack, 
                    'package\s+body\s+([^[:space:]]+)',
                    1,1,'i',1), 
                '[^.]+$'); -- method
        begin
            dbms_output.put_line(dbms_utility.format_call_stack);
            dbms_output.put_line(ThisMethod#FullName);
            dbms_output.put_line(ThisMethod#CurrentSchemaName);
            dbms_output.put_line(ThisMethod#MethodName);
        end;
    
    end;
    /
    

    【讨论】:

      猜你喜欢
      • 2011-08-27
      • 2012-07-28
      • 1970-01-01
      • 2010-10-03
      • 2023-03-10
      • 2010-09-07
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      相关资源
      最近更新 更多