我想知道我是否可以在触发器内创建用户。我收到错误,虽然我认为我的代码在语法上是正确的。
这是代码
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"
*原因:触发器尝试提交或回滚 *操作:重写触发器,使其不提交或回滚。
答案 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';
n
和p
并未引用您已定义的局部变量,它们是文字标识符。这创造了一个用户" n"使用单字符密码" p"。假设您要使用表中的用户名和密码,则在组装动态SQL语句时需要使用它们。像
EXECUTE IMMEDIATE
'CREATE USER ' || :new.name ||
' IDENTIFIED BY ' || :new.password;
这一切都引出了为什么你要在两个地方存储信息的问题。你真的想要一个包含一堆用户名的表plsql_test_users
似乎不太可能。密码也是Oracle用户。您要么希望Oracle处理身份验证,要么让您的应用程序处理身份验证,您不希望每个人都认为它正在处理身份验证(例如,当密码在一个而不是另一个中更改时会发生什么)。如果您的应用程序要处理身份验证,您永远不会存储密码(或加密密码)。您可以使用salt(在RAW列中)存储密码的哈希值。然后,当用户在登录时提供密码并比较哈希值时,您可以计算并验证哈希值。