From 46b4ba533cee9f64a60714d91607e74362abf67f Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 7 Apr 2025 21:33:42 -0400 Subject: [PATCH] Fix PG 17 [NOT] NULL optimization bug for domains 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/Z37p0paENWWUarj-@momjian.us Backpatch-through: 17 --- doc/src/sgml/ref/create_domain.sgml | 3 ++- src/backend/optimizer/plan/initsplan.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml index ce555203486..c111285a69c 100644 --- a/doc/src/sgml/ref/create_domain.sgml +++ b/doc/src/sgml/ref/create_domain.sgml @@ -283,7 +283,8 @@ CREATE TABLE us_snail_addy ( The syntax NOT NULL in this command is a PostgreSQL extension. (A standard-conforming - way to write the same would be CHECK (VALUE IS NOT + way to write the same for non-composite data types would be + CHECK (VALUE IS NOT NULL). However, per , such constraints are best avoided in practice anyway.) The NULL constraint is a diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 1d1aa27d450..01804b085b3 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -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); }