[英]enforcing database constraints: code vs sql
這是此問題的后續措施。
這是我的圖式
CREATE TABLE A(
id serial NOT NULL,
date timestamp without time zone,
type text,
sub_type text,
filename text,
filepath text,
filesize integer,
lock_status int
);
在此數據庫中,只要未設置“ lock_status”,用戶就可以更新類型,子類型,文件名,文件路徑,文件大小。
因此,在網頁代碼(php)中,我可以在更新項目之前檢查lock_status。
但是,可能存在另一位用戶在第一位用戶檢查和更新之間更新鎖定狀態的情況。
因此,在行更新之前,SQL中是否有辦法檢查鎖定狀態?
編輯添加的類型,子類型到上面的可編輯字段列表
當然,請使用UPDATE ... WHERE lock_status = 0
。 另外,您可以嘗試使用存儲過程 。
正如馬鈴薯先生所說,最干凈的方法是只使用WHERE子句只影響lock_status = 0的行。因為那是一條SQL語句,所以保證它是原子的。 然后,您可以查看是否有任何行受到影響(例如,使用@@ rowcount)並做出相應的反應,方法是無限期地重試或顯示錯誤消息等。
分兩步進行檢查和更新的問題在於,除非將它們包裝在顯式事務中(例如,“ BEGIN TRANSACTION ... COMMIT TRANSACTION”),否則不能保證它們是原子的,因此從理論上講,您可以得到多個進程認為鎖已關閉並繼續。 我說“理論上”是因為隨着這些語句的執行速度加快,除非您有一個巨大的並發環境,同時有成千上萬的用戶同時敲打這個東西,否則不可能發生這種情況。 這就是為什么這樣的錯誤通常會被忽略,但隨后又出現奇怪的無法解釋的錯誤的原因。
要了解有關此類問題的更多信息,您可能需要閱讀一本關於並發編程的書,例如《 Gregory Andrews的並發編程》 。
您可能需要研究行級鎖定。 看起來Postgres擁有了。
我建議同時檢查並設置lock_status
位。
發出一個UPDATE A SET lock_status = 1 WHERE id = ... AND lock_status = 0
。 該查詢是原子的,不需要顯式事務。 如果沒有返回更新的1個對象,則無法應用您的鎖。 然后,您只需要確認您的主鍵仍然存在。 如果要從多個位置和/或在多個表上調用它,則可能要考慮將其移動到存儲過程。
偽PHP:
$result = pg_query_params($conn, "UPDATE A SET lock_status = 1 WHERE id = $1 AND lock_status = 0", $id);
$tuples = pg_affected_rows($result);
if ($tuples < 1) {
// couldn't lock
} else {
// lock applied
}
在數據庫端只有一個示例過程,帶有觸發器和PL / Pgsql函數:
CREATE OR REPLACE FUNCTION trgfn_ensure_unlocked() RETURNS TRIGGER AS $trig$
DECLARE
BEGIN
IF (OLD.lock_status <> 0) THEN
RAISE EXCEPTION 'Row is locked';
END IF;
RETURN NEW;
END;
$trig$ LANGUAGE plpgsql;
CREATE TRIGGER trg_check_unlocked BEFORE UPDATE ON table_name
FOR EACH ROW EXECUTE PROCEDURE trgfn_ensure_unlocked();
基本上,您將使用一個plpgsql函數,該函數將檢查該行的舊版本(更新之前)中的lock_status為0。否則,該異常將引發異常。
此函數由觸發器調用,在對該表執行SQL UPDATE時將自動調用該觸發器。
希望這可以幫助 ...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.