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.
Related
We have a requirement to disable a trigger only for a specific procedure run in ORACLE.
We have Table A and Table B.
The trigger is designed to insert into Table B for each insert or update of Table A.
The procedure makes entries to Table A.
How to enable/disable trigger in procedure?
The above link explains how to enable and disable the trigger during the procedure.
But my requirement is during this procedure run, if there are other inserts to Table A, then the trigger should run as expected.
Is this possible? Please help on how to achieve the same if possible.
You can use the DBMS_APPLICATION_INFO and SYS_CONTEXT to set any parameter in your procedure and then check for that parameter in the TRIGGER to see if you should take any action in the trigger or not as follows:
Table description:
SQL> DESC AA;
Name Null? Type
----------------------------------------- -------- ----------------------------
COL1 NOT NULL NUMBER
SQL>
Trigger code:
SQL> CREATE OR REPLACE TRIGGER AA_TRG BEFORE
2 INSERT OR UPDATE ON AA
3 FOR EACH ROW
4 BEGIN
5 IF SYS_CONTEXT(
6 'USERENV',
7 'ACTION'
8 ) = 'DISABLE_TRIGGER' THEN
9 DBMS_OUTPUT.PUT_LINE('TRIGGER DISABLED');
10 ELSE
11 DBMS_OUTPUT.PUT_LINE('TRIGGER ENABLED');
12 END IF;
13
14 DBMS_APPLICATION_INFO.SET_ACTION(ACTION_NAME => NULL);
15 END;
16 /
Trigger created.
SQL>
Disabled trigger procedure code:
SQL> CREATE OR REPLACE PROCEDURE AA_DISABLED_TRIGGER_PROC AS
2 BEGIN
3 DBMS_APPLICATION_INFO.SET_ACTION(ACTION_NAME => 'DISABLE_TRIGGER');
4 INSERT INTO AA VALUES ( 3 );
5
6 ROLLBACK;
7 END;
8 /
Procedure created.
SQL>
Enabled trigger procedure code:
SQL> CREATE OR REPLACE PROCEDURE AA_ENABLED_TRIGGER_PROC AS
2 BEGIN
3 INSERT INTO AA VALUES ( 3 );
4
5 ROLLBACK;
6 END;
7 /
Procedure created.
SQL>
Calling the procedures:
SQL>
SQL> SET SERVEROUT ON
SQL> BEGIN
2 AA_ENABLED_TRIGGER_PROC;
3 END;
4 /
TRIGGER ENABLED
PL/SQL procedure successfully completed.
SQL>
SQL> SET SERVEROUT ON
SQL> BEGIN
2 AA_DISABLED_TRIGGER_PROC;
3 END;
4 /
TRIGGER DISABLED
PL/SQL procedure successfully completed.
SQL>
SQL> SET SERVEROUT ON
SQL> BEGIN
2 AA_ENABLED_TRIGGER_PROC;
3 END;
4 /
TRIGGER ENABLED
PL/SQL procedure successfully completed.
SQL>
Not possible. If trigger is disabled, then it is disabled and won't work.
But, if you alter the table and add a column which says who is inserting rows into it, then you could use trigger's WHEN clause to distinguish this procedure from other inserters and either fire the trigger or not. It means, of course, that trigger remains enabled all the time.
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.
CREATE TABLE product
( product_id number(10) NOT NULL,
product_name varchar2(50) NOT NULL,
price varchar2(50)
);
CREATE OR REPLACE PROCEDURE p1 (product_id in number,
product_name in varchar2,
price in number)
IS
BEGIN
INSERT INTO product values(1,'yash',100);
DBMS_OUTPUT.PUT_LINE('VALUE INSERTED');
end;
Error: ORA-00955: name is already used by an existing object
Can someone please provide me and suitable example
From the error message, the name p1 is already in use (as an object name whose type is different than a procedure). Just give the procedure a different (and more descriptive!) name:
CREATE OR REPLACE PROCEDURE insert_product
(product_id in number,
product_name in varchar2,
price in number)
IS
BEGIN
INSERT INTO product values(1,'yash',100);
DBMS_OUTPUT.PUT_LINE('VALUE INSERTED');
END;
For your this problem the solution is write the following line (EXECUTE p1;)
By typing this command you will get your desired output
You have twice asked for a 'suitable example' or 'justifying code'. I assume from that you don't understand or don't believe what has been explained about your procedure name conflicting with some other object name. So here's the example that proves it.
SQL> --
SQL> -- check that we don't own anything called MYTEST
SQL> --
SQL> select object_name
2 from user_objects
3 where upper(object_name) = 'MYTEST'
4 ;
no rows selected
SQL> --
SQL> -- create a table MYTEST
SQL> --
SQL> create table mytest (dob date);
Table created.
SQL> --
SQL> -- check objets
SQL> --
SQL> select object_type,
2 object_name
3 from user_objects
4 where upper(object_name) = 'MYTEST'
5 ;
OBJECT_TYPE OBJECT_NAME
------------------- --------------------
TABLE MYTEST
1 row selected.
SQL> --
SQL> -- create a procedure MYTEST
SQL> --
SQL> create or replace procedure mytest
2 as
3 begin
4 dbms_output.put_line('Hello world');
5 end;
6 /
create or replace procedure mytest
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> --
SQL> -- now, drop the table and try the procedure again
SQL> --
SQL> drop table mytest purge;
Table dropped.
SQL> create or replace procedure mytest
2 as
3 begin
4 dbms_output.put_line('Hello world');
5 end;
6 /
Procedure created.
SQL> --
SQL> -- cleanup
SQL> --
SQL> drop table mytest purge;
drop table mytest purge
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> drop procedure mytest;
Procedure dropped.
In your case if your procedure is named 'p1' then check:
select object_type,
object_name
from user_objects
where upper(object_name) = 'P1'
;
I have a simple question: Is it possible to run UTL_RECOMP.RECOMP_PARALLEL from a procedure?
I have a Package with a procedure which should recompile all invalid objects. It looks like this:
PROCEDURE Compile ()
IS
BEGIN
EXECUTE IMMEDIATE ('BEGIN SYS.UTL_RECOMP.RECOMP_PARALLEL(4,); END;');
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
However, I always get the Error PLS-00201: identifier 'UTL_RECOMP.RECOMP_PARALLEL' must be declared
I am logged in as sys/sysdba user. That's not the problem.
Any ideas how to get this working?
Thanks!
Actually it works if the procedure is owned by SYS and you grant EXECUTE privilege to another user (doc says "You must be connected AS SYSDBA to run this script").
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SQL> show user;
USER is "SYS"
SQL> --
SQL> CREATE OR REPLACE PROCEDURE Compile
2 IS
3 BEGIN
4 SYS.UTL_RECOMP.RECOMP_PARALLEL(4);
5 END;
6 /
Procedure created.
SQL> --
SQL> grant execute on compile to c##test;
Grant succeeded.
SQL> --
SQL> connect c##test/c##test
Connected.
SQL> show user
USER is "C##TEST"
SQL> --
SQL> drop table t purge;
Table dropped.
SQL> create table t(x int);
Table created.
SQL> create or replace procedure p
2 is
3 v int;
4 begin
5 select x into v from t;
6 end;
7 /
Procedure created.
SQL> --
SQL> show errors
No errors.
SQL> --
SQL> drop table t;
Table dropped.
SQL> --
SQL> select object_name, object_type, status
2 from user_objects
3 where object_name='P';
OBJECT_NAM OBJECT_TYP STATUS
---------- ---------- ----------
P PROCEDURE INVALID
SQL> --
SQL> create table t(x int);
Table created.
SQL> --
SQL> select object_name, object_type, status
2 from user_objects
3 where object_name='P';
OBJECT_NAM OBJECT_TYP STATUS
---------- ---------- ----------
P PROCEDURE INVALID
SQL> --
SQL> exec sys.compile;
PL/SQL procedure successfully completed.
SQL> --
SQL> select object_name, object_type, status
2 from user_objects
3 where object_name='P';
OBJECT_NAM OBJECT_TYP STATUS
---------- ---------- ----------
P PROCEDURE VALID
SQL> --
These kind of procedures should be run only by SYS (like utlrp.sql) - so this is for DBA only - as documented to avoid unexpected behaviour.
You can use it in a stored procedure:
SQL> set serveroutput on
SQL> --
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SQL> show user;
USER is "SYS"
SQL> --
SQL> CREATE OR REPLACE PROCEDURE Compile
2 IS
3 BEGIN
4 UTL_RECOMP.RECOMP_PARALLEL(4);
5 EXCEPTION
6 WHEN OTHERS
7 THEN
8 DBMS_OUTPUT.PUT_LINE(SQLERRM);
9 END;
10 /
Procedure created.
SQL> --
SQL> show errors
No errors.
SQL> --
SQL> set timing on
SQL> exec compile;
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.31
SQL> --
SQL> exit
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.