Dynamic SQL - Check syntax and semantics - sql

With Oracle dynamic SQL one is able to execute a string containing a SQL statement. e.g.
l_stmt := 'select count(*) from tab1';
execute immediate l_stmt;
Is it possible to not execute l_stmt but check that the syntax and semantics is correct programmitically?

EXPLAIN PLAN will check the syntax and semantics of almost all types of SQL statements. And unlike DBMS_SQL.PARSE it will not implicitly execute anything.
The point of the explain plan is to show how Oracle will execute a statement. As a side-effect of generating the plan it must also check syntax, privileges, and generally do everything except actually run the statement. The explain plan itself is pointless and can be ignored, the statement is only run to check for any errors. As long as there are no errors, the statement is valid.
For example, the PL/SQL blocks below check the validity of a SELECT statement and a CREATE TABLE statement. They run without error so the syntax is fine.
begin
execute immediate 'explain plan for select * from dual';
end;
/
begin
execute immediate 'explain plan for create table just_some_table(a number)';
end;
/
Running a bad statement will generate an error. In at least this one test case, it generates the same error as if the statement was run by itself.
begin
execute immediate 'explain plan for select * from this_table_does_not_exist';
end;
/
ORA-00942: table or view does not exist
ORA-06512: at line 2
The syntax diagram in the manual implies it should run for all statements. However, there appear to be at least a few statement types that do not work, such as ALTER SESSION.
begin
execute immediate 'explain plan for alter session set optimizer_features_enable = ''11.2.0.4''';
end;
/
ORA-00900: invalid SQL statement
ORA-06512: at line 2
Slightly off-topic - are you trying to build a completely generic SQL interface, like a private SQL Fiddle built in PL/SQL? Do you need to worry about things like preventing users from attempting to run certain statement types, and ensuring there are no trailing semicolons? If so I can edit the question to help with some of those difficult dynamic SQL tasks.

I think that the only "solution" is to use DBMS_SQL.PARSE().
It is not perfect but it is the best that you can get

Hope this way you can check the query formed before execution.
set serveroutput on;
DECLARE
lv_sql VARCHAR2(1000);
lv_cnt PLS_INTEGER;
BEGIN
lv_sql:='select count(*) from tab1';
dbms_output.put_line(lv_sql);
--EXECUTE IMMEDIATE lv_sql INTO lv_cnt;
END

Related

ora-00900 invalid sql statement execute immediate

I am trying to solve a task with a dynamic SQL, but facing an issue ora-00900 invalid sql statement.
execute immediate 'alter table my_table set interval (NUMTOYMINTERVAL(1, ''MONTH''))';
However, it works in the anonymous block treating the statement to be executed as a string.
DECLARE
str VARCHAR2 (250) := 'alter table my_table set interval (NUMTOYMINTERVAL(1, ''MONTH''))';
BEGIN
execute immediate str;
END;
So where is the issue in the first case? It looks like with escape quotes, but can’t catch this.
As the error says, execute immediate is not a SQL statement. There is an execute command in some clients - SQL*Plus, SQL Developer, SQLcl and possibly others - which is a shorthand wrapper around an anonymous block; but from the error you don't seem to be using one of those. If you were you'd get ORA-06550 and PLS-0010: Encounter edthe symbol "alter table..." when expecting...
execute immediate is a PL/SQL statement. So it is only valid within a PL/SQL block.
It has no meaning in plain SQL; the only execute in the SQL reference is referring to directory object privileges.
Your alter table doesn't make much sense anyway, not sure if that's supposed to be an update, but if so it isn't say what to set to that interval value. It isn't obvious why you need it to be dynamic either. Possibly that reason and the actual statement have been lost in simplifying for posting your question.

Creating parameterized cursors in DB2

I'm Facing below error:
An unexpected token "(" was found following " CURSOR ". Expected tokens may include: "CURSOR". SQLSTATE=42601
And I'm just trying to create a simple cursor, actually the example one found here in IBM documentation.
Cursor declaration looks something like:
DECLARE
CURSOR c1 (max_wage NUMBER) IS
SELECT * FROM emp WHERE sal < max_wage;
Not sure if this is do to the version of DB2 being used or not. Can anyone suggest maybe an alternative to creating a parameterized cursor?
You are trying to use PL/SQL syntax in DB2. This requires changes to the server environment. If you want to support the Oracle datatypes as well, the database must be created with the right settings, too. See this article for more details. The summary of that article is:
Open a DB2 Command Window (in Administrator mode)
Run db2start
Run db2set DB2_COMPATIBILITY_VECTOR=ORA
Run db2set DB2_DEFERRED_PREPARE_SEMANTICS=YES
Run db2stop
Run db2start
Execute your PL/SQL statements, e.g. in a DB2 CLP (run db2 -tv) command window.
Note that you should run
SET SQLCOMPAT PLSQL; in your DB2 CLP before trying PL/SQL. This enables using a forward slash (/) as a PL/SQL statement terminator. You should then obviously also then actually terminate your command with a forward slash :)
Here's an example taken from your link, modified to work with the default SAMPLE database in DB2:
SET SQLCOMPAT PLSQL;
DECLARE
my_record emp%ROWTYPE;
CURSOR c1 (max_wage integer) IS
SELECT * FROM employee WHERE salary < max_wage;
BEGIN
OPEN c1(40000);
LOOP
FETCH c1 INTO my_record;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || my_record.firstnme || ', salary = '
|| my_record.salary);
END LOOP;
CLOSE c1;
END;
/
If you don't want to do all the above, then use the standard DB2 cursor syntax:
DECLARE [cursor name] CURSOR FOR [...]
...but that doesn't support parameterized cursors. To do so, I'd recommend creating a stored procedure taking the parameter. This stored procedure can then create a cursor using that parameter directly in the SQL.

