What permissions are needed for PLSQL execute immediate drop index - plsql

I have a standard DB procedure to drop indexes on a table (code below) created in user1 schema. When run as user2 the "execute immediate 'drop index" fails with ORA-01418: specified index does not exist.
I don't understand how the index can be returned by the cursor, but not exist when the drop command is run.
Can someone help explain the privs user2 needs on the user1 table/indexes and what AUTHID should be used?
create or replace procedure dropIndexes(pTableName IN VARCHAR2) AUTHID
CURRENT_USER IS
CURSOR c1(pTableName VARCHAR2) IS
select index_name
from all_indexes
where table_name = pTableName;
BEGIN
FOR c1Rec IN c1(pTableName) LOOP
execute immediate 'drop index user1.'||c1Rec.index_name;
END LOOP;
END dropIndexes;

I wonder why not construct the SQL using the owner field of all_indexes. Better yet, push user1 into the query so we only get the index names that belong to user1.

Related

How to correctly make a procedure in Pl/SQL in which I create a TABLE and use a CURSOR

The assignment I am trying to do is
"Create a procedure that places the names of all presidents who were born in one specific
state, in a temporary table. Display the contents of this table."
The procedure complies but when I try to invoke it, it gives me:
00000 - "table or view does not exist"
Error(8,5): PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior
I have been stuck for a while now. Does anybody know what I am doing wrong?
My code so far is:
CREATE OR REPLACE PROCEDURE stateofpresident(p_state president.state_born%TYPE)
AS
CURSOR c_state IS
SELECT *
FROM president;
BEGIN
execute immediate 'CREATE TABLE presidentFromState;
(
president_name VARCHAR2
)';
FOR r_state IN c_state LOOP
IF(p_state = r_state.state_born) THEN
execute immediate 'INSERT INTO presidentFromState VALUES(r_state.pres_name)';
commit;
END IF;
END LOOP;
execute immediate 'DROP TABLE presidentFromState';
END stateofpresident;
/
SET SERVEROUT ON
BEGIN
stateofpresident('VIRGINIA');
END;
/
SELECT *
FROM presidentFromState;
The immediate cause of your error is the semi-colon (;) at "presidentFromState;" At run fhat terminates the statement and the SQL interpreter at that point does not know what is want, the create syntax is invalid. The statement compiles because at compile time it is a properly formatted string. That is why dynamic SQL should be avoid if at all possible. Your script also has an additional error. Your last select will fail as the table presidentFromState ws not only created but also dropped in the procedure. Finally, just an FYI, the entire FOR cursor and the cursor itself is entirely unnecessary, the entire operation can be completed is one statement: Look into the structure
Insert into table_name(columns)
Select columns ...
Since this obviously an assignment or tutorial I'll leave the exact for your research.

PL/SQL Procedures to create and drop tables

I run a procedure to Drop and re-create a table by using PL/SQL but it dosn't work and the table have no update on it .. the script below :
CREATE OR REPLACE procedure RUBA.pay_backup
as
r_create_table varchar(1000);
Begin
Begin
r_create_table := 'Drop table payment_backup';
Execute Immediate r_create_table;
Exception
When Others Then
Null;
End;
r_create_table := 'Create Table payment_backup as Select * from payment_table';
Execute Immediate r_create_table;
End;
/
The challenge here is that you are dealing with the security model of Oracle that normally requires direct grants vs. via a role when running in the context of a compiled PL/SQL procedure.
You are likely seeing ORA-01031: insufficient privileges.
Your user probably has CREATE TABLE granted via a role, but not directly.
Check this as follows:
-- List system privs granted via a role
select * from ROLE_SYS_PRIVS where ROLE in (select ROLE from USER_ROLE_PRIVS)
/
-- List system privs granted directly
select * from USER_SYS_PRIVS
/
There are two simple ways to make your procedure work:
Option 1
Grant CREATE TABLE directly to your user.
Connect as a DBA and do this:
GRANT CREATE TABLE TO RUBA;
Unfortunately, this is a bit of a blunt instrument and probably not what you want to do.
Option 2
Create your procedure with invoker's rights:
CREATE OR REPLACE procedure RUBA.pay_backup AUTHID CURRENT_USER
as
r_create_table varchar(1000);
Begin
Begin
r_create_table := 'Drop table payment_backup';
Execute Immediate r_create_table;
Exception
When Others Then
Null;
End;
r_create_table := 'Create Table payment_backup as Select * from payment_table';
Execute Immediate r_create_table;
End;
/
Do some experimentation to see how things behave with this version.
Note that you may run into issues if you are running this in a job or when connecting as a different user.

Query outside of the transaction scope - Oracle

