LCOV - code coverage report
Current view: top level - src/backend/commands - copy.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.3 % 383 365
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 7 7
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         5657 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
      64              :        int stmt_location, int stmt_len,
      65              :        uint64 *processed)
      66              : {
      67         5657 :     bool        is_from = stmt->is_from;
      68         5657 :     bool        pipe = (stmt->filename == NULL);
      69              :     Relation    rel;
      70              :     Oid         relid;
      71         5657 :     RawStmt    *query = NULL;
      72         5657 :     Node       *whereClause = NULL;
      73              : 
      74              :     /*
      75              :      * Disallow COPY to/from file or program except to users with the
      76              :      * appropriate role.
      77              :      */
      78         5657 :     if (!pipe)
      79              :     {
      80          236 :         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          236 :             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          236 :             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         5657 :     if (stmt->relation)
     114              :     {
     115         5398 :         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         5398 :         rel = table_openrv(stmt->relation, lockmode);
     126              : 
     127         5397 :         relid = RelationGetRelid(rel);
     128              : 
     129         5397 :         nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
     130              :                                                NULL, false, false);
     131              : 
     132         5397 :         perminfo = nsitem->p_perminfo;
     133         5397 :         perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
     134              : 
     135         5397 :         if (stmt->whereClause)
     136              :         {
     137           36 :             Bitmapset  *expr_attrs = NULL;
     138              :             int         i;
     139              : 
     140              :             /* add nsitem to query namespace */
     141           36 :             addNSItemToQuery(pstate, nsitem, false, true, true);
     142              : 
     143              :             /* Transform the raw expression tree */
     144           36 :             whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
     145              : 
     146              :             /* Make sure it yields a boolean result. */
     147           21 :             whereClause = coerce_to_boolean(pstate, whereClause, "WHERE");
     148              : 
     149              :             /* we have to fix its collations too */
     150           21 :             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           21 :             pull_varattnos(whereClause, 1, &expr_attrs);
     158           21 :             if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
     159              :             {
     160           12 :                 expr_attrs = bms_add_range(expr_attrs,
     161              :                                            1 - FirstLowInvalidHeapAttributeNumber,
     162            6 :                                            RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
     163            6 :                 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
     164              :             }
     165              : 
     166           21 :             i = -1;
     167           36 :             while ((i = bms_next_member(expr_attrs, i)) >= 0)
     168              :             {
     169           27 :                 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           27 :                 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
     181           12 :                     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            9 :             whereClause = eval_const_expressions(NULL, whereClause);
     189              : 
     190            9 :             whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
     191            9 :             whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
     192              :         }
     193              : 
     194         5370 :         tupDesc = RelationGetDescr(rel);
     195         5370 :         attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
     196        23722 :         foreach(cur, attnums)
     197              :         {
     198              :             int         attno;
     199              :             Bitmapset **bms;
     200              : 
     201        18394 :             attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
     202        18394 :             bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
     203              : 
     204        18394 :             *bms = bms_add_member(*bms, attno);
     205              :         }
     206         5328 :         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         5286 :         if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
     223              :         {
     224              :             SelectStmt *select;
     225              :             ColumnRef  *cr;
     226              :             ResTarget  *target;
     227              :             RangeVar   *from;
     228           42 :             List       *targetList = NIL;
     229              : 
     230           42 :             if (is_from)
     231            3 :                 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           39 :             if (!stmt->attlist)
     249              :             {
     250           21 :                 cr = makeNode(ColumnRef);
     251           21 :                 cr->fields = list_make1(makeNode(A_Star));
     252           21 :                 cr->location = -1;
     253              : 
     254           21 :                 target = makeNode(ResTarget);
     255           21 :                 target->name = NULL;
     256           21 :                 target->indirection = NIL;
     257           21 :                 target->val = (Node *) cr;
     258           21 :                 target->location = -1;
     259              : 
     260           21 :                 targetList = list_make1(target);
     261              :             }
     262              :             else
     263              :             {
     264              :                 ListCell   *lc;
     265              : 
     266           51 :                 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           33 :                     cr = makeNode(ColumnRef);
     274           33 :                     cr->fields = list_make1(lfirst(lc));
     275           33 :                     cr->location = -1;
     276              : 
     277              :                     /* Build the ResTarget and add the ColumnRef to it. */
     278           33 :                     target = makeNode(ResTarget);
     279           33 :                     target->name = NULL;
     280           33 :                     target->indirection = NIL;
     281           33 :                     target->val = (Node *) cr;
     282           33 :                     target->location = -1;
     283              : 
     284              :                     /* Add each column to the SELECT statement's target list */
     285           33 :                     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           39 :             from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
     300           39 :                                 pstrdup(RelationGetRelationName(rel)),
     301              :                                 -1);
     302           39 :             from->inh = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
     303              : 
     304              :             /* Build query */
     305           39 :             select = makeNode(SelectStmt);
     306           39 :             select->targetList = targetList;
     307           39 :             select->fromClause = list_make1(from);
     308              : 
     309           39 :             query = makeNode(RawStmt);
     310           39 :             query->stmt = (Node *) select;
     311           39 :             query->stmt_location = stmt_location;
     312           39 :             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           39 :             table_close(rel, NoLock);
     321           39 :             rel = NULL;
     322              :         }
     323              :     }
     324              :     else
     325              :     {
     326              :         Assert(stmt->query);
     327              : 
     328          259 :         query = makeNode(RawStmt);
     329          259 :         query->stmt = stmt->query;
     330          259 :         query->stmt_location = stmt_location;
     331          259 :         query->stmt_len = stmt_len;
     332              : 
     333          259 :         relid = InvalidOid;
     334          259 :         rel = NULL;
     335              :     }
     336              : 
     337         5533 :     if (is_from)
     338              :     {
     339              :         CopyFromState cstate;
     340              : 
     341              :         Assert(rel);
     342              : 
     343              :         /* check read-only transaction and parallel mode */
     344          854 :         if (XactReadOnly && !rel->rd_islocaltemp)
     345            0 :             PreventCommandIfReadOnly("COPY FROM");
     346              : 
     347          854 :         cstate = BeginCopyFrom(pstate, rel, whereClause,
     348          854 :                                stmt->filename, stmt->is_program,
     349          854 :                                NULL, stmt->attlist, stmt->options);
     350          719 :         *processed = CopyFrom(cstate);  /* copy from file to database */
     351          600 :         EndCopyFrom(cstate);
     352              :     }
     353              :     else
     354              :     {
     355              :         CopyToState cstate;
     356              : 
     357         4679 :         cstate = BeginCopyTo(pstate, rel, query, relid,
     358         4679 :                              stmt->filename, stmt->is_program,
     359         4679 :                              NULL, stmt->attlist, stmt->options);
     360         4572 :         *processed = DoCopyTo(cstate);  /* copy from database to file */
     361         4571 :         EndCopyTo(cstate);
     362              :     }
     363              : 
     364         5171 :     if (rel != NULL)
     365         4943 :         table_close(rel, NoLock);
     366         5171 : }
     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          136 : defGetCopyHeaderOption(DefElem *def, bool is_from)
     377              : {
     378          136 :     int         ival = COPY_HEADER_FALSE;
     379              : 
     380              :     /*
     381              :      * If no parameter value given, assume "true" is meant.
     382              :      */
     383          136 :     if (def->arg == NULL)
     384           15 :         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          121 :     switch (nodeTag(def->arg))
     392              :     {
     393           15 :         case T_Integer:
     394           15 :             ival = intVal(def->arg);
     395           15 :             break;
     396          106 :         default:
     397              :             {
     398          106 :                 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          106 :                 if (pg_strcasecmp(sval, "true") == 0)
     405           32 :                     return COPY_HEADER_TRUE;
     406           74 :                 if (pg_strcasecmp(sval, "false") == 0)
     407            0 :                     return COPY_HEADER_FALSE;
     408           74 :                 if (pg_strcasecmp(sval, "on") == 0)
     409            0 :                     return COPY_HEADER_TRUE;
     410           74 :                 if (pg_strcasecmp(sval, "off") == 0)
     411            3 :                     return COPY_HEADER_FALSE;
     412           71 :                 if (pg_strcasecmp(sval, "match") == 0)
     413              :                 {
     414           43 :                     if (!is_from)
     415            3 :                         ereport(ERROR,
     416              :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     417              :                                  errmsg("cannot use \"%s\" with HEADER in COPY TO",
     418              :                                         sval)));
     419           40 :                     return COPY_HEADER_MATCH;
     420              :                 }
     421              :                 else
     422              :                 {
     423           28 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
     424              : 
     425              :                     /* Check if the header is a valid integer */
     426           28 :                     ival = pg_strtoint32_safe(sval, (Node *) &escontext);
     427           28 :                     if (escontext.error_occurred)
     428           11 :                         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           17 :             break;
     439              :     }
     440              : 
     441           32 :     if (ival < 0)
     442            7 :         ereport(ERROR,
     443              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     444              :                  errmsg("a negative integer value cannot be "
     445              :                         "specified for %s", def->defname)));
     446              : 
     447           25 :     if (!is_from && ival > 1)
     448            6 :         ereport(ERROR,
     449              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     450              :                  errmsg("cannot use multi-line header in COPY TO")));
     451              : 
     452           19 :     return ival;
     453              : }
     454              : 
     455              : /*
     456              :  * Extract a CopyOnErrorChoice value from a DefElem.
     457              :  */
     458              : static CopyOnErrorChoice
     459           57 : defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
     460              : {
     461           57 :     char       *sval = defGetString(def);
     462              : 
     463           57 :     if (!is_from)
     464            3 :         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           54 :     if (pg_strcasecmp(sval, "stop") == 0)
     475            3 :         return COPY_ON_ERROR_STOP;
     476           51 :     if (pg_strcasecmp(sval, "ignore") == 0)
     477           47 :         return COPY_ON_ERROR_IGNORE;
     478              : 
     479            4 :     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           19 : defGetCopyRejectLimitOption(DefElem *def)
     496              : {
     497              :     int64       reject_limit;
     498              : 
     499           19 :     if (def->arg == NULL)
     500            0 :         ereport(ERROR,
     501              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     502              :                  errmsg("%s requires a numeric value",
     503              :                         def->defname)));
     504           19 :     else if (IsA(def->arg, String))
     505            7 :         reject_limit = pg_strtoint64(strVal(def->arg));
     506              :     else
     507           12 :         reject_limit = defGetInt64(def);
     508              : 
     509           19 :     if (reject_limit <= 0)
     510            4 :         ereport(ERROR,
     511              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     512              :                  errmsg("REJECT_LIMIT (%" PRId64 ") must be greater than zero",
     513              :                         reject_limit)));
     514              : 
     515           15 :     return reject_limit;
     516              : }
     517              : 
     518              : /*
     519              :  * Extract a CopyLogVerbosityChoice value from a DefElem.
     520              :  */
     521              : static CopyLogVerbosityChoice
     522           23 : defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate)
     523              : {
     524              :     char       *sval;
     525              : 
     526              :     /*
     527              :      * Allow "silent", "default", or "verbose" values.
     528              :      */
     529           23 :     sval = defGetString(def);
     530           23 :     if (pg_strcasecmp(sval, "silent") == 0)
     531           10 :         return COPY_LOG_VERBOSITY_SILENT;
     532           13 :     if (pg_strcasecmp(sval, "default") == 0)
     533            3 :         return COPY_LOG_VERBOSITY_DEFAULT;
     534           10 :     if (pg_strcasecmp(sval, "verbose") == 0)
     535            6 :         return COPY_LOG_VERBOSITY_VERBOSE;
     536              : 
     537            4 :     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         5815 : ProcessCopyOptions(ParseState *pstate,
     563              :                    CopyFormatOptions *opts_out,
     564              :                    bool is_from,
     565              :                    List *options)
     566              : {
     567         5815 :     bool        format_specified = false;
     568         5815 :     bool        freeze_specified = false;
     569         5815 :     bool        header_specified = false;
     570         5815 :     bool        on_error_specified = false;
     571         5815 :     bool        log_verbosity_specified = false;
     572         5815 :     bool        reject_limit_specified = false;
     573              :     ListCell   *option;
     574              : 
     575              :     /* Support external use for option sanity checking */
     576         5815 :     if (opts_out == NULL)
     577           57 :         opts_out = palloc0_object(CopyFormatOptions);
     578              : 
     579         5815 :     opts_out->file_encoding = -1;
     580              : 
     581              :     /* Extract options from the statement node tree */
     582         6898 :     foreach(option, options)
     583              :     {
     584         1174 :         DefElem    *defel = lfirst_node(DefElem, option);
     585              : 
     586         1174 :         if (strcmp(defel->defname, "format") == 0)
     587              :         {
     588          350 :             char       *fmt = defGetString(defel);
     589              : 
     590          350 :             if (format_specified)
     591            3 :                 errorConflictingDefElem(defel, pstate);
     592          347 :             format_specified = true;
     593          347 :             if (strcmp(fmt, "text") == 0)
     594              :                  /* default format */ ;
     595          303 :             else if (strcmp(fmt, "csv") == 0)
     596          267 :                 opts_out->csv_mode = true;
     597           36 :             else if (strcmp(fmt, "binary") == 0)
     598           35 :                 opts_out->binary = true;
     599              :             else
     600            1 :                 ereport(ERROR,
     601              :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     602              :                          errmsg("COPY format \"%s\" not recognized", fmt),
     603              :                          parser_errposition(pstate, defel->location)));
     604              :         }
     605          824 :         else if (strcmp(defel->defname, "freeze") == 0)
     606              :         {
     607           42 :             if (freeze_specified)
     608            3 :                 errorConflictingDefElem(defel, pstate);
     609           39 :             freeze_specified = true;
     610           39 :             opts_out->freeze = defGetBoolean(defel);
     611              :         }
     612          782 :         else if (strcmp(defel->defname, "delimiter") == 0)
     613              :         {
     614          151 :             if (opts_out->delim)
     615            3 :                 errorConflictingDefElem(defel, pstate);
     616          148 :             opts_out->delim = defGetString(defel);
     617              :         }
     618          631 :         else if (strcmp(defel->defname, "null") == 0)
     619              :         {
     620           72 :             if (opts_out->null_print)
     621            3 :                 errorConflictingDefElem(defel, pstate);
     622           69 :             opts_out->null_print = defGetString(defel);
     623              :         }
     624          559 :         else if (strcmp(defel->defname, "default") == 0)
     625              :         {
     626           45 :             if (opts_out->default_print)
     627            0 :                 errorConflictingDefElem(defel, pstate);
     628           45 :             opts_out->default_print = defGetString(defel);
     629              :         }
     630          514 :         else if (strcmp(defel->defname, "header") == 0)
     631              :         {
     632          139 :             if (header_specified)
     633            3 :                 errorConflictingDefElem(defel, pstate);
     634          136 :             header_specified = true;
     635          136 :             opts_out->header_line = defGetCopyHeaderOption(defel, is_from);
     636              :         }
     637          375 :         else if (strcmp(defel->defname, "quote") == 0)
     638              :         {
     639           51 :             if (opts_out->quote)
     640            3 :                 errorConflictingDefElem(defel, pstate);
     641           48 :             opts_out->quote = defGetString(defel);
     642              :         }
     643          324 :         else if (strcmp(defel->defname, "escape") == 0)
     644              :         {
     645           47 :             if (opts_out->escape)
     646            3 :                 errorConflictingDefElem(defel, pstate);
     647           44 :             opts_out->escape = defGetString(defel);
     648              :         }
     649          277 :         else if (strcmp(defel->defname, "force_quote") == 0)
     650              :         {
     651           39 :             if (opts_out->force_quote || opts_out->force_quote_all)
     652            3 :                 errorConflictingDefElem(defel, pstate);
     653           36 :             if (defel->arg && IsA(defel->arg, A_Star))
     654           15 :                 opts_out->force_quote_all = true;
     655           21 :             else if (defel->arg && IsA(defel->arg, List))
     656           21 :                 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          238 :         else if (strcmp(defel->defname, "force_not_null") == 0)
     665              :         {
     666           44 :             if (opts_out->force_notnull || opts_out->force_notnull_all)
     667            6 :                 errorConflictingDefElem(defel, pstate);
     668           38 :             if (defel->arg && IsA(defel->arg, A_Star))
     669           15 :                 opts_out->force_notnull_all = true;
     670           23 :             else if (defel->arg && IsA(defel->arg, List))
     671           23 :                 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          194 :         else if (strcmp(defel->defname, "force_null") == 0)
     680              :         {
     681           44 :             if (opts_out->force_null || opts_out->force_null_all)
     682            6 :                 errorConflictingDefElem(defel, pstate);
     683           38 :             if (defel->arg && IsA(defel->arg, A_Star))
     684           15 :                 opts_out->force_null_all = true;
     685           23 :             else if (defel->arg && IsA(defel->arg, List))
     686           23 :                 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          150 :         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            9 :             if (opts_out->convert_selectively)
     702            3 :                 errorConflictingDefElem(defel, pstate);
     703            6 :             opts_out->convert_selectively = true;
     704            6 :             if (defel->arg == NULL || IsA(defel->arg, List))
     705            6 :                 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          141 :         else if (strcmp(defel->defname, "encoding") == 0)
     714              :         {
     715           36 :             if (opts_out->file_encoding >= 0)
     716            3 :                 errorConflictingDefElem(defel, pstate);
     717           33 :             opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
     718           33 :             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          105 :         else if (strcmp(defel->defname, "on_error") == 0)
     726              :         {
     727           60 :             if (on_error_specified)
     728            3 :                 errorConflictingDefElem(defel, pstate);
     729           57 :             on_error_specified = true;
     730           57 :             opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
     731              :         }
     732           45 :         else if (strcmp(defel->defname, "log_verbosity") == 0)
     733              :         {
     734           26 :             if (log_verbosity_specified)
     735            3 :                 errorConflictingDefElem(defel, pstate);
     736           23 :             log_verbosity_specified = true;
     737           23 :             opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
     738              :         }
     739           19 :         else if (strcmp(defel->defname, "reject_limit") == 0)
     740              :         {
     741           19 :             if (reject_limit_specified)
     742            0 :                 errorConflictingDefElem(defel, pstate);
     743           19 :             reject_limit_specified = true;
     744           19 :             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         5724 :     if (opts_out->binary && opts_out->delim)
     759            3 :         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         5721 :     if (opts_out->binary && opts_out->null_print)
     765            3 :         ereport(ERROR,
     766              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     767              :                  errmsg("cannot specify %s in BINARY mode", "NULL")));
     768              : 
     769         5718 :     if (opts_out->binary && opts_out->default_print)
     770            3 :         ereport(ERROR,
     771              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     772              :                  errmsg("cannot specify %s in BINARY mode", "DEFAULT")));
     773              : 
     774              :     /* Set defaults for omitted options */
     775         5715 :     if (!opts_out->delim)
     776         5573 :         opts_out->delim = opts_out->csv_mode ? "," : "\t";
     777              : 
     778         5715 :     if (!opts_out->null_print)
     779         5652 :         opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
     780         5715 :     opts_out->null_print_len = strlen(opts_out->null_print);
     781              : 
     782         5715 :     if (opts_out->csv_mode)
     783              :     {
     784          258 :         if (!opts_out->quote)
     785          215 :             opts_out->quote = "\"";
     786          258 :         if (!opts_out->escape)
     787          220 :             opts_out->escape = opts_out->quote;
     788              :     }
     789              : 
     790              :     /* Only single-byte delimiter strings are supported. */
     791         5715 :     if (strlen(opts_out->delim) != 1)
     792            1 :         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         5714 :     if (strchr(opts_out->delim, '\r') != NULL ||
     798         5714 :         strchr(opts_out->delim, '\n') != NULL)
     799            1 :         ereport(ERROR,
     800              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     801              :                  errmsg("COPY delimiter cannot be newline or carriage return")));
     802              : 
     803         5713 :     if (strchr(opts_out->null_print, '\r') != NULL ||
     804         5713 :         strchr(opts_out->null_print, '\n') != NULL)
     805            1 :         ereport(ERROR,
     806              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     807              :                  errmsg("COPY null representation cannot use newline or carriage return")));
     808              : 
     809         5712 :     if (opts_out->default_print)
     810              :     {
     811           42 :         opts_out->default_print_len = strlen(opts_out->default_print);
     812              : 
     813           42 :         if (strchr(opts_out->default_print, '\r') != NULL ||
     814           39 :             strchr(opts_out->default_print, '\n') != NULL)
     815            6 :             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         5706 :     if (!opts_out->csv_mode &&
     831         5451 :         strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
     832         5451 :                opts_out->delim[0]) != NULL)
     833            5 :         ereport(ERROR,
     834              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     835              :                  errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
     836              : 
     837              :     /* Check header */
     838         5701 :     if (opts_out->binary && opts_out->header_line != COPY_HEADER_FALSE)
     839            1 :         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         5700 :     if (!opts_out->csv_mode && opts_out->quote != NULL)
     846            2 :         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         5698 :     if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
     852            1 :         ereport(ERROR,
     853              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     854              :                  errmsg("COPY quote must be a single one-byte character")));
     855              : 
     856         5697 :     if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
     857            1 :         ereport(ERROR,
     858              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     859              :                  errmsg("COPY delimiter and quote must be different")));
     860              : 
     861              :     /* Check escape */
     862         5696 :     if (!opts_out->csv_mode && opts_out->escape != NULL)
     863            3 :         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         5693 :     if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
     869            1 :         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         5692 :     if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
     875            6 :         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         5686 :     if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
     880            6 :         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         5680 :     if (!opts_out->csv_mode && (opts_out->force_notnull != NIL ||
     889         5430 :                                 opts_out->force_notnull_all))
     890            7 :         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         5673 :     if ((opts_out->force_notnull != NIL || opts_out->force_notnull_all) &&
     895           25 :         !is_from)
     896            6 :         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         5667 :     if (!opts_out->csv_mode && (opts_out->force_null != NIL ||
     905         5424 :                                 opts_out->force_null_all))
     906            6 :         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         5661 :     if ((opts_out->force_null != NIL || opts_out->force_null_all) &&
     912           25 :         !is_from)
     913            6 :         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         5655 :     if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
     922            1 :         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         5654 :     if (opts_out->csv_mode &&
     930          233 :         strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
     931            1 :         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         5653 :     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         5653 :     if (opts_out->default_print)
     947              :     {
     948           36 :         if (!is_from)
     949            3 :             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           33 :         if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
     958            3 :             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           30 :         if (opts_out->csv_mode &&
     966           15 :             strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
     967            3 :             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           27 :         if (opts_out->null_print_len == opts_out->default_print_len &&
     975           12 :             strncmp(opts_out->null_print, opts_out->default_print,
     976           12 :                     opts_out->null_print_len) == 0)
     977            3 :             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         5641 :     if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
     983            4 :         ereport(ERROR,
     984              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     985              :                  errmsg("only ON_ERROR STOP is allowed in BINARY mode")));
     986              : 
     987         5637 :     if (opts_out->reject_limit && !opts_out->on_error)
     988            4 :         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         5633 : }
     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        10948 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
    1012              : {
    1013        10948 :     List       *attnums = NIL;
    1014              : 
    1015        10948 :     if (attnamelist == NIL)
    1016              :     {
    1017              :         /* Generate default column list */
    1018         1934 :         int         attr_count = tupDesc->natts;
    1019              :         int         i;
    1020              : 
    1021         6802 :         for (i = 0; i < attr_count; i++)
    1022              :         {
    1023         4868 :             CompactAttribute *attr = TupleDescCompactAttr(tupDesc, i);
    1024              : 
    1025         4868 :             if (attr->attisdropped || attr->attgenerated)
    1026          151 :                 continue;
    1027         4717 :             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        41247 :         foreach(l, attnamelist)
    1036              :         {
    1037        32275 :             char       *name = strVal(lfirst(l));
    1038              :             int         attnum;
    1039              :             int         i;
    1040              : 
    1041              :             /* Lookup column name */
    1042        32275 :             attnum = InvalidAttrNumber;
    1043      4964371 :             for (i = 0; i < tupDesc->natts; i++)
    1044              :             {
    1045      4964356 :                 Form_pg_attribute att = TupleDescAttr(tupDesc, i);
    1046              : 
    1047      4964356 :                 if (att->attisdropped)
    1048          396 :                     continue;
    1049      4963960 :                 if (namestrcmp(&(att->attname), name) == 0)
    1050              :                 {
    1051        32260 :                     if (att->attgenerated)
    1052           24 :                         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        32236 :                     attnum = att->attnum;
    1058        32236 :                     break;
    1059              :                 }
    1060              :             }
    1061        32251 :             if (attnum == InvalidAttrNumber)
    1062              :             {
    1063           15 :                 if (rel != NULL)
    1064           15 :                     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        32236 :             if (list_member_int(attnums, attnum))
    1076            3 :                 ereport(ERROR,
    1077              :                         (errcode(ERRCODE_DUPLICATE_COLUMN),
    1078              :                          errmsg("column \"%s\" specified more than once",
    1079              :                                 name)));
    1080        32233 :             attnums = lappend_int(attnums, attnum);
    1081              :         }
    1082              :     }
    1083              : 
    1084        10906 :     return attnums;
    1085              : }
        

Generated by: LCOV version 2.0-1