diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index e837f417d0d..003b70852bb 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -137,22 +137,22 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, Bitmapset *expr_attrs = NULL; int i; - /* add nsitem to query namespace */ + /* Add nsitem to query namespace */ addNSItemToQuery(pstate, nsitem, false, true, true); /* Transform the raw expression tree */ whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE); - /* Make sure it yields a boolean result. */ + /* Make sure it yields a boolean result */ whereClause = coerce_to_boolean(pstate, whereClause, "WHERE"); - /* we have to fix its collations too */ + /* 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. + * Identify all columns used in the WHERE clause's expression. If + * there's a whole-row reference, replace it with a range of all + * the user columns (caution: that'll include dropped columns). */ pull_varattnos(whereClause, 1, &expr_attrs); if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs)) @@ -163,12 +163,30 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber); } + /* Now we can scan each column needed in the WHERE clause */ i = -1; while ((i = bms_next_member(expr_attrs, i)) >= 0) { AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber; + Form_pg_attribute att; - Assert(attno != 0); + Assert(attno != 0); /* removed above */ + + /* + * Prohibit system columns in the WHERE clause. They won't + * have been filled yet when the filtering happens. (We could + * allow tableoid, but right now it isn't really useful: it + * will read as the target table's OID. Any conceivable use + * for such a WHERE clause would probably wish it to read as + * the target partition's OID, which is not known yet. + * Disallow it to keep flexibility to change that sometime.) + */ + if (attno < 0) + ereport(ERROR, + errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("system columns are not supported in COPY FROM WHERE conditions"), + errdetail("Column \"%s\" is a system column.", + get_attname(RelationGetRelid(rel), attno, false))); /* * Prohibit generated columns in the WHERE clause. Stored @@ -177,7 +195,8 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * 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) + att = TupleDescAttr(RelationGetDescr(rel), attno - 1); + if (att->attgenerated && !att->attisdropped) ereport(ERROR, errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("generated columns are not supported in COPY FROM WHERE conditions"), @@ -185,6 +204,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, get_attname(RelationGetRelid(rel), attno, false))); } + /* Reduce WHERE clause to standard list-of-AND-terms form */ whereClause = eval_const_expressions(NULL, whereClause); whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false); diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index 01101c71051..7600e5239d2 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -204,6 +204,9 @@ COPY x from stdin WHERE a = row_number() over(b); ERROR: window functions are not allowed in COPY FROM WHERE conditions LINE 1: COPY x from stdin WHERE a = row_number() over(b); ^ +COPY x from stdin WHERE tableoid = 'x'::regclass; +ERROR: system columns are not supported in COPY FROM WHERE conditions +DETAIL: Column "tableoid" is a system column. -- check results of copy in SELECT * FROM x; a | b | c | d | e diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index 889dcf1383f..e0810109473 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -168,6 +168,8 @@ COPY x from stdin WHERE a IN (generate_series(1,5)); COPY x from stdin WHERE a = row_number() over(b); +COPY x from stdin WHERE tableoid = 'x'::regclass; + -- check results of copy in SELECT * FROM x;