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

Generated by: LCOV version 2.0-1