DBMS_SQL is a powerful PL/SQL package that has been available for a long time. This document demonstrates how to use it and a clever way of using packaged variables.
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
DBMS_SQL
1. DBMS_SQL Revisited
The DBMS_SQL package supplied by Oracle Corporation is a powerful tool to
evaluate and execute dynamic SQL expressions. The single most important component
of the dbms_sql package is the dbms_sql.parse procedure. In previous releases of the
RDBMS, the parse procedure was limited to 2000 characters. In the latest release of the
RDBMS, this procedure has been overloaded to a table of varchar2, thus setting no limit
on the size of the dynamic SQL. This article will demonstrate the use of this packaged
procedure and in addition will explain a method for executing anonymous PL/SQL blocks
and extracting the result from the block to the calling program.
New Overloaded Procedure dbms_sql.parse:
Description of dbms_sql.parse from Oracle RDBMS 7.3.3.3.0
SQL> desc dbms_sql.parse
PROCEDURE dbms_sql.parse
Argument NameType In/Out Default?
------------------------------ ----------------------- ---------------
C NUMBER(38) IN
STATEMENT VARCHAR2 IN
LANGUAGE_FLAG NUMBER(38) IN
PROCEDURE dbms_sql.parse
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
C NUMBER(38) IN
STATEMENT TABLE OF VARCHAR2(256) IN
LB NUMBER(38) IN
UB NUMBER(38) IN
LFFLG BOOLEAN IN
LANGUAGE_FLAG NUMBER(38) IN
Fig1: Description of dbms_sql.parse
2. Note that the table of varchar2 is actually a type of dbms_sql.varchar2s which is
defined in dbmssql.sql in $ORACLE_HOME/rdbms/admin as follows:
type varchar2s is table of varchar2(256) index by binary integer;
c is an integer and holds the familiar cursor handle.
LB denotes the lower bound of the PL/SQL table and UB denotes the upper bound of the
PL/SQL table. That is, a PL/SQL table of varchar2(256) could hold many lines of code,
but they can be selectively parsed and executed using these variables. That is one could
specify LB as 3 and UB as 7 and the SQL code in the PL/SQL table from 3 to 7 will be
parsed and executed. Conceptually, the SQL string is put together as follows:
string:=statement(lb) || statement (lb+1) ||…||statement(ub); .
If LFFLG which is a Boolean variable is set to true, then a new line is inserted after each
piece. This is strongly recommended due to the following reason. When building large
chunks of code, a line of PL/SQL code cannot exceed 2000 characters and the PL/SQL
compiler wraps it to accomplish it. The reason this happens is that all database source
code is stored in dba_source view and its underlying table and in the text column of that
table and is limited to 2000 characters.
Now users can build a PL/SQL table and specify the upper and lower boundary of
the table as input arguments to dbms_sql.parse instead of the previous varchar2 thus
surpassing the 2000 characters limitation. This is particularly useful when evaluating
complicated business rules and expressions.
Use of the New version of dbms_sql.parse:
The code that follows demonstrates the usage of the dbms_sql.parse procedure.
Before we demonstrate use of this procedure, we will create a packaged variable and a
function to retrieve that variable. This will be used to demonstrate the use of extracting
the value from an anonymous PL/SQL block using dbms_sql.
3. Create or Replace package myvar as
Pragma restrict_references(myvar,WNDS,WNPS,RNDS, RNPS);
result number;
end myvar;
Fig 2: Source Code for myvar package
A discerning user will notice that the package has been subjected to a Pragma
restrict_reference restriction. This is due to the following reason. A function which
retrieves the value of the variable will otherwise return the familiar ORA-06571 error
which is, “Function %s does not guarantee not to update database”.
Now we will demonstrate the function which will retrieve the value of the
packaged variable.
Create or replace function check_calc
return number is
begin
return myvar.result;
end;
Fig 3: Source Code for the check_calc package.
Note that the function merely reads the myvar.result package variable and returns
it back to the calling program.
In this example we will create a table of varchar2, and create an anonymous
PL/SQL block and declare two variables A and B. We will assign them some arbitrary
values and then pass an expression as an argument to the procedure. This expression
could be any valid PL/SQL expression not exceeding 256 characters. The task of
evaluating expressions greater than 256 characters is left as an exercise to the discerning
reader and just reduces to breaking up a character string greater than 256 characters into
multiple pieces of 256 characters and then appending it to the PL/SQL table.
4. Note that A and B could be assigned values by any valid SQL function which can
be user defined and hence A and B can be assigned dynamically too. This is not done
here for the sake of simplicity. For example, A and B could well be the age and gender of
an individual stored in a database and very well could be as follows:
a:=getage(855855855);
b:=getgender(855855855);
where 855855855 uniquely identifies an individual in a database table.
In fact, even the above two statements could be generated dynamically if the
information is stored in a table. It should also be noted that is these attributes are stored
in a database, once could potentially generate the statements in the anonymous PL/SQL
block. The following piece of code demonstrates the use of the new overloaded version
of dbms_sql.parse.
Note that the anonymous PL/SQL block updates a package variable, which is
available throughout the session. Examples of dbms_sql.parse with the three arguments
abound in Oracle literature and accessing data through define_column procedure and
column_value procedure. The limitation of dbms_sql.parse with three arguments is
already know. Define_column and column_value is fine when one has to access database
data and cursor data respectively. The packaged variable method does not require use of
these procedures and their limitations if any.
Note that even though sqlstring is a table of varchar2(256), it should be casted as
dbms_sql.varchar2s. The following section of code demonstrates the use of the new
dbms_sql.parse and the use of the packaged variable and its associated function which
retrieves it.
The good thing about anonymous PL/SQL blocks is that, they don‟t need to be
wrapped in procedures/functions/packages thus causing DDL issues.
5. create or replace procedure democalc
(
calc_text in varchar2, //* The calculation string for example: “result:=A*B;”
return_result in out number,
retval in out number
)
IS
sqlstring dbms_sql.varchar2s; /* Created to hold the PL/SQL Block */
cursor1 integer;
i, j integer;
new_line_flag BOOLEAN:=TRUE;
BEGIN
sqlstring(1):='declare ';
sqlstring(2):='result number(38,2):=0.0;';
sqlstring(3):='a number(38,2):=2;';
sqlstring(4):='b number(38,2):=3;';
sqlstring(5):='begin';
sqlstring(6):=calc_text;
sqlstring(7):='myvar.result:=result;';
sqlstring(8):='end;';
i=1;
j=8;
cursor1:=dbms_sql.open_cursor;
dbms_sql.parse(cursor1,sqlstring,i,j,new_line_flag,DBMS_SQL.V7);
retval:=dbms_sql.execute(cursor1);
dbms_sql.close_cursor(cursor1);
return_result:=check_calc;
END;
/
6. The following section demonstrates the execution of the democalc procedure.
SQL> set serveroutput on
SQL> declare
temp1 number; temp2 number;
begin
democalc(„result:=A*P;‟, temp1,temp2);
dbms_output.put_line(„The result is „||temp1);
end;
/
The result is 6
SQL>
Conclusions:
The new overloaded version of dbms_sql.parse obviates the previous limit
of the dbms_sql and provides even more flexibility to the developer. A system which
provides a framework to evaluate simple, dynamic PL/SQL expressions and which uses
the above version of dbms_sql.parse is in development at an Oracle client site by Oracle
consulting and is expected to go live soon.
Acknowledgements:
The brilliant idea of using packaged variables to exchange values from
anonymous PL/SQL blocks was suggested by an Oracle Technical manager, Roger Raj.
About the Author:
Mahesh Vallampati is a senior consultant with the Oracle National
Telecom practice, Oracle Corporation. Though he is a DBA by profession, occasionally
he is called on by Oracle customers for his PL/SQL expertise which he dabbles in. He
can be reached at his convenience at mvallamp@us.oracle.com.