When trying to place a GRANT statement in an Oracle 11 stored procedure, it reports that GRANT is an unexpected symbol. Does GRANT need to be prefaced by something, or does Oracle simply disallow running GRANTS inside SPs?
It's a bad idea to use DDL (like GRANT) inside stored procedures.
You will have to use dynamic SQL (EXECUTE IMMEDIATE) to do this, but, honestly, I don't see why would you want to do this inside a stored proc.
Here's a PL/SQL stored procedure that grants object privileges (SELECT, UPDATE, etc.) on all tables owned by the current user to another user, e.g. - "appuser".
CREATE OR REPLACE PROCEDURE grant_privs
IS
CURSOR ut_cur IS SELECT table_name from user_tables;
ut_rec ut_cur%rowtype;
BEGIN
FOR ut_rec IN ut_cur
LOOP
EXECUTE IMMEDIATE 'GRANT ALL ON ' || ut_rec.table_name || ' TO appuser';
END LOOP;
END;
/
It was executed automatically after deploying database changes for a web application. The current user owns the database tables. After deploying any database changes, the stored procedure was executed to ensure that "appuser" could run SQL statements on all of the tables. The web application connected as "appuser", which had only limited system privileges, for security reasons.
This is both a solution to the problem and a solid use case for the solution.
Related
i need to use synonym as variable in a block. I have 2 different schemas with same tables on them and job that switches between schemas making one active. Now I want to write a block checking which schema is active with ALL_SYNONYMS and using result as part of a query.
Here is example:
DECLARE
OWNER VARCHAR2(15);
BEGIN
SELECT TABLE_OWNER
INTO OWNER
FROM ALL_SYNONYMS
WHERE TABLE_NAME = 'MY_TABLE1';
SELECT *
FROM OWNER.MY_TABLE2 ;
END;
But I’m getting ORA-06550 table or view does not exist, and when i run query itself where i put value from ALL_SYNONYMS it returns result.
Any idea how to fix this?
Thanks
You are attempting using symptoms incorrectly. Synonyms are used so you do not need to know which is active. According to the documentation:
Synonyms provide both data independence and location transparency.
Synonyms permit applications to function without modification
regardless of which user owns the table or view and regardless of
which database holds the table or view.
You just use the synonym instead of the object itself.
create table evens( id integer generated always as identity
, val integer
) ;
create table odds( id integer generated always as identity
, val integer
) ;
insert all
when mod(val,2) = 0 then into evens(val)
when mod(val,2) = 1 then into odds(val)
select level val
from dual connect by level <= 10;
-- create the synonym then use it in Select;
create or replace synonym current_even_odd for evens;
select * from current_even_odd;
-- now change the synonym, then run the EXACT same query.
create or replace synonym current_even_odd for odds;
select * from current_even_odd;
In this case it is not quite without modification, you need to change the synonym, But it seems you are trying that already.
Note: You cannot create a synonym for a schema but must point it to a specific object.
I attempted a db<>fiddle for the above, but it appears it is having problems at the moment.
I agree with Belayer that the synonym should provide a layer of abstraction on your tables and your procedure shouldn't need to know what the schema is. But the "table or view does not exist" error is likely an issue related to privileges and definer's rights versus invoker's rights.
To directly reference an object in a procedure, the procedure's schema must have a direct grant to the table. However, an ad hoc query only needs a role with privileges on the object. This is why the SQL will work in your IDE but not in the procedure. Ensure the code that modifies objects and switches synonyms is granting privileges to both roles and directly to schemas.
If direct grants are not possible, you will need to modify the procedure to use AUTHID CURRENT_USER and change the SQL statements to use dynamic SQL - which can be a huge pain. For example:
create or replace procedure test_procedure authid current_user is
v_count number;
begin
execute immediate
q'[
select count(*)
from some_table
]'
into v_count;
end test_procedure;
/
If you really do need to manually switch between schemas, then you may want to consider using something like execute immediate 'alter session set current_schema=schema1'; in the procedure and using dynamic SQL for all of the querying.
Problem anamnesis:
In order to automate index recreation for a PVJOURNAL table in oracle I wrote a procedure in a package.
The table PVJOURNAL locates in PROVIEW schema. My SPACEMAN user has enough grants in that schema. Here below is a DDL of the package:
CREATE OR REPLACE
PACKAGE spaceman.tmp_itcm4052 is
-- This proc rebuilds ALL indexes of a specific.
PROCEDURE idx_rebuild;
END;
/
CREATE OR REPLACE
PACKAGE BODY spaceman.tmp_itcm4052 is
-- ===========================================================================
PROCEDURE idx_rebuild as
-- This proc rebuilds ALL indexes of a specific table. Run by Job
v_sql_str VARCHAR2(200);
begin
FOR rec in (
select owner ||'.'|| index_name as IDX_NAME,
tablespace_name as TblSpace
from sys.all_indexes
where upper(index_type) = 'NORMAL'
and upper(table_owner) = 'PROVIEW'
and upper(table_name) in ('PVJOURNAL')
)
LOOP
v_sql_str := 'ALTER INDEX '||rec.IDX_NAME||' rebuild tablespace '
||rec.TblSpace||' online';
-- ALTER INDEX ... SHRINK SPACE COMPACT
-- ALTER INDEX ... DEALLOCATE UNUSED SHRINK SPACE COMPACT
dbms_output.put_line('v_sql_str = '||v_sql_str);
execute immediate v_sql_str;
commit;
END LOOP;
end idx_rebuild;
END tmp_ITCM4052;
/
Problem itself:
The problem is that a call of the procedure under my SPACEMAN user
begin
spaceman.tmp_itcm4052.idx_rebuild;
end;
ends up with error
ORA-01031: insufficient privileges - full view of the error
But the direct run of the line, produced by the procedure, saved into v_sql_str variable and visualized by this piece of the procedure:
dbms_output.put_line('v_sql_str = '||v_sql_str);, done under the same SPACEMAN user works like a magic. proof that direct alter does work
Questions:
What wrong have I done to the gods of pl/sql? Why the call to the procedure fails whilst the same command, execurted directly - is not?
UPD: Not only GRANT INDEX ON PROVIEW.PVJOURNAL TO SPACEMAN; didn't help. Moreover, for whatewer reason not yet kown to me, GRANT ALTER ANY INDEX to SPACEMAN; didn't help either.
Had doublechecked it with
SELECT PRIVILEGE
FROM sys.dba_sys_privs
WHERE upper(grantee) = 'SPACEMAN'
UNION
SELECT PRIVILEGE
FROM dba_role_privs rp JOIN role_sys_privs rsp ON (rp.granted_role = rsp.role)
WHERE upper(rp.grantee) = 'SPACEMAN'
ORDER BY 1;
the privilege ALTER ANY INDEX (as well as ALTER ANY INDEXTYPE ) is in the list.
UPD 1: as it turned out - the ALTER ANY INDEX privilege is not sufficient in this case. What did helped me is that link, kindly provided by #Wernfried Domscheit. REspect, man!
The procedure created as
CREATE OR REPLACE
PACKAGE spaceman.tmp_itcm4052 authid CURRENT_USER is
works like a spell.
Inside a PL/SQL block you have only privileges which are granted to the user directly. Privileges which are granted by ROLE (e.g. DBA) do not apply inside a PL/SQL block (except role PUBLIC).
Grant CREATE ANY INDEX directly to the user, then it should work.
The behavior described above is the default, it is called "definer's rights unit". You can change it by adding AUTHID CURRENT_USER to your procedure/package. For more information see Invoker's Rights and Definer's Rights (AUTHID Property)
N.b. COMMIT is not required after a DDL statement. Oracle performs an implicit commit.
Different grants are used when you invoke direct SQL and when it is used in the procedure.
Privileges granted through a role (such as DBA) are not available to
definer's rights stored procedures which is the default.
Only privileges that are granted directly to the user are available in a
definer's rights stored procedure.
You must grant the ALTER ANY INDEX privileges directly to your user.
Cheers!!
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.
I have to automate as much of my code as possible. I have created some users, and now i need to create roles to assign to the users and grant privileges. Can I use functions for this, or is it best to use procedures? Any advice appreciated.
You can't directly code DDL statements such as CREATE USER or GRANT in a PL/SQL procedure. Instead, you should use EXECUTE IMMEDIATE, as in
CREATE OR REPLACE PROCEDURE PROC_CREATE_ROLES IS
BEGIN
EXECUTE IMMEDIATE 'CREATE ROLE ADMIN_STAFF';
EXECUTE IMMEDIATE 'GRANT SELECT ON BLAH TO ADMIN_STAFF';
EXECUTE IMMEDIATE 'GRANT SELECT, UPDATE ON BLORT TO ADMIN_STAFF';
EXECUTE IMMEDIATE 'GRANT SELECT, INSERT, UPDATE ON YADA TO ADMIN_STAFF';
EXECUTE IMMEDIATE 'GRANT ADMIN_STAFF TO USER1, USER2, USER3';
END PROC_CREATE_ROLES;
Share and enjoy.
EDIT
In my opinion it's better to use a procedure here.
Best of luck.
I would use a procedure, probably inside a 'UTILITIES' or 'USER_MANAGEMENT' package. The choice for procedure is because a procedure doesn't need to return anything. A function will always have some value to return to the caller. Use a procedure when:
You want to perform some predefined actions
You do not want to return a value directly
Use a function when:
You want to perform a calculation, or get a return value of some sort
Functions can also be used from inside SQL, while procedures are called from a separate BEGIN/END block.
Use procedures and make sure they are owned by an account that has the required privileged directly granted. Doing so makes your application more secure because you don't need to logon with a dba (or similar) account. Don't forget to add logging and auditing.
I would like to write kill_inactive sessions stored procedure for Oracle.
something like
create or replace procedure kill_inactive_sessions as
begin
for rec in (select sid, serial# from sys.v_$session where status = 'INACTIVE')
loop
execute immediate 'alter system kill session '''|| rec.sid || ',' || rec.serial# || ''' IMMEDIATE';
end loop;
end kill_inactive_sessions;
Previous doesn't work (table or view does not exists).
But next select is working perfectly for the same user:
select sid, serial# from sys.v_$session where status = 'INACTIVE';
What I'm missing?
The ORA-00942: table or view does not exist error almost certainly indicates that your access to the v$session view is via a role rather than a direct grant.
If you want to write a definer's rights stored procedure, the owner of the procedure must have the necessary privileges granted directly to the user, not via a role (and remember that DBA is just another role). Most likely, if you disabled roles in your session, the SELECT statement outside of the procedure would not work. In SQL*Plus, for example
SQL> set role none;
SQL> select sid, serial# from sys.v_$session where status = 'INACTIVE';
will likely throw the same ORA-00942 error. Assuming it does, you need to grant the user that owns the stored procedure privileges directly. For example
GRANT SELECT ANY DICTIONARY
TO user_that_owns_the_procedure;
The same thing will also apply to the ALTER SYSTEM command that you are building and running. The owner of the stored procedure will need to have privileges to run that command via a direct grant not via a role.
All that said, a procedure that kills all inactive sessions is highly problematic. The vast majority of sessions are going to be inactive the vast majority of the time. That doesn't mean that they should be killed. Even if you're cleaning up after an application server crash, you really ought to be applying some additional predicates (for example, looking for sessions from a particular machine that are logged in as a particular user that were established before the application server crashed). Long-term, though, I'd suggest enabling dead connection detection so that the database can automatically take care of closing sessions when the client process dies unexpectedly.