I am trying to implement this procedure into a package however the package
will not allow me to use the cursor for some reason. Can anyone help? Thank you.
Also when I try to put the procedure into my package a 'enter bind variable' box appears
minus anywhere to input a bind variable and this error
Not found
The requested URL /apex/wwv_flow.show was not found on this server
My code is
PROCEDURE total_calc(p_order NUMBER)
IS
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
v_c1 REFCURSOR;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
dbms_output.put_line(c_prod_desc || ': ' || c_price);
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
dbms_output.put_line('Total Cost:' || v_total_cost);
END;
Here is the code for the rest of the package
CREATE OR REPLACE PACKAGE orders_salary_manage AS
FUNCTION tax_func (p_sal IN NUMBER)
RETURN NUMBER;
PROCEDURE reduce_price(p_product_id NUMBER, p_sub_price NUMBER);
PROCEDURE increase_price(p_product_id NUMBER, p_add_price NUMBER);
PROCEDURE remove_order(p_order_id NUMBER);
PROCEDURE add_order(p_order_id NUMBER,
p_order_date VARCHAR2,
p_delivery_date VARCHAR2,
p_customer_id NUMBER,
p_employee_id NUMBER,
p_order_type_id NUMBER);
END orders_salary_manage;
CREATE OR REPLACE PACKAGE BODY orders_salary_manage AS
tot_orders NUMBER;
FUNCTION tax_func (p_sal IN NUMBER)
RETURN NUMBER
IS
tax_rate NUMBER := 0;
v_netsal NUMBER := 0;
v_sal NUMBER;
BEGIN
v_sal := p_sal;
IF v_sal > 70000 THEN
tax_rate := (v_sal * 0.4);
v_netsal := v_sal - tax_rate;
END IF;
IF v_sal < 70000 THEN
tax_rate := (v_sal * 0.2);
v_netsal := v_sal - tax_rate;
END IF;
RETURN v_netsal;
END;
PROCEDURE reduce_price(p_product_id NUMBER, p_sub_price NUMBER)
IS v_price NUMBER;
e_invalid_price EXCEPTION;
BEGIN
SELECT unit_price
INTO v_price
FROM product
WHERE product_id = p_product_id;
v_price := v_price - p_sub_price;
IF v_price < 1 THEN
RAISE e_invalid_price;
ELSE
UPDATE product SET unit_price = v_price WHERE product_id = p_product_id;
END IF;
END;
PROCEDURE increase_price(p_product_id NUMBER, p_add_price NUMBER)
IS v_price NUMBER;
BEGIN
SELECT unit_price
INTO v_price
FROM product
WHERE product_id = p_product_id;
v_price := v_price + p_add_price;
UPDATE product SET unit_price = v_price WHERE product_id = p_product_id;
END;
PROCEDURE remove_order(p_order_id NUMBER)
IS
BEGIN
DELETE FROM placed_order WHERE order_id = p_order_id;
DELETE FROM order_line WHERE fk1_order_id = p_order_id;
END;
PROCEDURE add_order(p_order_id NUMBER,
p_order_date VARCHAR2,
p_delivery_date VARCHAR2,
p_customer_id NUMBER,
p_employee_id NUMBER,
p_order_type_id NUMBER)
IS new_order NUMBER;
BEGIN
INSERT INTO placed_order (order_id, order_date, delivery_date, fk1_customer_id, fk2_employee_id, fk3_order_type_id)
VALUES (p_order_id, p_order_date, p_delivery_date, p_customer_id, p_employee_id, p_order_type_id);
END;
END;
This is really confusing.
First off - dbms_output requires special settings for the environment in order to work. And a call to DBMS_OUTPUT.ENABLE, plus packages do have access to a terminal anyway. Packages do not have your process context, they run in the process context of the db -- which runs without a controlling terminal. If you have to write something use a file -- UTL_FILE package is meant for that.
Next
Since you are basically adding up an order, you need a function to return the value.
A procedure is not going to work. As you coded anyway.
Next
v_C1 is not used and makes no sense for your code. You are not returning a refcursor.
Next
You should consider using a schema for tables with generic table names like that when you decalre a variable.
example:
v_foo schema_owner.table_name.column_name%type;
Consider this:
function total_calc(p_order in NUMBER)
return NUMBER
is
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
-- lose this: v_c1 REFCURSOR;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
-- lose this line: dbms_output.put_line(c_prod_desc || ': ' || c_price);
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
-- lose this line: dbms_output.put_line('Total Cost:' || v_total_cost);
return v_total_cost;
END;
In order for this to work it has to inside a
CREATE OR REPLACE FUNCTION a statement. Then - Added to the db schema by a user with correct permissions to create it.
After all that, then you can run it. Also, there is more to all this package/function creation stuff like using PRAGMAS if somebody needs to run this from SQLPLUS.
Related
Is it possible to fetch cursor data into array or table with dynamic cursor parameters ?
Example : We have 3 function fct1(), fct2(), fct3(). They all return a CURSOR with different data (from different table) and their size is between 20 and 100.
I'd like to call this 3 function with a generic function, fill a VARCHAR2 array and for example print this array.
I've found out how to fetch cursor into a VARRAY or VARCHAR2 but then you need to specify which cursor value you add into the array.
CREATE OR REPLACE PACKAGE BODY CURSOR_EXAMPLE AS
FUNCTION fct1 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT a, b, c FROM table1, table2, table3;
RETURN my_cursor;
END fct1;
FUNCTION fct2 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT q, w, e, r, t, y FROM table4, table5;
RETURN my_cursor;
END fct2;
FUNCTION fct3 (param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT x, y, z FROM table6, table7, table8, table8;
RETURN my_cursor;
END fct3;
PROCEDURE generic_function (fct IN NUMBER, param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2, cursor_out OUT SYS_REFCURSOR)
AS
BEGIN
IF fct = 1
cursor_out := fct1(param01);
ELSE IF fct = 2
cursor_out := fct2(param01);
ELSE IF
cursor_out := fct3(param01, param02, param03);
END IF;
LOOP
FETCH cursor_out INTO
-- HERE DEPENDING ON fct1, 2 and 3 add cursor_out variable into a VARRAY (extend it as needed)
EXIT WHEN cursor_out%NOTFOUND;
END LOOP;
-- PRINT VARRAY
END generic_function;
END CURSOR_EXAMPLE;
It appears that you are trying to devise a dynamic means of discovering what columns are contained within the cursor.
There is a way, but it involves using DBMS_SQL and once you convert the cursor to a DBMS_SQL cursor, you will have to use DBMS_SQL all the way as you process the records.
Here is an example of a test function that can return one of four different cursors, without static defined types:
CREATE OR REPLACE FUNCTION get_test_cursor (n in number default 1) return SYS_REFCURSOR
IS
c SYS_REFCURSOR;
BEGIN
IF n = 1
THEN
OPEN c FOR SELECT 'A' f1, 'B' f2 FROM DUAL;
END IF;
IF n = 2
THEN
OPEN c FOR SELECT cast(1.234 as number(6,2)) n1, cast(12 as number(8)) n2, 'C' f3 FROM DUAL;
END IF;
IF n = 3
THEN
OPEN c FOR select * from all_objects;
END IF;
if n = 4
then
OPEN c FOR select sysdate as current_dt from dual;
end if;
return c;
END;
/
Now, we can use the DBMS_SQL package to first convert the cursor from a REF CURSOR to a cursor number using dbms_sql.to_cursor_number.
Once we do this, we can use the rest of the DBMS_SQL API to inspect the cursor and do work on its data.
declare
cur sys_refcursor;
c number;
col_cnt INTEGER;
rec_tab DBMS_SQL.DESC_TAB;
--
-- This is a crude helper procedure to display one line of "DESCRIBE" output
--
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(rpad(rec.col_name, 41) || ' ' ||
rpad(case when rec.col_null_ok then ' ' else 'NOT NULL' end, 8) || ' ' ||
case when rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR or rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR2 then
'VARCHAR2(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_CHAR then
'CHAR(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_NUMBER then
case when rec.col_precision = 0 and rec.col_scale = -127 then 'NUMBER'
when rec.col_scale = 0 then 'NUMBER('||rec.col_precision||')'
else 'NUMBER(' || rec.col_precision || ', ' || rec.col_scale || ')'
end
when rec.col_type = DBMS_TYPES.TYPECODE_DATE then
'DATE'
else
'UNKNOWN'
end);
END;
begin
cur := test_cursor(3);
--
-- Convert the REF_CUR to a cursor number
--
c := dbms_sql.to_cursor_number(cur);
--
-- Use an API call to describe the columns
--
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
--
-- Now loop through the columns and show them
--
dbms_output.put_line('Name Null? Type');
dbms_output.put_line('----------------------------------------- -------- ----------------------------');
for j in 1..col_cnt loop
print_rec(rec_tab(j));
end loop;
--
-- We can do other things at this point.
-- When done, close the cursor.
--
DBMS_SQL.CLOSE_CURSOR(c);
end;
/
Here is the output when we pass in 3, which queries the ALL_OBJECTS view:
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(128)
OBJECT_NAME NOT NULL VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NOT NULL NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(13)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
Once you are able to see what the columns of the cursor are, you are on your way to solving the problem of creating a dynamic procedure that consumes random cursors and populates arrays.
create or replace package pachet_f_p is
function majorare_sal_manageri(sal in NUMBER, majorare in NUMBER) return NUMBER;
end pachet_f_p;
create or replace package body pachet_f_p
is
create or replace function majorare_sal_manageri(sal in NUMBER, majorare in NUMBER)
return INTEGER is
nr_angajati INTEGER :=0;
begin
dbms_output.put_line('Angajatii care primesc majorare de '|| majorare || 'au ID-urile urmatoare :');
/*
declare
cursor numara_ang_cccc is
select ID from MANAGERI where SALARIU = sal;
m_ID MANAGERI.ID%TYPE;
begin
open numara_ang_c;
loop
fetch numara_ang_c into m_ID;
exit when numara_angajati_cursor%NOTFOUND;
dbms_output.put_line(m_ID);
update MANAGERI
set
SALARIU = SALARIU + majorare
where ID = m_ID;
nr_angajati := nr_angajati +1;
end loop;
close numara_ang_cccc;
end;
*/
return nr_angajati;
end majorare_sal_manageri;
end pachet_f_p;
I can run the first 3 rows, but the rest of it gets an error:
Error(5,2): PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: begin end function pragma procedure subtype type current cursor delete exists prior
Can someone, please, find what I did wrong?
First create package specifications and Package body next(two separate pl/sql blocks)this compiles with no errors in Sql Developer
create or replace package pachet_f_p is
function majorare_sal_manageri(sal in NUMBER, majorare in NUMBER)
return NUMBER;
end pachet_f_p;
-- add back slash here
/
create or replace package body pachet_f_p
is
--create or replace (comment or remove this)
function majorare_sal_manageri(sal in NUMBER, majorare in NUMBER)
return number is
nr_angajati number :=0;
begin
dbms_output.put_line('Angajatii care primesc majorare de '|| majorare || 'au ID-urile urmatoare :');
/*
declare
cursor numara_ang_cccc is
select ID from MANAGERI where SALARIU = sal;
m_ID MANAGERI.ID%TYPE;
begin
open numara_ang_c;
loop
fetch numara_ang_c into m_ID;
exit when numara_angajati_cursor%NOTFOUND;
dbms_output.put_line(m_ID);
update MANAGERI
set
SALARIU = SALARIU + majorare
where ID = m_ID;
nr_angajati := nr_angajati +1;
end loop;
close numara_ang_cccc;
end;
*/
return nr_angajati;
end majorare_sal_manageri;
end pachet_f_p;
/
I have a requirement to write a procedure (that calls itself recursively).
The condition is like:
If the CONTACT NUMBER(assuming it as varchar2) STARTS WITH '100-%', then the procedure should print all the CONTACT NUMBERS that starts with 100 for the given group.
If CONTACT NUMBER starts with '50-%' then it should call recursively.
I have tried writing a sample procedure, but unfortunately not getting the result.
CREATE OR REPLACE TYPE type_t AS
TABLE OF VARCHAR2(100);
CREATE OR REPLACE PROCEDURE proc_test (
in_group IN VARCHAR2,
contact_nmbr OUT VARCHAR2
) AS
v_out type_t := type_t ();
BEGIN
SELECT
contact_id
BULK COLLECT INTO
v_out
FROM
my_table
WHERE
group_id = in_group;
FOR i IN 1..v_out.count LOOP
v_out.extend;
IF
v_out(i) LIKE '100-%'
THEN
contact_nmbr := v_out(i);
ELSIF v_out(i) LIKE '50-%' THEN
proc_test(v_out(i),contact_nmbr);
END IF;
END LOOP;
END;
/
I am not getting the output after running this proc.
DECLARE
in_group VARCHAR2(30) := '123ABC';
contact_nmbr VARCHAR2(30);
BEGIN
proc_test(in_group,contact_nmbr);
dbms_output.put_line(contact_nmbr);
END;
/
This is the sample data in table MY_TABLE
Group_Id Contact_Id
---------------------------------------
001 100-001-01
001 70-001-01
001 100-002-01
001 50-001-01
50-001-01 30-001-01
50-001-01 100-100-01
50-001-01 50-100-01
50-100-01 50-200-01
Couple of issues I can see with your script....
1) You are declaring the in_group to = 123ABC there is no data matching in your sample data so it will never return anything...
in_group VARCHAR2(30) := '123ABC';
2) You do not exit the loop when you successfully find a contact number so unless your last record is a match you will get no output;
3) when you increment the v_out collection on each loop this serves no purpose this does not do any harm but is not necassary
v_out.extend;
So remove the extend and add a exit as below....
CREATE OR REPLACE PROCEDURE proc_test (
in_group IN VARCHAR2,
contact_nmbr OUT VARCHAR2
) AS
v_out type_t := type_t ();
BEGIN
SELECT
contact_id
BULK COLLECT INTO
v_out
FROM
my_table
WHERE
group_id = in_group;
FOR i IN 1..v_out.count LOOP
IF
v_out(i) LIKE '100-%'
THEN
contact_nmbr := v_out(i);
exit;
ELSIF v_out(i) LIKE '50-%' THEN
proc_test(v_out(i),contact_nmbr);
END IF;
END LOOP;
END;
and call with a valid id as below and you should be good to go.
DECLARE
in_group VARCHAR2(30) := '001';
contact_nmbr VARCHAR2(30);
BEGIN
proc_test(in_group,contact_nmbr);
dbms_output.put_line(contact_nmbr);
END;
I am using Oracle 12, and I want to make a dynamic procedure which selects rows from specific table but according to an unknown conditio. That condition will be specified as input parameter.
Suppose I have a column called employee id and I want to call the procedure
with the following condition
execute s('employeeid = 2')
My code is
create or replace procedure s (condition varchar)
as
TYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR type
emp_cv EmpCurTyp; -- declare cursor variable
my_ename VARCHAR2(15);
my_sal NUMBER := 2;
mycondition varchar2(100):=condition;
BEGIN
OPEN emp_cv FOR -- open cursor variable
'SELECT employeeid, employeename FROM employees WHERE = :s' USING mycondition;
END;
but I am getting an error
missing expression
What am I doing wrong, and will the result of this procedure be selected rows from employees table that satisfy applied condition ?
The USING is meant to handle values, not pieces of code; if you need to edit your query depending on an input parameter ( and I believe this is a very dangerous way of coding), you should treat the condition as a string to concatenate to the query.
For example, say you have this table:
create table someTable(column1 number)
This procedure does somthing similar to what you need:
create or replace procedure testDyn( condition IN varchar2) is
cur sys_refcursor;
begin
open cur for 'select column1 from sometable where ' || condition;
/* your code */
end;
Hot it works:
SQL> exec testDyn('column1 is null');
PL/SQL procedure successfully completed.
SQL> exec testDyn('column99 is null');
BEGIN testDyn('column99 is null'); END;
*
ERROR at line 1:
ORA-00904: "COLUMN99": invalid identifier
ORA-06512: at "ALEK.TESTDYN", line 4
ORA-06512: at line 1
This is not embedded in a procedure yet but I tested this and works:
DECLARE
TYPE OUT_TYPE IS TABLE OF VARCHAR2 (20)
INDEX BY BINARY_INTEGER;
l_cursor INTEGER;
l_fetched_rows INTEGER;
l_sql_string VARCHAR2 (250);
l_where_clause VARCHAR2 (100);
l_employeeid VARCHAR2 (20);
l_employeename VARCHAR2 (20);
l_result INTEGER;
o_employeeid OUT_TYPE;
o_employeename OUT_TYPE;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
l_sql_string := 'SELECT employeeid, employeename FROM employees WHERE ';
l_where_clause := 'employeeid = 2';
l_sql_string := l_sql_string || l_where_clause;
DBMS_SQL.PARSE (l_cursor, l_sql_string, DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
1,
l_employeeid,
20);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
2,
l_employeename,
20);
l_fetched_rows := 0;
l_result := DBMS_SQL.EXECUTE_AND_FETCH (l_cursor);
LOOP
EXIT WHEN l_result = 0;
DBMS_SQL.COLUMN_VALUE (l_cursor, 1, l_employeeid);
DBMS_SQL.COLUMN_VALUE (l_cursor, 2, l_employeename);
l_fetched_rows := l_fetched_rows + 1;
o_employeeid (l_fetched_rows) := l_employeeid;
o_employeename (l_fetched_rows) := l_employeename;
l_result := DBMS_SQL.FETCH_ROWS (l_cursor);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (l_cursor);
DBMS_OUTPUT.PUT_LINE (o_employeeid (1));
DBMS_OUTPUT.PUT_LINE (o_employeename (1));
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('GENERAL FAILURE: ' || SQLERRM);
END;
I'm trying to write a PL/SQL function to store a select statement with a variable table name (a bit weird i know but it is actually a good design decision). The following code does not work...but I'm not sure how to both take a variable table name (building the query) and return a dataset. Anyone have any experience in this? TIA.
CREATE OR REPLACE FUNCTION fn_netstat_all (casename in varchar2)
RETURN resultset_subtype
IS
dataset resultset_subtype;
v_sql varchar2(25000);
v_tablename varchar2(50);
begin
v_sql := 'SELECT * FROM ' || casename || '_netstat;';
OPEN dataset FOR
execute immediate v_sql;
return dataset;
end;
If your resultset_subtype is a ref_cursor (or just replace resultset_subtype with a ref_cursor) you could:
CREATE OR REPLACE
FUNCTION fn_netstat_all (
casename IN VARCHAR2
)
RETURN resultset_subtype
IS
dataset resultset_subtype;
BEGIN
OPEN dataset
FOR 'SELECT * FROM ' || casename || '_netstat';
RETURN dataset;
END fn_netstat_all;
FWIW, you might want to look into the DBMS_ASSERT package to wrap the casename variable to help protect against SQL Injection attacks in your dynamic SQL.
Hope it helps...
Below it is assumed all tables are similar. It's also possible to select a subset of colums that are similar in every table without using DBMS_SQL. I have also paid some attention to SQL injection mentioned by Ollie.
create table so9at (
id number(1),
data varchar2(5)
);
insert into so9at values (1, 'A-AAA');
insert into so9at values (2, 'A-BBB');
insert into so9at values (3, 'A-CCC');
create table so9bt (
id number(1),
data varchar2(5)
);
insert into so9bt values (5, 'B-AAA');
insert into so9bt values (6, 'B-BBB');
insert into so9bt values (7, 'B-CCC');
create table secret_identities (
cover_name varchar2(20),
real_name varchar2(20)
);
insert into secret_identities values ('Batman', 'Bruce Wayne');
insert into secret_identities values ('Superman', 'Clark Kent');
/* This is a semi-secure version immune to certain kind of SQL injections. Note
that it can be still used to find information about any table that ends with
't'. */
create or replace function cursor_of (p_table_id in varchar2)
return sys_refcursor as
v_cur sys_refcursor;
v_stmt constant varchar2(32767) := 'select * from ' || dbms_assert.qualified_sql_name(p_table_id || 't');
begin
open v_cur for v_stmt;
return v_cur;
end;
/
show errors
/* This is an unsecure version vulnerable to SQL injection. */
create or replace function vulnerable_cursor_of (p_table_id in varchar2)
return sys_refcursor as
v_cur sys_refcursor;
v_stmt constant varchar2(32767) := 'select * from ' || p_table_id || 't';
begin
open v_cur for v_stmt;
return v_cur;
end;
/
show errors
create or replace procedure print_values_of (p_cur in sys_refcursor) as
type rec_t is record (
id number,
data varchar2(32767)
);
v_rec rec_t;
begin
fetch p_cur into v_rec;
while p_cur%found loop
dbms_output.put_line('id = ' || v_rec.id || ' data = ' || v_rec.data);
fetch p_cur into v_rec;
end loop;
end;
/
show errors
declare
v_cur sys_refcursor;
begin
v_cur := cursor_of('so9a');
print_values_of(v_cur);
close v_cur;
v_cur := cursor_of('so9b');
print_values_of(v_cur);
close v_cur;
/* SQL injection vulnerability */
v_cur := vulnerable_cursor_of('secret_identities --');
dbms_output.put_line('Now we have a cursor that reveals all secret identities. Just see DBMS_SQL.DESCRIBE_COLUMNS ...');
close v_cur;
/* SQL injection made (mostly) harmless - will throw ORA-44004: invalid qualified SQL name */
v_cur := cursor_of('secret_identities --');
close v_cur;
end;
/