how to dynamically purge the tables in AQADMIN - oracle

When I was trying to purge the QUEUE tables dynamically, I'm getting an error.Below is the code
set serveroutput on;
declare
l_stmt varchar2(2000):='';
po_t dbms_aqadm.aq$_purge_options_t;
BEGIN
for i in (select NAME,queue_table from all_Queues where owner='AQADMIN') loop
--dbms_output.put_line(i.queue_table);
l_stmt:='DBMS_AQADM.PURGE_QUEUE_TABLE ('''||i.queue_table||''',''trunc(enq_time)<sysdate-90'',block => po_t)';
execute immediate l_stmt;
--dbms_output.put_line(l_stmt);
commit;
end loop;
END;
/
Error message:
Error report -
ORA-00900: invalid SQL statement
ORA-06512: at line 8
00900. 00000 - "invalid SQL statement"
*Cause:
*Action:
To know if my query is framing correctly, I commented out the execute immediate part and uncommented the DBMS_OUTPUT command, and the result was posted in sql developer and it executed perfectly, (below is the code output) .so the query is correct, not sure what was wrong with my code,
Output:
anonymous block completed
DBMS_AQADM.PURGE_QUEUE_TABLE ('HEARTBEAT_MSG_QT','trunc(enq_time)<sysdate-90',block => po_t)
then I executed the above output in separate SQL block and it ran fine.Below
declare
po_t dbms_aqadm.aq$_purge_options_t;
begin
DBMS_AQADM.PURGE_QUEUE_TABLE ('HEARTBEAT_MSG_QT','trunc(enq_time)<sysdate-90',po_t);
end;
/
anonymous block completed

When you call execute immediate it is expecting dynamic SQL, not PL/SQL. To run a PL/SQL command you need to wrap it in an anonymous block, just as you did when you ran it manually:
l_stmt:='DECLARE po_t dbms_aqadm.aq$_purge_options_t; BEGIN DBMS_AQADM.PURGE_QUEUE_TABLE ('''||i.queue_table||''',''trunc(enq_time)<sysdate-90'',block => po_t); END;';
or split onto multiple line to make it slightly easier to read:
l_stmt:='DECLARE po_t dbms_aqadm.aq$_purge_options_t;'
|| 'BEGIN '
|| 'DBMS_AQADM.PURGE_QUEUE_TABLE ('''||i.queue_table||''','
|| '''trunc(enq_time)<sysdate-90'',block => po_t);'
|| 'END;';
Notice that you have to re-declare po_t inside that block as that the original declaration is out of scope for the dynamic SQL call (actually, the original one is now redundant...).
You don't need to use dynamic SQL though, you can pass the cursor value straight to the procedure:
DECLARE
l_po dbms_aqadm.aq$_purge_options_t;
BEGIN
for i in (select NAME,queue_table from all_Queues where owner='AQADMIN')
loop
DBMS_AQADM.PURGE_QUEUE_TABLE (queue_table => i.queue_table,
purge_condition => 'trunc(enq_time)<sysdate-90'
purge_options=> l_po);
end loop;
END;
/
I've also removed the commit as it's redundant; from the docs:
This procedure commits batches of messages in autonomous transactions.

Related

SQL*Plus does not run PL/SQL block

I have a problem running my PL/SQL script in SQL*Plus. I can run SQL commands normally but when I want to a run any PL/SQL code it gives nothing. See code and output below.
DECLARE
x_salary employee.salary%TYPE;
BEGIN
select salary
into x_salary
from employee
where ssn=&enter_ssn;
--Output the result
DBMS_OUTPUT.PUT_LINE('Salary is ' || x_salary);
EXCEPTION
--Output when no records are returned
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('No employee found');
WHEN others THEN
DBMS_OUTPUT.PUT_LINE ('Error encountered, but cause unknown');
END;
PL/SQL procedures needs / after procedure definition under sqlplus
DECLARE
...
BEGIN
...
END;
/
Put a slash / in a new line after END; in your script.
From the documentation:
You must include a semicolon at the end of each SQL command and a slash (/) on a line by itself after each PL/SQL block in the file.
Then execute the SQL file in SQL*Plus command line as:
#C:\your_script.sql;

