Oracle merge partition from procedure giving error - oracle

CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
The above procedure is giving me the following error -
ORA-01031: insufficient privileges
ORA-06512: at "test", line 8
ORA-06512: at line 6
But if I run the ALTER Command directly on the sql prompt, I am not receiving any error..
I am wondering what permission I need to provide the user to perform the merge from the procedure.

I fixed the issue by using AUTHID CURRENT_USER
CREATE OR REPLACE PROCEDURE test AUTHID CURRENT_USER
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/

Note: you should never use DDL in stored procedures. Below is the sample of very, very badly designed code which should be avoided.
CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'GRANT ALTER ON daily_table TO your_user';
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/

I'm sure that if someone creates a stored proc SPECIFICALLY for DDL, then they realize that it is commited automatically.

I do use DDL in stored procedures: most commonly to truncate summary tables that the procedure will then re-populate; now and then for DDLish tasks such as renaming columns of an imported table to conform to Oracle's standard rules for an identifier, or for creating primary keys and sequences for named tables. Generally I use
dbms_utility.exec_ddl_statement(blah);
rather than
EXECUTE IMMEDIATE blah;
a prejudice I won't attempt to justify. I will say, that having packaged procedure supplied and documented by Oracle suggests that it is not to be prohibited across the board.

Related

DROPPING TABLE FROM DIFFERENT SCHEMA

I wrote a Procedure which essentially used to drop table.
It works as expected , but the problem is As this Procedure is in "DW" schema and dropping table is in "SCRATCH".
Because of this, we get insufficient privileges.
Any way we can achieve this and solve the "privilege" issue?
CREATE OR REPLACE PROCEDURE DW.PROC AUTHID CURRENT_USER IS
V_TABLE_NAME VARCHAR2(255);
V_DELETE_DT NUMBER(33);
V_LIST SYS_REFCURSOR;
BEGIN
SELECT TO_NUMBER(VALUE) INTO V_DELETE_DT FROM DW.LIST_OF_TABLE
OPEN V_LIST FOR
SELECT OBJECT_NAME FROM ALL_OBJECTS WHERE OBJECT_TYPE= 'TABLE' AND
OBJECT_NAME LIKE '%DLY_BKP%' AND CREATED <=SYSDATE - V_DELETE_DT;
LOOP
FETCH V_LIST
INTO V_TABLE_NAME;
EXIT WHEN V_LIST%NOTFOUND;
EXECUTE IMMEDIATE 'DROP TABLE SCRATCH.'||V_TABLE_NAME ;
END LOOP;
CLOSE V_LIST;
END;
To drop a table in another schema, you must do one of the following:
Be granted the "DROP ANY TABLE" system privilege. This is highly elevated and it is likely your DBAs will balk at it, but it'll work.
Create a procedure owned by the table owner that accepts a table name as a parameter and issues the drop DDL on your behalf. Grant execute privs on the procedure to your connect user. This is usually the better choice. You can further enhance it to table-drive a list of tables or table name patterns that are white-listed or black-listed for drop, whatever makes sense.
At it's most simplest:
CREATE OR REPLACE PROCEDURE scratch.drop_table(in_table_name IN varchar2)
AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "'||in_table_name||'"';
END;
/
GRANT EXECUTE ON scratch.drop_table TO dw;
/
Then DW calls it:
BEGIN
scratch.drop_table('TABLE_TO_DROP');
END;
If for any reason you cannot create this procedure under the SCRATCH schema (which is an unreasonable restriction), you can create it under a privileged account like SYSTEM and also add the table owner name as an additional parameter. Then you most definitely will want to table-drive a set of white-listed owners to prevent misuse of the procedure.

Security issues with SQL Query

