From d3bb4b36538998a4f0e6f30a1664cd8c53a90e27 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" Date: Wed, 24 Jun 2026 15:25:17 +0800 Subject: [PATCH v3] Report unsupported COPY FROM targets before reading input COPY FROM rejected unsupported target relations, such as sequences, inside CopyFrom(). For COPY FROM STDIN, that is too late: the command may already have entered COPY input mode before reporting the target-relation error. Move the target-relation check to BeginCopyFrom(), before the command starts reading input. This keeps the existing precedence of earlier parse analysis, permission, and WHERE-clause errors, while making COPY FROM consistent with COPY TO, where the corresponding relation-kind checks are performed in BeginCopyTo(). This makes commands such as COPY FROM a sequence fail immediately with "cannot copy to sequence", instead of waiting for COPY input first. It also avoids opening the input file or starting an external program before rejecting an unsupported target relation. Author: Chao Li Reviewed-by: Kyotaro Horiguchi Reviewed-by: ZizhuanLiu <44973863@qq.com> Reviewed-by: Solai V Reviewed-by: Surya Poondla Discussion: https://postgr.es/m/84873FFF-4C50-4C85-9A36-62A01B860FF1@gmail.com --- src/backend/commands/copyfrom.c | 68 ++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 746b4fdf6c3..83e525f06aa 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -808,40 +808,6 @@ CopyFrom(CopyFromState cstate) if (cstate->opts.on_error != COPY_ON_ERROR_STOP) Assert(cstate->escontext); - /* - * The target must be a plain, foreign, or partitioned relation, or have - * an INSTEAD OF INSERT row trigger. (Currently, such triggers are only - * allowed on views, so we only hint about them in the view case.) - */ - if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && - cstate->rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && - cstate->rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && - !(cstate->rel->trigdesc && - cstate->rel->trigdesc->trig_insert_instead_row)) - { - if (cstate->rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to view \"%s\"", - RelationGetRelationName(cstate->rel)), - errhint("To enable copying to a view, provide an INSTEAD OF INSERT trigger."))); - else if (cstate->rel->rd_rel->relkind == RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to materialized view \"%s\"", - RelationGetRelationName(cstate->rel)))); - else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to sequence \"%s\"", - RelationGetRelationName(cstate->rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy to non-table relation \"%s\"", - RelationGetRelationName(cstate->rel)))); - } - /* * If the target file is new-in-transaction, we assume that checking FSM * for free space is a waste of time. This could possibly be wrong, but @@ -1563,6 +1529,40 @@ BeginCopyFrom(ParseState *pstate, 0 }; + /* + * The target must be a plain, foreign, or partitioned relation, or have + * an INSTEAD OF INSERT row trigger. (Currently, such triggers are only + * allowed on views, so we only hint about them in the view case.) + */ + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && + rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && + !(rel->trigdesc && + rel->trigdesc->trig_insert_instead_row)) + { + if (rel->rd_rel->relkind == RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to view \"%s\"", + RelationGetRelationName(rel)), + errhint("To enable copying to a view, provide an INSTEAD OF INSERT trigger."))); + else if (rel->rd_rel->relkind == RELKIND_MATVIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to materialized view \"%s\"", + RelationGetRelationName(rel)))); + else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to sequence \"%s\"", + RelationGetRelationName(rel)))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy to non-table relation \"%s\"", + RelationGetRelationName(rel)))); + } + /* Allocate workspace and zero all fields */ cstate = palloc0_object(CopyFromStateData); -- 2.50.1 (Apple Git-155)