Created
June 15, 2018 01:15
-
-
Save jgebal/7c7b3f8e77056e8aa8d64d70cc7e9cab to your computer and use it in GitHub Desktop.
Little utility to get call procedure/function parameter names in PLSQL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create or replace package checker is | |
subtype t_exception_no is pls_integer range -20500..-20000; | |
c_null_value constant t_exception_no := -20001; | |
procedure raise_if_null(p_value varchar2); | |
end; | |
/ | |
create or replace package body checker is | |
type r_caller_info is record( | |
owner varchar2(250), | |
name varchar2(250), | |
line_no positive | |
); | |
function who_called( | |
p_level positiven := 1 | |
) return r_caller_info is | |
c_pattern constant varchar2(500) := '^((.*'||CHR(10)||'){'||(p_level+4)||'})(.*)'||CHR(10); | |
c_stack constant varchar2(32767) := dbms_utility.format_call_stack(); | |
c_stack_offset constant integer := 4; | |
l_stack_line varchar2(4000); | |
l_result r_caller_info; | |
begin | |
l_stack_line := regexp_substr( c_stack, c_pattern, 1, 1, '', 3); | |
if l_stack_line like '%.%' then | |
l_result.line_no := to_number( regexp_substr(l_stack_line, '(0x)?[0-9a-f]+\s+(\d+)', subexpression => 2) ); | |
l_result.owner := regexp_substr( l_stack_line, '([A-Za-z0-9$#_]+)\.([A-Za-z0-9$#_]|\.)+', subexpression => 1); | |
l_result.name := regexp_substr( l_stack_line, '([A-Za-z0-9$#_]+)\.([A-Za-z0-9$#_]+)', subexpression => 2); | |
end if; | |
-- dbms_output.put_line(c_stack); | |
-- dbms_output.put_line('l_result =('||l_result.owner||', '||l_result.name||', '||l_result.line_no||')' ); | |
return l_result; | |
end; | |
function get_source_line(p_caller r_caller_info) return varchar2 is | |
l_line_text varchar2(4000); | |
begin | |
select /*+ first_rows(4) */ | |
listagg(s.text) within group(order by s.line) | |
into l_line_text | |
from all_source s | |
where s.name = p_caller.name | |
and s.owner = p_caller.owner | |
and s.line between p_caller.line_no and p_caller.line_no + 4 | |
and s.type = 'PACKAGE BODY'; | |
-- dbms_output.put_line(l_line_text); | |
return l_line_text; | |
end; | |
function get_call_params( | |
p_line varchar2, | |
p_self_name varchar2 | |
) return varchar2 is | |
begin | |
return | |
translate( | |
regexp_substr( | |
p_line | |
, '\s*'||$$PLSQL_UNIT||'\.'||p_self_name||'\s*\(([^\)]*)\)' | |
, 1, 1, 'i', 1 | |
) | |
/*remove spaces, tabs and newlines*/ | |
,'a '||chr(9)||chr(10)||chr(13) | |
,'a' | |
); | |
end; | |
procedure raise_if_null(p_value varchar2) is | |
l_param_name varchar2(4000); | |
begin | |
if p_value is null then | |
l_param_name := get_call_params(get_source_line(who_called()),'RAISE_IF_NULL'); | |
raise_application_error(c_null_value,'Call parameter: "'||l_param_name||'" is null'); | |
end if; | |
end; | |
end; | |
/ | |
--Example: | |
create or replace type t_obj as object( | |
id integer, | |
name varchar2(100) | |
); | |
/ | |
create or replace package some_api is | |
procedure do_stuff(p_obj t_obj); | |
end; | |
/ | |
create or replace package body some_api is | |
procedure do_stuff(p_obj t_obj) | |
is | |
begin | |
checker.raise_if_null( | |
p_obj.id | |
); | |
commit; | |
exception | |
when others then | |
rollback; | |
raise; | |
end; | |
end; | |
/ | |
--Check it | |
begin | |
some_api.do_stuff(t_obj(null,null)); | |
end; | |
/ | |
--Outcomes | |
/************* | |
ORA-20001: Call parameter: "p_obj.id" is null | |
ORA-06512: at "UT3_TESTER.SOME_API", line 12 | |
ORA-06512: at "UT3_TESTER.CHECKER", line 67 | |
ORA-06512: at "UT3_TESTER.SOME_API", line 5 | |
ORA-06512: at line 2 | |
*************/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment