]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
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 12:54:42 +0000 (13:54 +0100)
Stored generated columns are not yet computed when the filtering
happens, so we need to prohibit them to avoid incorrect behavior.

Virtual generated columns currently error out ("unexpected virtual
generated column reference").  They could probably work if we expand
them in the right place, but for now let's keep them consistent with
the stored variant.  This doesn't change the behavior, it only gives a
nicer error message.

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_stored.out
src/test/regress/expected/generated_virtual.out
src/test/regress/sql/generated_stored.sql
src/test/regress/sql/generated_virtual.sql

index 44020d0ae804a7ceaa2b96a533960010ad164e4e..28e878c3688702892d92c6cb0de3dbf1fcb7fc61 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,42 @@ 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.  Virtual generated columns could probably work (we
+                                * would need to expand them somewhere around here), but for
+                                * now we keep them consistent with the stored variant.
+                                */
+                               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 b3710a49de628f0a22caa2bcc6906a174b3acdc8..8b7a71d8f0c482db54797244bfeaf3d741814276 100644 (file)
@@ -502,6 +502,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 4ec3d330017503a0a99a469210a6738750e3f392..dde325e46c6401ad9dc0086c1d3e9925c9c8f631 100644 (file)
@@ -496,6 +496,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 99ea0105685cff2f011249c0fc04bdbc9a9a17a8..2001a47bcc6a3b6e0e5652f5ca18652a06f09211 100644 (file)
@@ -217,6 +217,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;
index 992c0cdae65ec0ba8af45ba963e1df4415753e24..2911439776c01eae1b73c55506add2712b05bfb4 100644 (file)
@@ -217,6 +217,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;