access to function - ORA-00904: invalid identifier - oracle

I am new to oracle, please assist.
I have written a function similar to here in schemaB, with minor alterations.
When I run the query from above function, in schemaA, it runs fine and returns the sequence.
select schemaB.sequence_name_seq.nextVal from dual;
Running the function from schemaB ofcourse return the sequence as expected.
However, when I try to access the same function (containing the above query) from schemaA, it gives me an error:
"ORA-00904: invalid identifier"
I have granted EXECUTE privileges to userA of schemaA (confirmed from 'DBA_TAB_PRIVS' table).
Function:
create or replace Function nextSeq
(
tableName in VARCHAR2
)return NUMBER as
nextNum Number;
begin
EXECUTE IMMEDIATE 'select '||tableName||'_SEQ.nextval from dual' into nextNum;
return nextNum;
END nextSeq;
To Call:
select nextSeq('SCHEMAB.TABLENAME') from dual;

Here's how I'd do that: connected as SCOTT, I'm creating a sequence and a function; then I'll grant EXECUTE on the function to user MIKE:
SQL> show user
USER is "SCOTT"
SQL> create sequence dept_seq;
Sequence created.
SQL> create or replace function nextseq (tablename in varchar2)
2 return number
3 as
4 nextnum number;
5 begin
6 execute immediate 'select ' || tablename||'_seq.nextval from dual' into nextnum;
7 return nextnum;
8 end;
9 /
Function created.
SQL> select nextseq('dept') from dual;
NEXTSEQ('DEPT')
---------------
1
SQL> grant execute on nextseq to mike;
Grant succeeded.
SQL>
Connect as MIKE and use SCOTT's function:
SQL> connect mike/lion#xe
Connected.
SQL> show user
USER is "MIKE"
SQL> select scott.nextseq('dept') from dual;
SCOTT.NEXTSEQ('DEPT')
---------------------
2
SQL>
As you can see it works. When compared to your code, the difference is here:
You: select nextSeq('SCHEMAB.TABLENAME') from dual;
Me : select scott.nextseq('dept') from dual;
You shouldn't precede table name with the owner, but the function name.

Related

Restrict creation of table on sunday in oracle

