Multi-schema select statement doesn't work in PL/SQL procedure? - sql

I'm trying to create a procedure to run multiple PL/SQL statements, but I haven't gotten very far. The select statement works fine if I run it out of a procedure, but if I try to execute it inside one -- it can't find the shttran table. I'm guessing it might be a schema issue, but I have no idea how-to correct. Ideas?
CREATE OR REPLACE PROCEDURE REGREPORTUSER.findUnsent
IS
BEGIN
INSERT INTO regreportuser.maltran (maltran.maltran_key,
maltran.maltran_sent)
SELECT shttran.shttran_id || shttran.shttran_seq_no AS maltran_key,
'No' AS maltran_sent
FROM saturn.shttran -- This is the table it can't find
WHERE TO_DATE (shttran.shttran_activity_date) > SYSDATE - 14
AND shttran.shttran_user = 'WWW2_USER'
AND shttran.shttran_id || shttran.shttran_seq_no NOT IN
(SELECT maltran.maltran_key FROM regreportuser.maltran);
END findUnsent;

Most likely, the problem is that the user that owns the stored procedure, REGREPORTUSER has access to the table saturn.shttran via a role rather than as a direct grant. A definer's rights stored procedure cannot use privileges that are granted to a definer via a role. It can only use privileges granted directly.
You can verify that this is, in fact, the problem by disabling roles in your SQL*Plus session. If you run the command
SQL> set role none;
and then try to execute the SQL statement, you should get the same error. In order to fix the problem, you need to give the grant directly
GRANT SELECT ON saturn.shttran
TO REGREPORTUSER

Related

ORACLE - Why does a dynamic SQL statement using DBMS_RANDOM fail when called from a stored procedure but not from an anonymous block

