[英]In PostgreSQL 10, why is a foreign key violation error not caught by pgplsql exception handling for deferrable constraints?
當從具有EXCEPTION WHEN OTHERS塊的PGPLSQL函數調用執行插入的SQL函數時,如果違反的外鍵約束是可延遲的,則會引發異常而不是捕獲異常。
我正在使用與Amazon Aurora PostgreSQL兼容(v 10.4)。 我發現我的異常處理程序並不總是捕獲異常,而是將異常引發給應用程序(在我的情況下是使用Pyscopg2的AWS Lambda Python函數)。
進行了大量的故障排除以將其縮小到可延遲的約束范圍,因此我創建了一個測試函數,可以非常可靠地重現該問題。
我還在運行10.5版的RDS(非Aurora)實例和9.6.6 RDS實例上重現了相同的行為,因此它不是Aurora,也不特定於10.4版。
這是錯誤嗎? 還是我遺漏了一些帶有可延期約束的文檔?
這是兩個表和兩個函數。
CREATE TABLE public.load (
load_id BIGINT NOT NULL,
created_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
ds_code TEXT NOT NULL,
route TEXT,
file_name TEXT,
user_name TEXT,
staged BOOLEAN,
staging_duration INTERVAL,
CONSTRAINT load_pkey
PRIMARY KEY (load_id));
CREATE TABLE load_content (
load_id BIGINT NOT NULL,
load_content TEXT,
CONSTRAINT data_load_pk
PRIMARY KEY (load_id),
CONSTRAINT data_load_load_load_id_fk
FOREIGN KEY (load_id) REFERENCES public.load
)
;
CREATE FUNCTION insert_something() RETURNS void
LANGUAGE SQL
AS
$$
INSERT INTO public.load_content values (1);
$$
;
CREATE FUNCTION test_load() RETURNS TEXT
LANGUAGE plpgsql
AS
$$
BEGIN
PERFORM public.insert_something();
RETURN 'success';
EXCEPTION
WHEN OTHERS THEN
RETURN 'failure';
END
;
$$
;
執行public.test_load()返回單行“失敗”。
如果您這樣做,請執行以下操作:
alter table public.load_content
drop constraint data_load_load_load_id_fk;
ALTER TABLE public.load_content
ADD CONSTRAINT data_load_load_load_id_fk
FOREIGN KEY (load_id) REFERENCES public.load
DEFERRABLE INITIALLY DEFERRED
;
然后執行public.test_load()會發生異常:
[2019-01-14 16:36:32] [23503] ERROR: insert or update on table "load_content" violates foreign key constraint "data_load_load_load_id_fk"
[2019-01-14 16:36:32] Detail: Key (load_id)=(1) is not present in table "load".
顯然,出於測試目的,我已經進行了相當大的簡化-系統中實際發生的是“加載”表上的觸發器,該觸發器觸發以將原始數據(通常為JSON)標准化為暫存數據模型。
將DEFERRED
約束檢查推遲到提交, IMMEDIATE
執行IMMEDIATE
約束檢查,顧名思義。 可以使用SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }
DEFERRABLE
約束的行為在當前事務中進行更改SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }
SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }
SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }
(鏈接到文檔) 。
如果要更改test_load()
函數的行為,請test_load()
以下代碼示例。
CREATE FUNCTION test_load() RETURNS TEXT
LANGUAGE plpgsql
AS
$$
BEGIN
SET CONSTRAINTS data_load_load_load_id_fk IMMEDIATE;
PERFORM public.insert_something();
RETURN 'success';
EXCEPTION
WHEN OTHERS THEN
RETURN 'failure';
END
;
$$
;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.