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;