EDIT (8/25)
Thanks to #Alex Poole for providing the answer below but I wanted to share additional detail on these role limitations around PL/SQL objects as it helps explain not only how Oracle is managing things under the hood but why it handles permissions this way.
Now knowing where I was going wrong, I was able to identify this question which discusses the issue at length. This answer describes how the Oracle data structures store the permissions for evaluation.
In addition, someone linked an explanation from Tom Kyte which explains why this behavior was coded intentionally. Long story short: PL/SQL Definer's Rights do not respect role based permissions due to how the Oracle engine compiles these objects. If role based permissions were allowed then any REVOKE statements could have the ability of invalidating large swaths of PL/SQL objects, requiring a costly full database recompile.
Original Question
Could someone help me understand why I can call a dynamic sql script containing a reference to a DBMS_RANDOM procedure when the logic is called from an anonymous block, however, when I take that same logic and drop it into my own stored procedure, the previously runnable script fails to execute with a ORA-00904: "DBMS_RANDOM"."STRING": invalid identifier error?
I feel confident that my privileges are correct. I can run the script that is being passed as a variable directly without issue and run this logic as an anonymous PL/SQL block. Do I need to change my syntax with the stored proc or is it possible that this practice is prevented for security reasons?
Any explanation would be great but if you can point me to the Oracle documentation, I would be ecstatic. I have looked extensively, especially around Oracle's Dynamic SQL documentation but I haven't seen a description of this behavior. I am using Oracle 11g.
To recreate the behavior I am seeing:
Test Data Creation:
SPOOL ON;
SET SERVEROUTPUT ON SIZE UNLIMITED;
--Create Test Table
CREATE TABLE TEST_DYNAMIC_TBL (
ID NUMBER PRIMARY KEY,
MY_COL VARCHAR2(50));
--INSERT a line of data and confirm
INSERT INTO TEST_DYNAMIC_TBL VALUES(1, 'SOME TEXT');
COMMIT;
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
MY_COL
SOME TEXT
PL/SQL Anonymous Block (Successful Example)
DECLARE
l_script VARCHAR2 (32767);
BEGIN
l_script := 'UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING(''U'',5)';
DBMS_OUTPUT.put_line ('Script sent to Exec Immediate: ' || l_script);
EXECUTE IMMEDIATE l_script;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line (' ERROR: ' || SUBSTR (SQLERRM, 1, 64));
ROLLBACK;
END;
/
--Check value (This results in a successful update)
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
Script sent to Exec Immediate: UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING('U',5)
PL/SQL procedure successfully completed.
MY_COL
XFTKV
Your query value will vary depending on the seed that DBMS_RANDOM picked
Stored Procedure Example (Failure Example)
--Procedure created with identical logic
CREATE OR REPLACE PROCEDURE TEST_DYNAMIC
AS
l_script VARCHAR2 (32767);
BEGIN
l_script := 'UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING(''U'',5)';
DBMS_OUTPUT.put_line ('Script sent to Exec Immediate: ' || l_script); -- This string will execute successfully if run directly
EXECUTE IMMEDIATE l_script;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (' ERROR: ' || SUBSTR (SQLERRM, 1, 64));
ROLLBACK;
END;
/
--Reset and verify Data
UPDATE TEST_DYNAMIC_TBL SET MY_COL = 'SOME TEXT';
COMMIT;
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
--Execute through Procedure (Will throw error)
EXECUTE TEST_DYNAMIC;
--Check Value of Table
SELECT MY_COL FROM TEST_DYNAMIC_TBL;
Stored Procedure Results:
MY_COL
SOME TEXT
Script sent to Exec Immediate: UPDATE TEST_DYNAMIC_TBL SET MY_COL = DBMS_RANDOM.STRING('U',5)
ERROR: ORA-00904: DBMS_RANDOM: invalid identifier
PL/SQL procedure successfully completed.
MY_COL
SOME TEXT
It isn't about it being dynamic, it's about the privileges and how they were granted. You would see the same thing if you had a static insert using dbms_random (and in your example anyway there is no need for it to be dynamic).
It appears that you have execute privilege on dbms_random granted through a role, not directly to the package owner. From the documentation (emphasis added):
If the procedure owner grants to another user the right to use the procedure, then the privileges of the procedure owner (on the objects the procedure references) apply to the grantee's exercise of the procedure. The privileges of the procedure's definer must be granted directly to the procedure owner, not granted through roles. These are called definer's rights.
The user of a procedure who is not its owner is called the invoker. Additional privileges on referenced objects are required for an invoker's rights procedure, but not for a definer's rights procedure.
That only applies to stored PL/SQL - i.e. procedures, functions, packages, triggers etc. - not to anonymous blocks.
You can either get the privilege on dbms_random granted directly to the package owner, or change your procedure to use invoker's rights:
CREATE OR REPLACE PROCEDURE TEST_DYNAMIC
AUTHID CURRENT_USER
AS
...
In the latter case, anyone calling your procedure will then need the privilege on dbms_random - but they can have it through a role.
As access to that package is sometimes locked down, a direct grant to the owner might be preferable, but it depends on your security constraints.
The reason it requires a direct grant, I believe, is that roles can be enabled and disabled, and be default or not, and can be nested. If a direct grant is revoked then it's fairly straightforward to figure out that should invalidate the procedure. And that's possibly true if a role is revoked, but quite a lot more complicated.
But what role-derived privileges should be taken into consideration when the procedure is created - only those that are enabled in that session? Only default roles? Or all roles? (And remember there can be a chain of roles to think about to determine privileges, and you can get the same privilege from multiple roles.)
However you do it will confuse or upset someone - if only enabled then the owner logging in a future session might not be able to perform the actions the procedure does, and what if they want to recompile it? If only default then those defaults can change, with the same issues - or should that invalidate the procedure? If all roles then including disabled ones will be confusing and could have security implications.
And for any of those, role revocation would still have to figure out which privileges that removes - which aren't also granted directly or via another role! - and only once it's really sure which privileges have actually gone, see which objects that affects. Which could be a lot of work - think how many individual privileges could be affected by revoking DBA.
It's much simpler for the invoker - you only need to look at the active privileges from the enabled roles at the moment then call the procedure.
So while at first glance it seems odd that privileges granted through roles aren't included for stored PL/SQL, once you look at the implications and complications - both as it's created, but more what happens afterwards - it seems like a sensible restriction.

