LCOV - code coverage report
Current view: top level - src/backend/commands - copy.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 365 383 95.3 %
Date: 2026-02-07 08:18:04 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * copy.c
       4             :  *      Implements the COPY utility command
       5             :  *
       6             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/copy.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include <ctype.h>
      18             : #include <unistd.h>
      19             : #include <sys/stat.h>
      20             : 
      21             : #include "access/sysattr.h"
      22             : #include "access/table.h"
      23             : #include "access/xact.h"
      24             : #include "catalog/pg_authid.h"
      25             : #include "commands/copy.h"
      26             : #include "commands/defrem.h"
      27             : #include "executor/executor.h"
      28             : #include "mb/pg_wchar.h"
      29             : #include "miscadmin.h"
      30             : #include "nodes/makefuncs.h"
      31             : #include "nodes/miscnodes.h"
      32             : #include "optimizer/optimizer.h"
      33             : #include "parser/parse_coerce.h"
      34             : #include "parser/parse_collate.h"
      35             : #include "parser/parse_expr.h"
      36             : #include "parser/parse_relation.h"
      37             : #include "utils/acl.h"
      38             : #include "utils/builtins.h"
      39             : #include "utils/lsyscache.h"
      40             : #include "utils/rel.h"
      41             : #include "utils/rls.h"
      42             : 
      43             : /*
      44             :  *   DoCopy executes the SQL COPY statement
      45             :  *
      46             :  * Either unload or reload contents of table <relation>, depending on <from>.
      47             :  * (<from> = true means we are inserting into the table.)  In the "TO" case
      48             :  * we also support copying the output of an arbitrary SELECT, INSERT, UPDATE
      49             :  * or DELETE query.
      50             :  *
      51             :  * If <pipe> is false, transfer is between the table and the file named
      52             :  * <filename>.  Otherwise, transfer is between the table and our regular
      53             :  * input/output stream. The latter could be either stdin/stdout or a
      54             :  * socket, depending on whether we're running under Postmaster control.
      55             :  *
      56             :  * Do not allow a Postgres user without the 'pg_read_server_files' or
      57             :  * 'pg_write_server_files' role to read from or write to a file.
      58             :  *
      59             :  * Do not allow the copy if user doesn't have proper permission to access
      60             :  * the table or the specifically requested columns.
      61             :  */
      62             : void
      63       11424 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
      64             :        int stmt_location, int stmt_len,
      65             :        uint64 *processed)
      66             : {
      67       11424 :     bool        is_from = stmt->is_from;
      68       11424 :     bool        pipe = (stmt->filename == NULL);
      69             :     Relation    rel;
      70             :     Oid         relid;
      71       11424 :     RawStmt    *query = NULL;
      72       11424 :     Node       *whereClause = NULL;
      73             : 
      74             :     /*
      75             :      * Disallow COPY to/from file or program except to users with the
      76             :      * appropriate role.
      77             :      */
      78       11424 :     if (!pipe)
      79             :     {
      80         448 :         if (stmt->is_program)
      81             :         {
      82           0 :             if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM))
      83           0 :                 ereport(ERROR,
      84             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      85             :                          errmsg("permission denied to COPY to or from an external program"),
      86             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY to or from an external program.",
      87             :                                    "pg_execute_server_program"),
      88             :                          errhint("Anyone can COPY to stdout or from stdin. "
      89             :                                  "psql's \\copy command also works for anyone.")));
      90             :         }
      91             :         else
      92             :         {
      93         448 :             if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
      94           0 :                 ereport(ERROR,
      95             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      96             :                          errmsg("permission denied to COPY from a file"),
      97             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY from a file.",
      98             :                                    "pg_read_server_files"),
      99             :                          errhint("Anyone can COPY to stdout or from stdin. "
     100             :                                  "psql's \\copy command also works for anyone.")));
     101             : 
     102         448 :             if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
     103           0 :                 ereport(ERROR,
     104             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     105             :                          errmsg("permission denied to COPY to a file"),
     106             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY to a file.",
     107             :                                    "pg_write_server_files"),
     108             :                          errhint("Anyone can COPY to stdout or from stdin. "
     109             :                                  "psql's \\copy command also works for anyone.")));
     110             :         }
     111             :     }
     112             : 
     113       11424 :     if (stmt->relation)
     114             :     {
     115       10842 :         LOCKMODE    lockmode = is_from ? RowExclusiveLock : AccessShareLock;
     116             :         ParseNamespaceItem *nsitem;
     117             :         RTEPermissionInfo *perminfo;
     118             :         TupleDesc   tupDesc;
     119             :         List       *attnums;
     120             :         ListCell   *cur;
     121             : 
     122             :         Assert(!stmt->query);
     123             : 
     124             :         /* Open and lock the relation, using the appropriate lock type. */
     125       10842 :         rel = table_openrv(stmt->relation, lockmode);
     126             : 
     127       10840 :         relid = RelationGetRelid(rel);
     128             : 
     129       10840 :         nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
     130             :                                                NULL, false, false);
     131             : 
     132       10840 :         perminfo = nsitem->p_perminfo;
     133       10840 :         perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
     134             : 
     135       10840 :         if (stmt->whereClause)
     136             :         {
     137          72 :             Bitmapset  *expr_attrs = NULL;
     138             :             int         i;
     139             : 
     140             :             /* add nsitem to query namespace */
     141          72 :             addNSItemToQuery(pstate, nsitem, false, true, true);
     142             : 
     143             :             /* Transform the raw expression tree */
     144          72 :             whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
     145             : 
     146             :             /* Make sure it yields a boolean result. */
     147          42 :             whereClause = coerce_to_boolean(pstate, whereClause, "WHERE");
     148             : 
     149             :             /* we have to fix its collations too */
     150          42 :             assign_expr_collations(pstate, whereClause);
     151             : 
     152             :             /*
     153             :              * Examine all the columns in the WHERE clause expression.  When
     154             :              * the whole-row reference is present, examine all the columns of
     155             :              * the table.
     156             :              */
     157          42 :             pull_varattnos(whereClause, 1, &expr_attrs);
     158          42 :             if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
     159             :             {
     160          24 :                 expr_attrs = bms_add_range(expr_attrs,
     161             :                                            1 - FirstLowInvalidHeapAttributeNumber,
     162          12 :                                            RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
     163          12 :                 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
     164             :             }
     165             : 
     166          42 :             i = -1;
     167          72 :             while ((i = bms_next_member(expr_attrs, i)) >= 0)
     168             :             {
     169          54 :                 AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
     170             : 
     171             :                 Assert(attno != 0);
     172             : 
     173             :                 /*
     174             :                  * Prohibit generated columns in the WHERE clause.  Stored
     175             :                  * generated columns are not yet computed when the filtering
     176             :                  * happens.  Virtual generated columns could probably work (we
     177             :                  * would need to expand them somewhere around here), but for
     178             :                  * now we keep them consistent with the stored variant.
     179             :                  */
     180          54 :                 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
     181          24 :                     ereport(ERROR,
     182             :                             errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
     183             :                             errmsg("generated columns are not supported in COPY FROM WHERE conditions"),
     184             :                             errdetail("Column \"%s\" is a generated column.",
     185             :                                       get_attname(RelationGetRelid(rel), attno, false)));
     186             :             }
     187             : 
     188          18 :             whereClause = eval_const_expressions(NULL, whereClause);
     189             : 
     190          18 :             whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
     191          18 :             whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
     192             :         }
     193             : 
     194       10786 :         tupDesc = RelationGetDescr(rel);
     195       10786 :         attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
     196       47656 :         foreach(cur, attnums)
     197             :         {
     198             :             int         attno;
     199             :             Bitmapset **bms;
     200             : 
     201       36954 :             attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
     202       36954 :             bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
     203             : 
     204       36954 :             *bms = bms_add_member(*bms, attno);
     205             :         }
     206       10702 :         ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true);
     207             : 
     208             :         /*
     209             :          * Permission check for row security policies.
     210             :          *
     211             :          * check_enable_rls will ereport(ERROR) if the user has requested
     212             :          * something invalid and will otherwise indicate if we should enable
     213             :          * RLS (returns RLS_ENABLED) or not for this COPY statement.
     214             :          *
     215             :          * If the relation has a row security policy and we are to apply it
     216             :          * then perform a "query" copy and allow the normal query processing
     217             :          * to handle the policies.
     218             :          *
     219             :          * If RLS is not enabled for this, then just fall through to the
     220             :          * normal non-filtering relation handling.
     221             :          */
     222       10618 :         if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
     223             :         {
     224             :             SelectStmt *select;
     225             :             ColumnRef  *cr;
     226             :             ResTarget  *target;
     227             :             RangeVar   *from;
     228          84 :             List       *targetList = NIL;
     229             : 
     230          84 :             if (is_from)
     231           6 :                 ereport(ERROR,
     232             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     233             :                          errmsg("COPY FROM not supported with row-level security"),
     234             :                          errhint("Use INSERT statements instead.")));
     235             : 
     236             :             /*
     237             :              * Build target list
     238             :              *
     239             :              * If no columns are specified in the attribute list of the COPY
     240             :              * command, then the target list is 'all' columns. Therefore, '*'
     241             :              * should be used as the target list for the resulting SELECT
     242             :              * statement.
     243             :              *
     244             :              * In the case that columns are specified in the attribute list,
     245             :              * create a ColumnRef and ResTarget for each column and add them
     246             :              * to the target list for the resulting SELECT statement.
     247             :              */
     248          78 :             if (!stmt->attlist)
     249             :             {
     250          42 :                 cr = makeNode(ColumnRef);
     251          42 :                 cr->fields = list_make1(makeNode(A_Star));
     252          42 :                 cr->location = -1;
     253             : 
     254          42 :                 target = makeNode(ResTarget);
     255          42 :                 target->name = NULL;
     256          42 :                 target->indirection = NIL;
     257          42 :                 target->val = (Node *) cr;
     258          42 :                 target->location = -1;
     259             : 
     260          42 :                 targetList = list_make1(target);
     261             :             }
     262             :             else
     263             :             {
     264             :                 ListCell   *lc;
     265             : 
     266         102 :                 foreach(lc, stmt->attlist)
     267             :                 {
     268             :                     /*
     269             :                      * Build the ColumnRef for each column.  The ColumnRef
     270             :                      * 'fields' property is a String node that corresponds to
     271             :                      * the column name respectively.
     272             :                      */
     273          66 :                     cr = makeNode(ColumnRef);
     274          66 :                     cr->fields = list_make1(lfirst(lc));
     275          66 :                     cr->location = -1;
     276             : 
     277             :                     /* Build the ResTarget and add the ColumnRef to it. */
     278          66 :                     target = makeNode(ResTarget);
     279          66 :                     target->name = NULL;
     280          66 :                     target->indirection = NIL;
     281          66 :                     target->val = (Node *) cr;
     282          66 :                     target->location = -1;
     283             : 
     284             :                     /* Add each column to the SELECT statement's target list */
     285          66 :                     targetList = lappend(targetList, target);
     286             :                 }
     287             :             }
     288             : 
     289             :             /*
     290             :              * Build RangeVar for from clause, fully qualified based on the
     291             :              * relation which we have opened and locked.  Use "ONLY" so that
     292             :              * COPY retrieves rows from only the target table not any
     293             :              * inheritance children, the same as when RLS doesn't apply.
     294             :              *
     295             :              * However, when copying data from a partitioned table, we don't
     296             :              * use "ONLY", since we need to retrieve rows from its descendant
     297             :              * tables too.
     298             :              */
     299          78 :             from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
     300          78 :                                 pstrdup(RelationGetRelationName(rel)),
     301             :                                 -1);
     302          78 :             from->inh = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
     303             : 
     304             :             /* Build query */
     305          78 :             select = makeNode(SelectStmt);
     306          78 :             select->targetList = targetList;
     307          78 :             select->fromClause = list_make1(from);
     308             : 
     309          78 :             query = makeNode(RawStmt);
     310          78 :             query->stmt = (Node *) select;
     311          78 :             query->stmt_location = stmt_location;
     312          78 :             query->stmt_len = stmt_len;
     313             : 
     314             :             /*
     315             :              * Close the relation for now, but keep the lock on it to prevent
     316             :              * changes between now and when we start the query-based COPY.
     317             :              *
     318             :              * We'll reopen it later as part of the query-based COPY.
     319             :              */
     320          78 :             table_close(rel, NoLock);
     321          78 :             rel = NULL;
     322             :         }
     323             :     }
     324             :     else
     325             :     {
     326             :         Assert(stmt->query);
     327             : 
     328         582 :         query = makeNode(RawStmt);
     329         582 :         query->stmt = stmt->query;
     330         582 :         query->stmt_location = stmt_location;
     331         582 :         query->stmt_len = stmt_len;
     332             : 
     333         582 :         relid = InvalidOid;
     334         582 :         rel = NULL;
     335             :     }
     336             : 
     337       11176 :     if (is_from)
     338             :     {
     339             :         CopyFromState cstate;
     340             : 
     341             :         Assert(rel);
     342             : 
     343             :         /* check read-only transaction and parallel mode */
     344        1754 :         if (XactReadOnly && !rel->rd_islocaltemp)
     345           0 :             PreventCommandIfReadOnly("COPY FROM");
     346             : 
     347        1754 :         cstate = BeginCopyFrom(pstate, rel, whereClause,
     348        1754 :                                stmt->filename, stmt->is_program,
     349        1754 :                                NULL, stmt->attlist, stmt->options);
     350        1484 :         *processed = CopyFrom(cstate);  /* copy from file to database */
     351        1246 :         EndCopyFrom(cstate);
     352             :     }
     353             :     else
     354             :     {
     355             :         CopyToState cstate;
     356             : 
     357        9422 :         cstate = BeginCopyTo(pstate, rel, query, relid,
     358        9422 :                              stmt->filename, stmt->is_program,
     359        9422 :                              NULL, stmt->attlist, stmt->options);
     360        9208 :         *processed = DoCopyTo(cstate);  /* copy from database to file */
     361        9206 :         EndCopyTo(cstate);
     362             :     }
     363             : 
     364       10452 :     if (rel != NULL)
     365        9932 :         table_close(rel, NoLock);
     366       10452 : }
     367             : 
     368             : /*
     369             :  * Extract the CopyFormatOptions.header_line value from a DefElem.
     370             :  *
     371             :  * Parses the HEADER option for COPY, which can be a boolean, an integer greater
     372             :  * than or equal to zero (number of lines to skip), or the special value
     373             :  * "match".
     374             :  */
     375             : static int
     376         272 : defGetCopyHeaderOption(DefElem *def, bool is_from)
     377             : {
     378         272 :     int         ival = COPY_HEADER_FALSE;
     379             : 
     380             :     /*
     381             :      * If no parameter value given, assume "true" is meant.
     382             :      */
     383         272 :     if (def->arg == NULL)
     384          30 :         return COPY_HEADER_TRUE;
     385             : 
     386             :     /*
     387             :      * Allow an integer value greater than or equal to zero (integers
     388             :      * specified as strings are also accepted, mainly for file_fdw foreign
     389             :      * table options), "true", "false", "on", "off", or "match".
     390             :      */
     391         242 :     switch (nodeTag(def->arg))
     392             :     {
     393          30 :         case T_Integer:
     394          30 :             ival = intVal(def->arg);
     395          30 :             break;
     396         212 :         default:
     397             :             {
     398         212 :                 char       *sval = defGetString(def);
     399             : 
     400             :                 /*
     401             :                  * The set of strings accepted here should match up with the
     402             :                  * grammar's opt_boolean_or_string production.
     403             :                  */
     404         212 :                 if (pg_strcasecmp(sval, "true") == 0)
     405          64 :                     return COPY_HEADER_TRUE;
     406         148 :                 if (pg_strcasecmp(sval, "false") == 0)
     407           0 :                     return COPY_HEADER_FALSE;
     408         148 :                 if (pg_strcasecmp(sval, "on") == 0)
     409           0 :                     return COPY_HEADER_TRUE;
     410         148 :                 if (pg_strcasecmp(sval, "off") == 0)
     411           6 :                     return COPY_HEADER_FALSE;
     412         142 :                 if (pg_strcasecmp(sval, "match") == 0)
     413             :                 {
     414          86 :                     if (!is_from)
     415           6 :                         ereport(ERROR,
     416             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     417             :                                  errmsg("cannot use \"%s\" with HEADER in COPY TO",
     418             :                                         sval)));
     419          80 :                     return COPY_HEADER_MATCH;
     420             :                 }
     421             :                 else
     422             :                 {
     423          56 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
     424             : 
     425             :                     /* Check if the header is a valid integer */
     426          56 :                     ival = pg_strtoint32_safe(sval, (Node *) &escontext);
     427          56 :                     if (escontext.error_occurred)
     428          22 :                         ereport(ERROR,
     429             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     430             :                         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     431             :                         second %s is the special value "match" for that option */
     432             :                                  errmsg("%s requires a Boolean value, an integer "
     433             :                                         "value greater than or equal to zero, "
     434             :                                         "or the string \"%s\"",
     435             :                                         def->defname, "match")));
     436             :                 }
     437             :             }
     438          34 :             break;
     439             :     }
     440             : 
     441          64 :     if (ival < 0)
     442          14 :         ereport(ERROR,
     443             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     444             :                  errmsg("a negative integer value cannot be "
     445             :                         "specified for %s", def->defname)));
     446             : 
     447          50 :     if (!is_from && ival > 1)
     448          12 :         ereport(ERROR,
     449             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     450             :                  errmsg("cannot use multi-line header in COPY TO")));
     451             : 
     452          38 :     return ival;
     453             : }
     454             : 
     455             : /*
     456             :  * Extract a CopyOnErrorChoice value from a DefElem.
     457             :  */
     458             : static CopyOnErrorChoice
     459         114 : defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
     460             : {
     461         114 :     char       *sval = defGetString(def);
     462             : 
     463         114 :     if (!is_from)
     464           6 :         ereport(ERROR,
     465             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     466             :         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     467             :          second %s is a COPY with direction, e.g. COPY TO */
     468             :                  errmsg("COPY %s cannot be used with %s", "ON_ERROR", "COPY TO"),
     469             :                  parser_errposition(pstate, def->location)));
     470             : 
     471             :     /*
     472             :      * Allow "stop", or "ignore" values.
     473             :      */
     474         108 :     if (pg_strcasecmp(sval, "stop") == 0)
     475           6 :         return COPY_ON_ERROR_STOP;
     476         102 :     if (pg_strcasecmp(sval, "ignore") == 0)
     477          94 :         return COPY_ON_ERROR_IGNORE;
     478             : 
     479           8 :     ereport(ERROR,
     480             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     481             :     /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
     482             :              errmsg("COPY %s \"%s\" not recognized", "ON_ERROR", sval),
     483             :              parser_errposition(pstate, def->location)));
     484             :     return COPY_ON_ERROR_STOP;  /* keep compiler quiet */
     485             : }
     486             : 
     487             : /*
     488             :  * Extract REJECT_LIMIT value from a DefElem.
     489             :  *
     490             :  * REJECT_LIMIT can be specified in two ways: as an int64 for the COPY command
     491             :  * option or as a single-quoted string for the foreign table option using
     492             :  * file_fdw. Therefore this function needs to handle both formats.
     493             :  */
     494             : static int64
     495          38 : defGetCopyRejectLimitOption(DefElem *def)
     496             : {
     497             :     int64       reject_limit;
     498             : 
     499          38 :     if (def->arg == NULL)
     500           0 :         ereport(ERROR,
     501             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     502             :                  errmsg("%s requires a numeric value",
     503             :                         def->defname)));
     504          38 :     else if (IsA(def->arg, String))
     505          14 :         reject_limit = pg_strtoint64(strVal(def->arg));
     506             :     else
     507          24 :         reject_limit = defGetInt64(def);
     508             : 
     509          38 :     if (reject_limit <= 0)
     510           8 :         ereport(ERROR,
     511             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     512             :                  errmsg("REJECT_LIMIT (%" PRId64 ") must be greater than zero",
     513             :                         reject_limit)));
     514             : 
     515          30 :     return reject_limit;
     516             : }
     517             : 
     518             : /*
     519             :  * Extract a CopyLogVerbosityChoice value from a DefElem.
     520             :  */
     521             : static CopyLogVerbosityChoice
     522          46 : defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate)
     523             : {
     524             :     char       *sval;
     525             : 
     526             :     /*
     527             :      * Allow "silent", "default", or "verbose" values.
     528             :      */
     529          46 :     sval = defGetString(def);
     530          46 :     if (pg_strcasecmp(sval, "silent") == 0)
     531          20 :         return COPY_LOG_VERBOSITY_SILENT;
     532          26 :     if (pg_strcasecmp(sval, "default") == 0)
     533           6 :         return COPY_LOG_VERBOSITY_DEFAULT;
     534          20 :     if (pg_strcasecmp(sval, "verbose") == 0)
     535          12 :         return COPY_LOG_VERBOSITY_VERBOSE;
     536             : 
     537           8 :     ereport(ERROR,
     538             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     539             :     /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
     540             :              errmsg("COPY %s \"%s\" not recognized", "LOG_VERBOSITY", sval),
     541             :              parser_errposition(pstate, def->location)));
     542             :     return COPY_LOG_VERBOSITY_DEFAULT;  /* keep compiler quiet */
     543             : }
     544             : 
     545             : /*
     546             :  * Process the statement option list for COPY.
     547             :  *
     548             :  * Scan the options list (a list of DefElem) and transpose the information
     549             :  * into *opts_out, applying appropriate error checking.
     550             :  *
     551             :  * If 'opts_out' is not NULL, it is assumed to be filled with zeroes initially.
     552             :  *
     553             :  * This is exported so that external users of the COPY API can sanity-check
     554             :  * a list of options.  In that usage, 'opts_out' can be passed as NULL and
     555             :  * the collected data is just leaked until CurrentMemoryContext is reset.
     556             :  *
     557             :  * Note that additional checking, such as whether column names listed in FORCE
     558             :  * QUOTE actually exist, has to be applied later.  This just checks for
     559             :  * self-consistency of the options list.
     560             :  */
     561             : void
     562       11740 : ProcessCopyOptions(ParseState *pstate,
     563             :                    CopyFormatOptions *opts_out,
     564             :                    bool is_from,
     565             :                    List *options)
     566             : {
     567       11740 :     bool        format_specified = false;
     568       11740 :     bool        freeze_specified = false;
     569       11740 :     bool        header_specified = false;
     570       11740 :     bool        on_error_specified = false;
     571       11740 :     bool        log_verbosity_specified = false;
     572       11740 :     bool        reject_limit_specified = false;
     573             :     ListCell   *option;
     574             : 
     575             :     /* Support external use for option sanity checking */
     576       11740 :     if (opts_out == NULL)
     577         114 :         opts_out = palloc0_object(CopyFormatOptions);
     578             : 
     579       11740 :     opts_out->file_encoding = -1;
     580             : 
     581             :     /* Extract options from the statement node tree */
     582       13858 :     foreach(option, options)
     583             :     {
     584        2300 :         DefElem    *defel = lfirst_node(DefElem, option);
     585             : 
     586        2300 :         if (strcmp(defel->defname, "format") == 0)
     587             :         {
     588         676 :             char       *fmt = defGetString(defel);
     589             : 
     590         676 :             if (format_specified)
     591           6 :                 errorConflictingDefElem(defel, pstate);
     592         670 :             format_specified = true;
     593         670 :             if (strcmp(fmt, "text") == 0)
     594             :                  /* default format */ ;
     595         594 :             else if (strcmp(fmt, "csv") == 0)
     596         522 :                 opts_out->csv_mode = true;
     597          72 :             else if (strcmp(fmt, "binary") == 0)
     598          70 :                 opts_out->binary = true;
     599             :             else
     600           2 :                 ereport(ERROR,
     601             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     602             :                          errmsg("COPY format \"%s\" not recognized", fmt),
     603             :                          parser_errposition(pstate, defel->location)));
     604             :         }
     605        1624 :         else if (strcmp(defel->defname, "freeze") == 0)
     606             :         {
     607          84 :             if (freeze_specified)
     608           6 :                 errorConflictingDefElem(defel, pstate);
     609          78 :             freeze_specified = true;
     610          78 :             opts_out->freeze = defGetBoolean(defel);
     611             :         }
     612        1540 :         else if (strcmp(defel->defname, "delimiter") == 0)
     613             :         {
     614         302 :             if (opts_out->delim)
     615           6 :                 errorConflictingDefElem(defel, pstate);
     616         296 :             opts_out->delim = defGetString(defel);
     617             :         }
     618        1238 :         else if (strcmp(defel->defname, "null") == 0)
     619             :         {
     620         144 :             if (opts_out->null_print)
     621           6 :                 errorConflictingDefElem(defel, pstate);
     622         138 :             opts_out->null_print = defGetString(defel);
     623             :         }
     624        1094 :         else if (strcmp(defel->defname, "default") == 0)
     625             :         {
     626          90 :             if (opts_out->default_print)
     627           0 :                 errorConflictingDefElem(defel, pstate);
     628          90 :             opts_out->default_print = defGetString(defel);
     629             :         }
     630        1004 :         else if (strcmp(defel->defname, "header") == 0)
     631             :         {
     632         278 :             if (header_specified)
     633           6 :                 errorConflictingDefElem(defel, pstate);
     634         272 :             header_specified = true;
     635         272 :             opts_out->header_line = defGetCopyHeaderOption(defel, is_from);
     636             :         }
     637         726 :         else if (strcmp(defel->defname, "quote") == 0)
     638             :         {
     639         102 :             if (opts_out->quote)
     640           6 :                 errorConflictingDefElem(defel, pstate);
     641          96 :             opts_out->quote = defGetString(defel);
     642             :         }
     643         624 :         else if (strcmp(defel->defname, "escape") == 0)
     644             :         {
     645          94 :             if (opts_out->escape)
     646           6 :                 errorConflictingDefElem(defel, pstate);
     647          88 :             opts_out->escape = defGetString(defel);
     648             :         }
     649         530 :         else if (strcmp(defel->defname, "force_quote") == 0)
     650             :         {
     651          78 :             if (opts_out->force_quote || opts_out->force_quote_all)
     652           6 :                 errorConflictingDefElem(defel, pstate);
     653          72 :             if (defel->arg && IsA(defel->arg, A_Star))
     654          30 :                 opts_out->force_quote_all = true;
     655          42 :             else if (defel->arg && IsA(defel->arg, List))
     656          42 :                 opts_out->force_quote = castNode(List, defel->arg);
     657             :             else
     658           0 :                 ereport(ERROR,
     659             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     660             :                          errmsg("argument to option \"%s\" must be a list of column names",
     661             :                                 defel->defname),
     662             :                          parser_errposition(pstate, defel->location)));
     663             :         }
     664         452 :         else if (strcmp(defel->defname, "force_not_null") == 0)
     665             :         {
     666          88 :             if (opts_out->force_notnull || opts_out->force_notnull_all)
     667          12 :                 errorConflictingDefElem(defel, pstate);
     668          76 :             if (defel->arg && IsA(defel->arg, A_Star))
     669          30 :                 opts_out->force_notnull_all = true;
     670          46 :             else if (defel->arg && IsA(defel->arg, List))
     671          46 :                 opts_out->force_notnull = castNode(List, defel->arg);
     672             :             else
     673           0 :                 ereport(ERROR,
     674             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     675             :                          errmsg("argument to option \"%s\" must be a list of column names",
     676             :                                 defel->defname),
     677             :                          parser_errposition(pstate, defel->location)));
     678             :         }
     679         364 :         else if (strcmp(defel->defname, "force_null") == 0)
     680             :         {
     681          88 :             if (opts_out->force_null || opts_out->force_null_all)
     682          12 :                 errorConflictingDefElem(defel, pstate);
     683          76 :             if (defel->arg && IsA(defel->arg, A_Star))
     684          30 :                 opts_out->force_null_all = true;
     685          46 :             else if (defel->arg && IsA(defel->arg, List))
     686          46 :                 opts_out->force_null = castNode(List, defel->arg);
     687             :             else
     688           0 :                 ereport(ERROR,
     689             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     690             :                          errmsg("argument to option \"%s\" must be a list of column names",
     691             :                                 defel->defname),
     692             :                          parser_errposition(pstate, defel->location)));
     693             :         }
     694         276 :         else if (strcmp(defel->defname, "convert_selectively") == 0)
     695             :         {
     696             :             /*
     697             :              * Undocumented, not-accessible-from-SQL option: convert only the
     698             :              * named columns to binary form, storing the rest as NULLs. It's
     699             :              * allowed for the column list to be NIL.
     700             :              */
     701          18 :             if (opts_out->convert_selectively)
     702           6 :                 errorConflictingDefElem(defel, pstate);
     703          12 :             opts_out->convert_selectively = true;
     704          12 :             if (defel->arg == NULL || IsA(defel->arg, List))
     705          12 :                 opts_out->convert_select = castNode(List, defel->arg);
     706             :             else
     707           0 :                 ereport(ERROR,
     708             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     709             :                          errmsg("argument to option \"%s\" must be a list of column names",
     710             :                                 defel->defname),
     711             :                          parser_errposition(pstate, defel->location)));
     712             :         }
     713         258 :         else if (strcmp(defel->defname, "encoding") == 0)
     714             :         {
     715          48 :             if (opts_out->file_encoding >= 0)
     716           6 :                 errorConflictingDefElem(defel, pstate);
     717          42 :             opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
     718          42 :             if (opts_out->file_encoding < 0)
     719           0 :                 ereport(ERROR,
     720             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     721             :                          errmsg("argument to option \"%s\" must be a valid encoding name",
     722             :                                 defel->defname),
     723             :                          parser_errposition(pstate, defel->location)));
     724             :         }
     725         210 :         else if (strcmp(defel->defname, "on_error") == 0)
     726             :         {
     727         120 :             if (on_error_specified)
     728           6 :                 errorConflictingDefElem(defel, pstate);
     729         114 :             on_error_specified = true;
     730         114 :             opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
     731             :         }
     732          90 :         else if (strcmp(defel->defname, "log_verbosity") == 0)
     733             :         {
     734          52 :             if (log_verbosity_specified)
     735           6 :                 errorConflictingDefElem(defel, pstate);
     736          46 :             log_verbosity_specified = true;
     737          46 :             opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
     738             :         }
     739          38 :         else if (strcmp(defel->defname, "reject_limit") == 0)
     740             :         {
     741          38 :             if (reject_limit_specified)
     742           0 :                 errorConflictingDefElem(defel, pstate);
     743          38 :             reject_limit_specified = true;
     744          38 :             opts_out->reject_limit = defGetCopyRejectLimitOption(defel);
     745             :         }
     746             :         else
     747           0 :             ereport(ERROR,
     748             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     749             :                      errmsg("option \"%s\" not recognized",
     750             :                             defel->defname),
     751             :                      parser_errposition(pstate, defel->location)));
     752             :     }
     753             : 
     754             :     /*
     755             :      * Check for incompatible options (must do these three before inserting
     756             :      * defaults)
     757             :      */
     758       11558 :     if (opts_out->binary && opts_out->delim)
     759           6 :         ereport(ERROR,
     760             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     761             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     762             :                  errmsg("cannot specify %s in BINARY mode", "DELIMITER")));
     763             : 
     764       11552 :     if (opts_out->binary && opts_out->null_print)
     765           6 :         ereport(ERROR,
     766             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     767             :                  errmsg("cannot specify %s in BINARY mode", "NULL")));
     768             : 
     769       11546 :     if (opts_out->binary && opts_out->default_print)
     770           6 :         ereport(ERROR,
     771             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     772             :                  errmsg("cannot specify %s in BINARY mode", "DEFAULT")));
     773             : 
     774             :     /* Set defaults for omitted options */
     775       11540 :     if (!opts_out->delim)
     776       11256 :         opts_out->delim = opts_out->csv_mode ? "," : "\t";
     777             : 
     778       11540 :     if (!opts_out->null_print)
     779       11414 :         opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
     780       11540 :     opts_out->null_print_len = strlen(opts_out->null_print);
     781             : 
     782       11540 :     if (opts_out->csv_mode)
     783             :     {
     784         504 :         if (!opts_out->quote)
     785         418 :             opts_out->quote = "\"";
     786         504 :         if (!opts_out->escape)
     787         428 :             opts_out->escape = opts_out->quote;
     788             :     }
     789             : 
     790             :     /* Only single-byte delimiter strings are supported. */
     791       11540 :     if (strlen(opts_out->delim) != 1)
     792           2 :         ereport(ERROR,
     793             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     794             :                  errmsg("COPY delimiter must be a single one-byte character")));
     795             : 
     796             :     /* Disallow end-of-line characters */
     797       11538 :     if (strchr(opts_out->delim, '\r') != NULL ||
     798       11538 :         strchr(opts_out->delim, '\n') != NULL)
     799           2 :         ereport(ERROR,
     800             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     801             :                  errmsg("COPY delimiter cannot be newline or carriage return")));
     802             : 
     803       11536 :     if (strchr(opts_out->null_print, '\r') != NULL ||
     804       11536 :         strchr(opts_out->null_print, '\n') != NULL)
     805           2 :         ereport(ERROR,
     806             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     807             :                  errmsg("COPY null representation cannot use newline or carriage return")));
     808             : 
     809       11534 :     if (opts_out->default_print)
     810             :     {
     811          84 :         opts_out->default_print_len = strlen(opts_out->default_print);
     812             : 
     813          84 :         if (strchr(opts_out->default_print, '\r') != NULL ||
     814          78 :             strchr(opts_out->default_print, '\n') != NULL)
     815          12 :             ereport(ERROR,
     816             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     817             :                      errmsg("COPY default representation cannot use newline or carriage return")));
     818             :     }
     819             : 
     820             :     /*
     821             :      * Disallow unsafe delimiter characters in non-CSV mode.  We can't allow
     822             :      * backslash because it would be ambiguous.  We can't allow the other
     823             :      * cases because data characters matching the delimiter must be
     824             :      * backslashed, and certain backslash combinations are interpreted
     825             :      * non-literally by COPY IN.  Disallowing all lower case ASCII letters is
     826             :      * more than strictly necessary, but seems best for consistency and
     827             :      * future-proofing.  Likewise we disallow all digits though only octal
     828             :      * digits are actually dangerous.
     829             :      */
     830       11522 :     if (!opts_out->csv_mode &&
     831       11024 :         strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
     832       11024 :                opts_out->delim[0]) != NULL)
     833          10 :         ereport(ERROR,
     834             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     835             :                  errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
     836             : 
     837             :     /* Check header */
     838       11512 :     if (opts_out->binary && opts_out->header_line != COPY_HEADER_FALSE)
     839           2 :         ereport(ERROR,
     840             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     841             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     842             :                  errmsg("cannot specify %s in BINARY mode", "HEADER")));
     843             : 
     844             :     /* Check quote */
     845       11510 :     if (!opts_out->csv_mode && opts_out->quote != NULL)
     846           4 :         ereport(ERROR,
     847             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     848             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     849             :                  errmsg("COPY %s requires CSV mode", "QUOTE")));
     850             : 
     851       11506 :     if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
     852           2 :         ereport(ERROR,
     853             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     854             :                  errmsg("COPY quote must be a single one-byte character")));
     855             : 
     856       11504 :     if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
     857           2 :         ereport(ERROR,
     858             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     859             :                  errmsg("COPY delimiter and quote must be different")));
     860             : 
     861             :     /* Check escape */
     862       11502 :     if (!opts_out->csv_mode && opts_out->escape != NULL)
     863           6 :         ereport(ERROR,
     864             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     865             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     866             :                  errmsg("COPY %s requires CSV mode", "ESCAPE")));
     867             : 
     868       11496 :     if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
     869           2 :         ereport(ERROR,
     870             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     871             :                  errmsg("COPY escape must be a single one-byte character")));
     872             : 
     873             :     /* Check force_quote */
     874       11494 :     if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
     875          12 :         ereport(ERROR,
     876             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     877             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     878             :                  errmsg("COPY %s requires CSV mode", "FORCE_QUOTE")));
     879       11482 :     if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
     880          12 :         ereport(ERROR,
     881             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     882             :         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     883             :          second %s is a COPY with direction, e.g. COPY TO */
     884             :                  errmsg("COPY %s cannot be used with %s", "FORCE_QUOTE",
     885             :                         "COPY FROM")));
     886             : 
     887             :     /* Check force_notnull */
     888       11470 :     if (!opts_out->csv_mode && (opts_out->force_notnull != NIL ||
     889       10982 :                                 opts_out->force_notnull_all))
     890          14 :         ereport(ERROR,
     891             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     892             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     893             :                  errmsg("COPY %s requires CSV mode", "FORCE_NOT_NULL")));
     894       11456 :     if ((opts_out->force_notnull != NIL || opts_out->force_notnull_all) &&
     895          50 :         !is_from)
     896          12 :         ereport(ERROR,
     897             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     898             :         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     899             :          second %s is a COPY with direction, e.g. COPY TO */
     900             :                  errmsg("COPY %s cannot be used with %s", "FORCE_NOT_NULL",
     901             :                         "COPY TO")));
     902             : 
     903             :     /* Check force_null */
     904       11444 :     if (!opts_out->csv_mode && (opts_out->force_null != NIL ||
     905       10970 :                                 opts_out->force_null_all))
     906          12 :         ereport(ERROR,
     907             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     908             :         /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
     909             :                  errmsg("COPY %s requires CSV mode", "FORCE_NULL")));
     910             : 
     911       11432 :     if ((opts_out->force_null != NIL || opts_out->force_null_all) &&
     912          50 :         !is_from)
     913          12 :         ereport(ERROR,
     914             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     915             :         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     916             :          second %s is a COPY with direction, e.g. COPY TO */
     917             :                  errmsg("COPY %s cannot be used with %s", "FORCE_NULL",
     918             :                         "COPY TO")));
     919             : 
     920             :     /* Don't allow the delimiter to appear in the null string. */
     921       11420 :     if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
     922           2 :         ereport(ERROR,
     923             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     924             :         /*- translator: %s is the name of a COPY option, e.g. NULL */
     925             :                  errmsg("COPY delimiter character must not appear in the %s specification",
     926             :                         "NULL")));
     927             : 
     928             :     /* Don't allow the CSV quote char to appear in the null string. */
     929       11418 :     if (opts_out->csv_mode &&
     930         454 :         strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
     931           2 :         ereport(ERROR,
     932             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     933             :         /*- translator: %s is the name of a COPY option, e.g. NULL */
     934             :                  errmsg("CSV quote character must not appear in the %s specification",
     935             :                         "NULL")));
     936             : 
     937             :     /* Check freeze */
     938       11416 :     if (opts_out->freeze && !is_from)
     939           0 :         ereport(ERROR,
     940             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     941             :         /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     942             :          second %s is a COPY with direction, e.g. COPY TO */
     943             :                  errmsg("COPY %s cannot be used with %s", "FREEZE",
     944             :                         "COPY TO")));
     945             : 
     946       11416 :     if (opts_out->default_print)
     947             :     {
     948          72 :         if (!is_from)
     949           6 :             ereport(ERROR,
     950             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     951             :             /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
     952             :              second %s is a COPY with direction, e.g. COPY TO */
     953             :                      errmsg("COPY %s cannot be used with %s", "DEFAULT",
     954             :                             "COPY TO")));
     955             : 
     956             :         /* Don't allow the delimiter to appear in the default string. */
     957          66 :         if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
     958           6 :             ereport(ERROR,
     959             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     960             :             /*- translator: %s is the name of a COPY option, e.g. NULL */
     961             :                      errmsg("COPY delimiter character must not appear in the %s specification",
     962             :                             "DEFAULT")));
     963             : 
     964             :         /* Don't allow the CSV quote char to appear in the default string. */
     965          60 :         if (opts_out->csv_mode &&
     966          30 :             strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
     967           6 :             ereport(ERROR,
     968             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     969             :             /*- translator: %s is the name of a COPY option, e.g. NULL */
     970             :                      errmsg("CSV quote character must not appear in the %s specification",
     971             :                             "DEFAULT")));
     972             : 
     973             :         /* Don't allow the NULL and DEFAULT string to be the same */
     974          54 :         if (opts_out->null_print_len == opts_out->default_print_len &&
     975          24 :             strncmp(opts_out->null_print, opts_out->default_print,
     976          24 :                     opts_out->null_print_len) == 0)
     977           6 :             ereport(ERROR,
     978             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     979             :                      errmsg("NULL specification and DEFAULT specification cannot be the same")));
     980             :     }
     981             :     /* Check on_error */
     982       11392 :     if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
     983           8 :         ereport(ERROR,
     984             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     985             :                  errmsg("only ON_ERROR STOP is allowed in BINARY mode")));
     986             : 
     987       11384 :     if (opts_out->reject_limit && !opts_out->on_error)
     988           8 :         ereport(ERROR,
     989             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     990             :         /*- translator: first and second %s are the names of COPY option, e.g.
     991             :          * ON_ERROR, third is the value of the COPY option, e.g. IGNORE */
     992             :                  errmsg("COPY %s requires %s to be set to %s",
     993             :                         "REJECT_LIMIT", "ON_ERROR", "IGNORE")));
     994       11376 : }
     995             : 
     996             : /*
     997             :  * CopyGetAttnums - build an integer list of attnums to be copied
     998             :  *
     999             :  * The input attnamelist is either the user-specified column list,
    1000             :  * or NIL if there was none (in which case we want all the non-dropped
    1001             :  * columns).
    1002             :  *
    1003             :  * We don't include generated columns in the generated full list and we don't
    1004             :  * allow them to be specified explicitly.  They don't make sense for COPY
    1005             :  * FROM, but we could possibly allow them for COPY TO.  But this way it's at
    1006             :  * least ensured that whatever we copy out can be copied back in.
    1007             :  *
    1008             :  * rel can be NULL ... it's only used for error reports.
    1009             :  */
    1010             : List *
    1011       22052 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
    1012             : {
    1013       22052 :     List       *attnums = NIL;
    1014             : 
    1015       22052 :     if (attnamelist == NIL)
    1016             :     {
    1017             :         /* Generate default column list */
    1018        3904 :         int         attr_count = tupDesc->natts;
    1019             :         int         i;
    1020             : 
    1021       13828 :         for (i = 0; i < attr_count; i++)
    1022             :         {
    1023        9924 :             CompactAttribute *attr = TupleDescCompactAttr(tupDesc, i);
    1024             : 
    1025        9924 :             if (attr->attisdropped || attr->attgenerated)
    1026         302 :                 continue;
    1027        9622 :             attnums = lappend_int(attnums, i + 1);
    1028             :         }
    1029             :     }
    1030             :     else
    1031             :     {
    1032             :         /* Validate the user-supplied list and extract attnums */
    1033             :         ListCell   *l;
    1034             : 
    1035       82974 :         foreach(l, attnamelist)
    1036             :         {
    1037       64910 :             char       *name = strVal(lfirst(l));
    1038             :             int         attnum;
    1039             :             int         i;
    1040             : 
    1041             :             /* Lookup column name */
    1042       64910 :             attnum = InvalidAttrNumber;
    1043     9929462 :             for (i = 0; i < tupDesc->natts; i++)
    1044             :             {
    1045     9929432 :                 Form_pg_attribute att = TupleDescAttr(tupDesc, i);
    1046             : 
    1047     9929432 :                 if (att->attisdropped)
    1048         792 :                     continue;
    1049     9928640 :                 if (namestrcmp(&(att->attname), name) == 0)
    1050             :                 {
    1051       64880 :                     if (att->attgenerated)
    1052          48 :                         ereport(ERROR,
    1053             :                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
    1054             :                                  errmsg("column \"%s\" is a generated column",
    1055             :                                         name),
    1056             :                                  errdetail("Generated columns cannot be used in COPY.")));
    1057       64832 :                     attnum = att->attnum;
    1058       64832 :                     break;
    1059             :                 }
    1060             :             }
    1061       64862 :             if (attnum == InvalidAttrNumber)
    1062             :             {
    1063          30 :                 if (rel != NULL)
    1064          30 :                     ereport(ERROR,
    1065             :                             (errcode(ERRCODE_UNDEFINED_COLUMN),
    1066             :                              errmsg("column \"%s\" of relation \"%s\" does not exist",
    1067             :                                     name, RelationGetRelationName(rel))));
    1068             :                 else
    1069           0 :                     ereport(ERROR,
    1070             :                             (errcode(ERRCODE_UNDEFINED_COLUMN),
    1071             :                              errmsg("column \"%s\" does not exist",
    1072             :                                     name)));
    1073             :             }
    1074             :             /* Check for duplicates */
    1075       64832 :             if (list_member_int(attnums, attnum))
    1076           6 :                 ereport(ERROR,
    1077             :                         (errcode(ERRCODE_DUPLICATE_COLUMN),
    1078             :                          errmsg("column \"%s\" specified more than once",
    1079             :                                 name)));
    1080       64826 :             attnums = lappend_int(attnums, attnum);
    1081             :         }
    1082             :     }
    1083             : 
    1084       21968 :     return attnums;
    1085             : }

Generated by: LCOV version 1.16