How do I implement stack in PL/SQL? - plsql

In Python it would look something like this:
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
Which data structure do I use and how do I implement stack in Oracle PL/SQL?

You can achieve the object oriented kind of things by using OBJECT types.
Like a class in other programming languages, you can use OBJECT in PL/SQL for encapsulating data.
For example the stack definition: (lifted from ORACLE documentation link.... posted down below. You can find definition of below declaration in that link).
CREATE TYPE Stack AS OBJECT (
max_size INTEGER,
top INTEGER,
position IntArray,
MEMBER PROCEDURE initialize,
MEMBER FUNCTION full RETURN BOOLEAN,
MEMBER FUNCTION empty RETURN BOOLEAN,
MEMBER PROCEDURE push (n IN INTEGER),
MEMBER PROCEDURE pop (n OUT INTEGER)
);
https://docs.oracle.com/cd/A97630_01/appdev.920/a96624/13_elems32.htm

You can implement stacks in pl/sql using Associative array tables.
Consider the following example.
You can read more at the following locations:
Oracle Collections Documentation
Collections Tutorial
declare
type stack_type is table of binary_integer index by binary_integer;
---^ Use your data type here
stack stack_type;
procedure push( p_element in binary_integer ) as
-----^ Use your data type here
begin
stack( stack.count + 1 ) := p_element;
end push;
procedure pop as
begin
stack.delete( stack.count ) ;
end;
function find ( p_element in binary_integer )
-----^ Use your data type here
return boolean as
begin
for element in 1 .. stack.count loop
if stack( element ) = p_element then
return true;
end if;
end loop;
return false;
end;
function top
return binary_integer as
-------^ Use your data type here
begin
return stack( stack.count );
end;
begin
push( 123 );
push( 456 );
push( 678 );
push( 929 );
push( 104 );
for element in 1 .. stack.count loop
dbms_output.put_line( 'Element:' || element || ' Value: ' || stack( element ) );
end loop;
dbms_output.put_line( 'Find|Top:' || top() );
if find( 929 ) then
dbms_output.put_line( '929 Found' );
else
dbms_output.put_line( '929 Not found');
end if;
if find(321) then
dbms_output.put_line( '321 Found' );
else
dbms_output.put_line( '321 Not found');
end if;
while stack.count > 0 loop
dbms_output.put_line( 'Pop|Top:' || top() );
pop();
end loop;
end;

Related

Stored Procedure not reading from variable in IF-ELSE statement

