From: Peter Eisentraut Date: Thu, 6 Nov 2025 10:52:47 +0000 (+0100) Subject: Disallow generated columns in COPY WHERE clause X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=26958f4d99b16b1c638e6d43d46623e9c79579d5;p=thirdparty%2Fpostgresql.git Disallow generated columns in COPY WHERE clause 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 Reviewed-by: Kirill Reshke Reviewed-by: Masahiko Sawada Discussion: https://www.postgresql.org/message-id/flat/CACJufxHb8YPQ095R_pYDr77W9XKNaXg5Rzy-WP525mkq+hRM3g@mail.gmail.com --- diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f14fae33083..b957468828e 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -135,6 +135,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); @@ -147,6 +150,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); diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out index 4905f700c62..f7abdc3e44b 100644 --- a/src/test/regress/expected/generated.out +++ b/src/test/regress/expected/generated.out @@ -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 ---+--- diff --git a/src/test/regress/sql/generated.sql b/src/test/regress/sql/generated.sql index ca97461c772..37c3638276a 100644 --- a/src/test/regress/sql/generated.sql +++ b/src/test/regress/sql/generated.sql @@ -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;