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

Generated by: LCOV version 1.16