PL/SQL, Cannot print variable

I'm trying to learn PL/SQL by simply assigning a variable from a select statement and then, to confirm it's working, print it sql output.
DECLARE ALLOW_STUFF NUMBER;
BEGIN
SELECT VAL_N INTO ALLOW_STUFF FROM MY_TABLE WHERE MY_KEY = 'ALLOW_ME';
DBMS_OUTPUT.PUT_LINE(ALLOW_STUFF);
END;
I'm using SQL Developer and/or SQL PLus. When I run this, all I get is
Anonymous block completed
Rather than than the value of MY_TABLE.VAL_N
You need to enable output, otherwise the DBMS_OUTPUT.PUT_LINE statements are ignored.
Output can be enabled using:
DBMS_OUTPUT.ENABLE();
For more information about DBMS_OUTPUT read Oracle documentation: http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_output.htm#i1000634
As stated in the comments also set serveroutput on can be used.

Oracle: DBMS_UTILITY.EXEC_DDL_STATEMENT vs EXECUTE IMMEDIATE

Which are the differences between DBMS_UTILITY.EXEC_DDL_STATEMENT and EXECUTE IMMEDIATE?
Fundamentally they do the same thing, which is to provide a mechanism to execute DDL statements in PL/SQL, which isn't supported natively. If memory serves me well, the EXEC_DDL_STATEMENT was available in the Oracle 7 version of the DBMS_UTILITY package, whereas Native Dynamic SQL (EXECUTE IMMEDIATE) was only introduced in 8.
There are a couple of differences. EXECUTE IMMEDIATE is mainly about executing dynamic SQL (as its NDS alias indicates). the fact that we can use it for DDL is by-the-by. Whereas EXEC_DDL_STATEMENT() - as the suggests - can only execute DDL.
But the DBMS_UTILITY version isn't retained just for backwards compatibility, it has one neat trick we cannot do with EXECUTE IMMEDIATE - running DDL in a distributed fashion. We can run this statement from our local database to create a table on a remote database (providing our user has the necessary privileges there):
SQL> exec DBMS_UTILITY.EXEC_DDL_STATEMENT#remote_db('create table t1 (id number)');
I'm not recommending this, just saying it can be done.
I realize I am 9 years late to the reply but there is one additional difference.
dbms_utility.exec_ddl_statement will not execute anything but DDL. If you try to do say an insert, it will not do it. It will also not return an error either so you won't know that you did not insert.
-- drop table kevtemp1;
create table kevtemp1 (a integer);
insert into kevtemp1 values (1);
commit;
begin
insert into kevtemp1 values (2);
end;
/
commit;
begin
DBMS_UTILITY.EXEC_DDL_STATEMENT('insert into kevtemp1 values (3)');
end;
/
commit;
select * from kevtemp1;

ORACLE Batching DDL statements within a Execute Immediate

I'm trying to run multiple ddl statements within one Execute Immediate statement.
i thought this would be pretty straight forward but it seems i'm mistaken.
The idea is this:
declare v_cnt number;
begin
select count(*) into v_cnt from all_tables where table_name='TABLE1' and owner = 'AMS';
if v_cnt = 0 then
execute immediate 'CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL) ALTER TABLE TABLE1 ADD (MYVAL2 NVARCHAR2(10))';
end if;
end;
however this results in an error
ORA-00911: invalid character
ORA-06512: at line 10
Each of the statements within the batch run fine if i execute them by themselves. and if i take this statement and execute it, it will run fine (with the ; between the 2 statements). If i remove the ; between statements i get a different error about invalid option
the plan is that i'll be able to build a table, export the table schema for this table including all it's alter statements, and then run the batch against another system as part of an install/update process.
So, how do i batch these DDL statements within a single execute immediate? Or is there a better way to do what i'm needing?
I'm a bit of a Oracle newb, i must admit. Thank you all for your patience.
The semicolon is not part of Oracle's SQL syntax. SQL*Plus and other client side tools use semicolon to signal the end of a SQL Statement, but the server doesn't see it.
We can force SQL*Plus to pass the semicolon to the DB:
SQL> set sqlterminator off
SQL> select * from user_tables;
2 /
select * from user_tables;
*
ERROR at line 1:
ORA-00911: invalid character
If i take this statement and execute it, it will run fine (with the ; between the 2 statements) The client tool you are using, breaks it into two calls to the DB.
So, I don't think it is possible to pass multiple statements inside an execute immediate.
I suppose one could call execute immediate with a string containing an anonymous PL/SQL block, with individual calls to execute immediate inside it ... and I don't know what the point of doing that would be. ;)
Why do you need a single EXECUTE IMMEDIATE call? Surely just do it as 2 calls?
Bear in mind that each DDL statement contains an implicit COMMIT, so there's no concurency benefit to doing it as a single call.
Also, why not just set up the table correctly in the first call? You could do...
CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL, MYVAL2 NVARCHAR2(10))
...instead of needing 2 calls.
Also, have you looked at DBMS_METADATA... it can generate DDL for objects such as tables for you.