How to write a procedure to execute set of queries automatically - for-loop

I have written a procedure to grant permissions on all the tables of a particular schema to rest other schemas.
create or replace PROCEDURE GRANTS_PROC
IS
CURSOR GRANTS_CURSOR
IS
SELECT 'GRANT SELECT, INSERT, UPDATE, DELETE ON "'
||T.OWNER
||'"."'
||TABLE_NAME
||'" TO '
||(SELECT rtrim(listagg(U.username||',')
within group (order by U.username),',') USERNAME
FROM ALL_USERS U
WHERE U.USERNAME!=T.OWNER
AND U.USERNAME IN
('AAA','BBB','CCC','DDD','EEE','FFF','GGG','HHH','III'))||';' FINAL_TXT
FROM ALL_TABLES T
WHERE T.OWNER IN
('AAA','BBB','CCC','DDD','EEE','FFF','GGG','HHH','III')
ORDER BY T.OWNER,UPPER(T.TABLE_NAME);
BEGIN
--DBMS_OUTPUT.PUT_LINE('CURSOR_GRANTS.FINAL_TXT');
--QRY_TEXT:='ABC';
FOR CURSOR_GRANTS IN GRANTS_CURSOR
LOOP
DBMS_OUTPUT.PUT_LINE(CURSOR_GRANTS.FINAL_TXT);
EXECUTE IMMEDIATE CURSOR_GRANTS.FINAL_TXT;
END LOOP;
END;
/
The above procedure compiled successfully, but while execution it is not getting into FOR loop to run the EXECUTE IMMEDIATE block but PL/SQL procedure compiled successfully.
What could be done to fix my procedure and make it work?

All_USERS and ALL_TABLES should be changed to DBA_USERS and DBA_TABLES, Hence my procedure works.
Because the all_tables view shows the tables that the owner of the stored procedure has privileges on, so, unless the stored procedure is being created by a privileged user, or a user that already has those grants (with grant option so they can actually give the privileges), there may be nothing in all_tables that qualifies.

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.

Insufficient INHERIT PRIVILEGES for a stored procedure ORACLE

I am having a stored procedure in user META. This SP truncates a table in user STAGE.
CREATE PROCEDURE META.sp_truncate_tablex
AUTHID CURRENT_USER
as
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE STAGE.Tablex';
END;
When I run the SP I get RA-06598: insufficient INHERIT PRIVILEGES.
I have tried to make both users to DBA.
This means if I run SELECT grantee
WHERE granted_role = 'DBA'
ORDER BY grantee;
it shows me that META dn STAGE are DBA.
I have also tried: GRANT INHERIT PRIVILEGES ON USER STAGE TO META; But I still get the same error in Oracle 18.
Your problem is this clause:
AUTHID CURRENT_USER
This means the user who executes the procedure does so with their own privileges. So a user who is not META cannot run the procedure unless they have the DROP ANY TABLE privilege, in which case they don't need to run the procedure as they can truncate the table anyway.
The solution is to declare the procedure with
AUTHID DEFINER
Now the procedure executes with the privileges of its owner - META - who presumably owns the target table. Except they don't. The table is actually owned by STAGE. So STAGE needs to own the procedure as well.
As it happens, DEFINER rights are the default so we don't need to explicitly define them, except for clarity.
CREATE PROCEDURE STAGE.sp_truncate_tablex
AS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE STAGE.Tablex';
END;

Strange ORA-942 when using DBMS_CRYPTO but table exists

