我可以在触发器内创建用户吗?

时间:2014-12-19 02:54:45

标签: sql oracle plsql triggers

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

这是代码

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;
/

那么可以这样做吗?

以下是我遇到的错误:

  

插入PLSQL_TEST_USERS VALUES(1,' rob',' asdf')
  错误报告 -
  SQL错误:ORA-04092:无法在触发器中执行
  ORA-06512:at" SYSTEM.ADDUSER",第6行   ORA-04088:执行触发器时出错&SYSTEM; ADDUSER'
  04092. 00000 - "触发器中不能%s"
  *原因:触发器尝试提交或回滚   *操作:重写触发器,使其不提交或回滚。

1 个答案:

答案 0 :(得分: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并未引用您已定义的局部变量,它们是文字标识符。这创造了一个用户" n"使用单字符密码" p"。假设您要使用表中的用户名和密码,则在组装动态SQL语句时需要使用它们。像

这样的东西
EXECUTE IMMEDIATE 
  'CREATE USER ' || :new.name ||
  ' IDENTIFIED BY ' || :new.password;

这一切都引出了为什么你要在两个地方存储信息的问题。你真的想要一个包含一堆用户名的表plsql_test_users似乎不太可能。密码也是Oracle用户。您要么希望Oracle处理身份验证,要么让您的应用程序处理身份验证,您不希望每个人都认为它正在处理身份验证(例如,当密码在一个而不是另一个中更改时会发生什么)。如果您的应用程序要处理身份验证,您永远不会存储密码(或加密密码)。您可以使用salt(在RAW列中)存储密码的哈希值。然后,当用户在登录时提供密码并比较哈希值时,您可以计算并验证哈希值。