Disallow generated columns in COPY WHERE clause
authorPeter Eisentraut <peter@eisentraut.org>
Thu, 6 Nov 2025 10:52:47 +0000 (11:52 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Thu, 6 Nov 2025 13:01:42 +0000 (14:01 +0100)
Stored generated columns are not yet computed when the filtering
happens, so we need to prohibit them to avoid incorrect behavior.

Co-authored-by: jian he <jian.universality@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CACJufxHb8YPQ095R_pYDr77W9XKNaXg5Rzy-WP525mkq+hRM3g@mail.gmail.com

src/backend/commands/copy.c
src/test/regress/expected/generated.out
src/test/regress/sql/generated.sql

index e557982071da51c93e92345ecde308fb422d442e..da7164e6d1bd8b8953dc87a32a24ffaa9c227442 100644 (file)
@@ -133,6 +133,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
 
        if (stmt->whereClause)
        {
+           Bitmapset  *expr_attrs = NULL;
+           int         i;
+
            /* add nsitem to query namespace */
            addNSItemToQuery(pstate, nsitem, false, true, true);
 
@@ -145,6 +148,40 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
            /* we have to fix its collations too */
            assign_expr_collations(pstate, whereClause);
 
+           /*
+            * Examine all the columns in the WHERE clause expression.  When
+            * the whole-row reference is present, examine all the columns of
+            * the table.
+            */
+           pull_varattnos(whereClause, 1, &expr_attrs);
+           if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
+           {
+               expr_attrs = bms_add_range(expr_attrs,
+                                          1 - FirstLowInvalidHeapAttributeNumber,
+                                          RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
+               expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
+           }
+
+           i = -1;
+           while ((i = bms_next_member(expr_attrs, i)) >= 0)
+           {
+               AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
+
+               Assert(attno != 0);
+
+               /*
+                * Prohibit generated columns in the WHERE clause.  Stored
+                * generated columns are not yet computed when the filtering
+                * happens.
+                */
+               if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
+                   ereport(ERROR,
+                           errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                           errmsg("generated columns are not supported in COPY FROM WHERE conditions"),
+                           errdetail("Column \"%s\" is a generated column.",
+                                     get_attname(RelationGetRelid(rel), attno, false)));
+           }
+
            whereClause = eval_const_expressions(NULL, whereClause);
 
            whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
index 3347205923d5af71672eddce28d0245c4358e2bc..7d7f7fb775ba7c15fea5724d0ed6ee97ac234e25 100644 (file)
@@ -432,6 +432,12 @@ COPY gtest1 FROM stdin;
 COPY gtest1 (a, b) FROM stdin;
 ERROR:  column "b" is a generated column
 DETAIL:  Generated columns cannot be used in COPY.
+COPY gtest1 FROM stdin WHERE b <> 10;
+ERROR:  generated columns are not supported in COPY FROM WHERE conditions
+DETAIL:  Column "b" is a generated column.
+COPY gtest1 FROM stdin WHERE gtest1 IS NULL;
+ERROR:  generated columns are not supported in COPY FROM WHERE conditions
+DETAIL:  Column "b" is a generated column.
 SELECT * FROM gtest1 ORDER BY a;
  a | b 
 ---+---
index 76e40c985898d07113fda5ccf1663662d17476c0..9152e191dafef37357323878e0f56be9929c1d46 100644 (file)
@@ -199,6 +199,10 @@ COPY gtest1 FROM stdin;
 
 COPY gtest1 (a, b) FROM stdin;
 
+COPY gtest1 FROM stdin WHERE b <> 10;
+
+COPY gtest1 FROM stdin WHERE gtest1 IS NULL;
+
 SELECT * FROM gtest1 ORDER BY a;
 
 TRUNCATE gtest3;