Is it possible to have insert statement outside of the scope of a transaction in a Stored Proc?
So there is a stored proc that writes some inserts, as part of a transaction
The reason for this is to write to Audit table and keep the audit records even if the stored proc fails or gets an exception
clarification, sorry if it was not clear, I am writing multiple audits for each action of the procedure so I can keep track what it did and what failed. not just a single Audit when there is an exception...
CREATE OR REPLACE PROCEDURE sampleProc
IS
BEGIN
start a transaction
INSERT to table 1
write to audit table about insert 1
INSERT to table 2
write to audit table about insert 2
INSERT to table 3
write to audit table about insert 3
INSERT to table 4
write to audit table about insert 4
if there is an exception - rollback except audit
all ok? commit.
END;
/
Yes, there is an option to perform actions in a separate transaction running simulteneously with your main one. See detail here
Example:
create table logs(creation_date date default sysdate, msg varchar2(4000));
create or replace procedure log_proc(sMessage varchar2)
is
pragma autonomous_transaction;
begin
insert into logs(msg)
values(sMessage);
commit; -- don't forget to commit in this separate transaction
end;
/
begin
log_proc('some message');
rollback;
end;
/
select * from logs
What you're after is PRAGMA AUTONOMOUS_TRANSACTION, which kicks off the module using it in a separate session.
Your code would be something like:
CREATE OR REPLACE PROCEDURE sample_proc
AS
procedure audit_insert (<params>)
is
pragma autonomous_transaction;
begin
<log details>
commit;
end audit_insert;
BEGIN
<INSERT to table 1>;
audit_insert(...);
<INSERT to table 2>;
audit_insert(...);
...
EXCEPTION
when others then
rollback;
raise;
END sample_proc;
/
Doing it like this would mean your audit details would be saved regardless of whether the calling code succeeds or fails.
N.B. I've created the audit_insert as a sub procedure of sample_proc. You would do better to have the code as individual procedures inside a package, rather than as one or more procedures.

plsq collection type definition

I was trying to do insert based on the instruction provided in this site.
I can run this example
CREATE OR REPLACE PROCEDURE test_proc (p_array_size IN PLS_INTEGER DEFAULT 100)
IS
TYPE ARRAY IS TABLE OF all_objects%ROWTYPE;
l_data ARRAY;
CURSOR c IS SELECT * FROM all_objects;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;
FORALL i IN 1..l_data.COUNT
INSERT INTO t1 VALUES l_data(i);
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
END test_proc;
/
Similarly, I have changed the table name for real use as below.
CREATE OR REPLACE PROCEDURE test_proc (p_array_size IN PLS_INTEGER DEFAULT 100)
IS
TYPE ARRAY IS TABLE OF web.salesline%ROWTYPE;
l_data ARRAY;
CURSOR c IS SELECT * FROM web.salesline;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;
FORALL i IN 1..l_data.COUNT
INSERT INTO t2 VALUES l_data(i);
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
END test_proc;
/
But I'm getting following error, even though the table exists and accessing from the schema I was running.
SQL> show errors
Errors for PROCEDURE :
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/4 PL/SQL: Item ignored
6/34 PLS-00201: identifier 'WEB.SALESLINE' must be declared
11/7 PL/SQL: SQL Statement ignored
16/9 PL/SQL: ORA-00942: table or view does not exist
20/5 PL/SQL: SQL Statement ignored
20/40 PLS-00597: expression 'OBJECTTABLE$' in the INTO list is of wrong
type
23/5 PL/SQL: SQL Statement ignored
23/27 PL/SQL: ORA-00904: : invalid identifier
Unless the procedure is created in the web schema, you are referring to another schema's table, and so that schema needs to give you direct permission to use it. Note that there are no roles in stored procedure.
As the WEB user:
grant select on salesline to devuser;
(or whatever schema the procedure resides in).
Typically we try to avoid hardcoding schema names and instead manage these references using synonyms, so as DEVUSER:
create or replace synonym salesline for web.salesline;
Roles come back into play if you define the procedure authid current_user (the default is authid definer), however this is generally not a good idea for procedures that perform DML.

bind variable substitution in oracle

The below simple procedure is suppose to provide grants to the user scott in oracle.
The value for &scott_SCHEMA is already defined in a seperate file(define_variable.sql) and the value is getting substituted correctly, but im getting the error(as specified in the bottom of the script), a help is much appreciated.
SET SERVEROUTPUT ON
declare
l_sql varchar2(3200);
begin
for i in ( select table_name as oname,'TABLE' as type from all_tables where owner='HR' AND table_name not like 'BIN$%' union all select view_name as oname,'VIEW' as type from all_views where owner='HR' and view_name not like 'BIN$%' )
loop
if i.type = 'TABLE' then
dbms_output.put_line(l_sql);
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to :owner with grant option';
execute immediate l_sql using '&scott_SCHEMA';
else
l_sql:= 'grant select on hr.'||i.oname||' to :owner with grant option';
end if;
end loop;
end;
/
*declare
*
ERROR at line 1:
ORA-00987: missing or invalid username(s)
ORA-06512: at line 12*
You can't use bind variables in place of identifiers (specifically schema or object names) in DDL or DML statements, they can only be used in place of value expressions.
Since you're using substitution variables, you could just place it in the sql statement itself:
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to &scott_SCHEMA with grant option';
and execute it with out passing it as a parameter:
execute immediate l_sql;
On a side note, your DBMS_OUTPUT line should come after you assignment to l_sql, otherwise you'll miss outputting one or more of the statements being processed. A good place would be between the assignment statement and the execute statement.

Resources