PL/SQL Oracle - Encountered the symbol “end-of-file” - oracle

I'm trying to make a procedure which gives grants to users on a certain type of objects
Here's what I did
CREATE OR REPLACE PROCEDURE grants AS
DECLARE
Cursor c IS select OBJECT_NAME as view_name from all_objects where object_type in ('VIEW');
BEGIN
FOR tmp in c
LOOP
EXECUTE IMMEDIATE 'GRANT ALL on ' || tmp.view_name || ' TO my_users';
END LOOP;
END;
But I'm getting an 'end-of-file' symbole encountered [...]' error...
Do you guys have an idea to make this procedure work ?

Remove DECLARE:
SQL> CREATE OR REPLACE PROCEDURE grants AS
2 CURSOR c IS
3 SELECT OBJECT_NAME AS view_name
4 FROM all_objects
5 WHERE object_type IN ('VIEW');
6 BEGIN
7 FOR tmp IN c
8 LOOP
9 EXECUTE IMMEDIATE 'GRANT ALL on ' || tmp.view_name || ' TO my_users';
10 END LOOP;
11 END;
12 /
Procedure created.

Related

executing select statement stored in table column by replacing variable value dynamically

I have simple PL/SQL block with below code
declare
rule1 varchar2(100 char);
begin
for i in (select table_name from all_tables where table_owner='EqEDI') loop
execute immediate 'select rule_stmt from rulebook ' into rule1 ;
execute immediate rule1 into result;
dbms_output.put_line('Result is '||result);
end loop;
end;
the rule statement stored in table rulebook is :
"'select count(1) from '|| tablename"
I want this above statement to be executed for all tables present for given owner
but while executing, it does not replace tablename in query with actual tables.
How can I achieve this with simple PL/SQL block.
Regards
rulebook table's contents is kind of wrong. Not that you can NOT do it the way you stored select statement into it - it is just impractical as you have to remove single quotes, remove tablename (as you can't even bind it, but concatenate what cursor returned) ... too much unnecessary jobs to be done.
Also, check all_tables and names of its columns - there's no table_owner, just owner.
Therefore, I'd suggest you to store such a statement:
SQL> SELECT * FROM rulebook;
RULE_STMT
--------------------------------------------------------------------------------
select count(*) from
Fix your PL/SQL script:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 rule1 VARCHAR2 (100 CHAR);
3 l_str VARCHAR2 (100);
4 result NUMBER;
5 BEGIN
6 FOR i IN (SELECT table_name
7 FROM all_tables
8 WHERE owner = 'SCOTT'
9 AND table_name = 'EMP')
10 LOOP
11 EXECUTE IMMEDIATE 'select rule_stmt from rulebook '
12 INTO rule1;
13
14 l_str := rule1 || i.table_name;
15
16 EXECUTE IMMEDIATE l_str
17 INTO result;
18
19 DBMS_OUTPUT.put_line ('Result is ' || result);
20 END LOOP;
21 END;
22 /
Result is 14
PL/SQL procedure successfully completed.
SQL>

Why am I getting ORA-00900 Invalid SQL statement when trying to create a directory object with execute immediate?

