LCOV - code coverage report
Current view: top level - src/backend/commands - copy.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 287 311 92.3 %
Date: 2023-05-30 17:15:13 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * copy.c
       4             :  *      Implements the COPY utility command
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/copy.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include <ctype.h>
      18             : #include <unistd.h>
      19             : #include <sys/stat.h>
      20             : 
      21             : #include "access/sysattr.h"
      22             : #include "access/table.h"
      23             : #include "access/xact.h"
      24             : #include "catalog/pg_authid.h"
      25             : #include "commands/copy.h"
      26             : #include "commands/defrem.h"
      27             : #include "executor/executor.h"
      28             : #include "mb/pg_wchar.h"
      29             : #include "miscadmin.h"
      30             : #include "nodes/makefuncs.h"
      31             : #include "optimizer/optimizer.h"
      32             : #include "parser/parse_coerce.h"
      33             : #include "parser/parse_collate.h"
      34             : #include "parser/parse_expr.h"
      35             : #include "parser/parse_relation.h"
      36             : #include "rewrite/rewriteHandler.h"
      37             : #include "utils/acl.h"
      38             : #include "utils/builtins.h"
      39             : #include "utils/lsyscache.h"
      40             : #include "utils/memutils.h"
      41             : #include "utils/rel.h"
      42             : #include "utils/rls.h"
      43             : 
      44             : /*
      45             :  *   DoCopy executes the SQL COPY statement
      46             :  *
      47             :  * Either unload or reload contents of table <relation>, depending on <from>.
      48             :  * (<from> = true means we are inserting into the table.)  In the "TO" case
      49             :  * we also support copying the output of an arbitrary SELECT, INSERT, UPDATE
      50             :  * or DELETE query.
      51             :  *
      52             :  * If <pipe> is false, transfer is between the table and the file named
      53             :  * <filename>.  Otherwise, transfer is between the table and our regular
      54             :  * input/output stream. The latter could be either stdin/stdout or a
      55             :  * socket, depending on whether we're running under Postmaster control.
      56             :  *
      57             :  * Do not allow a Postgres user without the 'pg_read_server_files' or
      58             :  * 'pg_write_server_files' role to read from or write to a file.
      59             :  *
      60             :  * Do not allow the copy if user doesn't have proper permission to access
      61             :  * the table or the specifically requested columns.
      62             :  */
      63             : void
      64        8890 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
      65             :        int stmt_location, int stmt_len,
      66             :        uint64 *processed)
      67             : {
      68        8890 :     bool        is_from = stmt->is_from;
      69        8890 :     bool        pipe = (stmt->filename == NULL);
      70             :     Relation    rel;
      71             :     Oid         relid;
      72        8890 :     RawStmt    *query = NULL;
      73        8890 :     Node       *whereClause = NULL;
      74             : 
      75             :     /*
      76             :      * Disallow COPY to/from file or program except to users with the
      77             :      * appropriate role.
      78             :      */
      79        8890 :     if (!pipe)
      80             :     {
      81         892 :         if (stmt->is_program)
      82             :         {
      83           0 :             if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM))
      84           0 :                 ereport(ERROR,
      85             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      86             :                          errmsg("permission denied to COPY to or from an external program"),
      87             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY to or from an external program.",
      88             :                                    "pg_execute_server_program"),
      89             :                          errhint("Anyone can COPY to stdout or from stdin. "
      90             :                                  "psql's \\copy command also works for anyone.")));
      91             :         }
      92             :         else
      93             :         {
      94         892 :             if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
      95           0 :                 ereport(ERROR,
      96             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      97             :                          errmsg("permission denied to COPY from a file"),
      98             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY from a file.",
      99             :                                    "pg_read_server_files"),
     100             :                          errhint("Anyone can COPY to stdout or from stdin. "
     101             :                                  "psql's \\copy command also works for anyone.")));
     102             : 
     103         892 :             if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
     104           0 :                 ereport(ERROR,
     105             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     106             :                          errmsg("permission denied to COPY to a file"),
     107             :                          errdetail("Only roles with privileges of the \"%s\" role may COPY to a file.",
     108             :                                    "pg_write_server_files"),
     109             :                          errhint("Anyone can COPY to stdout or from stdin. "
     110             :                                  "psql's \\copy command also works for anyone.")));
     111             :         }
     112             :     }
     113             : 
     114        8890 :     if (stmt->relation)
     115             :     {
     116        8504 :         LOCKMODE    lockmode = is_from ? RowExclusiveLock : AccessShareLock;
     117             :         ParseNamespaceItem *nsitem;
     118             :         RTEPermissionInfo *perminfo;
     119             :         TupleDesc   tupDesc;
     120             :         List       *attnums;
     121             :         ListCell   *cur;
     122             : 
     123             :         Assert(!stmt->query);
     124             : 
     125             :         /* Open and lock the relation, using the appropriate lock type. */
     126        8504 :         rel = table_openrv(stmt->relation, lockmode);
     127             : 
     128        8502 :         relid = RelationGetRelid(rel);
     129             : 
     130        8502 :         nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
     131             :                                                NULL, false, false);
     132             : 
     133        8502 :         perminfo = nsitem->p_perminfo;
     134        8502 :         perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
     135             : 
     136        8502 :         if (stmt->whereClause)
     137             :         {
     138             :             /* add nsitem to query namespace */
     139          48 :             addNSItemToQuery(pstate, nsitem, false, true, true);
     140             : 
     141             :             /* Transform the raw expression tree */
     142          48 :             whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
     143             : 
     144             :             /* Make sure it yields a boolean result. */
     145          18 :             whereClause = coerce_to_boolean(pstate, whereClause, "WHERE");
     146             : 
     147             :             /* we have to fix its collations too */
     148          18 :             assign_expr_collations(pstate, whereClause);
     149             : 
     150          18 :             whereClause = eval_const_expressions(NULL, whereClause);
     151             : 
     152          18 :             whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
     153          18 :             whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
     154             :         }
     155             : 
     156        8472 :         tupDesc = RelationGetDescr(rel);
     157        8472 :         attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
     158       39994 :         foreach(cur, attnums)
     159             :         {
     160             :             int         attno;
     161             :             Bitmapset **bms;
     162             : 
     163       31582 :             attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
     164       31582 :             bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
     165             : 
     166       31582 :             *bms = bms_add_member(*bms, attno);
     167             :         }
     168        8412 :         ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true);
     169             : 
     170             :         /*
     171             :          * Permission check for row security policies.
     172             :          *
     173             :          * check_enable_rls will ereport(ERROR) if the user has requested
     174             :          * something invalid and will otherwise indicate if we should enable
     175             :          * RLS (returns RLS_ENABLED) or not for this COPY statement.
     176             :          *
     177             :          * If the relation has a row security policy and we are to apply it
     178             :          * then perform a "query" copy and allow the normal query processing
     179             :          * to handle the policies.
     180             :          *
     181             :          * If RLS is not enabled for this, then just fall through to the
     182             :          * normal non-filtering relation handling.
     183             :          */
     184        8328 :         if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
     185             :         {
     186             :             SelectStmt *select;
     187             :             ColumnRef  *cr;
     188             :             ResTarget  *target;
     189             :             RangeVar   *from;
     190          60 :             List       *targetList = NIL;
     191             : 
     192          60 :             if (is_from)
     193           6 :                 ereport(ERROR,
     194             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     195             :                          errmsg("COPY FROM not supported with row-level security"),
     196             :                          errhint("Use INSERT statements instead.")));
     197             : 
     198             :             /*
     199             :              * Build target list
     200             :              *
     201             :              * If no columns are specified in the attribute list of the COPY
     202             :              * command, then the target list is 'all' columns. Therefore, '*'
     203             :              * should be used as the target list for the resulting SELECT
     204             :              * statement.
     205             :              *
     206             :              * In the case that columns are specified in the attribute list,
     207             :              * create a ColumnRef and ResTarget for each column and add them
     208             :              * to the target list for the resulting SELECT statement.
     209             :              */
     210          54 :             if (!stmt->attlist)
     211             :             {
     212          18 :                 cr = makeNode(ColumnRef);
     213          18 :                 cr->fields = list_make1(makeNode(A_Star));
     214          18 :                 cr->location = -1;
     215             : 
     216          18 :                 target = makeNode(ResTarget);
     217          18 :                 target->name = NULL;
     218          18 :                 target->indirection = NIL;
     219          18 :                 target->val = (Node *) cr;
     220          18 :                 target->location = -1;
     221             : 
     222          18 :                 targetList = list_make1(target);
     223             :             }
     224             :             else
     225             :             {
     226             :                 ListCell   *lc;
     227             : 
     228         102 :                 foreach(lc, stmt->attlist)
     229             :                 {
     230             :                     /*
     231             :                      * Build the ColumnRef for each column.  The ColumnRef
     232             :                      * 'fields' property is a String node that corresponds to
     233             :                      * the column name respectively.
     234             :                      */
     235          66 :                     cr = makeNode(ColumnRef);
     236          66 :                     cr->fields = list_make1(lfirst(lc));
     237          66 :                     cr->location = -1;
     238             : 
     239             :                     /* Build the ResTarget and add the ColumnRef to it. */
     240          66 :                     target = makeNode(ResTarget);
     241          66 :                     target->name = NULL;
     242          66 :                     target->indirection = NIL;
     243          66 :                     target->val = (Node *) cr;
     244          66 :                     target->location = -1;
     245             : 
     246             :                     /* Add each column to the SELECT statement's target list */
     247          66 :                     targetList = lappend(targetList, target);
     248             :                 }
     249             :             }
     250             : 
     251             :             /*
     252             :              * Build RangeVar for from clause, fully qualified based on the
     253             :              * relation which we have opened and locked.  Use "ONLY" so that
     254             :              * COPY retrieves rows from only the target table not any
     255             :              * inheritance children, the same as when RLS doesn't apply.
     256             :              */
     257          54 :             from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
     258          54 :                                 pstrdup(RelationGetRelationName(rel)),
     259             :                                 -1);
     260          54 :             from->inh = false;   /* apply ONLY */
     261             : 
     262             :             /* Build query */
     263          54 :             select = makeNode(SelectStmt);
     264          54 :             select->targetList = targetList;
     265          54 :             select->fromClause = list_make1(from);
     266             : 
     267          54 :             query = makeNode(RawStmt);
     268          54 :             query->stmt = (Node *) select;
     269          54 :             query->stmt_location = stmt_location;
     270          54 :             query->stmt_len = stmt_len;
     271             : 
     272             :             /*
     273             :              * Close the relation for now, but keep the lock on it to prevent
     274             :              * changes between now and when we start the query-based COPY.
     275             :              *
     276             :              * We'll reopen it later as part of the query-based COPY.
     277             :              */
     278          54 :             table_close(rel, NoLock);
     279          54 :             rel = NULL;
     280             :         }
     281             :     }
     282             :     else
     283             :     {
     284             :         Assert(stmt->query);
     285             : 
     286             :         /* MERGE is allowed by parser, but unimplemented. Reject for now */
     287         386 :         if (IsA(stmt->query, MergeStmt))
     288           6 :             ereport(ERROR,
     289             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     290             :                     errmsg("MERGE not supported in COPY"));
     291             : 
     292         380 :         query = makeNode(RawStmt);
     293         380 :         query->stmt = stmt->query;
     294         380 :         query->stmt_location = stmt_location;
     295         380 :         query->stmt_len = stmt_len;
     296             : 
     297         380 :         relid = InvalidOid;
     298         380 :         rel = NULL;
     299             :     }
     300             : 
     301        8684 :     if (is_from)
     302             :     {
     303             :         CopyFromState cstate;
     304             : 
     305             :         Assert(rel);
     306             : 
     307             :         /* check read-only transaction and parallel mode */
     308        1800 :         if (XactReadOnly && !rel->rd_islocaltemp)
     309           0 :             PreventCommandIfReadOnly("COPY FROM");
     310             : 
     311        1800 :         cstate = BeginCopyFrom(pstate, rel, whereClause,
     312        1800 :                                stmt->filename, stmt->is_program,
     313             :                                NULL, stmt->attlist, stmt->options);
     314        1668 :         *processed = CopyFrom(cstate);  /* copy from file to database */
     315        1498 :         EndCopyFrom(cstate);
     316             :     }
     317             :     else
     318             :     {
     319             :         CopyToState cstate;
     320             : 
     321        6884 :         cstate = BeginCopyTo(pstate, rel, query, relid,
     322        6884 :                              stmt->filename, stmt->is_program,
     323             :                              NULL, stmt->attlist, stmt->options);
     324        6696 :         *processed = DoCopyTo(cstate);  /* copy from database to file */
     325        6694 :         EndCopyTo(cstate);
     326             :     }
     327             : 
     328        8192 :     if (rel != NULL)
     329        7886 :         table_close(rel, NoLock);
     330        8192 : }
     331             : 
     332             : /*
     333             :  * Extract a CopyHeaderChoice value from a DefElem.  This is like
     334             :  * defGetBoolean() but also accepts the special value "match".
     335             :  */
     336             : static CopyHeaderChoice
     337         156 : defGetCopyHeaderChoice(DefElem *def, bool is_from)
     338             : {
     339             :     /*
     340             :      * If no parameter value given, assume "true" is meant.
     341             :      */
     342         156 :     if (def->arg == NULL)
     343          12 :         return COPY_HEADER_TRUE;
     344             : 
     345             :     /*
     346             :      * Allow 0, 1, "true", "false", "on", "off", or "match".
     347             :      */
     348         144 :     switch (nodeTag(def->arg))
     349             :     {
     350           0 :         case T_Integer:
     351           0 :             switch (intVal(def->arg))
     352             :             {
     353           0 :                 case 0:
     354           0 :                     return COPY_HEADER_FALSE;
     355           0 :                 case 1:
     356           0 :                     return COPY_HEADER_TRUE;
     357           0 :                 default:
     358             :                     /* otherwise, error out below */
     359           0 :                     break;
     360             :             }
     361           0 :             break;
     362         144 :         default:
     363             :             {
     364         144 :                 char       *sval = defGetString(def);
     365             : 
     366             :                 /*
     367             :                  * The set of strings accepted here should match up with the
     368             :                  * grammar's opt_boolean_or_string production.
     369             :                  */
     370         144 :                 if (pg_strcasecmp(sval, "true") == 0)
     371          46 :                     return COPY_HEADER_TRUE;
     372          98 :                 if (pg_strcasecmp(sval, "false") == 0)
     373           0 :                     return COPY_HEADER_FALSE;
     374          98 :                 if (pg_strcasecmp(sval, "on") == 0)
     375           0 :                     return COPY_HEADER_TRUE;
     376          98 :                 if (pg_strcasecmp(sval, "off") == 0)
     377           6 :                     return COPY_HEADER_FALSE;
     378          92 :                 if (pg_strcasecmp(sval, "match") == 0)
     379             :                 {
     380          86 :                     if (!is_from)
     381           6 :                         ereport(ERROR,
     382             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     383             :                                  errmsg("cannot use \"%s\" with HEADER in COPY TO",
     384             :                                         sval)));
     385          80 :                     return COPY_HEADER_MATCH;
     386             :                 }
     387             :             }
     388           6 :             break;
     389             :     }
     390           6 :     ereport(ERROR,
     391             :             (errcode(ERRCODE_SYNTAX_ERROR),
     392             :              errmsg("%s requires a Boolean value or \"match\"",
     393             :                     def->defname)));
     394             :     return COPY_HEADER_FALSE;   /* keep compiler quiet */
     395             : }
     396             : 
     397             : /*
     398             :  * Process the statement option list for COPY.
     399             :  *
     400             :  * Scan the options list (a list of DefElem) and transpose the information
     401             :  * into *opts_out, applying appropriate error checking.
     402             :  *
     403             :  * If 'opts_out' is not NULL, it is assumed to be filled with zeroes initially.
     404             :  *
     405             :  * This is exported so that external users of the COPY API can sanity-check
     406             :  * a list of options.  In that usage, 'opts_out' can be passed as NULL and
     407             :  * the collected data is just leaked until CurrentMemoryContext is reset.
     408             :  *
     409             :  * Note that additional checking, such as whether column names listed in FORCE
     410             :  * QUOTE actually exist, has to be applied later.  This just checks for
     411             :  * self-consistency of the options list.
     412             :  */
     413             : void
     414        9134 : ProcessCopyOptions(ParseState *pstate,
     415             :                    CopyFormatOptions *opts_out,
     416             :                    bool is_from,
     417             :                    List *options)
     418             : {
     419        9134 :     bool        format_specified = false;
     420        9134 :     bool        freeze_specified = false;
     421        9134 :     bool        header_specified = false;
     422             :     ListCell   *option;
     423             : 
     424             :     /* Support external use for option sanity checking */
     425        9134 :     if (opts_out == NULL)
     426          86 :         opts_out = (CopyFormatOptions *) palloc0(sizeof(CopyFormatOptions));
     427             : 
     428        9134 :     opts_out->file_encoding = -1;
     429             : 
     430             :     /* Extract options from the statement node tree */
     431       10612 :     foreach(option, options)
     432             :     {
     433        1564 :         DefElem    *defel = lfirst_node(DefElem, option);
     434             : 
     435        1564 :         if (strcmp(defel->defname, "format") == 0)
     436             :         {
     437         486 :             char       *fmt = defGetString(defel);
     438             : 
     439         486 :             if (format_specified)
     440           6 :                 errorConflictingDefElem(defel, pstate);
     441         480 :             format_specified = true;
     442         480 :             if (strcmp(fmt, "text") == 0)
     443             :                  /* default format */ ;
     444         422 :             else if (strcmp(fmt, "csv") == 0)
     445         364 :                 opts_out->csv_mode = true;
     446          58 :             else if (strcmp(fmt, "binary") == 0)
     447          56 :                 opts_out->binary = true;
     448             :             else
     449           2 :                 ereport(ERROR,
     450             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     451             :                          errmsg("COPY format \"%s\" not recognized", fmt),
     452             :                          parser_errposition(pstate, defel->location)));
     453             :         }
     454        1078 :         else if (strcmp(defel->defname, "freeze") == 0)
     455             :         {
     456          70 :             if (freeze_specified)
     457           6 :                 errorConflictingDefElem(defel, pstate);
     458          64 :             freeze_specified = true;
     459          64 :             opts_out->freeze = defGetBoolean(defel);
     460             :         }
     461        1008 :         else if (strcmp(defel->defname, "delimiter") == 0)
     462             :         {
     463         266 :             if (opts_out->delim)
     464           6 :                 errorConflictingDefElem(defel, pstate);
     465         260 :             opts_out->delim = defGetString(defel);
     466             :         }
     467         742 :         else if (strcmp(defel->defname, "null") == 0)
     468             :         {
     469         126 :             if (opts_out->null_print)
     470           6 :                 errorConflictingDefElem(defel, pstate);
     471         120 :             opts_out->null_print = defGetString(defel);
     472             :         }
     473         616 :         else if (strcmp(defel->defname, "default") == 0)
     474             :         {
     475          84 :             if (opts_out->default_print)
     476           0 :                 errorConflictingDefElem(defel, pstate);
     477          84 :             opts_out->default_print = defGetString(defel);
     478             :         }
     479         532 :         else if (strcmp(defel->defname, "header") == 0)
     480             :         {
     481         162 :             if (header_specified)
     482           6 :                 errorConflictingDefElem(defel, pstate);
     483         156 :             header_specified = true;
     484         156 :             opts_out->header_line = defGetCopyHeaderChoice(defel, is_from);
     485             :         }
     486         370 :         else if (strcmp(defel->defname, "quote") == 0)
     487             :         {
     488          84 :             if (opts_out->quote)
     489           6 :                 errorConflictingDefElem(defel, pstate);
     490          78 :             opts_out->quote = defGetString(defel);
     491             :         }
     492         286 :         else if (strcmp(defel->defname, "escape") == 0)
     493             :         {
     494          76 :             if (opts_out->escape)
     495           6 :                 errorConflictingDefElem(defel, pstate);
     496          70 :             opts_out->escape = defGetString(defel);
     497             :         }
     498         210 :         else if (strcmp(defel->defname, "force_quote") == 0)
     499             :         {
     500          66 :             if (opts_out->force_quote || opts_out->force_quote_all)
     501           6 :                 errorConflictingDefElem(defel, pstate);
     502          60 :             if (defel->arg && IsA(defel->arg, A_Star))
     503          18 :                 opts_out->force_quote_all = true;
     504          42 :             else if (defel->arg && IsA(defel->arg, List))
     505          42 :                 opts_out->force_quote = castNode(List, defel->arg);
     506             :             else
     507           0 :                 ereport(ERROR,
     508             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     509             :                          errmsg("argument to option \"%s\" must be a list of column names",
     510             :                                 defel->defname),
     511             :                          parser_errposition(pstate, defel->location)));
     512             :         }
     513         144 :         else if (strcmp(defel->defname, "force_not_null") == 0)
     514             :         {
     515          52 :             if (opts_out->force_notnull)
     516           6 :                 errorConflictingDefElem(defel, pstate);
     517          46 :             if (defel->arg && IsA(defel->arg, List))
     518          46 :                 opts_out->force_notnull = castNode(List, defel->arg);
     519             :             else
     520           0 :                 ereport(ERROR,
     521             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     522             :                          errmsg("argument to option \"%s\" must be a list of column names",
     523             :                                 defel->defname),
     524             :                          parser_errposition(pstate, defel->location)));
     525             :         }
     526          92 :         else if (strcmp(defel->defname, "force_null") == 0)
     527             :         {
     528          52 :             if (opts_out->force_null)
     529           6 :                 errorConflictingDefElem(defel, pstate);
     530          46 :             if (defel->arg && IsA(defel->arg, List))
     531          46 :                 opts_out->force_null = castNode(List, defel->arg);
     532             :             else
     533           0 :                 ereport(ERROR,
     534             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     535             :                          errmsg("argument to option \"%s\" must be a list of column names",
     536             :                                 defel->defname),
     537             :                          parser_errposition(pstate, defel->location)));
     538             :         }
     539          40 :         else if (strcmp(defel->defname, "convert_selectively") == 0)
     540             :         {
     541             :             /*
     542             :              * Undocumented, not-accessible-from-SQL option: convert only the
     543             :              * named columns to binary form, storing the rest as NULLs. It's
     544             :              * allowed for the column list to be NIL.
     545             :              */
     546          16 :             if (opts_out->convert_selectively)
     547           6 :                 errorConflictingDefElem(defel, pstate);
     548          10 :             opts_out->convert_selectively = true;
     549          10 :             if (defel->arg == NULL || IsA(defel->arg, List))
     550          10 :                 opts_out->convert_select = castNode(List, defel->arg);
     551             :             else
     552           0 :                 ereport(ERROR,
     553             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     554             :                          errmsg("argument to option \"%s\" must be a list of column names",
     555             :                                 defel->defname),
     556             :                          parser_errposition(pstate, defel->location)));
     557             :         }
     558          24 :         else if (strcmp(defel->defname, "encoding") == 0)
     559             :         {
     560          24 :             if (opts_out->file_encoding >= 0)
     561           6 :                 errorConflictingDefElem(defel, pstate);
     562          18 :             opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
     563          18 :             if (opts_out->file_encoding < 0)
     564           0 :                 ereport(ERROR,
     565             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     566             :                          errmsg("argument to option \"%s\" must be a valid encoding name",
     567             :                                 defel->defname),
     568             :                          parser_errposition(pstate, defel->location)));
     569             :         }
     570             :         else
     571           0 :             ereport(ERROR,
     572             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     573             :                      errmsg("option \"%s\" not recognized",
     574             :                             defel->defname),
     575             :                      parser_errposition(pstate, defel->location)));
     576             :     }
     577             : 
     578             :     /*
     579             :      * Check for incompatible options (must do these two before inserting
     580             :      * defaults)
     581             :      */
     582        9048 :     if (opts_out->binary && opts_out->delim)
     583           6 :         ereport(ERROR,
     584             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     585             :                  errmsg("cannot specify DELIMITER in BINARY mode")));
     586             : 
     587        9042 :     if (opts_out->binary && opts_out->null_print)
     588           6 :         ereport(ERROR,
     589             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     590             :                  errmsg("cannot specify NULL in BINARY mode")));
     591             : 
     592        9036 :     if (opts_out->binary && opts_out->default_print)
     593           6 :         ereport(ERROR,
     594             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     595             :                  errmsg("cannot specify DEFAULT in BINARY mode")));
     596             : 
     597             :     /* Set defaults for omitted options */
     598        9030 :     if (!opts_out->delim)
     599        8782 :         opts_out->delim = opts_out->csv_mode ? "," : "\t";
     600             : 
     601        9030 :     if (!opts_out->null_print)
     602        8922 :         opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
     603        9030 :     opts_out->null_print_len = strlen(opts_out->null_print);
     604             : 
     605        9030 :     if (opts_out->csv_mode)
     606             :     {
     607         358 :         if (!opts_out->quote)
     608         290 :             opts_out->quote = "\"";
     609         358 :         if (!opts_out->escape)
     610         300 :             opts_out->escape = opts_out->quote;
     611             :     }
     612             : 
     613             :     /* Only single-byte delimiter strings are supported. */
     614        9030 :     if (strlen(opts_out->delim) != 1)
     615           2 :         ereport(ERROR,
     616             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     617             :                  errmsg("COPY delimiter must be a single one-byte character")));
     618             : 
     619             :     /* Disallow end-of-line characters */
     620        9028 :     if (strchr(opts_out->delim, '\r') != NULL ||
     621        9028 :         strchr(opts_out->delim, '\n') != NULL)
     622           2 :         ereport(ERROR,
     623             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     624             :                  errmsg("COPY delimiter cannot be newline or carriage return")));
     625             : 
     626        9026 :     if (strchr(opts_out->null_print, '\r') != NULL ||
     627        9026 :         strchr(opts_out->null_print, '\n') != NULL)
     628           2 :         ereport(ERROR,
     629             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     630             :                  errmsg("COPY null representation cannot use newline or carriage return")));
     631             : 
     632        9024 :     if (opts_out->default_print)
     633             :     {
     634          78 :         opts_out->default_print_len = strlen(opts_out->default_print);
     635             : 
     636          78 :         if (strchr(opts_out->default_print, '\r') != NULL ||
     637          72 :             strchr(opts_out->default_print, '\n') != NULL)
     638          12 :             ereport(ERROR,
     639             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     640             :                      errmsg("COPY default representation cannot use newline or carriage return")));
     641             :     }
     642             : 
     643             :     /*
     644             :      * Disallow unsafe delimiter characters in non-CSV mode.  We can't allow
     645             :      * backslash because it would be ambiguous.  We can't allow the other
     646             :      * cases because data characters matching the delimiter must be
     647             :      * backslashed, and certain backslash combinations are interpreted
     648             :      * non-literally by COPY IN.  Disallowing all lower case ASCII letters is
     649             :      * more than strictly necessary, but seems best for consistency and
     650             :      * future-proofing.  Likewise we disallow all digits though only octal
     651             :      * digits are actually dangerous.
     652             :      */
     653        9012 :     if (!opts_out->csv_mode &&
     654        8660 :         strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
     655        8660 :                opts_out->delim[0]) != NULL)
     656          10 :         ereport(ERROR,
     657             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     658             :                  errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
     659             : 
     660             :     /* Check header */
     661        9002 :     if (opts_out->binary && opts_out->header_line)
     662           2 :         ereport(ERROR,
     663             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     664             :                  errmsg("cannot specify HEADER in BINARY mode")));
     665             : 
     666             :     /* Check quote */
     667        9000 :     if (!opts_out->csv_mode && opts_out->quote != NULL)
     668           4 :         ereport(ERROR,
     669             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     670             :                  errmsg("COPY quote available only in CSV mode")));
     671             : 
     672        8996 :     if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
     673           2 :         ereport(ERROR,
     674             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     675             :                  errmsg("COPY quote must be a single one-byte character")));
     676             : 
     677        8994 :     if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
     678           2 :         ereport(ERROR,
     679             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     680             :                  errmsg("COPY delimiter and quote must be different")));
     681             : 
     682             :     /* Check escape */
     683        8992 :     if (!opts_out->csv_mode && opts_out->escape != NULL)
     684           6 :         ereport(ERROR,
     685             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     686             :                  errmsg("COPY escape available only in CSV mode")));
     687             : 
     688        8986 :     if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
     689           2 :         ereport(ERROR,
     690             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     691             :                  errmsg("COPY escape must be a single one-byte character")));
     692             : 
     693             :     /* Check force_quote */
     694        8984 :     if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
     695           6 :         ereport(ERROR,
     696             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     697             :                  errmsg("COPY force quote available only in CSV mode")));
     698        8978 :     if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
     699           6 :         ereport(ERROR,
     700             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     701             :                  errmsg("COPY force quote only available using COPY TO")));
     702             : 
     703             :     /* Check force_notnull */
     704        8972 :     if (!opts_out->csv_mode && opts_out->force_notnull != NIL)
     705           8 :         ereport(ERROR,
     706             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     707             :                  errmsg("COPY force not null available only in CSV mode")));
     708        8964 :     if (opts_out->force_notnull != NIL && !is_from)
     709           6 :         ereport(ERROR,
     710             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     711             :                  errmsg("COPY force not null only available using COPY FROM")));
     712             : 
     713             :     /* Check force_null */
     714        8958 :     if (!opts_out->csv_mode && opts_out->force_null != NIL)
     715           6 :         ereport(ERROR,
     716             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     717             :                  errmsg("COPY force null available only in CSV mode")));
     718             : 
     719        8952 :     if (opts_out->force_null != NIL && !is_from)
     720           6 :         ereport(ERROR,
     721             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     722             :                  errmsg("COPY force null only available using COPY FROM")));
     723             : 
     724             :     /* Don't allow the delimiter to appear in the null string. */
     725        8946 :     if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
     726           2 :         ereport(ERROR,
     727             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     728             :                  errmsg("COPY delimiter must not appear in the NULL specification")));
     729             : 
     730             :     /* Don't allow the CSV quote char to appear in the null string. */
     731        8944 :     if (opts_out->csv_mode &&
     732         326 :         strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
     733           2 :         ereport(ERROR,
     734             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     735             :                  errmsg("CSV quote character must not appear in the NULL specification")));
     736             : 
     737        8942 :     if (opts_out->default_print)
     738             :     {
     739          66 :         if (!is_from)
     740           6 :             ereport(ERROR,
     741             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     742             :                      errmsg("COPY DEFAULT only available using COPY FROM")));
     743             : 
     744             :         /* Don't allow the delimiter to appear in the default string. */
     745          60 :         if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
     746           6 :             ereport(ERROR,
     747             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     748             :                      errmsg("COPY delimiter must not appear in the DEFAULT specification")));
     749             : 
     750             :         /* Don't allow the CSV quote char to appear in the default string. */
     751          54 :         if (opts_out->csv_mode &&
     752          30 :             strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
     753           6 :             ereport(ERROR,
     754             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     755             :                      errmsg("CSV quote character must not appear in the DEFAULT specification")));
     756             : 
     757             :         /* Don't allow the NULL and DEFAULT string to be the same */
     758          48 :         if (opts_out->null_print_len == opts_out->default_print_len &&
     759          24 :             strncmp(opts_out->null_print, opts_out->default_print,
     760          24 :                     opts_out->null_print_len) == 0)
     761           6 :             ereport(ERROR,
     762             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     763             :                      errmsg("NULL specification and DEFAULT specification cannot be the same")));
     764             :     }
     765        8918 : }
     766             : 
     767             : /*
     768             :  * CopyGetAttnums - build an integer list of attnums to be copied
     769             :  *
     770             :  * The input attnamelist is either the user-specified column list,
     771             :  * or NIL if there was none (in which case we want all the non-dropped
     772             :  * columns).
     773             :  *
     774             :  * We don't include generated columns in the generated full list and we don't
     775             :  * allow them to be specified explicitly.  They don't make sense for COPY
     776             :  * FROM, but we could possibly allow them for COPY TO.  But this way it's at
     777             :  * least ensured that whatever we copy out can be copied back in.
     778             :  *
     779             :  * rel can be NULL ... it's only used for error reports.
     780             :  */
     781             : List *
     782       17302 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
     783             : {
     784       17302 :     List       *attnums = NIL;
     785             : 
     786       17302 :     if (attnamelist == NIL)
     787             :     {
     788             :         /* Generate default column list */
     789        3134 :         int         attr_count = tupDesc->natts;
     790             :         int         i;
     791             : 
     792       10640 :         for (i = 0; i < attr_count; i++)
     793             :         {
     794        7506 :             if (TupleDescAttr(tupDesc, i)->attisdropped)
     795         196 :                 continue;
     796        7310 :             if (TupleDescAttr(tupDesc, i)->attgenerated)
     797          54 :                 continue;
     798        7256 :             attnums = lappend_int(attnums, i + 1);
     799             :         }
     800             :     }
     801             :     else
     802             :     {
     803             :         /* Validate the user-supplied list and extract attnums */
     804             :         ListCell   *l;
     805             : 
     806       70252 :         foreach(l, attnamelist)
     807             :         {
     808       56144 :             char       *name = strVal(lfirst(l));
     809             :             int         attnum;
     810             :             int         i;
     811             : 
     812             :             /* Lookup column name */
     813       56144 :             attnum = InvalidAttrNumber;
     814     9871966 :             for (i = 0; i < tupDesc->natts; i++)
     815             :             {
     816     9871936 :                 Form_pg_attribute att = TupleDescAttr(tupDesc, i);
     817             : 
     818     9871936 :                 if (att->attisdropped)
     819         760 :                     continue;
     820     9871176 :                 if (namestrcmp(&(att->attname), name) == 0)
     821             :                 {
     822       56114 :                     if (att->attgenerated)
     823          24 :                         ereport(ERROR,
     824             :                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
     825             :                                  errmsg("column \"%s\" is a generated column",
     826             :                                         name),
     827             :                                  errdetail("Generated columns cannot be used in COPY.")));
     828       56090 :                     attnum = att->attnum;
     829       56090 :                     break;
     830             :                 }
     831             :             }
     832       56120 :             if (attnum == InvalidAttrNumber)
     833             :             {
     834          30 :                 if (rel != NULL)
     835          30 :                     ereport(ERROR,
     836             :                             (errcode(ERRCODE_UNDEFINED_COLUMN),
     837             :                              errmsg("column \"%s\" of relation \"%s\" does not exist",
     838             :                                     name, RelationGetRelationName(rel))));
     839             :                 else
     840           0 :                     ereport(ERROR,
     841             :                             (errcode(ERRCODE_UNDEFINED_COLUMN),
     842             :                              errmsg("column \"%s\" does not exist",
     843             :                                     name)));
     844             :             }
     845             :             /* Check for duplicates */
     846       56090 :             if (list_member_int(attnums, attnum))
     847           6 :                 ereport(ERROR,
     848             :                         (errcode(ERRCODE_DUPLICATE_COLUMN),
     849             :                          errmsg("column \"%s\" specified more than once",
     850             :                                 name)));
     851       56084 :             attnums = lappend_int(attnums, attnum);
     852             :         }
     853             :     }
     854             : 
     855       17242 :     return attnums;
     856             : }

Generated by: LCOV version 1.14