This is very strange, and not easy to explain, so please bear with me.
Oracle 12.2.0.1 on Linux x86_64.
We have a user called BATCH who has minimal privileges.
CREATE USER batch IDENTIFIED BY batch
DEFAULT TABLESPACE users
QUOTA UNLIMITED ON users;
GRANT CREATE SESSION TO batch;
GRANT EXECUTE ON DBMS_CRYPTO TO batch;
The is a PLSQL package in a schema called ATLED which is :
CREATE OR REPLACE PACKAGE ALTED.the_pkh AUTHID current_user AS
PROCEDURE crttab;
END;
/
CREATE OR REPLACE PACKAGE BODY ALTED.the_pkh AS
PROCEDURE crttab IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b';
END crttab;
END;
/
We are using Code Based Access Control (CBAC - 12c feature) to restrict/control/allow certain canned actions to an otherwise toothless user, so we create a wrapper procedure, grant that a high priv role, and grant execute on that to the batch user:
CREATE OR REPLACE PROCEDURE ALTED.wrapper_crttab AS
PROCEDURE p1 IS
CURSOR c1 is SELECT * FROM SESSION_PRIVS;
BEGIN
FOR r1 IN c1 LOOP
DBMS_OUTPUT.PUT_LINE( r1.privilege );
END LOOP;
END;
BEGIN
p1;
ALTED.the_pkh.crttab;
END;
/
GRANT IMP_FULL_DATABASE TO ALTED;
GRANT IMP_FULL_DATABASE ALTED.wrapper_crttab;
GRANT EXECUTE ON ALTED.wrapper_crttab TO batch;
Now let's run it:
CONN batch/batch
SET SERVEROUTPUT ON
EXEC ALTED.wrapper_crttab;
This causes the error:
Error at line 1:
ORA-00942: table or view does not exist
The tables referenced do exist.
The call to the p1 proc confirms that all the privileges bundled with IMP_FULL_DATABASE are present, including CREATE ANY TABLE, DROP ANY TABLE, EXECUTE ANY PROCEDURE.
If I do this:
GRANT CREATE ANY TABLE TO batch;
GRANT SELECT ANY TABLE TO batch;
CONN batch/batch
EXEC EXECUTE IMMEDIATE 'CREATE TABLE atled.the_tab AS SELECT id, DBMS_CRYPTO.HASH(cc,2) AS cc FROM ARCHIVE.table_b;
This works.
If I change the CREATE TABLE stmt to remove the DBMS_CRYPTO call, it works as well.
The actual package/proc that is called creates a number of tables fine when run as above, but fails on the case when DBMS_CRYPTO is called in any CREATE TABLE stmt.
If I grant the batch user the CREATE ANY TABLE, SELECT ANY TABLE and EXECUTE ANY PROCEDURE privs directly and run the CREATE TABLE command as batch directly then that works too.
So this is not (I think) a straight ORA-942 error, but something related to a chain of privileges to DBMS_CRYPTO, and only when executed in a package stored procedure, but what exactly I do not know.
UPDATE 1
If I create a wrapper for DBMS_CRYPTO.HASH as follows:
CREATE OR REPLACE FUNCTION batch.crypto_hash ( pcc IN CLOB ) RETURN VARCHAR2 IS
BEGIN
RETURN DBMS_CRYPTO.HASH(pcc,2);
END;
/
Then replace the DBMS_CRYPTO.HASH(cc,2) in the CREATE TABLE stmt with batch.crypto_hash(cc) then it works!!!
So, DEFINITELY not a issue with grants on teh tables being referenced, but more likely something internal with the way DBMS_CRYPTO works. Perhaps it reads a look up table somewhere. I tried GRANT EXECUTE ON UTL_I18N to batch as well before this but that didn't work.
So I have a workaround, but woudl still like to know why this happens.
You're doing this:
... FROM ARCHIVE.table_b
User, who is supposed to select from that table, has to have SELECT privileges on it. It can be granted
via role
directly
If you granted the privilege via role, it works - but not in named PL/SQL procedures. It will work in anonymous PL/SQL, but not in procedures, function, packages, so - check that and, possibly, grant SELECT on table_b directly to that user.

truncate and insert within procedure don't work together

