Fix PG 17 [NOT] NULL optimization bug for domains
authorBruce Momjian <[email protected]>
Tue, 8 Apr 2025 01:33:42 +0000 (21:33 -0400)
committerBruce Momjian <[email protected]>
Tue, 8 Apr 2025 01:33:42 +0000 (21:33 -0400)
A PG 17 optimization allowed columns with NOT NULL constraints to skip
table scans for IS NULL queries, and to skip IS NOT NULL checks for IS
NOT NULL queries.  This didn't work for domain types, since domain types
don't follow the IS NULL/IS NOT NULL constraint logic.  To fix, disable
this optimization for domains for PG 17+.

Reported-by: Jan Behrens
Diagnosed-by: Tom Lane
Discussion: https://postgr.es/m/[email protected]

Backpatch-through: 17

doc/src/sgml/ref/create_domain.sgml
src/backend/optimizer/plan/initsplan.c

index ce55520348620b0cb2292e785873a4168f627c30..c111285a69c6d0a6e29d7863782b9e973a0caeb2 100644 (file)
@@ -283,7 +283,8 @@ CREATE TABLE us_snail_addy (
   <para>
    The syntax <literal>NOT NULL</literal> in this command is a
    <productname>PostgreSQL</productname> extension.  (A standard-conforming
-   way to write the same would be <literal>CHECK (VALUE IS NOT
+   way to write the same for non-composite data types would be
+   <literal>CHECK (VALUE IS NOT
    NULL)</literal>.  However, per <xref linkend="sql-createdomain-notes"/>,
    such constraints are best avoided in practice anyway.)  The
    <literal>NULL</literal> <quote>constraint</quote> is a
index 1d1aa27d450649a15d43e74d124b065e1daf9548..01804b085b3bad6b28978314fa00ef1deddcfe42 100644 (file)
@@ -3109,6 +3109,13 @@ restriction_is_always_true(PlannerInfo *root,
        if (nulltest->nulltesttype != IS_NOT_NULL)
            return false;
 
+       /*
+        * Empty rows can appear NULL in some contexts and NOT NULL in others,
+        * so avoid this optimization for row expressions.
+        */
+       if (nulltest->argisrow)
+           return false;
+
        return expr_is_nonnullable(root, nulltest->arg);
    }
 
@@ -3167,6 +3174,13 @@ restriction_is_always_false(PlannerInfo *root,
        if (nulltest->nulltesttype != IS_NULL)
            return false;
 
+       /*
+        * Empty rows can appear NULL in some contexts and NOT NULL in others,
+        * so avoid this optimization for row expressions.
+        */
+       if (nulltest->argisrow)
+           return false;
+
        return expr_is_nonnullable(root, nulltest->arg);
    }