Facing an error : table or view does not exist

I am using insert statement and trying to insert data into the database table. I am using stored procedures.
But I am getting this error while doing so.
Message: ORA-00942: table or view does
not exist ORA-06512
I checked if the tables/stored procedures are present or not and everything is in place. Also there is no typo in table names or in sp. If I run the part of SP from query editor it works fine but when I execute the entire SP it throws an error.
I tried the steps provided by Stephen but since I have logged in with the same user/owner when I run Grant command it gives me an error saying 'Cannot Grant/revoke on own'.
One more addition to this. I have a stored procedure SP1 in which I am using a select statement as
Select a from table_name where condition;
When I execute this seperately, it returns me some results. But when I execute sp it gives an error at the same line where it is written.
Can anyone help me out to resolve this issue. I am using SQL +.
Thanks in advance
Vijay
Justin's answer is correct but let me expand a bit.
Everyone who said that the table doesn't exist didn't read your whole post. Since you are able to:
If I run the part of SP from query editor it works fine
Obviously the table is there.
Obviously you have some access to it. Otherwise this wouldn't work when it clearly does.
but when I execute the entire SP it throws an error.
This is because Oracle distinguishes between permissions granted directly and those granted via a role.
Say I do this:
Create Table TABLE_A
Create Role READ_ONLY
Grant Select on TABLE_A to READ_ONLY
Grant READ_ONLY to VIJAY
In a SQL Window/prompt you could query that table without issue. So now you need to create a view
Create VIJAY.VIEW_A as SELECT * FROM TABLE_A
You'll get the error that TABLE_A does exist. Because a view is compiled, like a procedure it runs without any roles. Since it runs without the READ_ONLY role, it's blind to the fact that TABLE_A exists. Now what I need to do is
Grant Select on TABLE_A to VIJAY.
Now that you have a direct permission, you can compile a view or procedure/package that uses that table.
Does the table exist in the schema where the stored procedure exists? If not, the simplest explanation is that the owner of your procedure has been granted access to the table via a role not via a direct grant. A definer's rights stored procedure needs to have direct access to the objects it accesses. A quick way to test this is to disable roles for the session, i.e.
SQL> set role none;
SQL> <<execute your query>>
If that generates the error, the problem is the lack of a direct grant.
In Oracle you can choose if the stored procedure is executed with the rights of the invoker or the definer: http://download.oracle.com/docs/cd/E11882_01/appdev.112/e17126/subprograms.htm#i18574
Check if the AUTHID property of the stored procedure is correct and if the resulting user has appropriate permissions.
Well, put very simply, the table that you are trying to insert data into does not exist in the database you are connected to. You need to check both those things (i.e. what are you connected to, and is the table there and accessible for the user context you are using).
As Joe Stefanelli said .. there are a lot of possibilities for the error being shown here.
Check whether:
You are connecting to the correct Oracle Instance.
You have permissions to query or perform processing on table that you are referencing in your query.
There is a difference between ordinary select statements and procedures. Procedures in oracle do not respect the roles assigned to a user; rather the permission needs to be explicitly granted to the user. For more information read the following linkORA-00942

Why should GRANT be written after GO in a SQL Stored Procedure?