I started writing this stored procedure and I faced some issues when I try to pass a variable in my conditional statement.
I can use the parameter BANKN which works fine, but when never I passed the declared variable AC_t somehow the PL/SQL ignores it.
Any idea please what I'm missing here?
create or replace PROCEDURE TTEST1 (
CR IN VARCHAR2,
BANKN IN VARCHAR2,
P_CURSOR OUT SYS_REFCURSOR
)
AS
G_AC CHAR(10);
AC_t Billing.Account %Type;
BEGIN
IF BANKN = 'WLNV' AND AC_t = 'Private'
THEN
IF CR IN (
'EUR',
'CZK',
'USD'
)
THEN
OPEN P_CURSOR
FOR
SELECT G_AC AS GL_ACC,
Billing.Account AS ACC_Type
INTO
G_AC,
AC_t
FROM Billing
INNER JOIN invoice ON Billing.ACC_NO = invoice.ACC_NO;
END IF ;
END IF ;
END;
My aim here is to expand this code by using AC_t value from Billing.ACCount and retrieve what ever data that can be 'Private' or 'Public'.
To do this, I need to use case or IF statement, however when I use
Billing.ACCount, I got an error "not allowed in this context", for this reason I use synonym AC_t but this don't read values from Billing table unless I use it in WHERE clause.
ACC_NO
Account
1
Private
2
Public
Extended code:
IF BANKN = 'WLNV' AND AC_t = 'Private'
THEN
...
...
ELSIF IF BANKN = 'WLNV' AND AC_t = 'Public'
THEN
....
...
You cannot use SELECT ... INTO with a cursor and you need to declare a value for the ac_t variable (but since it is a column in the table you may want a WHERE clause in the cursor). Like this:
CREATE PROCEDURE TTEST1 (
p_CR IN VARCHAR2,
p_BANKN IN VARCHAR2,
P_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
IF p_BANKN = 'WLNV'
AND p_CR IN ( 'EUR', 'CZK', 'USD' )
THEN
OPEN P_CURSOR FOR
SELECT G_AC AS GL_ACC,
b.account AS ACC_Type
FROM Billing b
INNER JOIN invoice i
ON b.ACC_NO = i.ACC_NO
WHERE b.account = 'Private';
END IF;
END;
/
Which, if you have the sample data:
CREATE TABLE invoice (acc_no, g_AC) AS
SELECT 1, 2 FROM DUAL;
CREATE TABLE billing (acc_no, account) AS
SELECT 1, 'Private' FROM DUAL;
Then you can call the procedure and print the contents of the cursor using:
DECLARE
cur SYS_REFCURSOR;
v_g_ac INVOICE.G_AC%TYPE;
v_ac_t BILLING.ACCOUNT%TYPE;
BEGIN
ttest1('EUR', 'WLNV', cur);
LOOP
FETCH cur INTO v_g_ac, v_ac_t;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( v_g_ac || ', ' || v_ac_t );
END LOOP;
CLOSE cur;
END;
/
Which outputs:
2, Private
db<>fiddle here

Getting error 'Table,View Or Sequence reference 'EMPLOYEES.EMP_NAME' not allowed in this context'

create table Employees (emp_id number, emp_name varchar2(50), salary number, department_id number, DESIGNATION varchar2(50), DEVELOPED_TESTED varchar2(50)) ;
insert into Employees values(1,'ALex',10000,10,'Developer','VLC');
insert into Employees values(2,'Duplex',20000,20,'Developer','VLC');
insert into Employees values(3,'Charles',30000,30,'Tester','Tested_VLC');
insert into Employees values(4,'Demon',40000,40,'Tester ','Tested_VLC');
insert into employees values(5,'Chaem',5000,50,'Developer','');
Requirement :
I want to return 2 if the designation is 'Developer' and developed_tested column is null. But in the output I am getting null and NOT 2. Can you please check on this.
Code :
create or replace FUNCTION calculate_royalty (
i_empno IN NUMBER
) RETURN VARCHAR2 IS
l_employee employees%ROWTYPE;
BEGIN
SELECT *
INTO l_employee
FROM
employees
WHERE emp_id = i_empno;
IF l_employee.designation = 'Developer' THEN
RETURN l_employee.developed_tested;
ELSIF l_employee.designation = 'Developer' and l_employee.developed_tested is null THEN
RETURN 2;
ELSE
RETURN 1;
END IF;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN 0;
END ;
select CALCULATE_ROYALTY(5) from dual; -- Output coming as NULL but it should be 2 ideally.
Just select the entire row into a variable, no need to use a count. Use the NO_DATA_FOUND exception to handle the case of user does not exist.
Your question mentions some functionality related to the "royalty" column. That is missing from your test case but it should be a piece of cake to modify code below so it caters to all your needs.
create or replace FUNCTION calculate_royalty (
empno_i IN NUMBER
-- don't use column names as names of input parameters, that is a recipe for disaster. Use prefix/suffix.
) RETURN VARCHAR2 IS
lv_count NUMBER;
l_employee employees%ROWTYPE;
BEGIN
SELECT *
INTO l_employee
FROM
employees
WHERE emp_id = empno_i;
IF l_employee.designation = 'Developer' THEN
RETURN l_employee.salary;
ELSE
RETURN 1;
END IF;
-- dbms_output just sends data to a buffer that you then print to the console. it cannot be used as a return value.
-- RETURN dbms_output.put_line('Emp Name :'...
-- elsif needs to be followed by an expression. It cannot be followed by a RETURN keyword. Use "ELSE" instead if there is no condition.
-- elsif return 1;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN 0;
END ;
/
set serveroutput on size 999999
clear screen
BEGIN
dbms_output.put_line('output for Developer: '|| calculate_royalty(EMPNO_I => 2));
dbms_output.put_line('output for no Developer: '|| calculate_royalty(EMPNO_I => 3));
dbms_output.put_line('output for non-existing user: '|| calculate_royalty(EMPNO_I => 10));
END;
output for Developer: 20000
output for no Developer: 1
output for non-existing user: 0
PL/SQL procedure successfully completed.

Cannot use procedure in the select statement?

Below is my code to create types and tables. It's all correct:
CREATE TYPE shape_typ AS OBJECT (
l INTEGER, -- length
w INTEGER, -- width
h INTEGER, -- height
MEMBER FUNCTION area RETURN INTEGER,
MEMBER FUNCTION volume RETURN INTEGER,
MEMBER PROCEDURE display (SELF IN OUT NOCOPY shape_typ) );
CREATE TYPE BODY shape_typ AS
MEMBER FUNCTION volume RETURN INTEGER IS
BEGIN
RETURN l * w * h;
-- same as previous line RETURN SELF.l * SELF.w * SELF.h;
END;
MEMBER FUNCTION area RETURN INTEGER IS
BEGIN -- not necessary to include SELF in following
RETURN 2 * (l * w + l * h + w * h);
END;
MEMBER PROCEDURE display (SELF IN OUT NOCOPY shape_typ) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Length: ' || l || ' - ' ||
'Width: ' || w || ' - ' ||
'Height: ' || h );
DBMS_OUTPUT.PUT_LINE('Volume: ' || volume || ' - ' ||
'Area: ' || area );
END;
END;
CREATE TABLE shapes (
shape shape_typ,
create_date DATE );
INSERT INTO shapes VALUES (
shape_typ (3,3,3), '17-MAR-2008' );
INSERT INTO shapes VALUES (
shape_typ (1,8,2), '17-FEB-2008' );
INSERT INTO shapes VALUES (
shape_typ (1,1,1), '27-MAR-2008' );
SELECT s.shape.l,s.shape.w,s.shape.h,s.shape.area() FROM shapes s;
DECLARE
shap shapes%rowtype;
BEGIN -- PL/SQL block for selecting-displaying a student
SELECT * INTO shap
FROM shapes s
WHERE s.shape.l=1 and s.shape.w=1 and s.shape.h=1;
shap.shape.display;
END;
However, when I try to execute the procedure in a the following statement, it gives me an error message.
SELECT s.shape.l,s.shape.w,s.shape.h,s.shape.display() FROM shapes s;
The error message is that:
ORA-06553: PLS-222: no function with name 'DISPLAY' exists in this scope
06553. 00000 - "PLS-%s: %s"
*Cause:
*Action:
Error at Line: 49 Column: 38
I am wondering whether it is because I cannot use the procedure in a select statement?
No, you can't use procedures in SQL-Statements.
The procedure DISPLAY can only be used in a PL/SQL context.
See Oracle Documentation : Coding PL/SQL Subprograms and Packages

F_checksal is not a procedure or is undefined when compiling plsql

When I am trying to compile this plsql I keep getting this error message:
'F_checksal is not a procedure or is undefined'
This is the code:
declare
e_high_increase EXCEPTION;
old_sal NUMBER;
function f_checkSal(i_sal number,i_old_sal IN OUT NUMBER) return VARCHAR2 is
begin
if((i_sal/old_sal)*100>300) then
raise e_high_increase;
else
return 'yes';
end if;
exception when e_high_increase then
insert into t_logerror(error_tx) values ('this is user exception');
dbms_output.put_line('this is user exception');
return 'No';
end;
begin
select sal into old_sal FROM emp where empno=153;
f_checksal(30000,old_sal);
update emp set sal=30000 where empno=153;
end;
It's just your call to your function that needs to be changed.
You can consume the result with a call to a procedure (e.g. DBMS_OUTPUT.put_line( f_checksal(30000,old_sal));), or assign it to a local variable, e.g. RESULT := f_checksal(30000,old_sal);
Your syntax is way off. You need to first CREATE the function, then reference it in your pl/sql block:
Step1 :
CREATE OR REPLACE FUNCTION f_checksal (i_sal NUMBER, i_old_sal IN OUT NUMBER)
RETURN VARCHAR2
IS
old_sal NUMBER;
e_high_increase EXCEPTION;
BEGIN
IF ((i_sal / old_sal) * 100 > 300)
THEN
RAISE e_high_increase;
ELSE
RETURN 'yes';
END IF;
EXCEPTION
WHEN e_high_increase
THEN
INSERT INTO t_logerror
(error_tx
)
VALUES ('this is user exception'
);
DBMS_OUTPUT.put_line ('this is user exception');
RETURN 'No';
END;
/
Step 2:
DECLARE
old_sal NUMBER;
RESULT VARCHAR2 (100);
BEGIN
SELECT sal
INTO old_sal
FROM emp
WHERE empno = 153;
RESULT := f_checksal (30000, old_sal);
UPDATE emp
SET sal = 30000
WHERE empno = 153;
END;
Note that your function has a return value that you were not assinging in the calling code.

How to make enum types in PL/SQL?

For example I want to make my own Boolean type and call it Bool. How do I do that?
Or a type for traffic lights, i.e. that has only Red, Yellow, Green in it (and null of course).
I don't think that solution, provided by A.B.Cade is totally correct. Let's assume procedure like this:
procedure TestEnum(enum_in lights);
What is the value of enum_in? red? yellow? green?
I propose another solution. Here is package example
CREATE OR REPLACE PACKAGE pkg_test_enum IS
SUBTYPE TLight IS BINARY_INTEGER RANGE 0..2;
Red CONSTANT TLight := 0;
Yellow CONSTANT TLight := 1;
Green CONSTANT TLight := 2;
--get sting name for my "enum" type
FUNCTION GetLightValueName(enum_in TLight) RETURN VARCHAR2;
PROCEDURE EnumTest(enum_in TLight);
END pkg_test_enum;
CREATE OR REPLACE PACKAGE BODY pkg_test_enum IS
FUNCTION GetLightValueName(enum_in TLight)
RETURN VARCHAR2
IS
ResultValue VARCHAR2(6);
BEGIN
CASE enum_in
WHEN Red THEN ResultValue := 'Red';
WHEN Green THEN ResultValue := 'Green';
WHEN Yellow THEN ResultValue := 'Yellow';
ELSE ResultValue := '';
END CASE;
RETURN ResultValue;
END GetLightValueName;
PROCEDURE EnumTest(enum_in TLight)
IS
BEGIN
--do stuff
NULL;
END EnumTest;
END pkg_test_enum;
I can now use TLight in different packages. I can now test enum_in against predefined values or null.
Here is usage example
begin
pkg_test_enum.EnumTest(pkg_test_enum.Red);
end;
Besides, you can make this type not nullable.
SUBTYPE TLight IS BINARY_INTEGER RANGE 0..2 NOT NULL;
This blog describes a way to do it using constant values
In addition to the constants, the blog defines a subtype for valid colors.
SQL> declare
2 RED constant number(1):=1;
3 GREEN constant number(1):=2;
4 BLUE constant number(1):=3;
5 YELLOW constant number(1):=4;
6 --
7 VIOLET constant number(1):=7;
8 --
9 subtype colors is binary_integer range 1..4;
10 --
11 pv_var colors;
12 --
13 function test_a (pv_var1 in colors) return colors
14 is
15 begin
16 if(pv_var1 = YELLOW) then
17 return(BLUE);
18 else
19 return(RED);
20 end if;
21 end;
22 --
The closest think I could think of is:
create or replace type lights as object
(
red varchar2(8),
yellow varchar2(8),
green varchar2(8),
constructor function lights return self as result
)
and the body:
create or replace type body lights is
constructor function lights return self as result is
begin
self.red = 'red';
self.yellow = 'yellow';
self.green = 'green';
return;
end;
end;
Then in the code you can use it:
declare
l lights := new lights;
begin
dbms_output.put_line(l.red);
end;
I have previously used the same approach as #mydogtom and #klas-lindbäck. I found this when I was trying to refresh my memory. However, the object approach suggested by #a-b-cade got me thinking. I agree with the problems described by #mydogtom (what is the value?) but it got me thinking is using an object was possible.
What I came up with was an approach that used an object with a single member property for the value of the enum and static functions for each possible value. I couldn't see how to combine with a subtype to get a real restriction on the value field, not to formally make it not-null. However, we can validate it in the constructor. The functional downside, compared to a "proper" enum (e.g. in Java) is that we can't stop someone directly updating the val property to an invalid value. However, as long as people use the constructor and the set_value function, it's safe. I'm not sure the overhead (both run-time in terms of creating an object and in terms of maintaining the objects, etc.) is worth it, so I'll probably keep using the approach described by #mydogtom but I'm not sure.
You could also have name as a property and set in in set_value (kind of like #a-b-cade's version) but that adds another property that could be updated directly and so another set of states where the val and name don't match, so I preferred the approach with name being a function.
An example usage of this could be (using my demo_enum type below):
procedure do_stuff(enum in demo_enum) is
begin
if enum.eqals(demo_enum.foo()) then
-- do something
end if;
end do_stuff;
or
procedure do_stuff(enum1 in demo_enum, enum2 in demo_enum) is
begin
if enum1.eqals(enum2) then
-- do something
end if;
end do_stuff;
What I did was define a base class, with as much as possible there: the actual val field, equals function for static values, set_value and to_string fucntions. Also name function, but this just be overridden (couldn't see how to formally make a member function abstract, so the base version just throws an exception). I'm using name also as the way to check the value is valid, in order to reduce the number of places I need to enumerate the possible values
create or replace type enum_base as object(
-- member field to store actual value
val integer,
-- Essentially abstract name function
-- Should be overridden to return name based on value
-- Should throw exception for null or invalid values
member function name return varchar2,
--
-- Used to update the value. Called by constructor
--
member procedure set_value(pvalue in integer),
--
-- Checks the current value is valid
-- Since we can't stop someone updating the val property directly, you can supply invalid values
--
member function isValid return boolean,
--
-- Checks for equality with integer value
-- E.g. with a static function for a possible value: enum_var.equals( my_enum_type.someval() )
--
member function equals(other in integer) return boolean,
--
-- For debugging, prints out name and value (to get just name, use name function)
--
member function to_string return varchar2
) not instantiable not final;
/
create or replace type body enum_base is
member function name return varchar2 is
begin
-- This function must be overriden in child enum classes.
-- Can't figure out how to do an abstract function, so just throw an error
raise invalid_number;
end;
member procedure set_value(pvalue in integer) is
vName varchar2(3);
begin
self.val := pvalue;
-- call name() in order to also validate that value is valid
vName := self.name;
end set_value;
member function isValid return boolean is
vName varchar2(3);
begin
begin
-- call name() in order to also validate that value is valid
vName := self.name;
return true;
exception
when others then
return false;
end;
end isValid;
member function equals(other in integer) return boolean is
begin
return self.val = other;
end equals;
member function to_string return varchar2 is
begin
if self.val is null then
return 'NULL';
end if;
return self.name || ' (' || self.val || ')';
end to_string;
end;
/
In the actual enum class I have to define a constructor (which just calls set_value) and override the name function to return a name for each possible value. I then define a static function for each possible value that returns the integer index of that value. Finally I define an overload of equals that compares to another enum of the same type. If you wanted to attach other properties to each value then you an do so by defining additional functions.
create or replace type demo_enum under enum_base (
-- Note: the name of the parameter in the constructor MUST be the same as the name of the variable.
-- Otherwise a "PLS-00307: too many declarations" error will be thrown when trying to instanciate
-- the object using this constructor
constructor function demo_enum(val in integer) return self as result,
--
-- Override name function from base to give name for each possible value and throw
-- exception for null/invalid values
--
overriding member function name return varchar2,
--
-- Check for equality with another enum object
--
member function equals(other in demo_enum) return boolean,
--
-- Define a function for each possible value
--
static function foo return integer,
static function bar return integer
) instantiable final;
/
create or replace type body demo_enum is
constructor function demo_enum(val in integer) return self as result is
begin
self.set_value(val);
return;
end demo_enum;
overriding member function name return varchar2 is
begin
if self.val is null then
raise invalid_number;
end if;
case self.val
when demo_enum.foo() then
return 'FOO';
when demo_enum.bar() then
return 'BAR';
else
raise case_not_found;
end case;
end;
member function equals(other in demo_enum) return boolean is
begin
return self.val = other.val;
end equals;
static function foo return integer is
begin
return 0;
end foo;
static function bar return integer is
begin
return 1;
end bar;
end;
/
This can be tested. I defined two sets of tests. one was a manual set of tests for this particular enum, also to illustrate usage:
--
-- Manual tests of the various functions in the enum
--
declare
foo demo_enum := demo_enum(demo_enum.foo());
alsoFoo demo_enum := demo_enum(demo_enum.foo());
bar demo_enum := demo_enum(demo_enum.bar());
vName varchar2(100);
procedure assertEquals(a in varchar2, b in varchar2) is
begin
if a <> b then
raise invalid_number;
end if;
end assertEquals;
procedure assertEquals(a in boolean, b in boolean) is
begin
if a <> b then
raise invalid_number;
end if;
end assertEquals;
procedure test(vName in varchar2, enum in demo_enum, expectFoo in boolean) is
begin
dbms_output.put_line('Begin Test of ' || vName);
if enum.equals(demo_enum.foo()) then
dbms_output.put_line(vName || ' is foo');
assertEquals(expectFoo, true);
else
dbms_output.put_line(vName || ' is not foo');
assertEquals(expectFoo, false);
end if;
if enum.equals(demo_enum.bar()) then
dbms_output.put_line(vName || ' is bar');
assertEquals(expectFoo, false);
else
dbms_output.put_line(vName || ' is not bar');
assertEquals(expectFoo, true);
end if;
if enum.equals(foo) then
dbms_output.put_line(vName || '.equals(vFoo)');
assertEquals(expectFoo, true);
else
assertEquals(expectFoo, false);
end if;
if expectFoo then
assertEquals(enum.name, 'FOO');
else
assertEquals(enum.name, 'BAR');
end if;
assertEquals(enum.isValid, true);
case enum.val
when demo_enum.foo() then
dbms_output.put_line(vName || ' matches case foo');
when demo_enum.bar() then
dbms_output.put_line(vName || ' matches case bar');
else
dbms_output.put_line(vName || ' matches no case!!!');
end case;
dbms_output.put_line(vName || ': ' || enum.to_string());
dbms_output.put_line('--------------------------------------------------');
dbms_output.put_line('');
end test;
begin
test('foo', foo, true);
test('bar', bar, false);
test('alsoFoo', alsoFoo, true);
foo.val := -1;
assertEquals(foo.isValid, false);
begin
vName := foo.name;
exception
when case_not_found then
dbms_output.put_line('Correct exception for fetching name when invalid value: ' || sqlerrm);
end;
foo.val := null;
assertEquals(foo.isValid, false);
begin
vName := foo.name;
exception
when invalid_number then
dbms_output.put_line('Correct exception for fetching name when null value: ' || sqlerrm);
end;
end;
The other was slightly more automated, and could be used for any enum that inherits enum_base (as long as it doesn't add other functions - couldn't see a way to find only static functions, if anyone knows let me know). This checks that you haven't defined the same integer value to multiple possible value static functions by mistake:
--
-- generated test that no two values are equal
--
declare
vSql varchar2(4000) := '';
typename constant varchar2(20) := 'demo_enum';
cursor posvals is
select procedure_name
from user_procedures
where object_name = upper(typename)
and procedure_name not in (upper(typename), 'EQUALS', 'NAME');
cursor posvals2 is
select procedure_name
from user_procedures
where object_name = upper(typename)
and procedure_name not in (upper(typename), 'EQUALS', 'NAME');
procedure addline(line in varchar2) is
begin
vSql := vSql || line || chr(10);
end;
begin
addline('declare');
addline(' enum ' || typename || ';');
addline('begin');
for posval in posvals loop
addline(' enum := ' || typename || '(' || typename || '.' || posval.procedure_name || '());');
for otherval in posvals2 loop
addline(' if enum.equals(' || typename || '.' || otherval.procedure_name || '()) then');
if otherval.procedure_name = posval.procedure_name then
addline(' dbms_output.put_line(''' || otherval.procedure_name || ' = ' || posval.procedure_name || ''');');
else
addline(' raise_application_error(-20000, ''' || otherval.procedure_name || ' = ' || posval.procedure_name || ''');');
end if;
addline(' else');
if otherval.procedure_name = posval.procedure_name then
addline(' raise_application_error(-20000, ''' || otherval.procedure_name || ' != ' || posval.procedure_name || ''');');
else
addline(' dbms_output.put_line(''' || otherval.procedure_name || ' != ' || posval.procedure_name || ''');');
end if;
addline(' end if;');
end loop;
addline('');
end loop;
addline('end;');
execute immediate vSql;
end;

Resources