How to grant priviledges to a read only user on a function? - sql

With PL/SQL I created a util function:
create or replace function DOSOMETHING(article varchar) return varchar is ...
begin
...
end;
The function works well so far. But only for the user who created it. I have also a user named "read" who can only read in the db. And he can't create functions of course because he has read only rights. The thing is he can't see that the function exists like with:
SELECT * FROM ALL_OBJECTS WHERE (OBJECT_TYPE = 'FUNCTION')
>>> This <<< seems to be that could fix that. So I could say "read" may use this function for select statements etc. ? Right ? That's what I want. I tried the following and none of them worked. How do I do it or do I do it differently ?
begin GRANT SELECT ON DOSOMETHING TO READ; end;
begin execute immediate('GRANT SELECT ON DOSOMETHING TO READ'); end;
Error message for the first:
[ODBC driver for Oracle][Oracle]ORA-06550: line 1, column 7:
PLS-00103: Encountered the symbol "GRANT" when expecting one of the following:
begin declare exit for goto if loop mod null pragma raise
return select update while <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
close current delete fetch lock insert open rollback
savepoint set sql execute commit forall
<a single-quoted SQL string>
(Oracle version is 8i, the old thing, in case this matters)

You're close. For procedures, you need the EXECUTE privilege instead of SELECT:
GRANT EXECUTE ON dosomething TO READ;
for running it as a SQL statement, or
begin execute immediate 'GRANT EXECUTE ON dosomething TO READ'; end;
to run it as a PL/SQL block.
When you use that function as the READ user, you'll have to prefix its name with the owner:
SELECT <owner>.dosomething('abc') FROM dual;

Related

how to select into an array in pl/sql?

I'm trying to add some ids into an array:
CREATE OR REPLACE TYPE array_of_numbers AS VARRAY(10)OF NUMBER(10);
declare
student_ids array_of_numbers;
begin
select nrleg BULK COLLECT into student_ids from student where nrleg = 123458;
FOR i IN 1..student_ids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(student_ids(i));
END LOOP;
end;
but I get the following errors:
--------- -------------------------------------------------------------
3/1 PLS-00103: Encountered the symbol "DECLARE"
10/4 PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge json_exists json_value json_query json_object json_array
Errors: check compiler log
Could someone explain what I did wrong?
Some DDL needs to be terminated with a /; a type can have a PL/SQL body so this is one of them. To run that all at once as a script do:
CREATE OR REPLACE TYPE array_of_numbers AS VARRAY(10)OF NUMBER(10);
/
declare
student_ids array_of_numbers;
begin
select nrleg BULK COLLECT into student_ids from student where nrleg = 123458;
FOR i IN 1..student_ids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(student_ids(i));
END LOOP;
end;
/
You don't need the semicolon on the end of the CREATE here, but it doesn't hurt; but for some other commands (including other DDL like create table) having both would cause it to try to execute the statement twice, which could cause an error.
SQL Developer won't complain about the lack of a / after the last PL/SQL block in a script, but other tools will, so it's better to always include that one too.
db<>fiddle
Incidentally, another way to see the contents of the array in SQL Developer is with a ref cursor:
var rc refcursor
declare
student_ids array_of_numbers;
begin
select nrleg BULK COLLECT into student_ids from student where nrleg = 123458;
open :rc for select * from table(student_ids);
end;
/
print rc
... but then you might as well just select directly from the table, without any PL/SQL.

PL/SQL procedure not compiling

I have a PL/SQL procedure that is not compiling. The errors are:
Error(3,7): PLS-00103: Encountered the symbol "INTO" when expecting one of the following: ( begin case declare exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge The symbol "INTO" was ignored.
Error(8,1): PLS-00428: an INTO clause is expected in this SELECT statement.
My code:
CREATE OR REPLACE PROCEDURE findvisitsandlaborcosts
as
begin
select * from SI.customer;
end;
/
I have googled an online syntax checker and it says on the first line there is an error. But WHERE?!? It seems to be correct. I have googled the syntax of declaring a procedure and I have cross-checked many times. It must be something simple that I am overlooking...
in a PLSQL code, you need a placeholder to keep results of a SELECT query. Since PLSQL engine is expecting INTO clause within SELECT statement.
To begin with, you can select a set of columns and assign their values to local variables.
Your code should be like this -
CREATE OR REPLACE PROCEDURE findvisitsandlaborcosts
as
v_column1 SI.customer.column1%type;
v_column2 SI.customer.column2%type;
begin
select column1, column2 into v_column1, v_column2 from SI.customer;
end;
/
Note - you need to replace column1 and column2 with actual column names before running this code at your end.
If you want the results to get displayed on the caller of the procedure, then you would define an out parameter and print the records outside of the procedure
CREATE OR REPLACE PROCEDURE findvisitsandlaborcosts(x out sys_refcursor)
as
begin
open x for
select * from dual;
end;
/
--Note this block of code needs to be run in sqlci or sqldeveloper
define m ref cursor;
exec findvisitsandlaborcosts(:x);
print x;
Oracle 12c has support for implict return results
Have a look at this link
https://oracle-base.com/articles/12c/implicit-statement-results-12cr1

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 ?

Oracle SQL Stored Procedure Cursor print

Ok so I am working on a homework assignment using stored procedures.
Essentially i am just trying to use a stored procedure to run a query then print the results.
Here is what i have so far.
create or replace procedure movie_actors (mtitle varchars)as
DECLARE
CURSOR c1 is SELECT "NAME",GENDER,ADDRESS FROM MOVIESTAR WHERE "NAME" in(
SELECT STARNAME FROM STARSIN WHERE MOVIETITLE=mtitle);
actor_name MOVIESTAR.NAME%TYPE;
actor_gender MOVIESTAR.NAME%TYPE;
actor_address MOVIESTAR.ADDRESS%TYPE;
BEGIN
LOOP
FETCH c1 INTO actor_name;
FETCH c1 INTO actor_gender;
FETCH c1 INTO actor_address;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(mtitle ||', '||actor_name||': '||actor_gender||', '||actor_address);
END LOOP;
END;
I am new to databases and stored procedures. I am not sure if i am really going about this the best way.
It should be pretty simple, I am not sure what I am doing wrong.
This is the compiler error i am getting.
Error(2,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one of the
following: begin function pragma procedure subtype type <an identifier> <a
double-quoted delimited-identifier> current cursor delete exists prior external
language The symbol "begin" was substituted for "DECLARE" to continue.
Error(16,4): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the
following: ( begin case declare end exception exit for goto if loop mod
null pragma raise return select update while with <an identifier> <a double-quoted
delimited-identifier> <a bind variable> << continue close current delete fetch lock
insert open rollback savepoint set sql execute commit forall merge pipe purge
Any help would be greatly appreciated.
First, you have an invalid type:
create or replace procedure movie_actors (mtitle varchars)as
^
This should be varchar2, not varchars.
Second, you don't need the DECLARE here. The "as" kinda substitutes for it. Start your proc like this:
create or replace procedure movie_actors (mtitle varchar2)as
CURSOR c1
Finally, I strongly recommend changing this:
CURSOR c1 is SELECT "NAME",GENDER,ADDRESS FROM MOVIESTAR WHERE "NAME"
... to this (no double quotes):
CURSOR c1 is SELECT NAME,GENDER,ADDRESS FROM MOVIESTAR WHERE NAME
The double quotes will make the column name case sensitive. You're lucky in this case because the default in Oracle is uppercase, but sooner or later using double quotes like this will cause you trouble - there are plenty of StackOverflow postings from frustrated users who've lost hours or time from using double quotes when they didn't have to.

Condition in SQL script

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.