I am working on an Oracle SQL query. The query is getting flagged by Fortify SCA for Privilege Management: Default Function or Procedure Rights. Can someone help me with the correct way of using the query?
The query I want to use:
CREATE OR REPLACE PROCEDURE "reset_sequence"
IS
l_value NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY -' || l_value || ' MINVALUE 0';
EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY 1 MINVALUE 0';
END;
I can spot a couple of issues here:
There's no authid clause, so it defaults to definer
execute immediate with string concatenation
This means anyone with execute privileges on the procedure is running with the full rights of the procedure owner. And with string concatenation, there's the risk of SQL injection. Yes, even with numbers.
Also you can get the next value of a sequence by assigning it. No need for execute immediate.
To be safe, I'd make the following changes:
Add authid current_user
Explicitly to_char the increment, avoiding attacks on this
Giving:
create sequence ordering_seq
start with 100;
select ordering_seq.nextval from dual;
NEXTVAL
100
CREATE OR REPLACE PROCEDURE reset_sequence
authid current_user
IS
l_value NUMBER;
BEGIN
l_value := ordering_seq.nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE ordering_seq INCREMENT BY -' ||
to_char ( l_value, 'TM', 'NLS_Numeric_Characters = ''.,''' ) ||
' MINVALUE 0';
l_value := ordering_seq.nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE ordering_seq INCREMENT BY 1 MINVALUE 0';
END;
/
exec reset_sequence;
select ordering_seq.nextval from dual;
NEXTVAL
1
Of course, using invoker's rights means you have to give out alter sequence rights to whoever calls. Which brings its own issues. To overcome this you could use Code-Based Access Control.
From the Fortify docs:
Privilege Management: Default Package Rights
PLSQL/TSQL
Abstract
Packages without an AUTHID clause default to AUTHID DEFINER.
Explanation
PL/SQL packages can be either AUTHID DEFINER or AUTHID CURRENT_USER. Functions and procedures in a package with definer's rights execute under the privileges of the user that defines the package. This can allow updates and access to specific pieces of data without granting access to entire tables or schemas. In a package with invoker's rights, or AUTHID CURRENT_USER, functions and procedures execute under the privileges of the user who invokes them. This does not allow a user to gain access to data it didn't already have access to. If no AUTHID clause is provided, the package defaults to definer's rights.
Packages are usually defined by SYS or another highly privileged user, making any exploits of the code potentially more dangerous.
So it seems you just need to add an AUTHID clause, even if that is just explicitly stating the default value again (though you should establish the correct value, of course).
Not relevant, but neither select needs to be dynamic - you may have chosen to do that so it looks more consistent with the alter statements, but it isn't necessary; and they don't even need to be selects any more - you could do:
CREATE OR REPLACE PROCEDURE "reset_sequence"
AUTHID CURRENT_USER
IS
l_value NUMBER;
BEGIN
l_value := "ordering_seq".nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY -' || l_value || ' MINVALUE 0';
l_value := "ordering_seq".nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY 1 MINVALUE 0';
END;

ORA-04068: existing state of packages has been discarded

I've wrapper procedure which looks like this:
Procedure A
BEGIN;
Procedure B
Procedure C
END;
/
Procedure B drops and re-creates a table used in Procedure C. Due to this, I get the below error:
ERROR at line 1:
ORA-04068: existing state of packages has been discarded ORA-04065: not executed, altered or dropped stored procedure "SCHEMA.PROCEDURE C"
ORA-06508: PL/SQL: could not find program unit being called: "SCHEMA.PROCEDURE C"
ORA-06512: at "SCHEMA.PROCEDURE A", line 4
ORA-06512: at line 1
How do I go about getting this procedure to work?
So your proc get invalid during the execution, that is why you need recompile Procedure C. To achive this, you can call a dynamic SQL statement:
EXECUTE IMMEDIATE 'ALTER PROCEDURE my_procedure COMPILE';
Procedure_A IS
BEGIN;
Procedure_B;
EXECUTE IMMEDIATE 'ALTER PROCEDURE Procedure_C COMPILE';
Procedure_C;
END;
/
I recommend using dynamic SQL to call the DML in procedure C. By removing the dependency nothing gets invalidated. This would likely be more reliable than dynamically re-compiling code or dynamically running the code and catching all exceptions.
You can see this in the below sample code. Comment out the static SQL call on line 10 in procedure C to replicate the error.
drop table drop_me;
create table drop_me(id number);
create or replace procedure C is
v_count number;
begin
--Static SQL would fail with this error:
-- ORA-04068: existing state of packages has been discarded
-- ORA-04065: not executed, altered or dropped stored procedure "JHELLER.C"
-- ORA-06508: PL/SQL: could not find program unit being called: "JHELLER.C"
-- ORA-06512: at "JHELLER.A", line 4
-- ORA-06512: at line 2
--select count(*) into v_count from drop_me;
--Dynamic SQL runs correctly:
execute immediate 'select count(*) from drop_me' into v_count;
end;
/
create or replace procedure B is
begin
execute immediate 'drop table drop_me purge';
execute immediate 'create table drop_me(id number)';
end;
/
create or replace procedure A is
begin
B;
C;
end;
/
begin
A;
end;
/
But I agree with Alex Poole that there is almost certainly a better way to do this without dropping and re-creating objects. I can think of a few reasons why a DROP and CREATE might run faster than a TRUNCATE and INSERT, but only because of some weird side-effect, like dropping bad table statistics. Investigate the difference more and you might find the real reason.
You can do something like it.
begin
procedureB;
begin
execute immediate 'begin procedureC; end;';
exception when others then
execute immediate 'begin procedureC; end;';
end;
end;

