LCOV - code coverage report
Current view: top level - src/backend/commands - copy.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 457 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 7 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 571 0

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

Generated by: LCOV version 2.3.2-1