Oracle creating sequences with execute immediate - sql

This might have already been asked but I am not able to resolve it, so posting again.
I need to create an oracle sequence with the start value coming from a variable. So obviously I need to use execute immediate for the same within a pl/sql block.
I used the following PL/SQL block to create the sequence:
declare nl_seqmax NUMBER :=0;
BEGIN
SELECT 1000000009
into nl_seqmax
from dual;
if nl_seqmax > 0 THEN
execute immediate 'CREATE SEQUENCE my_seq INCREMENT BY 1 START WITH '||nl_seqmax || ' MAXVALUE 4000000000 CACHE 20 ORDER';
end if;
end;
/
PL/SQL procedure successfully completed.
But later when referencing this sequence, I see that it throws 942 error.
SQL> AUDIT GRANT ON my_seq BY ACCESS WHENEVER SUCCESSFUL;
ERROR at line 1:
ORA-00942: table or view does not exist
Any idea on this ? Should all the references to sequence also should use dynamic sql ?

Related

Oracle dynamic SQL: UDF within execute immediate

I am trying to make some of my code dynamic. While typing the question how to use UDFs in dynamic SQL, I figured out the answer:
One can call the UDF from outside!
This works:
Update my_table
Set col1 = get_some_value(col2,col2)
Where 1 = 1;
This did not work:
Execute Immediate '
Update my_table
Set col1 = get_some_value(col2,col3)
Where 1 = 1
';
But this works:
Execute Immediate '
Update my_table
Set col1 = my_package_name.get_some_value(col2,col3)
Where 1 = 1
';
I am using Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
In case you have an idea, how to skip the call from outside, feel free to let me know.
Many greeetings,
Peter
Check your grants and make sure you either include explicit schema owner in the quoted call, use a synonym, or connect directly as schema owner.
Remember that stored procedures execute normally with the privileges of the code creator, so you should make certain that the username you use to run the execute immediate has direct grant (not via a role) access to execute the function.
This works fine in Oracle 12c when logged in as schema owner:
create function myfunc(p_text in varchar2) return varchar2 is
begin
return initcap(p_text);
end;
/
begin
execute immediate 'update emp set ename = myfunc(ename)';
end;
/
select ename from emp;
Returns:
King
Blake
Clark
...
EDIT:
Based on the additional information that function and calling procedure are in the same package, it is likely that the problem is merely naming and scope.
When using execute immediate, the statement is parsed and executed at runtime, by Oracle's SQL engine, with very limited context of the surrounding code.
In short, the payload of execute immediate doesn't know it's running in a package.
Here's a demo that should clear things up a bit.
create or replace package mytest as
function public_func(p_text in varchar2) return varchar2;
procedure demo;
end;
/
create or replace package body mytest as
-------------------------------------------------------------------------------
function public_func(p_text in varchar2) return varchar2 is
begin
return initcap(p_text);
end;
-------------------------------------------------------------------------------
function private_func(p_text in varchar2) return varchar2 is
begin
return lower(p_text);
end;
-------------------------------------------------------------------------------
procedure demo is
begin
-- Test 1 should fail because the function name is not fully qualified
begin
execute immediate 'update emp set ename = public_func(ename)';
exception when others then
dbms_output.put_line('Test1: ' || SQLERRM);
end;
-- Test 2 should pass
begin
execute immediate 'update emp set ename = mytest.public_func(ename)';
exception when others then
dbms_output.put_line('Test2: ' || SQLERRM);
end;
-- Test 3 should fail because the private function is not visible
begin
execute immediate 'update emp set ename = mytest.private_func(ename)';
exception when others then
dbms_output.put_line('Test3: ' || SQLERRM);
end;
end;
end;
/
Here's the results:
SQL> set serveroutput on;
SQL> begin
2 mytest.demo;
3 end;
4 /
Test1: ORA-00904: "PUBLIC_FUNC": invalid identifier
Test3: ORA-00904: "MYTEST"."PRIVATE_FUNC": invalid identifier
PL/SQL procedure successfully completed.
SQL>
For test 1, the SQL engine is looking for something called "public_func" and can't find it. This make sense because you could have two packages that each have something called "public_func" in them. The SQL engine does not know that it is being invoked from within a package.
Test 2 is what you did, and it works as expected.
For test 3, a function is called that exists only within the package body. Normally, other procedures in the package can see private functions, but since this is interpreted at runtime and the SQL engine doesn't know it's being called within the scope of the package, this call fails as well.

Invalid identifier error while running plsql block

