Reputation: 2631
I have a database named Tamaris, which contains a table User. I created a trigger to create a new user in database each time a row is inserted in my User table. Here's the PL/SQL code :
CREATE OR REPLACE
TRIGGER UTILISATEUR_CREATE_USER_TRG
AFTER INSERT ON UTILISATEUR
FOR EACH ROW
DECLARE
nom_compte NVARCHAR2(20 CHAR);
str_create VARCHAR2(300);
str_grant VARCHAR(250);
type_compte NUMBER;
unauthorized_exception EXCEPTION;
BEGIN
CASE
WHEN :new.idtypecompte = 1 THEN
nom_compte := :new.pseudoutilisateur;
type_compte := 1;
WHEN :new.idtypecompte = 2 THEN
nom_compte := 'AC_'|| :new.pseudoutilisateur;
type_compte := 2;
WHEN :new.idtypecompte = 3 THEN
RAISE unauthorized_exception;
END CASE;
str_create := 'CREATE USER '|| nom_compte ||' IDENTIFIED BY '|| :new.passwordutilisateur ||' DEFAULT TABLESPACE tamaris TEMPORARY TABLESPACE temp QUOTA UNLIMITED ON tamaris;' ;
EXECUTE IMMEDIATE str_create;
IF type_compte = 1 THEN
str_grant := 'GRANT Base_User TO '|| nom_compte ||';' ;
EXECUTE IMMEDIATE str_grant;
ELSE
str_grant := 'GRANT Adv_User TO '|| nom_compte ||';' ;
EXECUTE IMMEDIATE str_grant;
END IF;
EXCEPTION
WHEN unauthorized_exception THEN
dbms_output.put_line('Impossible de créer un autre gestionnaire');
END;
When I insert a row in table User, the trigger fires and I get this :
Error during saving of modifications on "TAMARIS"."UTILISATEUR" : Line 3 : ORA-00911: Invalid character ORA-06512: at "TAMARIS.UTILISATEUR_CREATE_USER_TRG", Line 22 ORA-04088: Error in the execution of 'TAMARIS.UTILISATEUR_CREATE_USER_TRG' ORA-06512: at Line 1
For the record, the request in str_create is working outside the trigger with random parameter (only if wrapped with BEGIN; END;). Therefore I tried :
str_create := 'BEGIN CREATE USER '|| nom_compte ||' IDENTIFIED BY '|| :new.passwordutilisateur ||' DEFAULT TABLESPACE tamaris TEMPORARY TABLESPACE temp QUOTA UNLIMITED ON tamaris; END;' ;
Still not working. I would appreciate any inputs on this, thanks.
EDIT :
Content of my procedure as suggested:
CREATE OR REPLACE
PROCEDURE CREATE_USER_IN_DB(p_username IN NVARCHAR2, p_password IN UTILISATEUR.passwordutilisateur%type, p_type IN NUMBER ) AS
BEGIN
EXECUTE IMMEDIATE 'CREATE USER '|| p_username ||' IDENTIFIED BY '|| p_password ||' DEFAULT TABLESPACE tamaris
TEMPORARY TABLESPACE temp QUOTA UNLIMITED ON tamaris';
IF p_type = 1 THEN
EXECUTE IMMEDIATE 'GRANT Base_User TO '|| p_username;
ELSE
EXECUTE IMMEDIATE 'GRANT Adv_User TO '|| p_username;
END IF;
END CREATE_USER_IN_DB;
EDIT2 :
How I call the procedure outside of trigger :
BEGIN
CREATE_USER_IN_DB('whatever','quickpass', 2);
END;
I get
ORA-00900:
Invalid SQL instruction
ORA-06512: at "TAMARIS.CREATE_USER_IN_DB", line 3
ORA-06512: at line 2
00900. 00000 - "invalid SQL statement"
*Cause:
*Action:
Upvotes: 2
Views: 2697
Reputation: 231861
1) As @Bob Jarvis suggested, when you build a SQL statement that you intend to pass to EXECUTE IMMEDIATE
, that SQL statement should not contain a trailing semicolon ;
2) Since CREATE USER
and GRANT
are DDL statements, they issue implicit commits before and after they are executed. That means that you cannot call DDL statements in a trigger since a trigger cannot cause a transaction to be ended. If you really want to do this (and creating users as a side effect of inserting a row in a table seems like a very problematic architecture), you would have to do it asynchronously. Your trigger can call DBMS_JOB
to schedule a job to run after your current transaction completes and that job can execute DDL statements. For example, if you create a procedure that actually creates the user (this is where all your DDL would go)
CREATE PROCEDURE create_user( p_username IN NVARCHAR2,
p_password IN UTILISATEUR.passwordutilisateur%type,
p_type IN NUMBER )
AS
<<implement procedure>>
then your trigger could do something like
CREATE OR REPLACE
TRIGGER UTILISATEUR_CREATE_USER_TRG
AFTER INSERT ON UTILISATEUR
FOR EACH ROW
DECLARE
nom_compte NVARCHAR2(20 CHAR);
str_create VARCHAR2(300);
str_grant VARCHAR(250);
type_compte NUMBER;
l_jobno PLS_INTEGER;
unauthorized_exception EXCEPTION;
BEGIN
CASE
WHEN :new.idtypecompte = 1 THEN
nom_compte := :new.pseudoutilisateur;
type_compte := 1;
WHEN :new.idtypecompte = 2 THEN
nom_compte := 'AC_'|| :new.pseudoutilisateur;
type_compte := 2;
WHEN :new.idtypecompte = 3 THEN
RAISE unauthorized_exception;
END CASE;
dbms_job.submit( l_jobno,
'BEGIN create_user( ''' || nom_compte || ''', ' ||
'''' || :new.passwordutilisateur || ''', ' ||
type_compte || '); END;' );
END;
Upvotes: 2