This question already has answers here:
Why is static ddl not allowed in PL/SQL?
(3 answers)
Closed 2 years ago.
I want to run this inside my stored procedure, but it's giving me error:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "temp1"';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
create table temp1(id number);
ELSE
RAISE;
END IF;
END;
I've also tried to put BEGIN END block around create table temp1(id number), but error stays the same.
Error:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the
following:
( begin case declare exit for goto if loop mod null pragma raise
return select update while with << continue close current
delete fetch lock insert open rollback savepoint set sql execute
commit forall merge pipe purge
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The CREATE TABLE is also DDL just like the DROP so must also be run as dynamic SQL:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "temp1"';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
EXECUTE IMMEDIATE 'create table temp1(id number)';
ELSE
RAISE;
END IF;
END;
Related
I want to handle no data found. Whenever this exception is raised I want the program to continue, without stopping on error. Below is code snippet
BEGIN
OPEN C_TABLE_PARTITON_LIST;
LOOP
FETCH C_TABLE_PARTITON_LIST INTO TABLE_PARTITION_LIST;
EXIT WHEN C_TABLE_PARTITON_LIST%NOTFOUND;
SELECT COLUMN_NAME INTO PARTITION_COLUMN_NAME from ALL_PART_KEY_COLUMNS
sqlstring :='SELECT ( '|| PARTITION_COLUMN_NAME ||'from test';
EXECUTE IMMEDIATE sqlstring INTO F_RESULT;
exception when no_data_found then
dbms_output.put_line('no data found.');
DBMS_OUTPUT.put_line( F_RESULT);
END LOOP;
CLOSE C_TABLE_PARTITON_LIST;
END;
When I add Exception, my code is breaking with below error
PLS-00103: Encountered the symbol "EXCEPTION" when expecting one of the following:
( begin case declare end exit for goto if loop mod null
pragma raise return select update while with
<<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
json_exists json_value json_query json_object json_array
ORA-06550: line 29, column 3:
PLS-00103: Encountered the symbol "CLOSE" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map
You have to enclose offending part of the script into its own BEGIN-EXCEPTION-END block, e.g.
BEGIN
OPEN C_TABLE_PARTITON_LIST;
LOOP
FETCH C_TABLE_PARTITON_LIST INTO TABLE_PARTITION_LIST;
EXIT WHEN C_TABLE_PARTITON_LIST%NOTFOUND;
begin --> you need this ...
SELECT COLUMN_NAME INTO PARTITION_COLUMN_NAME from ALL_PART_KEY_COLUMNS
sqlstring :='SELECT ( '|| PARTITION_COLUMN_NAME ||'from test';
EXECUTE IMMEDIATE sqlstring INTO F_RESULT;
exception when no_data_found then
dbms_output.put_line('no data found.');
DBMS_OUTPUT.put_line( F_RESULT);
end; --> ... and this
END LOOP;
CLOSE C_TABLE_PARTITON_LIST;
END;
Note that I just showed the way to do that. Code you posted
is incomplete (misses the DECLARE section)
is invalid (SELECT statement lacks semi-colon, and probably a WHERE clause
SQLSTRING variable won't work; 'from test' should have a leading space, otherwise that statement will be invalid
I suggest you first DBMS_OUTPUT the SQLSTRING to make sure it is correct; then execute it.
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.
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.
I am trying to create this stored procedure which should take customer no and email address as input. Then update the email address for that customer. if new email address is same as old then exception should be raised.
CREATE OR REPLACE PROCEDURE UpdateEmail
(CUSTOMERID IN CUSTOMER.CUSTOMERNO%TYPE,
N_EMAIL IN CUSTOMER.EMAIL%TYPE)
IS
DECLARE
e_same EXCEPTION;
V_ROWCOUNT NUMBER;
BEGIN
SELECT COUNT(EMAIL)
INTO V_ROWCOUNT
FROM CUSTOMER#FIT5148B
WHERE CUSTOMER.EMAIL = N_EMAIL
AND CUSTOMER.CUSTOMERNO = CUSTOMERID;
IF V_ROWCOUNT > 0
THEN RAISE e_same;
ELSE
UPDATE CUSTOMER
SET EMAIL = N_EMAIL
WHERE CUSTOMER.CUSTOMERNO = CUSTOMERID;
END IF;
EXCEPTION
WHEN e_same THEN dbms_output.put_line ('email address exist');
END;
/
But it is throwing error. Not sure if I am doing it right. I am using the latest Oracle SQL Developer.
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting
one of the following: begin function pragma procedure subtype type
current
cursor delete exists prior external language The symbol "begin" was
substituted for "DECLARE" to continue.
Error(28,4): PLS-00103:
Encountered the symbol "end-of-file" when expecting one of the
following: ( begin case declare end exception exit for goto if
loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open
rollback savepoint set sql execute commit forall merge pipe purge
Remove DECLARE .
You should not use Declare in a Procedure. If you are writing a PLSQL block then only you should use Declare. Not in an actual Procedure body.
I am trying to run the following stored procedure:
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL;
END;
END;
/
and calling it in changelog.xml as:
<sql>CALL RETRY_TRANS_EXCEPTION();</sql>
i get error:
Liquibase update Failed: Migration failed for change set eldata-changelog.xml::2016-08-25-cn-01::Ch Will:Reason: liquibase.exception.DatabaseException: Error executing SQL CALL RETRY_TRANS_EXCEPTION(): ORA-06575: Package or function RETRY_TRANS_EXCEPTION is in an invalid state
What i am trying to achieve is to be able to run a stored procedure through Liquibase with a loop in it.
Thanks for your help Prashant. What worked in my case was your solution plus one change:
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
v_query varchar2(100);
BEGIN
FOR i IN 1..500 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
v_query := 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NULL';
execute immediate v_query;
END loop;
END;
/
and then calling the Stored Procedure from the changelog, as:
<changeSet id="2016-08-25-cw-01" author="Ch Will">
<comment>
Testing retry logic on liquibase
</comment>
<sql>CALL RETRY_TRANS_EXCEPTION();</sql>
</changeSet>
You can't call it because the procedure does not compile correctly. Go back and fix the compilation errors, then try again.
Here are a couple of errors that stand out to me:
the for loop should end with end loop;, not end;
You can't have DDL statements directly in the code. You need dynamic SQL to execute a DDL statement from a procedure: execute immediate 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL';
Additional note: I don't understand why you are trying to execute the same DDL statement multiple times inside a loop. Obviously, you won't be able to add the same column with the same name over and over. You will get a runtime error.
SQL> CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
2 AS
3 BEGIN
4 FOR i IN 1..5 LOOP
5 DBMS_OUTPUT.PUT('Try #' || i);
6 ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL;
7 END;
8 END;
9 /
Warning: Procedure created with compilation errors
SQL> show err
Errors for PROCEDURE PRASHANT-MISHRA.RETRY_TRANS_EXCEPTION:
LINE/COL ERROR
-------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
6/4 PLS-00103: Encountered the symbol "ALTER" when expecting one of the following: ( begin case declare end exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Did required fixes :
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
v_query varchar2(100);
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
v_query := 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL' ;
execute immediate v_query;
END loop;
END;
PLSQL stored procedure can't use DDL statements, like
alter table ...
so the
execute immediate ("...")
statement is required because in fact it creates an autonomous implicit transition that can't be rollbacked