We had a Stored Procedure written and it had a GRANT written at the last line of the SP.. Our DBA recommended that there should be a GO before the GRANT statement, else it will be executed every time.
What does this mean? I am not sure how GO will prevent GRANT executing "every time".
A stored procedure definition doesn't have to end with and END. If you define your procedure
like this:
CREATE PROCEDURE MySP AS
SELECT field1, field2 FROM table;
GO
GRANT EXECUTE ON MySP TO user1;
...then SQL Server will create a procedure that returns a result set, and grant execute permissions on that stored procedure to user1.
If you do something like this, though:
CREATE PROCEDURE MySP AS
SELECT field1, field2 FROM table;
GRANT EXECUTE ON MySP TO user1;
...then SQL Server will create a procedure that both returns a result set, and grants execute permission every time that stored procedure is executed. In other words, the GRANT is included in the definition of the stored procedure. Probably not what you wanted.
A GO statement is used to mark the end of a batch in SQL Server. It unambiguously tells SQL Server "I'm done with whatever I was executing in the previous set of statements". It's very good practice to add this to the end of every definition in your create scripts; this exact situation has bitten me more than once in the past.
Your DBA meant that this GRANT will be a part of your stored procedure, and as such will execute every time your procedure is executed.
Typically users should not be allowed to grant privileges, so your procedure should raise errors every time its last command is executed.
GO is the default command separator used in query analyzer, you can actually configure it to be whatever you want. you could also achieve the same by using BEGIN..END around the proc body.

Regarding Execute Immediate in stored procedure

I am having the below statement from stored procedure. It's giving Insufficient Privileges.
But If i run the create statement alone from the sql prompt it's creating table.
execute immediate 'create table TEST_ABC(
NO_AC NUMBER(8)
, ILL_PER VARCHAR2(15)
, INIT_C DATE
)';
What needs to be done to have priviliges to create table via execute immediate from stored procedure. Not sure how it's working from sql command prompt
Procedures don't inherit privileges granted via a role. More info here. Please check if that's what happening to you.
One way to solve this problem is to grant "CREATE TABLE" privilege directly to the account that owns the procedure.
Is the procedure created by the same user? If it is created by some one else and you have EXECUTE privilege alone, then the error is right (assuming the create procedure doesn't have AUTHID CURRENT USER clause).
Can you create any other table? If you can, then there is some issue. We would need more details to analyse.

dba_jobs_running: table or view does not exist when trying to access from procedure

Simply querying running jobs using something like
select * from dba_jobs_running;
works fine when executed in my sqldevelopers SQL console.
However, it does not work, when having exactly the same statement within a procedure.
Compilation fails with
PL/SQL: ORA-00942: table or view does not exist
Any ideas? Is there something like a scope to be considered?
Any suggestions are highly appreciated, thanks in advance :)
You probably need to do a direct GRANT of DBA_JOBS_RUNNING to the user that owns the procedure. Doing a GRANT via a role won't work.... the grant needs to be explicit.
EDIT:
Doing a SELECT from within a procedure requires subtly different permissions to doing a SELECT from outside a procedure (e.g. in SQL-Developer). The user that owns a procedure must have been explicitly granted rights to the table or view... if running a query from outside a view this is not the case (you can be granted the permission through a role for example)
You need to connect as SYS and go:
GRANT SELECT ON SYS.DBA_JOBS_RUNNING TO <user-that-owns-proc>;
Procedures are executed without roles. One way to see if you can run a command in a procedure is to execute:
SQL> set role none;
Role set
You will have the same set of rights as your procedures:
SQL> SELECT * FROM dba_jobs_running;
SELECT * FROM dba_jobs_running
ORA-00942: table or view does not exist
You have to grant select on the view directly to the user:
SQL> -- with dba account
SQL> grant select on dba_jobs_running to a;
Grant succeeded
You will then be able to compile the procedure:
SQL> -- with application schema
SQL> CREATE OR REPLACE PROCEDURE test_dba AS
2 BEGIN
3 FOR cc IN (SELECT * FROM dba_jobs_running) LOOP
4 NULL;
5 END LOOP;
6 END test_dba;
7 /
Procedure created
Is procedure owned by another user? If so have a look at:
Definer and Invoker Rights for stored routines in PL/SQL manual.
Rob