How to grant trigger access to a table - oracle

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.

Related

Update second column

I have a table "TEST_TABLE" with two columns TABLE_NAME and RECORD_COUNT.
enter image description here
We want to update the column RECORD_COUNT by taking the total records in table specified in TABLE_NAME.
You could do this with dynamic SQL but why be complicated?
Create a view
Create view my_tables as
Select
'table_1' as "table name",
count(*) as "rows"
From table_1
Add the following for each table
Union all
Select 'table_2',count(*) From table_2
You can then use the view like a table:
Select * from my_tables;
OK, but - why wouldn't you use information Oracle already provides for you? You should regularly gather statistics anyway, so:
SQL> execute dbms_stats.gather_schema_stats(ownname => 'SCOTT', estimate_percent => null);
PL/SQL procedure successfully completed.
and then fetch num_rows from user_tables:
SQL> select table_name, num_rows from user_tables where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
It can't be much worse than your attempt (due to possible frequent changes to tables' contents; inserts and deletes) because you'd collect your own data periodically (how often?) anyway.
If it has to be your way, then you'd use dynamic SQL, looping through all tables in your schema and actually count number of rows:
SQL> create table test_table
2 (table_name varchar2(30),
3 num_rows number);
Table created.
SQL> create or replace procedure p_test as
2 l_cnt number;
3 begin
4 execute immediate 'truncate table test_table';
5 for cur_R in (select table_name from user_tables) loop
6 execute immediate 'select count(*) from ' ||cur_R.table_name into l_Cnt;
7 insert into test_table (table_name, num_rows) values (cur_r.table_name, l_cnt);
8 end loop;
9 end;
10 /
Procedure created.
Running the procedure and viewing the result:
SQL> exec p_test;
PL/SQL procedure successfully completed.
SQL> select * From test_Table where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
Note that performance will suffer as number of tables and number of rows stored in them grows.
If I were you, I'd go for the first option.

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.

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>

How can I change before update trigger to before insert trigger?

I have a table and this table must have at most 1000 row. When user wants to add 1001st row, there must be an error. I know i should use insert trigger. I'm a new person in SQL can you help me?
My code is:
create or replace trigger update_ext_app_serv
before update on EXTERNAL_APP_SERVICE
for each row
declare
row_count number;
old_service_id number;
new_service_trigger varchar(30);
old_service_trigger varchar(30);
begin
row_count := 0;
new_service_trigger := :NEW.TRIGGER_NAME;
old_service_trigger := :OLD.TRIGGER_NAME;
old_service_id := :OLD.SERVICE_ID;
select count(*) into row_count
from EXTERNAL_APP_PROFILE
where ORIG_ID = old_service_id
or TERM_FAILURE_RESP_ID = old_service_id
or TERM_ID = old_service_id;
if (row_count > 0) and (new_service_trigger not like old_service_trigger)
then
raise_application_error(-20706, 'Unable to update Trigger. The service is referenced at least External Application Profile.');
end if;
end;
The way to do this is with an AFTER trigger, and at the statement rather than row level.
SQL> create or replace trigger t1000_trg
2 after insert on t1000
3 declare
4 n pls_integer;
5 begin
6 select count(*) into n
7 from t1000;
8 if n > 1000 then
9 raise_application_error(-20999, 'No more than 1000 records!');
10 end if;
11 end;
12 /
Trigger created.
SQL>
Here's our test data.
SQL> select count(*) from t1000;
COUNT(*)
----------
999
SQL>
1000th record okay...
SQL> insert into t1000 values (1000);
1 row created.
SQL>
... but 1001st record is rejected:
SQL> insert into t1000 values (1001);
insert into t1000 values (1001)
*
ERROR at line 1:
ORA-20999: No more than 1000 records!
ORA-06512: at "FOX.T1000_TRG", line 7
ORA-04088: error during execution of trigger 'FOX.T1000_TRG'
SQL>
Works for multi-row insert statements too.
SQL> roll
Rollback complete.
SQL> select count(*) from t1000;
COUNT(*)
----------
999
SQL> insert into t1000 select 1000 + level from dual connect by level <= 5;
insert into t1000 select 1000 + level from dual connect by level <= 5
*
ERROR at line 1:
ORA-20999: No more than 1000 records!
ORA-06512: at "FOX.T1000_TRG", line 7
ORA-04088: error during execution of trigger 'FOX.T1000_TRG'
SQL>

Execute select/insert statement within an IF clause Oracle

I need to execute some statements within the IF clause only if a table exists.
But the issue I am facing is, even when the condition is false, the statements are getting executed.
DECLARE
count_matching_row NUMBER := 0;
count_matching_tbl NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO count_matching_tbl
FROM user_tables
WHERE LOWER(table_name) = 'tab1';
IF(count_matching_tbl = 1)
THEN
SELECT COUNT (*)
INTO count_matching_row
FROM test1
WHERE ID IN (SELECT ID FROM tab1);
IF(count_matching_row = 0)
THEN
INSERT INTO review_case
SELECT
DISTINCT ID, d,e
FROM tab1
WHERE ID IS NOT NULL;
INSERT INTO review_case_payer
SELECT
a,b,c
FROM tab1
WHERE a IS NOT NULL;
COMMIT;
END IF;
END IF;
END;
/
Whenever I execute these statements, if the table 'tab1' exists it works fine.
If the table tab1 does not exist I get the error
"ORA-06550: line 13, column 14:
PL/SQL: ORA-00942: table or view does not exist"
I get similar errors for each line where I try to access table "tab1"
I tried with ref cursor but still the same, I cannot use it for insert statements.
Your error is due to the fact that you're using a table that may not exist; this error is thrown because the script has compile problems, not data problems, so the way you try to use the IF is not enough to handle your situation.
You need to use some dynamic SQL to handle an object that could not exist; for example, see the following.
If the table does not exist, nothing will be done:
SQL> select * from tab1;
select * from tab1
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
If the table exists, the insert will be done:
SQL> create table tab1(a number, b number);
Table created.
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
SQL> select * from tab1;
A B
---------- ----------
1 2
SQL>

Resources