My script has first one invalid procedure and 2nd one valid procedure. I want the script to execute for at least 2nd valid object

I have below PLSQL Script where the PROC1 is invalid due to some reason(we are not discussing why) but PROC2 is valid.
Question: I want the script to run without any issue for at least PROC2? I want Valid PROC2 to be executed irrespective of invalid PROC 1.
BEGIN
PROC1; --This is due to x reason is invalid
PROC2;--This is valid.
END;
You can not execute any INVALID object in your PL/SQL block.
You can not even catch such exception in PL/SQL block. Oracle checks for the status of the all objects used in the pl/sql block and throws error if it is INVALID.
The best way to handle this is to change the proc1, recompile it so that it becomes VALID. That's it.
But, one way of doing it is using execute immediate as following:
declare
lv_status user_objects.status%type;
begin
select status into lv_status from user_objects where object_name = 'PROC1';
if lv_status = 'VALID' THEN
execute immediate 'call PROC1()';
END IF;
proc2;
end;
/
db<>fiddle demo
Cheers!!

Get a value from sql file to the invoking sql file

I have a file1.sql which invokes file2.sql.
file2.sql:
declare
v_stmt varchar2(1000);
begin
v_stmt := 'create index idx on tab1(&1)';
:stmt := v_stmt;
end;
/
file1.sql:
var stmt varchar2(4000);
#file2.sql 'col1'
:stmt;
Executing file1.sql throws the following errors:
SP2-0552: Bind variable "STMT" not declared.
SP2-0042: unknown command ":stmt" - rest of line ignored.
How do I get the value of variable v_stmt in file1.sql?
You can't run arbitrary code from the SQL*Plus command line using a bind variable; the bind variable is relevant to SQL and PL/SQL statements, not natively to SQL*Plus (even though it's declared there with the var).
To execute the statement you'll need to use dynamic SQL, in an anonymous PL/SQL block; so changing file1.sql to:
var stmt varchar2(4000);
#file2.sql 'col1'
begin
execute immediate :stmt;
end;
/
or slightly shorter, but also perhaps slightly confusing::
var stmt varchar2(4000);
#file2.sql 'col1'
exec execute immediate :stmt;
When I run that I see:
SQL> #file1
PL/SQL procedure successfully completed.
begin
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 2
which is reasonable as I don't have your tab1 table in my schema. You can see that is trying to execute the statement though; and there is no SP2-0552 error (though I don't see that with your original code either).
You can also use print stmt to see the generated value before it's run (note there is no colon prefix); or you could use dbms_output within the anonymous block, of course.

how to insert the result of dbms_output.put_line to a table for error catch?

