2014-12-19 74 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列中)存儲密碼的散列。然後,當用戶在登錄時提供密碼並比較哈希值時,計算並驗證哈希值。

相關問題