Expand virtual generated columns in constraint expressions
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 15 Sep 2025 14:27:50 +0000 (16:27 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 15 Sep 2025 14:27:50 +0000 (16:27 +0200)
Virtual generated columns in constraint expressions need to be
expanded because the optimizer matches these expressions to qual
clauses.  Failing to do so can cause us to miss opportunities for
constraint exclusion.

Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/204804c0-798f-4c72-bd1f-36116024fda3%40eisentraut.org

src/backend/optimizer/util/plancat.c
src/test/regress/expected/generated_virtual.out
src/test/regress/sql/generated_virtual.sql

index 572d626b2c4d2dda0558fd6e6eccf1458fca20a8..f8641204a67ee5a124d7dc7ae82087ee8257c196 100644 (file)
@@ -42,6 +42,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
 #include "partitioning/partdesc.h"
+#include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "statistics/statistics.h"
 #include "storage/bufmgr.h"
@@ -1482,6 +1483,14 @@ get_relation_constraints(PlannerInfo *root,
        result = list_concat(result, rel->partition_qual);
    }
 
+   /*
+    * Expand virtual generated columns in the constraint expressions.
+    */
+   if (result)
+       result = (List *) expand_generated_columns_in_expr((Node *) result,
+                                                          relation,
+                                                          varno);
+
    table_close(relation, NoLock);
 
    return result;
index aca6347babe9680f174145ab3837e7892c85ca0f..d8645192351cf72c61ea31d3998aa3653437564a 100644 (file)
@@ -1636,3 +1636,26 @@ select 1 from gtest32 t1 where exists
 (1 row)
 
 drop table gtest32;
+-- Ensure that virtual generated columns in constraint expressions are expanded
+create table gtest33 (a int, b int generated always as (a * 2) virtual not null, check (b > 10));
+set constraint_exclusion to on;
+-- should get a dummy Result, not a seq scan
+explain (costs off)
+select * from gtest33 where b < 10;
+        QUERY PLAN        
+--------------------------
+ Result
+   One-Time Filter: false
+(2 rows)
+
+-- should get a dummy Result, not a seq scan
+explain (costs off)
+select * from gtest33 where b is null;
+        QUERY PLAN        
+--------------------------
+ Result
+   One-Time Filter: false
+(2 rows)
+
+reset constraint_exclusion;
+drop table gtest33;
index ba19bc4c701e06320aae01ee8c86496ef994343c..adfe88d74ae98382794593b0b47203a4c9341375 100644 (file)
@@ -868,3 +868,18 @@ select 1 from gtest32 t1 where exists
   (select 1 from gtest32 t2 where t1.a > t2.a and t2.b = 2);
 
 drop table gtest32;
+
+-- Ensure that virtual generated columns in constraint expressions are expanded
+create table gtest33 (a int, b int generated always as (a * 2) virtual not null, check (b > 10));
+set constraint_exclusion to on;
+
+-- should get a dummy Result, not a seq scan
+explain (costs off)
+select * from gtest33 where b < 10;
+
+-- should get a dummy Result, not a seq scan
+explain (costs off)
+select * from gtest33 where b is null;
+
+reset constraint_exclusion;
+drop table gtest33;