Testing native dynamic SQL in Oracle before execution

I have implemented a certain feature in my application where the user can compose queries dynamically from the user interface by pushing around buttons and inserting some values here and there.
The user will not see the generated SQL statement at all.
I was wondering if there is a way to perhaps check the syntax and grammar (e.g. he opened a parantheses '(' and forgot to close it ) of the dynamically generated SQL to ensure that no run-time compilation errors would happen before actually executing the statement using EXECUTE IMMEDIATE.
You could use the dbms_sql.parse procedure to parse the SQL statement assuming the statement is DML not DDL. It would be rather unusual to parse a dynamic SQL statement using the dbms_sql package and then use EXECUTE IMMEDIATE to execute it rather than using dbms_sql.execute but nothing prevents you from mixing dbms_sql and execute immediate.
The code to just parse the SQL statement would be something like
DECLARE
l_cursor integer;
l_sql_stmt varchar2(1000) := <<some SQL statement>>;
BEGIN
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse( l_cursor, l_sql_stmt, dbms_sql.native );
dbms_sql.close_cursor( l_cursor );
END;
You can also use explain plan, this is basically doing the first step of the execution.
SQL> explain plan for select * from dual;
Explained.
SQL is valid, you can also use the explain tables to get the tables, views etc, maybe even estimated run time ...
SQL> explain plan for select * from duall;
explain plan for select * from duall
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL is invalid, this is why ...
you can also use it in a dynamic statement
SQL> begin execute immediate 'explain plan for ' || ' select * from dual'; end;
2 /
PL/SQL procedure successfully completed.
As always use error-handling, e.g. write a little error log creater package or procedure what u call when it's needed.
DECLARE
my_stmt VARCHAR2(4000):='this will contain my statements';
BEGIN
'begin' || my_stmt || 'end';
EXCEPTION
WHEN OTHERS THEN
prc_my_error_log_creator();
END;
in the log_creator use for example the DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() func to get the stack's contain.
Regards

How to create and use temporary table in oracle stored procedure?

I want to create temporary table in stored procedure and access it in the same but I got error that ORA-00942:Table or view does not exists.
Following is the procedure that i tried,
Create procedure myproc
IS
stmt varchar2(1000);
BEGIN
stmt:='CREATE GLOBAL TEMPORARY table temp(list if columns) ON COMMIT DELETE ROWS';
execute immediate stmt;
insert into temp values('list of column values');
END;
This is the way I used to create temporary table but I got error, is there any other way to perform this task?
Just create it first (once, outside of your procedure), and then use it in your procedure. You don't want to (try to) create it on every call of the procedure.
create global temporary table tmp(x clob)
on commit delete rows;
create or replace procedure...
-- use tmp here
end;
Create or replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
I have edited this answer as it was wrong. I am a recent MSSQL convert and because of the way oracle implements global temp tables, if you really DO need to use temp tables, creating them once and leaving them there is the way to go. Unless you use dynamic sql in your procs exclusively (have fun debugging), you will not be able to successfully compile your package unless the tables referenced already exist. Oracle validates any objects referenced in methods that you attempt to compile, which is why you got the 942 error. I love the way Oracle manages scope with these global temp tables. That, alone, sold me on the idea.
Use this
Create of replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
CREATE OR REPLACE PROCEDURE myproc IS
BEGIN
CREATE GLOBAL TEMPORARY TABLE temp (id NUMBER(10)) ON COMMIT DELETE ROWS AS
SELECT 10 FROM dual;
END;
/

Resources