I need to do truncate table and then insert data in that table using procedure.
However, one does like dinamic sql but the other one doesn't:
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
TRUNCATE TABLE AGGREGATE;
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(6,14): PLS-00103: Encountered the symbol "TABLE" when expecting one of the following: := . ( # % ; The symbol ":= was inserted before "TABLE" to continue.
If I add execute immediate around TRUNCATE statement, it works fine, but insert is erroring out.
If I remove it, TRUNCATE TABLE complains...
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
execute immediate 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
Error(7,5): PL/SQL: SQL Statement ignored
Error(7,84): PL/SQL: ORA-00942: table or view does not exist
Can some shed some light here?
create or replace
procedure RECREATE_AGGREGATE
AUTHID DEFINER
AS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE AGGREGATE';
INSERT INTO AGGREGATE SELECT * FROM OLD_AGGREGATE;
END;
will work assuming that you have appropriate privileges.
Based on your edit (and echoing #OracleUser's comment), you're likely getting an error running your INSERT statement because old_aggregate is owned by another user and you only have SELECT privileges on that table via a role. If you want to do something in a definer's rights stored procedure, you'll need to have those privileges granted directly to you (or be using 12c which lets you grant privileges to blocks of code rather than to users).
Assuming you want to use a definer's rights stored procedure, you'd need the owner of old_aggregate (or the DBA) to
GRANT SELECT
ON old_user.old_aggregate
TO new_user;
You can verify that you only have the privilege via a role by disabling roles for the session. I'll wager that if you do
SQL> set role none;
SQL> SELECT * FROM old_aggregate
that you'll get an ORA-00942 error as well. This is a good way of simulating what privileges will be available to the user inside a definer's rights stored procedure.
"TRUNCATE is DDL (data definition language). You cannot perform DDL from within PL/SQL. "
http://www.orafaq.com/forum/t/119427

delete specific tables using stored procedure in Oracle

I am puzzled. I have a user which can run the below in SQL Developer and it works perfectly.
begin
FOR C IN (SELECT TABLE_NAME FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'TEMP_%'
AND OWNER = '<user name in caps>')
LOOP
EXECUTE IMMEDIATE('DROP TABLE <user name in caps>.' || C.table_name || ' PURGE');
END LOOP;
end;
However - if I wrap the functionality in a stored procedure and run it, while no exceptions are thrown, the table beginning with "temp" does not delete. I actually am deleting from three different schemas - thus the repetition.
CREATE OR REPLACE PROCEDURE DELETETEMPTABLES AS
BEGIN
--DROP ANY TABLES THAT START WITH "TEMP_"
FOR C IN (SELECT TABLE_NAME FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'TEMP_%'
AND OWNER = '<user name in caps>')
LOOP
EXECUTE IMMEDIATE('DROP TABLE <user name in caps>.' || C.table_name || ' PURGE');
END LOOP;
FOR C IN (SELECT TABLE_NAME FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'TEMP_%'
AND OWNER = '<user name in caps>')
LOOP
EXECUTE IMMEDIATE('DROP TABLE <user name in caps>.' || C.table_name || ' PURGE');
END LOOP;
FOR C IN (SELECT TABLE_NAME FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'TEMP_%'
AND OWNER = '<user name in caps>')
LOOP
EXECUTE IMMEDIATE('DROP TABLE <user name in caps>.' || C.table_name || ' PURGE');
END LOOP;
exception
WHEN OTHERS THEN
log_errors (p_error_message => 'Nightly Processing->DeleteTempTables-> ' ||SQLERRM);
END DELETETEMPTABLES;
Justin is correct. But you can often use AUTHID to overcome the priv issue. Try:
CREATE OR REPLACE PROCEDURE DELETETEMPTABLES
AUTHID CURRENT_USER
AS
This is called "invokers rights" in Oracle. The default is definers rights (authid definer). For more see here and here.
The use of roles in a subprogram depends on whether it executes with
definer's rights or invoker's rights. Within a definer's rights
subprogram, all roles are disabled. Roles are not used for privilege
checking, and you cannot set roles.
Within an invoker's rights subprogram, roles are enabled (unless the
subprogram was called directly or indirectly by a definer's rights
subprogram). Roles are used for privilege checking, and you can use
native dynamic SQL to set roles for the session. However, you cannot
use roles to grant privileges on template objects because roles apply
at run time, not at compile time.
Most likely, this is a privilege issue.
ALL_TABLES lists all the tables that you have privileges on. When you run it interactively, that includes all the tables that you have privileges on via a role. When you try to create a stored procedure, however, privileges that are granted via a role are excluded and you only see those tables that you have direct grants on. If you want your code to work, the owner of the objects (or the DBA) would need to grant privileges on the object directly to the procedure owner, not via a role. Realistically, the owner of the procedure would also need the DROP ANY TABLE privilege granted directly to it (not via a role like DBA) for the DROP TABLE statements to succeed.

Resources