Since my school does not allow me to post the code, hence i had to come back home and put up an example to show the issue i am facing. My school asked me to do a homework on dynamic sql to create a table and later insert one dummy record to it. But while doing it I am facing the below issue. Please find the code below for your reference. Thank you.
Procedure:
create or replace procedure table_creation(table_name in varchar2,col1 varchar2,col2 varchar2)
is
l_stat varchar2(3000);
v_stat varchar2(1000);
a varchar2(10):='1';
b varchar2(10):='Dummy';
begin
l_stat:='create table '||table_name||' ("'||col1||'" varchar2(10),"'||col2||'" varchar2(10))';
execute immediate l_stat;
execute immediate 'insert into '||table_name||'('||col1||','||col2||') values (:x,:y)' using a,b;
end;
/
Plsql Block to call the procedure:
set serveroutput on;
declare
a varchar2(10);
b varchar2(10);
c varchar2(10);
begin
a:='Example';
b:='id';
c:='nm';
table_creation(a,b,c);
dbms_output.put_line('Yes');
end;
/
The procedure is getting created perfectly and while running the above block i am getting the below error .
declare
*
ERROR at line 1:
ORA-00904: "NM": invalid identifier
ORA-06512: at "SYS.TABLE_CREATION", line 9
ORA-06512: at line 9
But when I checked the created table. The table exists with 0 records. The structure of the table is as follows.
Name Null? Type
----------------------------------------- -------- ---------------
ID VARCHAR2(10)
NM VARCHAR2(10)
Any help from your end is much appreciated.
Nick,
Please note that the above procedure will create the column with a double qoutes ("ID","NM") hence the error occurred. Please find the updated code and check if the issue has resolved.
According to oracle=>
Oracle is case sensitive in column and table names. It just converts everything to upper case by default. But if you use names in double quotes, you tell Oracle to create the column in the exact spelling you provided (lower case in the CREATE statement).
Code:
create or replace procedure table_creation(table_name in varchar2,col1 varchar2,col2 varchar2)
is
l_stat varchar2(3000);
v_stat varchar2(1000);
a varchar2(10):='1';
b varchar2(10):='Dummy';
begin
l_stat:='create table '||table_name||' ('||col1||' varchar2(10),'||col2||' varchar2(10))';
execute immediate l_stat;
execute immediate 'insert into '||table_name||'('||col1||','||col2||') values (:x,:y)' using a,b;
end;
/
Note: I have not touched any other logic of the code. Please try and let us know the result.
Only change is
From :
l_stat:='create table '||table_name||' ("'||col1||'" varchar2(10),"'||col2||'" varchar2(10))';
to :
l_stat:='create table '||table_name||' ('||col1||' varchar2(10),'||col2||' varchar2(10))';

How do I fix auto increment in oracle after a data refresh

Every time my team does a data refresh to our UAT environment we have issues with the 'auto incremented' columns in oracle They hold onto the old value and therefore cause errors when a new insert happens. The only solution I have found is to use
select test_SEQ.nextval from test_table;
Until the next sequence is bigger then the max seq number in the table. I have over 200 tables to update, is there an easier why to do this?
Thanks
Erin
One better way to do this would be to drop the sequences and create new ones with the desired START WITH value. You could generate the DDL to do this dynamically.
Check the following sqlfiddle http://sqlfiddle.com/#!4/17345/1
It doesn't completely work due to limitations in sqlfiddle, but here's the function that makes it happen:
create or replace function
reset_sequence(p_sequence_name varchar,
p_table_name varchar,
p_column_name varchar)
return integer is
v_temp integer;
v_sql varchar(2000);
begin
v_sql := 'select nvl(max('||p_column_name||'),0)+1 col_name from '||p_table_name;
execute immediate v_sql INTO v_temp;
v_sql := 'drop sequence '||p_sequence_name;
execute immediate v_sql;
v_sql := 'create sequence '||p_sequence_name||' start with '||v_temp;
execute immediate v_sql;
return v_temp;
end;
Basically you call this function and pass a schema name, table name and column name and it will set the function to the correct value. You can put a begin/exception/end block to ignore errors when dropping the sequence, in case it doesn't exist, but all that is just icing. You could also have it detect the column that is the primary key if you wanted, but no real way in Oracle to detect the sequence name.
You could also make a procedure version of this, but I tend to prefer functions for whatever reason.

Oracle: Find the position of an error in dynamic SQL using SQL or PL/SQL

