ORA-06553: PLS-221: 'A_VAR' is not a procedure or is undefined
Unfortunately it is not possible to access such a variable directly in SQL. The solution would be to create a getter function which returns value of the variable.
CREATE OR REPLACE PACKAGE onevar IS a_var VARCHAR2(10) DEFAULT 'Hello'; a_date DATE DEFAULT SYSDATE; a_nr NUMBER DEFAULT 100; FUNCTION get_a_var RETURN VARCHAR2; END; / CREATE OR REPLACE PACKAGE BODY onevar IS FUNCTION get_a_var RETURN VARCHAR2 IS BEGIN RETURN (a_var); END; END; /
We could then use this function in a select statement
The advantage of this approach is that we can define the variable as private, which is a good idea from a security point of view, but we would have to define a getter function for each variable which could be quite tedious. If it is a third party package we might not be able to change the source code because of copyright reasons. So I got to think about another, more generic, way to achieve the same effect.
The best alternative solution I could think of, which could be made universal, is by using an application context. I created a package which accepts a package variable name (which has to be public for obvious reasons) and then use dynamic PL/SQL to define a context variable and then return it from a function.
First the context needs to be created:
CREATE OR REPLACE CONTEXT get_any_var_ctx USING get_any_var;
The package get_any_var is authorized to set the context. After that I created the package that sets the context, fetches it and returns it from a function:
CREATE OR REPLACE PACKAGE get_any_var IS PROCEDURE set_context(p_name VARCHAR2 ,p_value VARCHAR2); FUNCTION get_str(p_var_name VARCHAR2) RETURN VARCHAR2; END; / CREATE OR REPLACE PACKAGE BODY get_any_var IS PROCEDURE set_context(p_name VARCHAR2 ,p_value VARCHAR2) IS BEGIN -- Create a session with a previously defined context. dbms_session.set_context('GET_ANY_VAR_CTX' ,p_name ,p_value); END; FUNCTION get_str(p_var_name VARCHAR2) RETURN VARCHAR2 IS BEGIN EXECUTE IMMEDIATE 'begin get_any_var.set_context(''l_temp_var'',' || p_var_name || '); end;'; RETURN (SYS_CONTEXT('GET_ANY_VAR_CTX' ,'l_temp_var')); END; END; /
Now it is possible to use any public package variable or constant in a select statement:
The package can be extended with multiple functions for each data type, eg. NUMBER, DATE etc.
Of course there are some caveats:
- The schema under which the package is created must have CREATE ANY CONTEXT privilege, and probably also DELETE ANY CONTEXT.
- Because the variables are public they can be set by anyone who has execute rights on the package.
- There could be a negative impact on performance.
- Because of the dynamic SQL typo’s will not be noticed at compile time.
- Using the wrong function could lead to implicit type conversions unless extensive error handling is added to the package.
CREATE OR REPLACE PACKAGE get_any_var IS PROCEDURE set_context(p_name VARCHAR2 ,p_value VARCHAR2); FUNCTION get_nr(p_var_name VARCHAR2) RETURN NUMBER; FUNCTION get_str(p_var_name VARCHAR2) RETURN VARCHAR2; FUNCTION get_date(p_var_name VARCHAR2 ,p_date_format VARCHAR2 DEFAULT 'dd-mm-yyyy hh24:mi:ss') RETURN DATE; END; / CREATE OR REPLACE PACKAGE BODY get_any_var IS PROCEDURE set_context(p_name VARCHAR2 ,p_value VARCHAR2) IS BEGIN -- Create a session with a previously defined context. dbms_session.set_context('GET_ANY_VAR_CTX' ,p_name ,p_value); END; FUNCTION get_nr(p_var_name VARCHAR2) RETURN NUMBER IS BEGIN EXECUTE IMMEDIATE 'begin get_any_var.set_context(''l_temp_var'',' || p_var_name || '); end;'; RETURN (TO_NUMBER(SYS_CONTEXT('GET_ANY_VAR_CTX' ,'l_temp_var'))); END; FUNCTION get_str(p_var_name VARCHAR2) RETURN VARCHAR2 IS BEGIN EXECUTE IMMEDIATE 'begin get_any_var.set_context(''l_temp_var'',' || p_var_name || '); end;'; RETURN (SYS_CONTEXT('GET_ANY_VAR_CTX' ,'l_temp_var')); END; FUNCTION get_date(p_var_name VARCHAR2 ,p_date_format VARCHAR2 DEFAULT 'dd-mm-yyyy hh24:mi:ss') RETURN DATE IS BEGIN EXECUTE IMMEDIATE 'begin get_any_var.set_context(''l_temp_var'', to_char(' || p_var_name || ',''' || p_date_format || ''')); end;'; RETURN (TO_DATE(SYS_CONTEXT('GET_ANY_VAR_CTX' ,'l_temp_var') ,p_date_format)); END; END; /
No comments:
Post a Comment