簡體   English   中英

在PostgreSQL 10中,為什么對於可延遲約束,pgplsql異常處理未捕獲到外鍵沖突錯誤?

[英]In PostgreSQL 10, why is a foreign key violation error not caught by pgplsql exception handling for deferrable constraints?

當從具有EXCEPTION WHEN OTHERS塊的P​​GPLSQL函數調用執行插入的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM