2014-12-19 71 views
0

我想知道是否可以在触发器内创建用户。尽管我认为我的代码在语法上是正确的,但我收到了错误。我可以在触发器内创建用户吗?

下面是代码

CREATE OR REPLACE TRIGGER addUser 
BEFORE INSERT 
ON PLSQL_TEST_USERS 
FOR EACH ROW 
DECLARE 
    n VARCHAR2(20) := :new.name; 
    p VARCHAR2(20) := :new.password; 
BEGIN 
    dbms_output.put_line(n); 
    EXECUTE IMMEDIATE ('CREATE USER n IDENTIFIED BY p'); 
END; 
/

所以是有可能做到这一点?

下面是我收到的错误:

INSERT INTO PLSQL_TEST_USERS VALUES (1, 'rob', 'asdf')
Error report -
SQL Error: ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "SYSTEM.ADDUSER", line 6
ORA-04088: error during execution of trigger 'SYSTEM.ADDUSER'
04092. 00000 - "cannot %s in a trigger"
*Cause: A trigger attempted to commit or rollback.
*Action: Rewrite the trigger so it does not commit or rollback.

回答

4

不,你不能正确地做到这一点。

你不能在触发器内提交(通常我会讨论下面的异常)。像CREATE USER这样的DDL发出两个隐含的提交(一个在语句之前,另一个在语句之后),所以你不能把DDL放在触发器中。

如果您将触发器声明为使用自治事务,则会出现关于触发器内部提交的规则的例外情况。但由于几个原因,这并不能正确解决问题。首先,正如名称所言,自治事务是自治的,所以即使触发语句回滚,它也会被提交。这意味着如果INSERT语句成功插入一行但该更改已回滚,则自治事务将保持已提交状态,因此您最终会创建一个正在创建的用户,但plsql_test_users表中没有任何行。出于写入一致性的原因,Oracle可能还会在内部回滚并重新执行一条语句,这将导致自治事务被执行两次,其中第二次将因用户已存在而失败。自治事务处理实际上只应用于想要记录信息的情况,而无论基础变更是否成功(例如,记录尝试登录或密码更改,以便即使登录或密码更改不成功也不会更改数据的状态)。

您可以让您的触发器调用dbms_job.submit来提交后台作业,该后台作业将在语句提交后立即运行(如果它确实提交),这会实际创建用户。这将涉及添加到表的行和创建的用户之间的小延迟,但它至少在事务上是正确的(延迟可能会变得更大,这取决于您试图创建多少用户以及多少背景你允许的工作)。不幸的是,您必须使用较旧的dbms_job程序包而不是较新的dbms_scheduler程序包来提交作业,因为较新的程序包包含隐式提交。

如果您确实需要执行后台作业路径,请注意传递给EXECUTE IMMEDIATE的语句无法引用局部变量中的值 - 当执行动态SQL语句时,这些变量不在范围内。当你写

EXECUTE IMMEDIATE 'CREATE USER n IDENTIFIED BY p'; 

np不是指你定义的局部变量,它们是文字标识。这将创建一个带有单字符密码“p”的用户“n”。假设你想使用表中的用户名和密码,你需要在组装动态SQL语句时使用它们。像

EXECUTE IMMEDIATE 
    'CREATE USER ' || :new.name || 
    ' IDENTIFIED BY ' || :new.password; 

而这一切的东西提出为什么你试图将信息存储在两个地方的问题。你真的想要拥有一张表plsql_test_users以及一堆用户名为&的密码,这似乎也不太可能。您可能希望Oracle处理身份验证,或者希望让应用程序处理身份验证,但您不希望每个人都认为它正在处理身份验证(例如,当密码在一个密码中更改而另一个密码未更改时会发生什么情况)。如果你的应用程序要处理认证,你永远不会存储密码(或加密的密码)。你可以用盐(在RAW列中)存储密码的散列。然后,当用户在登录时提供密码并比较哈希值时,计算并验证哈希值。

相关问题