Fix check_exclusion_or_unique_constraint for UNIQUE NULLS NOT DISTINCT.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Aug 2022 18:16:26 +0000 (14:16 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Aug 2022 18:16:26 +0000 (14:16 -0400)
Adjusting this function was overlooked in commit 94aa7cc5f.  The only
visible symptom (so far) is that INSERT ... ON CONFLICT could go into
an endless loop when inserting a null that has a conflict.

Richard Guo and Tom Lane, per bug #17558 from Andrew Kesper

Discussion: https://postgr.es/m/17558-3f6599ffcf52fd4a@postgresql.org

src/backend/executor/execIndexing.c
src/test/regress/expected/constraints.out
src/test/regress/sql/constraints.sql

index 0cb0b8f1111060900c263a136f9ea30b38439081..6a8735edf7fcfead56bc0ad1c158c0c3ee2663f5 100644 (file)
@@ -699,13 +699,19 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
    }
 
    /*
-    * If any of the input values are NULL, the constraint check is assumed to
-    * pass (i.e., we assume the operators are strict).
+    * If any of the input values are NULL, and the index uses the default
+    * nulls-are-distinct mode, the constraint check is assumed to pass (i.e.,
+    * we assume the operators are strict).  Otherwise, we interpret the
+    * constraint as specifying IS NULL for each column whose input value is
+    * NULL.
     */
-   for (i = 0; i < indnkeyatts; i++)
+   if (!indexInfo->ii_NullsNotDistinct)
    {
-       if (isnull[i])
-           return true;
+       for (i = 0; i < indnkeyatts; i++)
+       {
+           if (isnull[i])
+               return true;
+       }
    }
 
    /*
@@ -717,7 +723,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
    for (i = 0; i < indnkeyatts; i++)
    {
        ScanKeyEntryInitialize(&scankeys[i],
-                              0,
+                              isnull[i] ? SK_ISNULL | SK_SEARCHNULL : 0,
                               i + 1,
                               constr_strats[i],
                               InvalidOid,
index 36ccbb5f155742906ba49b6f50a45fb611d4de3f..05b7244e4a7fd1cedc18d73be05a758523cfa5dd 100644 (file)
@@ -449,15 +449,16 @@ DROP TABLE UNIQUE_TBL;
 CREATE TABLE UNIQUE_TBL (i int UNIQUE NULLS NOT DISTINCT, t text);
 INSERT INTO UNIQUE_TBL VALUES (1, 'one');
 INSERT INTO UNIQUE_TBL VALUES (2, 'two');
-INSERT INTO UNIQUE_TBL VALUES (1, 'three');
+INSERT INTO UNIQUE_TBL VALUES (1, 'three');  -- fail
 ERROR:  duplicate key value violates unique constraint "unique_tbl_i_key"
 DETAIL:  Key (i)=(1) already exists.
 INSERT INTO UNIQUE_TBL VALUES (4, 'four');
 INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
-INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
+INSERT INTO UNIQUE_TBL (t) VALUES ('seven');  -- fail
 ERROR:  duplicate key value violates unique constraint "unique_tbl_i_key"
 DETAIL:  Key (i)=(null) already exists.
+INSERT INTO UNIQUE_TBL (t) VALUES ('eight') ON CONFLICT DO NOTHING;  -- no-op
 SELECT * FROM UNIQUE_TBL;
  i |  t   
 ---+------
index 34de0c969ae136da035545b13df86c96cf5491d0..833819a32e29b1fca1fcb9ff7472dea0af4b3fc9 100644 (file)
@@ -310,11 +310,12 @@ CREATE TABLE UNIQUE_TBL (i int UNIQUE NULLS NOT DISTINCT, t text);
 
 INSERT INTO UNIQUE_TBL VALUES (1, 'one');
 INSERT INTO UNIQUE_TBL VALUES (2, 'two');
-INSERT INTO UNIQUE_TBL VALUES (1, 'three');
+INSERT INTO UNIQUE_TBL VALUES (1, 'three');  -- fail
 INSERT INTO UNIQUE_TBL VALUES (4, 'four');
 INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
-INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
+INSERT INTO UNIQUE_TBL (t) VALUES ('seven');  -- fail
+INSERT INTO UNIQUE_TBL (t) VALUES ('eight') ON CONFLICT DO NOTHING;  -- no-op
 
 SELECT * FROM UNIQUE_TBL;