Question: create a pl/sql using a trigger to restrict creation of any
table on Sunday
I tried
CREATE OR REPLACE TRIGGER sunday_trigger
BEFORE CREATE ON ALSPRD
FOR EACH ROW
DECLARE
v_day DATE := TRUNC(SYSDATE);
BEGIN
IF TO_CHAR(v_day,'DY')IN ('SUN') THEN
REVOKE create table from harsh;
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error is:'||SQLERRM);
END;
It creating error ORA-30506: system triggers cannot be based on tables or views
I'd suggest a different approach.
Connected as SYS, create a trigger that prevents user (scott in my example) to create anything on desired day (I'll use Monday so that I could test whether it works as planned).
SQL> show user
USER is "SYS"
SQL>
SQL> create or replace trigger sunday_trigger
2 before create on scott.schema
3 begin
4 if to_char(sysdate, 'DY', 'nls_date_language = english') = 'MON'
5 then
6 raise_application_error(-20000, 'You can not create any objects on Monday');
7 end if;
8 end;
9 /
Trigger created.
Let's test it:
SQL> connect scott/tiger
Connected.
SQL> select to_char(sysdate, 'DY', 'nls_date_language = english') today from dual;
TODAY
------------
MON
SQL> create table so_test (id number);
create table so_test (id number)
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: You can not create any objects on Monday
ORA-06512: at line 4
SQL> create or replace procedure p_test is
2 begin
3 null;
4 end;
5 /
create or replace procedure p_test is
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: You can not create any objects on Monday
ORA-06512: at line 4
SQL>
Let's pretend it is tomorrow (Tuesday):
SQL> connect sys as sysdba
Enter password:
Connected.
SQL> select sysdate from dual;
SYSDATE
--------
21.06.21
SQL> alter system set fixed_date = '22.06.21';
System altered.
SQL> connect scott/tiger
Connected.
SQL> select to_char(sysdate, 'DY', 'nls_date_language = english') today from dual;
TODAY
------------
TUE
SQL> create table so_test (id number);
Table created.
SQL> create or replace procedure p_test is
2 begin
3 null;
4 end;
5 /
Procedure created.
SQL>
Looks OK to me.
Revoke user rights to create tables from harsh.
Create another user <OWNER_PROC> with rights to create tables.
Make a procedure <CREATE_TABLE_PROC> that creates tables and in this procedure you can make any logic.
Grant execute on <OWNER_PROC>.<CREATE_TABLE_PROC> to harsh.

Oracle Read only access to specific user

I have a schema and many users in it. For development I would like to give permission to the user to only read the tables and not having permission to manipulate database. Are there any commands to set access rights for a particular user to read only?
As Ed commented, you aren't allowed to do anything unless granted. For read-only users, you'd grant only the SELECT privilege on your tables. If you have only a few of them, do it manually. Otherwise, create a procedure which will do it for you. Here's an example.
These are my tables:
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BONUS TABLE
DEPT TABLE
EMP TABLE
LINKS TABLE
SALGRADE TABLE
A procedure which loops through all tables in my schema and grants SELECT to user passed as a parameter:
SQL> create or replace procedure p_grant_ro(par_user in varchar2) is
2 l_str varchar2(200);
3 begin
4 for cur_r in (select table_name from user_tables
5 order by table_name
6 )
7 loop
8 l_str := 'grant select on ' || cur_r.table_name ||
9 ' to ' || dbms_assert.schema_name(par_user);
10 dbms_output.put_line(l_str);
11 execute immediate(l_str);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on;
SQL> exec p_grant_ro('MIKE');
grant select on BONUS to MIKE
grant select on DEPT to MIKE
grant select on EMP to MIKE
grant select on LINKS to MIKE
grant select on SALGRADE to MIKE
PL/SQL procedure successfully completed.
SQL>
If you wonder what's the purpose of dbms_assert function's call: preventing possible SQL injection. Function takes care that parameter is an existing schema name.
SQL> exec p_grant_ro('TECH_DELHI');
BEGIN p_grant_ro('TECH_DELHI'); END;
*
ERROR at line 1:
ORA-44001: invalid schema
ORA-06512: at "SYS.DBMS_ASSERT", line 266
ORA-06512: at "SCOTT.P_GRANT_RO", line 8
ORA-06512: at line 1
SQL>
You should be able to configure this in e.g. MYSQL Workbench.
I think there is also a way to link this to an Apex user.

How to run a query on different schemes at once

I want to run a query on different schemes to get data and export it. I use the following code
DECLARE
sql_statment VARCHAR2(2000);
BEGIN
FOR c IN (SELECT brchcode FROM brchs) LOOP
sql_statment := 'select distinct ''' || c.brchcode ||''', t.risuid from ' || c.brchcode ||
'.reg_individualacnt_detail t
where t.historytypecode = 60';
EXECUTE IMMEDIATE sql_statment;
END LOOP;
END;
where brchcode is the name of different schemes
I can't see any output. what can I do?
Code you wrote can't work as you have to return the result into something; it is PL/SQL and requires an INTO clause. As you chose to return two values (columns) and multiple rows, that can't be a scalar variable; you could pick ref cursor or a collection, for example.
Here's an example which shows one option.
I'll be using two schemas: SCOTT (current schema) and HR. Both will be having the DEPT table.
As Scott already has it, I'll create one in HR schema and grant access to Scott (otherwise, Scott won't even see it and the procedure (i.e. the function) will fail):
SQL> connect hr/hr
Connected.
SQL> create table dept (deptno number, dname varchar2(10), loc varchar2(10));
Table created.
SQL> insert into dept values (55, 'IT', 'Zagreb');
1 row created.
SQL> grant select on dept to scott;
Grant succeeded.
SQL> commit;
Commit complete.
Back to Scott, to create a table (which contains schema names I'll be selecting from) and a function. I chose to return REF CURSOR; you could return something else, if you want.
SQL> connect scott/tiger
Connected.
SQL> create table brchs (brchcode varchar2(10));
Table created.
SQL> insert into brchs (brchcode)
2 select 'scott' from dual union all
3 select 'hr' from dual;
2 rows created.
SQL> create or replace function f_br
2 return sys_refcursor
3 is
4 l_str varchar2(4000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select brchcode from brchs) loop
8 l_str := l_str ||
9 'union all
10 select ' || chr(39)|| cur_r.brchcode ||chr(39) || ', d.dname
11 from ' || cur_r.brchcode ||'.dept d
12 where d.deptno > 0';
13 end loop;
14
15 l_str := ltrim(l_str, 'union all');
16
17 open l_rc for l_str;
18 return l_rc;
19 end;
20 /
Function created.
SQL>
Finally, testing:
SQL> select f_br from dual;
F_BR
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'SCOT DNAME
----- --------------
scott ACCOUNTING
scott RESEARCH
scott SALES
scott OPERATIONS
hr IT
SQL>

PLSQL : ORA-14551: cannot perform a DML operation inside a query

I went through some of the already answered questions, mentioned below, but could not find a solution that worked for me.
ORA-14551: cannot perform a DML operation inside a query
ORA-14551: cannot perform a DML operation inside a query error while using Execute immediate
I have written this within a function, that I am calling from a procedure.
OPEN c_rules_details;
LOOP
FETCH c_rules_details INTO rule_conditions_record;
EXIT WHEN c_rules_details %notfound;
dbms_output.put_line('Range and rule is '|| rule_conditions_record.rdr_tmplt_id || rule_conditions_record.rdr_rng_from ||rule_conditions_record.rdr_rng_to);
SELECT COUNT(*) INTO v_balance_records FROM t_scb_temp_objects WHERE RULE_CONDITION=rule_conditions_record.rdr_tmplt_id AND CHARGE is NULL;
dbms_output.put_line('Number of records in rule are '|| v_balance_records);
IF v_balance_records>rule_conditions_record.rdr_rng_from AND v_balance_records<rule_conditions_record.rdr_rng_to THEN
v_ssql_stmnt:='UPDATE t_scb_temp_objects
SET CHARGE='||rule_conditions_record.rfr_chrg_amt||' WHERE RULE_CONDITION='||rule_conditions_record.rdr_tmplt_id;
EXECUTE IMMEDIATE v_ssql_stmnt;
END IF;
END LOOP;
CLOSE c_rules_details;
That's what you've been told - you can't perform DML (in your case, UPDATE t_scb_temp_objects ...) within a function you're (somehow - you didn't show how) calling from a procedure. That might be - for example:
create or replace function f_test (par_deptno in number)
return number
is
...
begin
open c_rules_details;
loop
-- your current code goes here
execute immediate v_sql_stmnt;
end loop;
return 1;
end;
create or replace procedure p_test is
l_ename emp.ename%type;
begin
select e.ename
into l_ename
from emp e
where e.deptno = f_test(e.deptno); --> this will cause the error
end;
So, what to do? Perform UPDATE from within a procedure, not a function.
If it must be a function, make it an autonomous transaction (using PRAGMA), but that's something I wouldn't recommend. You'd rather figure something different out. Here's how, though:
SQL> create table test (col number);
Table created.
SQL> insert into test (col) values (100);
1 row created.
SQL> commit;
Commit complete.
First, a function that'll fail (just like yours):
SQL> create or replace function f_test return number is
2 begin
3 update test set col = 10;
4 commit;
5 return 1;
6 end;
7 /
Function created.
SQL> select f_test from dual;
select f_test from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.F_TEST", line 3
Now, the one which will succeed:
SQL> create or replace function f_test return number is
2 pragma autonomous_transaction; --> this
3 begin
4 update test set col = 10;
5 commit;
6 return 1;
7 end;
8 /
Function created.
SQL> select f_test from dual;
F_TEST
----------
1
SQL> select * from test;
COL
----------
10
SQL>
Once again: you probably don't want to do that.

How to grant trigger access to a table

I'm getting an error message for a trigger I've successfully created.
I've tried...
GRANT SELECT ON OTHER_TABLE TO [me];
call SYS.DBA_ASSIST.GRANT_OBJ_PERMS('dbo.MYTABLE','SELECT','dbo.OTHER_TABLE');`
Here is the code for the trigger...
CREATE OR REPLACE TRIGGER PREVENT_INVALID_ID
BEFORE INSERT OR UPDATE ON dbo.MYTABLE
FOR EACH ROW
DECLARE ROW_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO ROW_COUNT
FROM [OTHER_TABLE] WHERE OTHER_TABLE_ID = :new.MYTABLE_ID;
IF ROW_COUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20101, 'The ID provided is invalid.');
END IF;
END;`
The error message in user_errors says, "PL/SQL: ORA-00942: table or view does not exist"
Any idea how I can get the trigger to access OTHER_TABLE?
I understand that it is not possible to grant access/authorizations to triggers. But I'm unclear what code I need to run in order to allow the trigger to work.
Thanks in advance,
josh
If both MYTABLE and OTHER_TABLE belong to the same user, no privilege is required:
SQL> show user
USER is "SCOTT"
SQL> create table other_table (other_Table_id number);
Table created.
SQL> create table mytable (mytable_id number);
Table created.
SQL> create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13 end;
14 /
Trigger created.
SQL>
However, if they belong to different user, then OTHER_TABLE's owner has to grant SELECT privilege to me. Though, that's not enough - you have to either precede OTHER_TABLE with its owner name (e.g. other_user.other_table), or create a synonym in my own schema which points to other user's OTHER_TABLE. For example:
SQL> drop table other_table;
Table dropped.
SQL> connect mike/lion#xe
Connected.
SQL> create table other_table (other_Table_id number);
Table created.
SQL> grant select on other_table to scott;
Grant succeeded.
SQL> connect scott/tiger#xe
Connected.
SQL> create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from MIKE.other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13 end;
14 /
Trigger created.
SQL>
Note line 8: MIKE.other_table.
Just to show what happens if you omit/remove owner's name:
SQL> l8
8* from MIKE.other_table where other_table_id = :new.mytable_id;
SQL> c/mike.//
8* from other_table where other_table_id = :new.mytable_id;
SQL> l
1 create or replace trigger prevent_invalid_id
2 before insert or update on mytable
3 for each row
4 declare
5 row_count number;
6 begin
7 select count(*) into row_count
8 from other_table where other_table_id = :new.mytable_id;
9
10 if row_count = 0 then
11 raise_application_error(-20101, 'The ID provided is invalid');
12 end if;
13* end;
SQL> /
Warning: Trigger created with compilation errors.
SQL> show err
Errors for TRIGGER PREVENT_INVALID_ID:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/3 PL/SQL: SQL Statement ignored
5/8 PL/SQL: ORA-00942: table or view does not exist
SQL>
See? ORA-00942.
Figured it out: GRANT SELECT ON OTHER_TABLE TO dbo
What threw me off is the use the words 'table owner' when I was looking for instructions on how to do this. I thought I was the table owner. Rather, it's the schema (dbo) that needs the privileges.

Resources