Exception
WHEN OTHERS THEN
--dbms_output.put_line('pl_update_sidm_user r: ERROR CODE:' || sqlcode || '~' ||
--sqlerrm || ' EMPL_NBR:' || r.EMPL_NBR);
insert into ERROR_MSG (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL, 'pl_update_sidm_user_duty_role r2');
END;
I would like to put the error result to a table.
However, how can I do that?
Can I put the result of dbms_output to a table as a string?
If not, can I get the sqlcode,sqlerrm without using dbms_output?
Thank you !!
From the documentation,
A SQL statement cannot invoke SQLCODE or SQLERRM. To use their values
in a SQL statement, assign them to local variables first
Also,
Oracle recommends using DBMS_UTILITY.FORMAT_ERROR_STACK except when using the FORALL statement with its SAVE EXCEPTIONS clause
So, for SQLCODE or SQLERRM, you should assign them into variables and use them.
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION
WHEN OTHERS THEN
v_errcode := SQLCODE;
v_errmsg := SQLERRM;
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) --change your table name
values (ERROR_MSG_ID_SEQ.NEXTVAL,
v_errcode||':'||v_errmsg);
END;
/
Preferably use insert like this instead, as per Oracle's recommendation.
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL,
DBMS_UTILITY.FORMAT_ERROR_STACK);
Demo
Technically what others are suggesting is correct: the "insert" operation executed in the "exception when others" block will actually insert a new row in the log table.
the problem is that such insert statement will be part of the same transaction of the main procedure and, since you had an error while executing it, you are very likely to rollback that transaction, and this will rollback also the insert in your log table
I suppose the problem you are facing is not that you aren't successfully logging the error message: it is that you are rolling it back immediately afterwards, along with all the other writes you did in the same transaction.
Oracle gives you a way of executing code in a SEPARATE transaction, by using "autonomous transaction" procedures.
you need to create such a procedure:
create or replace procedure Write_Error_log(
arg_error_code number,
arg_error_msg varchar2,
arg_error_backtrace varchar2) is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
INSERT INTO error_msg (
error_msg_id,
error_code,
error_msg,
error_stack)
VALUES (
error_msg_id_seq.NEXTVAL,
arg_error_code,
arg_error_msg,
arg_error_backtrace);
commit; -- you have to commit or rollback always, before exiting a
-- pragma autonomous_transaction procedure
end;
What this procedure does is to write a new record in the log table using a totally separate and independent transaction: the data will stay in the log table even if you execute a roll back in your calling procedure. You can also use such a procedure to create a generic log (not only errors).
All you have to do now is to call the procedure above whenever you need to log something, so your code becomes:
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION WHEN OTHERS THEN
Write_Error_log(SQLCODE, SQLERRM, dbms_utility.format_error_backtrace);
END;
/
P.S: there might be some typos in my code: I can't test it right now since I can't reach an oracle server in this moment.

Find bad dynamic SQL statements containing syntax errors in Oracle

I was wondering if there is a way to find dynamic SQL statements whose execution broke during runtime in case there was no proper exception handling in the PL/SQL program unit that called the dynanic SQL.
procedure will_crash is
begin
-- 1000 dynamic sql statements here ..
execute immediate 'updaXte dual set X = ''Z'' ' ;
-- ... and 1000 more dynamic sql statements here ..
commit;
end; --> NO proper exception handling for logging the last sql statement
In case a nighttime scheduled program unit contains hundreds of dynamic sql statements, I'd like to find out which one broke without debugging. Does Oracle log anything in its system views?
In you're example there's no need at all to use dynamic SQL, so the easiest solution is to rewrite it as
procedure will_crash is
begin
-- 1000 dynamic sql statements here ..
updaXte dual set X = ''Z''; -- Error at compile time!!
-- ... and 1000 more dynamic sql statements here ..
commit;
end; --> NO proper exception handling for logging the last sql statement
If the real procedure uses dynamic SQL because the SQL statements are saved elsewhere (variables or SQL table), and we're talking only about syntax error, you can achieve your objective before executing statements using DBMS_SQL package.
Here's an example:
DECLARE
lc_good VARCHAR2(200) := 'SELECT 1 FROM dual';
lc_bad VARCHAR2(200) := 'SEL1ECT a FROM dual';
lc_cursor NUMBER;
BEGIN
lc_cursor := SYS.DBMS_SQL.OPEN_CURSOR;
BEGIN
SYS.DBMS_SQL.PARSE(lc_cursor, lc_good, DBMS_SQL.V7);
dbms_output.put_line('Statement 1 is GOOD');
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Statement 1 is BAD: ' || SQLERRM);
END;
BEGIN
SYS.DBMS_SQL.PARSE(lc_cursor, lc_bad, DBMS_SQL.V7);
dbms_output.put_line('Statement 2 is GOOD');
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Statement 2 is BAD: ' || SQLERRM);
END;
SYS.DBMS_SQL.close_cursor(lc_cursor);
END;
The function output is the following:
Statement 1 is GOOD
Statement 2 is BAD: ORA-00900: ...
Note that the code only parses the SQL statements, it don't executes them.

Resources