I see many examples of this being possible in Oracle. It's just not working for me. Oracle 11. I am getting this error on line 15. Thank you all!
declare
v_path nvarchar2(256);
v_object_exists number;
begin
-- Use the directory Oracle DB provide for tracing.
select VALUE into v_path from V$DIAG_INFO where NAME = 'Diag Trace';
--dbms_output.put_line(v_path);
-- Set up new directory!
select count(*) into v_object_exists from all_objects where object_name = 'DIAG_TRACE' and object_type = 'DIRECTORY';
if v_object_exists > 0 then
execute immediate 'DROP DIRECTORY DIAG_TRACE';
end if;
dbms_output.put_line('CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ''' || v_path || '''');
execute immediate 'CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ''' || v_path || '''';
end;
This appears to be a bug; though not one listed on the ORA-00900 reference note on MoS.
It doesn't like the path variable being concatenated in as part of the execute immediate. This fails:
v_path := '/some/path';
execute immediate 'CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ''' || v_path || '''';
but this is OK, even though the final statement is the same:
execute immediate 'CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ''/some/path''';
After some searching, it may be something to do with bug 7036176: "CONCATENATED DYNAMIC OBJECT NAME RAISES ORA-00900 IN 10G & 11G". It's not exactly the same but close. You'll need to look on My Oracle Support for further info, though there isn't much.
You can work around it with a variable:
declare
v_stmt varchar2(256);
v_path nvarchar2(256);
v_object_exists number;
begin
-- Use the directory Oracle DB provide for tracing.
select VALUE into v_path from V$DIAG_INFO where NAME = 'Diag Trace';
--dbms_output.put_line(v_path);
-- Set up new directory!
select count(*) into v_object_exists from all_objects where object_name = 'DIAG_TRACE' and object_type = 'DIRECTORY';
if v_object_exists > 0 then
execute immediate 'DROP DIRECTORY DIAG_TRACE';
end if;
v_stmt := 'CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ''' || v_path || '''';
dbms_output.put_line(v_stmt);
execute immediate v_stmt;
end;
/
Which saves repeating the string to print it, though you might only have done that because of this issue.
Not sure why you're dropping first with or replace, incidentally.
I prefer putting a command into a variable, display it (for verification) and then execute it:
SQL> declare
2 v_path nvarchar2(256);
3 v_object_exists number;
4 l_str varchar2(200);
5 begin
6 -- Use the directory Oracle DB provide for tracing.
7 select VALUE into v_path from V$DIAG_INFO where NAME = 'Diag Trace';
8 --dbms_output.put_line(v_path);
9
10 -- Set up new directory!
11 select count(*) into v_object_exists from all_objects where object_name = 'DIAG_TRACE' and object_type = 'DIRECTORY';
12 if v_object_exists > 0 then
13 execute immediate 'DROP DIRECTORY DIAG_TRACE';
14 end if;
15 l_str := 'CREATE OR REPLACE DIRECTORY DIAG_TRACE AS ' || chr(39) || v_path ||chr(39);
16 dbms_output.put_line(l_str);
17 execute immediate l_str;
18 end;
19 /
CREATE OR REPLACE DIRECTORY DIAG_TRACE AS
'C:\ORACLEXE\APP\ORACLE\diag\rdbms\xe\xe\trace'
PL/SQL procedure successfully completed.
SQL>
Certainly, you have to run it as a privileged user (such as SYS).

PL/SQL: How to enable all triggers in the schema?

I'm trying to make a procedure that uses a cursor that enables all triggers in my schema to be enabled. However, I'm getting this Error at line 16: PL/SQL: SQL Statement ignored which is the line that I use fetch for.
CREATE OR REPLACE PROCEDURE enable_trigg_proc
IS
v_trigger_name trigger_name.user_triggers%TYPE;
CURSOR enable_trigg_cur
IS
SELECT TRIGGER_NAME INTO v_trigger_name
FROM user_triggers
WHERE STATUS = 'DISABLED';
BEGIN
OPEN enable_trigg_cur;
LOOP
FETCH enable_trigg_cur INTO v_trigger_name ;
EXIT WHEN enable_trigg_cur%NOTFOUND;
EXECUTE IMMEDIATE 'ALTER TRIGGER v_trigger_name ENABLE';
END LOOP;
close enable_trigg_cur;
END;
You don't need that much code:
SQL> begin
2 for cur_r in (select trigger_name from user_triggers) loop
3 execute immediate 'alter trigger ' || cur_r.trigger_name || ' enable';
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
What's wrong with your code? INTO is required in PL/SQL's SELECT statement, but not within the cursor declaration. Also, you declared the variable in the wrong manner; should be
SQL> CREATE OR REPLACE PROCEDURE enable_trigg_proc
2 IS
3 CURSOR enable_trigg_cur
4 IS
5 SELECT TRIGGER_NAME
6 FROM user_triggers
7 WHERE STATUS = 'DISABLED';
8
9 v_trigger_name user_triggers.trigger_name%TYPE;
10 BEGIN
11 OPEN enable_trigg_cur;
12 LOOP
13 FETCH enable_trigg_cur INTO v_trigger_name ;
14 EXIT WHEN enable_trigg_cur%NOTFOUND;
15
16 EXECUTE IMMEDIATE 'ALTER TRIGGER ' || v_trigger_name || ' ENABLE';
17 END LOOP;
18 close enable_trigg_cur;
19 END;
20 /
Procedure created.
SQL> exec enable_trigg_proc;
PL/SQL procedure successfully completed.
SQL>

How to build dynamic SQL with using

I am trying to make dynamic sql like this 'select col1,col2 from '|| my_table ||' it works fine but I want to write like this sql_stmt:='select col1,col2 from :myTable'; execute immediate sql_stmt using my_table; but I have error I have same error when I want to do something like this tooo v_filter := my_proc(); sql_stmt:='select col1,col2 from my_table where :filter' execute immediate sql_stmt using v_filter; is it impossible to build dynamic sql like this with using ? If it is impossible what is another way to avoid sql injections ?
When you want to use table names in dynamic SQL, yes - you'll have to concatenate them. In order to avoid SQL injection, use DBMS_ASSERT.SQL_OBJECT_NAME.
Here's an example:
SQL> create or replace procedure p_test (par_table in varchar2) is
2 l_table varchar2(30);
3 l_str varchar2(200);
4 l_cnt number;
5 begin
6 l_table := dbms_assert.sql_object_name(par_table);
7
8 l_str := 'select count(*) from ' || par_table;
9 execute immediate (l_str) into l_cnt;
10 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
11 end;
12 /
Procedure created.
SQL>
SQL> exec p_test('dept');
Table contains 4 rows
PL/SQL procedure successfully completed.
SQL> exec p_test('delete from emp');
BEGIN p_test('delete from emp'); END;
*
ERROR at line 1:
ORA-44002: invalid object name
ORA-06512: at "SYS.DBMS_ASSERT", line 316
ORA-06512: at "SCOTT.P_TEST", line 6
ORA-06512: at line 1
SQL>
[EDIT: WHERE clause]
This works:
SQL> create or replace procedure p_test (par_table in varchar2,
2 par_filter in varchar2) is
3 l_table varchar2(30);
4 l_str varchar2(200);
5 l_cnt number;
6 begin
7 l_table := dbms_assert.sql_object_name(par_table);
8
9 l_str := 'select count(*) from ' || par_table ||
10 ' where deptno = :filter';
11 execute immediate (l_str) into l_cnt using par_filter;
12 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
13 end;
14 /
Procedure created.
SQL> exec p_test('emp', '10');
Table contains 3 rows
PL/SQL procedure successfully completed.
SQL>
WHERE clause, modified so that it contains only the WHERE keyword, while the rest is to be used as a parameter:
SQL> create or replace procedure p_test (par_table in varchar2,
2 par_filter in varchar2) is
3 l_table varchar2(30);
4 l_str varchar2(200);
5 l_cnt number;
6 begin
7 l_table := dbms_assert.sql_object_name(par_table);
8
9 l_str := 'select count(*) from ' || par_table ||
10 ' where :filter';
11 execute immediate (l_str) into l_cnt using par_filter;
12 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
13 end;
14 /
Procedure created.
SQL> exec p_test('emp', 'deptno = 10');
BEGIN p_test('emp', 'deptno = 10'); END;
*
ERROR at line 1:
ORA-00920: invalid relational operator
ORA-06512: at "SCOTT.P_TEST", line 11
ORA-06512: at line 1
SQL>
It won't work; is that what you're asking?
Some more reading about dynamic SQL on Oracle, as well as here, on Stack overflow (How can I create a dynamic WHERE clause.

drop procedure if exists in DB Oracle

Can someone tell me how I can drop a PROCEDURE in Oracle, but just if it exists ?
DROP PROCEDURE IF EXISTS XYZ;
The above does not work.
If your goal is to eliminate error messages in a script, then you can try
begin
execute immediate 'drop procedure xyz';
exception when others then
if sqlcode != -4043 then
raise;
end if;
end;
/
You can also check dictionary view before:
SELECT * FROM USER_PROCEDURES WHERE PROCEDURE_NAME = 'XYZ'
My solution:
DECLARE
V_NUM NUMBER;
BEGIN
SELECT COUNT(*)
INTO V_NUM
FROM USER_OBJECTS
WHERE OBJECT_NAME = 'XYZ'
AND OBJECT_TYPE = 'PROCEDURE';
IF V_NUM > 0 THEN
EXECUTE IMMEDIATE 'DROP PROCEDURE XYZ';
DBMS_OUTPUT.PUT_LINE('Dropped');
END IF;
END;
/
A complete example:
declare
c int;
begin
select count(*) into c from user_procedures where object_type = 'FUNCTION' and object_name = 'ABC';
if c = 1 then
execute immediate 'DROP FUNCTION ABC';
end if;
end;

Resources