LCOV - code coverage report
Current view: top level - src/backend/commands - copy.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 95.6 % 411 393
Test Date: 2026-10-16 08:15:56 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 80.9 % 528 427

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

Generated by: LCOV version 2.0-1