How can I find the position of an error in a Dynamic SQL statement in PL/SQL or SQL?
From SQL*Plus I see the position of an error in, for example, an invalid SQL DML statement:
SYS#orcl> SELECT
2 X
3 FROM
4 TABLEX
5 /
TABLEX
*
ERROR at line 4:
ORA-00942: table or view does not exist
SQL*Plus shows the error with the line number, and prints and marks that line with an asterisk where the error is found.
Converting to Dynamic SQL, I can get the error code (SQLCODE) and error message (SQLERRM):
SYS#orcl> SET SERVEROUTPUT ON
SYS#orcl> BEGIN
2 EXECUTE IMMEDIATE 'SELECT X FROM TABLEX';
3 EXCEPTION
4 WHEN OTHERS THEN
5 DBMS_OUTPUT.PUT_LINE('SQLCODE:' || SQLCODE);
6 DBMS_OUTPUT.PUT_LINE('SQLERRM:' || SQLERRM);
7 END;
8 /
SQLCODE:-942
SQLERRM:ORA-00942: table or view does not exist
But how do I get the position of the error in the Dynamic SQL string?
I see that Oracle provides a SQL Communications Area (SQLCA) that contains interesting information about an error. In particular:
the SQLCODE and SQLERRM fields (that might be the source of the data retrieved with the respective PL/SQL functions),
the SQLERRD field where the SQLERRD(5) element that gives the 'parse error offset'.
Is it possible to access SQLERRD from PL/SQL or SQL? If so, how? If not, what other technique can give the location of the error from PL/SQL or SQL?
(Here http://docs.oracle.com/cd/B28359_01/appdev.111/b31231/chapter8.htm#BABIGBFF the SQLCA is documented and accessed with Pro*C.)
(The answer here how to declare SQLCA.SQLERRD? seems to indicate that SQLERRD is not defined in PL/SQL and therefore not accessible.)
(The discussion here Why doesn't Oracle tell you WHICH table or view does not exist? gives some suggestions to show bad SQL using trace files and to show the location of errors in some development tools.)
you got a package for extracting error messages in dbms_utility
begin
.. generate error
exception when others then
dbms_output.put_line(
dbms_utility.format_call_stack() || chr(10) ||
dbms_utility.format_error_backtrace() || chr(10) ||
dbms_utility.format_error_stack())
end;
Running the statement through dynamic PL/SQL will store the relevant line number in the error stack.
For example, this statement has an error on line 4:
declare
v_count number;
v_bad_sql varchar2(32767) :=
'SELECT
X
FROM
TABLEX';
begin
execute immediate v_bad_sql into v_count;
exception when others then
begin
execute immediate
'begin for i in ( '||v_bad_sql||') loop null; end loop; end;';
exception when others then
dbms_output.put_line(sqlerrm);
end;
end;
/
ORA-06550: line 4, column 4:
PL/SQL: ORA-00942: table or view does not exist
ORA-00942: table or view does not exist
ORA-06550: line 1, column 18:
PL/SQL: SQL Statement ignored
ORA-00942: table or view does not exist
There are some drawbacks to this method:
It requires some extra, ugly code to catch the exception and re-try the SQL.
The example only works for selects. You'll need to tweak it for insert, update, delelete, merge, dynamic PL/SQL, etc. Normally you should know what kind of SQL statement it is. If you're unlucky, you'll need to parse the statement, which can be very difficult.
The column number is wrong if the entire PL/SQL statement is on one line.

Oracle Stored Procedure with Alter command

I am trying to build an Oracle stored procedure which will accept a table name as a parameter. The procedure will then rebuild all indexes on the table.
My problem is I get an error while using the ALTER command from a stored procedure, as if PLSQL does not allow that command.
Use the execute immediate statement to execute DDL inside PL/SQL.
create procedure RebuildIndex(index_name varchar2) as
begin
execute immediate 'alter index ' || index_name || ' rebuild';
end;
I tested this code; it works.
Documentation.
Passing Schema Object Names As Parameters
Suppose you need a procedure that
accepts the name of any database
table, then drops that table from your
schema. You must build a string with a
statement that includes the object
names, then use EXECUTE IMMEDIATE to
execute the statement:
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
CREATE PROCEDURE drop_table (table_name IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
END;
/
Use concatenation to build the string,
rather than trying to pass the table
name as a bind variable through the
USING clause.
In addition, if you need to call a
procedure whose name is unknown until
runtime, you can pass a parameter
identifying the procedure. For
example, the following procedure can
call another procedure (drop_table) by
specifying the procedure name when
executed.
CREATE PROCEDURE run_proc (proc_name IN VARCHAR2, table_name IN VARCHAR2) ASBEGIN
EXECUTE IMMEDIATE 'CALL "' || proc_name || '" ( :proc_name )' using table_name;
END;
/
If you want to drop a table with the
drop_table procedure, you can run the
procedure as follows. Note that the
procedure name is capitalized.
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
BEGIN
run_proc('DROP_TABLE', 'employees_temp');
END;
/
Here are a couple of possibilities. First, you would have to treat the SQL as dynamic SQL. Second, Oracle DDL statements cannot be run in a transaction (or, they terminate the current transaction and cannot themselves be rolled back). This may affect whether you can use them in stored procedures, or where you can use stored procedures that contain them.
If none of the above apply at all - there could easily be something else astray - I suggest posting some code.