Condition in SQL script - sql

I've got an SQL-script executed by SQL*Plus, that needs to run with Oracle 10g and Oracle 11g.
That script gives grants on a package that does not exist before 11g:
GRANT EXECUTE ON sys.dbms_result_cache TO my_user;
I would like to avoid the exception on 10g, since I want to react to other exceptions in the script.
One way is to use Conditional Compilation and dbms_db_version:
BEGIN
$IF dbms_db_version.ver_le_10 $THEN NULL; $ELSE
EXECUTE IMMEDIATE 'GRANT EXECUTE ON sys.dbms_result_cache TO my_user';
$END
END;
/
Is there any other way, preferable without using PL/SQL?

Your question and one of the comments indicate that you want to avoid PL/SQL blocks and EXECUTE IMMEDIATE. I also assume that by "react to other exceptions" you mean abort execution of the script when an exception is encountered.
If so, I think the best you can do in pure SQL/SQL*Plus is to ignore the exception exit for the grant statement:
... first part of script (with exit on sqlerror in effect)
WHENEVER SQLERROR CONTINUE
GRANT EXECUTE ON sys.dbms_result_cache TO my_user;
WHENEVER SQLERROR EXIT SQL.SQLCODE
... remaining part of script

you could check if the object exists beforehand:
BEGIN
FOR cc IN (SELECT NULL
FROM all_objects
WHERE owner = 'SYS'
AND object_name = 'DBMS_RESULT_CACHE'
AND ROWNUM = 1) LOOP
EXECUTE IMMEDIATE 'GRANT EXECUTE ON sys.dbms_result_cache TO my_user';
END LOOP;
END;

You can simulate branching by writing SQL that generates SQL and spools it to a sql script. Then run the sql script:
define temp_file='somefile.sql'
set heading off
set feedback off
spool &&temp_file
SELECT 'GRANT EXECUTE ON sys.dbms_result_cache TO my_user;'
FROM all_objects
WHERE owner = 'SYS'
AND object_name = 'DBMS_RESULT_CACHE';
spool off
#&&temp_file
host rm &&temp_file
Thanks to #Vincent for the data dictionary query.

Related

Oracle: grant to role if role exists

How to execute
GRANT SELECT ON <ownschema>.<sometable> TO <somerole>;
but backing off gracefully if somerole does not exist. The user executing the statement is a standard user (think SCOTT) without any special privileges.
Version: Oracle Database 19 or later
I don't think you can.
If you're running it at SQL level, then Oracle will raise an error if role doesn't exist.
If you want to check whether it exists, you'll need some kind of a PL/SQL procedure (which is not a problem), but - DBA should grant you select privilege on dba_roles so that you could check it. Then, if it exists, you'd grant that privilege; otherwise, return some kind of an information (dbms_output.put_line in my example is pretty much stupid; it wouldn't be visible out of SQL*Plus, SQL Developer, TOAD and similar), but you got the idea, I hope.
Something like this:
create or replace procedure p_grant_to_role (par_role in varchar2) is
l_cnt number;
begin
select count(*)
into l_cnt
from dba_roles
where role_name = par_role;
if l_cnt > 0 then
execute immediate 'grant select on emp to ' || par_role;
else
dbms_output.put_line('Role does not exist');
end if;
end;
/
It all depends on the tool, but you can do something like this (very crude as usually you should have better exception handling):
begin
execute immediate 'grant .....';
exception
when others then null;
end;

Execute Oracle SQL command after PL/SQL block

I've an SQL file which has some PL/SQL scripts and some DML scripts, but I'm not able to run normal DML commands just after a PL/SQL block IN SQL Developer. For e.g.
BEGIN
-- Some Statements
END;
UPDATE TABLE TABLE_NAME SET FLD_NAME = SOMETHING;
Do I need to change anything here so that I can run these commands.
PS: I don't want to put everything in BEGIN ... END block.
try put / after anonymous block:
BEGIN
-- Some Statements
END;
/
UPDATE TABLE TABLE_NAME SET FLD_NAME = SOMETHING;

Oracle creating sequences with execute immediate

This might have already been asked but I am not able to resolve it, so posting again.
I need to create an oracle sequence with the start value coming from a variable. So obviously I need to use execute immediate for the same within a pl/sql block.
I used the following PL/SQL block to create the sequence:
declare nl_seqmax NUMBER :=0;
BEGIN
SELECT 1000000009
into nl_seqmax
from dual;
if nl_seqmax > 0 THEN
execute immediate 'CREATE SEQUENCE my_seq INCREMENT BY 1 START WITH '||nl_seqmax || ' MAXVALUE 4000000000 CACHE 20 ORDER';
end if;
end;
/
PL/SQL procedure successfully completed.
But later when referencing this sequence, I see that it throws 942 error.
SQL> AUDIT GRANT ON my_seq BY ACCESS WHENEVER SUCCESSFUL;
ERROR at line 1:
ORA-00942: table or view does not exist
Any idea on this ? Should all the references to sequence also should use dynamic sql ?

PL/SQL: bind variable does not exist

How to modify this procedure to let it use bind variables
PROCEDURE KILL(user IN VARCHAR2) AS
BEGIN
FOR REC IN (SELECT sid,serial# serial FROM V$SESSION WHERE username = user)
LOOP
execute immediate 'alter system kill session '' :1 , :2 '' immediate'
using rec.sid, rec.serial;
END LOOP;
END;
It gives:
bind variable does not exist
The bind variables in your statement are being treated as literal strings rather than place holders. If you output the statement you're generating:
BEGIN
FOR REC IN (SELECT sid,serial# serial FROM V$SESSION WHERE username = user)
LOOP
dbms_output.put_line('alter system kill session '':1,:2'' immediate');
END LOOP;
END;
/
... you see lines like:
alter system kill session ':1,:2' immediate
The ':1,:2' is treated as a static value and not as two bind variables. You can't use bind variables in dynamic DDL, and I'm not sure if that applies to alter commands, so this may be impossible anyway.
The simplest way to achieve this may be to generate the whole statement in the cursor:
BEGIN
FOR REC IN (
SELECT 'alter system kill session ''' || sid ||','|| serial#
||''' immediate' stmt
FROM V$SESSION WHERE username = user)
LOOP
dbms_output.put_line(rec.stmt);
--execute immediate rec.stmt;
END LOOP;
END;
/
With the execute commented out (I don't really want to kill my sessions just now) you can just see the commands it will run, like:
alter system kill session '58,47157' immediate
Your approach may still be flawed though as it will kill the session that is executing the block, and it may or may not kill it last. I think this is in the realms of undefined behaviour, and I don't really want to try it to find out what happens... I doubt that's what you actually want anyway.
Edit: 'flawed' comment was based on using user, which in my anonymous block would be the executing user; in your proc it would be the user from the parameter. Using a keyword as a parameter name is confusing though, so I'd recommend changing the name to something like p_user, in the args and the statement.
I believe this might work
PROCEDURE KILL(user IN VARCHAR2) AS
BEGIN
FOR REC IN (SELECT sid,serial# serial FROM V$SESSION WHERE username = user)
LOOP
execute immediate 'alter system kill session :1 '||','|| ':2 immediate'
using rec.sid, rec.serial;
END LOOP;
END;

drop tablespace if do not exist

I have written pl/sql script (works, but doesn't look nice):
DECLARE
v_exists NUMBER;
BEGIN
SELECT count(*) INTO v_exists FROM dba_tablespaces WHERE tablespace_name = 'hr_test';
IF v_exists > 0 THEN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLESPACE hr_test INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS';
END;
END IF;
EXECUTE IMMEDIATE 'CREATE TABLESPACE hr_RJ DATAFILE ''E:\hr_test_01.dbf'' SIZE 16M';
END;
Is there any way to rewrite this script without EXECUTE IMMEDIATE?
No. You cannot issue DDL statements in static PL/SQL.
And yes, it is perfectly fine to use native dynamic SQL for DDL purposes:
You need dynamic SQL in the following
situations:
You want to execute a SQL data
definition statement (such as CREATE),
a data control statement (such as
GRANT), or a session control statement
(such as ALTER SESSION). In PL/SQL,
such statements cannot be executed
statically.
Oracle dynamic SQL