Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * copy.c
4 : : * Implements the COPY utility command
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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 "utils/acl.h"
37 : : #include "utils/builtins.h"
38 : : #include "utils/lsyscache.h"
39 : : #include "utils/rel.h"
40 : : #include "utils/rls.h"
41 : :
42 : : /*
43 : : * DoCopy executes the SQL COPY statement
44 : : *
45 : : * Either unload or reload contents of table <relation>, depending on <from>.
46 : : * (<from> = true means we are inserting into the table.) In the "TO" case
47 : : * we also support copying the output of an arbitrary SELECT, INSERT, UPDATE
48 : : * or DELETE query.
49 : : *
50 : : * If <pipe> is false, transfer is between the table and the file named
51 : : * <filename>. Otherwise, transfer is between the table and our regular
52 : : * input/output stream. The latter could be either stdin/stdout or a
53 : : * socket, depending on whether we're running under Postmaster control.
54 : : *
55 : : * Do not allow a Postgres user without the 'pg_read_server_files' or
56 : : * 'pg_write_server_files' role to read from or write to a file.
57 : : *
58 : : * Do not allow the copy if user doesn't have proper permission to access
59 : : * the table or the specifically requested columns.
60 : : */
61 : : void
2647 tgl@sss.pgh.pa.us 62 :CBC 4815 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
63 : : int stmt_location, int stmt_len,
64 : : uint64 *processed)
65 : : {
4806 itagaki.takahiro@gma 66 : 4815 : bool is_from = stmt->is_from;
67 : 4815 : bool pipe = (stmt->filename == NULL);
68 : : Relation rel;
69 : : Oid relid;
2647 tgl@sss.pgh.pa.us 70 : 4815 : RawStmt *query = NULL;
1837 andres@anarazel.de 71 : 4815 : Node *whereClause = NULL;
72 : :
73 : : /*
74 : : * Disallow COPY to/from file or program except to users with the
75 : : * appropriate role.
76 : : */
2200 sfrost@snowman.net 77 [ + + ]: 4815 : if (!pipe)
78 : : {
4064 heikki.linnakangas@i 79 [ - + ]: 185 : if (stmt->is_program)
80 : : {
748 mail@joeconway.com 81 [ # # ]:UBC 0 : if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM))
2200 sfrost@snowman.net 82 [ # # ]: 0 : ereport(ERROR,
83 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
84 : : errmsg("permission denied to COPY to or from an external program"),
85 : : errdetail("Only roles with privileges of the \"%s\" role may COPY to or from an external program.",
86 : : "pg_execute_server_program"),
87 : : errhint("Anyone can COPY to stdout or from stdin. "
88 : : "psql's \\copy command also works for anyone.")));
89 : : }
90 : : else
91 : : {
748 mail@joeconway.com 92 [ + + - + ]:CBC 185 : if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
2200 sfrost@snowman.net 93 [ # # ]:UBC 0 : ereport(ERROR,
94 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
95 : : errmsg("permission denied to COPY from a file"),
96 : : errdetail("Only roles with privileges of the \"%s\" role may COPY from a file.",
97 : : "pg_read_server_files"),
98 : : errhint("Anyone can COPY to stdout or from stdin. "
99 : : "psql's \\copy command also works for anyone.")));
100 : :
748 mail@joeconway.com 101 [ + + - + ]:CBC 185 : if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
2200 sfrost@snowman.net 102 [ # # ]:UBC 0 : ereport(ERROR,
103 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
104 : : errmsg("permission denied to COPY to a file"),
105 : : errdetail("Only roles with privileges of the \"%s\" role may COPY to a file.",
106 : : "pg_write_server_files"),
107 : : errhint("Anyone can COPY to stdout or from stdin. "
108 : : "psql's \\copy command also works for anyone.")));
109 : : }
110 : : }
111 : :
4806 itagaki.takahiro@gma 112 [ + + ]:CBC 4815 : if (stmt->relation)
113 : : {
2023 tgl@sss.pgh.pa.us 114 [ + + ]: 4618 : LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock;
115 : : ParseNamespaceItem *nsitem;
116 : : RTEPermissionInfo *perminfo;
117 : : TupleDesc tupDesc;
118 : : List *attnums;
119 : : ListCell *cur;
120 : :
4806 itagaki.takahiro@gma 121 [ - + ]: 4618 : Assert(!stmt->query);
122 : :
123 : : /* Open and lock the relation, using the appropriate lock type. */
1910 andres@anarazel.de 124 : 4618 : rel = table_openrv(stmt->relation, lockmode);
125 : :
4124 rhaas@postgresql.org 126 : 4617 : relid = RelationGetRelid(rel);
127 : :
1564 tgl@sss.pgh.pa.us 128 : 4617 : nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
129 : : NULL, false, false);
130 : :
495 alvherre@alvh.no-ip. 131 : 4617 : perminfo = nsitem->p_perminfo;
132 [ + + ]: 4617 : perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
133 : :
1912 tomas.vondra@postgre 134 [ + + ]: 4617 : if (stmt->whereClause)
135 : : {
136 : : /* add nsitem to query namespace */
1564 tgl@sss.pgh.pa.us 137 : 24 : addNSItemToQuery(pstate, nsitem, false, true, true);
138 : :
139 : : /* Transform the raw expression tree */
1912 tomas.vondra@postgre 140 : 24 : whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
141 : :
142 : : /* Make sure it yields a boolean result. */
143 : 9 : whereClause = coerce_to_boolean(pstate, whereClause, "WHERE");
144 : :
145 : : /* we have to fix its collations too */
146 : 9 : assign_expr_collations(pstate, whereClause);
147 : :
148 : 9 : whereClause = eval_const_expressions(NULL, whereClause);
149 : :
150 : 9 : whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
151 : 9 : whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
152 : : }
153 : :
4806 itagaki.takahiro@gma 154 : 4602 : tupDesc = RelationGetDescr(rel);
155 : 4602 : attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
4802 tgl@sss.pgh.pa.us 156 [ + + + + : 20501 : foreach(cur, attnums)
+ + ]
157 : : {
158 : : int attno;
159 : : Bitmapset **bms;
160 : :
495 alvherre@alvh.no-ip. 161 : 15929 : attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
162 [ + + ]: 15929 : bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
163 : :
164 : 15929 : *bms = bms_add_member(*bms, attno);
165 : : }
166 : 4572 : ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true);
167 : :
168 : : /*
169 : : * Permission check for row security policies.
170 : : *
171 : : * check_enable_rls will ereport(ERROR) if the user has requested
172 : : * something invalid and will otherwise indicate if we should enable
173 : : * RLS (returns RLS_ENABLED) or not for this COPY statement.
174 : : *
175 : : * If the relation has a row security policy and we are to apply it
176 : : * then perform a "query" copy and allow the normal query processing
177 : : * to handle the policies.
178 : : *
179 : : * If RLS is not enabled for this, then just fall through to the
180 : : * normal non-filtering relation handling.
181 : : */
182 [ + + ]: 4530 : if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
183 : : {
184 : : SelectStmt *select;
185 : : ColumnRef *cr;
186 : : ResTarget *target;
187 : : RangeVar *from;
2750 sfrost@snowman.net 188 : 30 : List *targetList = NIL;
189 : :
3495 190 [ + + ]: 30 : if (is_from)
191 [ + - ]: 3 : ereport(ERROR,
192 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
193 : : errmsg("COPY FROM not supported with row-level security"),
194 : : errhint("Use INSERT statements instead.")));
195 : :
196 : : /*
197 : : * Build target list
198 : : *
199 : : * If no columns are specified in the attribute list of the COPY
200 : : * command, then the target list is 'all' columns. Therefore, '*'
201 : : * should be used as the target list for the resulting SELECT
202 : : * statement.
203 : : *
204 : : * In the case that columns are specified in the attribute list,
205 : : * create a ColumnRef and ResTarget for each column and add them
206 : : * to the target list for the resulting SELECT statement.
207 : : */
208 [ + + ]: 27 : if (!stmt->attlist)
209 : : {
2750 210 : 9 : cr = makeNode(ColumnRef);
3495 211 : 9 : cr->fields = list_make1(makeNode(A_Star));
2750 212 : 9 : cr->location = -1;
213 : :
214 : 9 : target = makeNode(ResTarget);
215 : 9 : target->name = NULL;
216 : 9 : target->indirection = NIL;
217 : 9 : target->val = (Node *) cr;
218 : 9 : target->location = -1;
219 : :
220 : 9 : targetList = list_make1(target);
221 : : }
222 : : else
223 : : {
224 : : ListCell *lc;
225 : :
226 [ + - + + : 51 : foreach(lc, stmt->attlist)
+ + ]
227 : : {
228 : : /*
229 : : * Build the ColumnRef for each column. The ColumnRef
230 : : * 'fields' property is a String node that corresponds to
231 : : * the column name respectively.
232 : : */
233 : 33 : cr = makeNode(ColumnRef);
234 : 33 : cr->fields = list_make1(lfirst(lc));
235 : 33 : cr->location = -1;
236 : :
237 : : /* Build the ResTarget and add the ColumnRef to it. */
238 : 33 : target = makeNode(ResTarget);
239 : 33 : target->name = NULL;
240 : 33 : target->indirection = NIL;
241 : 33 : target->val = (Node *) cr;
242 : 33 : target->location = -1;
243 : :
244 : : /* Add each column to the SELECT statement's target list */
245 : 33 : targetList = lappend(targetList, target);
246 : : }
247 : : }
248 : :
249 : : /*
250 : : * Build RangeVar for from clause, fully qualified based on the
251 : : * relation which we have opened and locked. Use "ONLY" so that
252 : : * COPY retrieves rows from only the target table not any
253 : : * inheritance children, the same as when RLS doesn't apply.
254 : : */
3184 255 : 27 : from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
2596 tgl@sss.pgh.pa.us 256 : 27 : pstrdup(RelationGetRelationName(rel)),
257 : : -1);
401 258 : 27 : from->inh = false; /* apply ONLY */
259 : :
260 : : /* Build query */
3495 sfrost@snowman.net 261 : 27 : select = makeNode(SelectStmt);
2750 262 : 27 : select->targetList = targetList;
3495 263 : 27 : select->fromClause = list_make1(from);
264 : :
2647 tgl@sss.pgh.pa.us 265 : 27 : query = makeNode(RawStmt);
266 : 27 : query->stmt = (Node *) select;
267 : 27 : query->stmt_location = stmt_location;
268 : 27 : query->stmt_len = stmt_len;
269 : :
270 : : /*
271 : : * Close the relation for now, but keep the lock on it to prevent
272 : : * changes between now and when we start the query-based COPY.
273 : : *
274 : : * We'll reopen it later as part of the query-based COPY.
275 : : */
1910 andres@anarazel.de 276 : 27 : table_close(rel, NoLock);
3495 sfrost@snowman.net 277 : 27 : rel = NULL;
278 : : }
279 : : }
280 : : else
281 : : {
4806 itagaki.takahiro@gma 282 [ - + ]: 197 : Assert(stmt->query);
283 : :
2647 tgl@sss.pgh.pa.us 284 : 197 : query = makeNode(RawStmt);
285 : 197 : query->stmt = stmt->query;
286 : 197 : query->stmt_location = stmt_location;
287 : 197 : query->stmt_len = stmt_len;
288 : :
4122 peter_e@gmx.net 289 : 197 : relid = InvalidOid;
4806 itagaki.takahiro@gma 290 : 197 : rel = NULL;
291 : : }
292 : :
293 [ + + ]: 4715 : if (is_from)
294 : : {
295 : : CopyFromState cstate;
296 : :
4414 peter_e@gmx.net 297 [ - + ]: 699 : Assert(rel);
298 : :
299 : : /* check read-only transaction and parallel mode */
4136 tgl@sss.pgh.pa.us 300 [ - + - - ]: 699 : if (XactReadOnly && !rel->rd_islocaltemp)
4806 itagaki.takahiro@gma 301 :UBC 0 : PreventCommandIfReadOnly("COPY FROM");
302 : :
1238 heikki.linnakangas@i 303 :CBC 699 : cstate = BeginCopyFrom(pstate, rel, whereClause,
304 : 699 : stmt->filename, stmt->is_program,
2579 peter_e@gmx.net 305 : 699 : NULL, stmt->attlist, stmt->options);
4124 rhaas@postgresql.org 306 : 609 : *processed = CopyFrom(cstate); /* copy from file to database */
4806 itagaki.takahiro@gma 307 : 512 : EndCopyFrom(cstate);
308 : : }
309 : : else
310 : : {
311 : : CopyToState cstate;
312 : :
2777 peter_e@gmx.net 313 : 4016 : cstate = BeginCopyTo(pstate, rel, query, relid,
4064 heikki.linnakangas@i 314 : 4016 : stmt->filename, stmt->is_program,
551 michael@paquier.xyz 315 : 4016 : NULL, stmt->attlist, stmt->options);
4124 rhaas@postgresql.org 316 : 3913 : *processed = DoCopyTo(cstate); /* copy from database to file */
4806 itagaki.takahiro@gma 317 : 3912 : EndCopyTo(cstate);
318 : : }
319 : :
320 [ + + ]: 4424 : if (rel != NULL)
1430 akapila@postgresql.o 321 : 4267 : table_close(rel, NoLock);
4806 itagaki.takahiro@gma 322 : 4424 : }
323 : :
324 : : /*
325 : : * Extract a CopyHeaderChoice value from a DefElem. This is like
326 : : * defGetBoolean() but also accepts the special value "match".
327 : : */
328 : : static CopyHeaderChoice
661 michael@paquier.xyz 329 : 78 : defGetCopyHeaderChoice(DefElem *def, bool is_from)
330 : : {
331 : : /*
332 : : * If no parameter value given, assume "true" is meant.
333 : : */
746 peter@eisentraut.org 334 [ + + ]: 78 : if (def->arg == NULL)
335 : 6 : return COPY_HEADER_TRUE;
336 : :
337 : : /*
338 : : * Allow 0, 1, "true", "false", "on", "off", or "match".
339 : : */
340 [ - + ]: 72 : switch (nodeTag(def->arg))
341 : : {
746 peter@eisentraut.org 342 :UBC 0 : case T_Integer:
343 [ # # # ]: 0 : switch (intVal(def->arg))
344 : : {
345 : 0 : case 0:
346 : 0 : return COPY_HEADER_FALSE;
347 : 0 : case 1:
348 : 0 : return COPY_HEADER_TRUE;
349 : 0 : default:
350 : : /* otherwise, error out below */
351 : 0 : break;
352 : : }
353 : 0 : break;
746 peter@eisentraut.org 354 :CBC 72 : default:
355 : : {
703 tgl@sss.pgh.pa.us 356 : 72 : char *sval = defGetString(def);
357 : :
358 : : /*
359 : : * The set of strings accepted here should match up with the
360 : : * grammar's opt_boolean_or_string production.
361 : : */
746 peter@eisentraut.org 362 [ + + ]: 72 : if (pg_strcasecmp(sval, "true") == 0)
363 : 23 : return COPY_HEADER_TRUE;
364 [ - + ]: 49 : if (pg_strcasecmp(sval, "false") == 0)
746 peter@eisentraut.org 365 :UBC 0 : return COPY_HEADER_FALSE;
746 peter@eisentraut.org 366 [ - + ]:CBC 49 : if (pg_strcasecmp(sval, "on") == 0)
746 peter@eisentraut.org 367 :UBC 0 : return COPY_HEADER_TRUE;
746 peter@eisentraut.org 368 [ + + ]:CBC 49 : if (pg_strcasecmp(sval, "off") == 0)
369 : 3 : return COPY_HEADER_FALSE;
370 [ + + ]: 46 : if (pg_strcasecmp(sval, "match") == 0)
371 : : {
661 michael@paquier.xyz 372 [ + + ]: 43 : if (!is_from)
373 [ + - ]: 3 : ereport(ERROR,
374 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
375 : : errmsg("cannot use \"%s\" with HEADER in COPY TO",
376 : : sval)));
746 peter@eisentraut.org 377 : 40 : return COPY_HEADER_MATCH;
378 : : }
379 : : }
380 : 3 : break;
381 : : }
382 [ + - ]: 3 : ereport(ERROR,
383 : : (errcode(ERRCODE_SYNTAX_ERROR),
384 : : errmsg("%s requires a Boolean value or \"match\"",
385 : : def->defname)));
386 : : return COPY_HEADER_FALSE; /* keep compiler quiet */
387 : : }
388 : :
389 : : /*
390 : : * Extract a CopyOnErrorChoice value from a DefElem.
391 : : */
392 : : static CopyOnErrorChoice
86 akorotkov@postgresql 393 :GNC 30 : defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
394 : : {
395 : : char *sval;
396 : :
89 397 [ + + ]: 30 : if (!is_from)
398 [ + - ]: 3 : ereport(ERROR,
399 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
400 : : errmsg("COPY ON_ERROR cannot be used with COPY TO"),
401 : : parser_errposition(pstate, def->location)));
402 : :
403 : : /*
404 : : * If no parameter value given, assume the default value.
405 : : */
406 [ - + ]: 27 : if (def->arg == NULL)
86 akorotkov@postgresql 407 :UNC 0 : return COPY_ON_ERROR_STOP;
408 : :
409 : : /*
410 : : * Allow "stop", or "ignore" values.
411 : : */
89 akorotkov@postgresql 412 :GNC 27 : sval = defGetString(def);
86 413 [ + + ]: 27 : if (pg_strcasecmp(sval, "stop") == 0)
414 : 3 : return COPY_ON_ERROR_STOP;
415 [ + + ]: 24 : if (pg_strcasecmp(sval, "ignore") == 0)
416 : 21 : return COPY_ON_ERROR_IGNORE;
417 : :
89 418 [ + - ]: 3 : ereport(ERROR,
419 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
420 : : errmsg("COPY ON_ERROR \"%s\" not recognized", sval),
421 : : parser_errposition(pstate, def->location)));
422 : : return COPY_ON_ERROR_STOP; /* keep compiler quiet */
423 : : }
424 : :
425 : : /*
426 : : * Extract a CopyLogVerbosityChoice value from a DefElem.
427 : : */
428 : : static CopyLogVerbosityChoice
13 msawada@postgresql.o 429 : 12 : defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate)
430 : : {
431 : : char *sval;
432 : :
433 : : /*
434 : : * Allow "default", or "verbose" values.
435 : : */
436 : 12 : sval = defGetString(def);
437 [ + + ]: 12 : if (pg_strcasecmp(sval, "default") == 0)
438 : 3 : return COPY_LOG_VERBOSITY_DEFAULT;
439 [ + + ]: 9 : if (pg_strcasecmp(sval, "verbose") == 0)
440 : 6 : return COPY_LOG_VERBOSITY_VERBOSE;
441 : :
442 [ + - ]: 3 : ereport(ERROR,
443 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
444 : : errmsg("COPY LOG_VERBOSITY \"%s\" not recognized", sval),
445 : : parser_errposition(pstate, def->location)));
446 : : return COPY_LOG_VERBOSITY_DEFAULT; /* keep compiler quiet */
447 : : }
448 : :
449 : : /*
450 : : * Process the statement option list for COPY.
451 : : *
452 : : * Scan the options list (a list of DefElem) and transpose the information
453 : : * into *opts_out, applying appropriate error checking.
454 : : *
455 : : * If 'opts_out' is not NULL, it is assumed to be filled with zeroes initially.
456 : : *
457 : : * This is exported so that external users of the COPY API can sanity-check
458 : : * a list of options. In that usage, 'opts_out' can be passed as NULL and
459 : : * the collected data is just leaked until CurrentMemoryContext is reset.
460 : : *
461 : : * Note that additional checking, such as whether column names listed in FORCE
462 : : * QUOTE actually exist, has to be applied later. This just checks for
463 : : * self-consistency of the options list.
464 : : */
465 : : void
2777 peter_e@gmx.net 466 :CBC 4952 : ProcessCopyOptions(ParseState *pstate,
467 : : CopyFormatOptions *opts_out,
468 : : bool is_from,
469 : : List *options)
470 : : {
5319 tgl@sss.pgh.pa.us 471 : 4952 : bool format_specified = false;
1287 michael@paquier.xyz 472 : 4952 : bool freeze_specified = false;
473 : 4952 : bool header_specified = false;
86 akorotkov@postgresql 474 :GNC 4952 : bool on_error_specified = false;
13 msawada@postgresql.o 475 : 4952 : bool log_verbosity_specified = false;
476 : : ListCell *option;
477 : :
478 : : /* Support external use for option sanity checking */
1238 heikki.linnakangas@i 479 [ + + ]:CBC 4952 : if (opts_out == NULL)
480 : 43 : opts_out = (CopyFormatOptions *) palloc0(sizeof(CopyFormatOptions));
481 : :
482 : 4952 : opts_out->file_encoding = -1;
483 : :
484 : : /* Extract options from the statement node tree */
4806 itagaki.takahiro@gma 485 [ + + + + : 5769 : foreach(option, options)
+ + ]
486 : : {
2561 tgl@sss.pgh.pa.us 487 : 881 : DefElem *defel = lfirst_node(DefElem, option);
488 : :
5319 489 [ + + ]: 881 : if (strcmp(defel->defname, "format") == 0)
490 : : {
5161 bruce@momjian.us 491 : 264 : char *fmt = defGetString(defel);
492 : :
5319 tgl@sss.pgh.pa.us 493 [ + + ]: 264 : if (format_specified)
1004 dean.a.rasheed@gmail 494 : 3 : errorConflictingDefElem(defel, pstate);
5319 tgl@sss.pgh.pa.us 495 : 261 : format_specified = true;
496 [ + + ]: 261 : if (strcmp(fmt, "text") == 0)
497 : : /* default format */ ;
498 [ + + ]: 232 : else if (strcmp(fmt, "csv") == 0)
1238 heikki.linnakangas@i 499 : 197 : opts_out->csv_mode = true;
5319 tgl@sss.pgh.pa.us 500 [ + + ]: 35 : else if (strcmp(fmt, "binary") == 0)
1238 heikki.linnakangas@i 501 : 34 : opts_out->binary = true;
502 : : else
5319 tgl@sss.pgh.pa.us 503 [ + - ]: 1 : ereport(ERROR,
504 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
505 : : errmsg("COPY format \"%s\" not recognized", fmt),
506 : : parser_errposition(pstate, defel->location)));
507 : : }
4152 simon@2ndQuadrant.co 508 [ + + ]: 617 : else if (strcmp(defel->defname, "freeze") == 0)
509 : : {
1287 michael@paquier.xyz 510 [ + + ]: 39 : if (freeze_specified)
1004 dean.a.rasheed@gmail 511 : 3 : errorConflictingDefElem(defel, pstate);
1287 michael@paquier.xyz 512 : 36 : freeze_specified = true;
1238 heikki.linnakangas@i 513 : 36 : opts_out->freeze = defGetBoolean(defel);
514 : : }
7969 bruce@momjian.us 515 [ + + ]: 578 : else if (strcmp(defel->defname, "delimiter") == 0)
516 : : {
1238 heikki.linnakangas@i 517 [ + + ]: 132 : if (opts_out->delim)
1004 dean.a.rasheed@gmail 518 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 519 : 129 : opts_out->delim = defGetString(defel);
520 : : }
7969 bruce@momjian.us 521 [ + + ]: 446 : else if (strcmp(defel->defname, "null") == 0)
522 : : {
1238 heikki.linnakangas@i 523 [ + + ]: 63 : if (opts_out->null_print)
1004 dean.a.rasheed@gmail 524 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 525 : 60 : opts_out->null_print = defGetString(defel);
526 : : }
398 andrew@dunslane.net 527 [ + + ]: 383 : else if (strcmp(defel->defname, "default") == 0)
528 : : {
529 [ - + ]: 45 : if (opts_out->default_print)
398 andrew@dunslane.net 530 :UBC 0 : errorConflictingDefElem(defel, pstate);
398 andrew@dunslane.net 531 :CBC 45 : opts_out->default_print = defGetString(defel);
532 : : }
6917 bruce@momjian.us 533 [ + + ]: 338 : else if (strcmp(defel->defname, "header") == 0)
534 : : {
1287 michael@paquier.xyz 535 [ + + ]: 81 : if (header_specified)
1004 dean.a.rasheed@gmail 536 : 3 : errorConflictingDefElem(defel, pstate);
1287 michael@paquier.xyz 537 : 78 : header_specified = true;
661 538 : 78 : opts_out->header_line = defGetCopyHeaderChoice(defel, is_from);
539 : : }
7300 bruce@momjian.us 540 [ + + ]: 257 : else if (strcmp(defel->defname, "quote") == 0)
541 : : {
1238 heikki.linnakangas@i 542 [ + + ]: 42 : if (opts_out->quote)
1004 dean.a.rasheed@gmail 543 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 544 : 39 : opts_out->quote = defGetString(defel);
545 : : }
7300 bruce@momjian.us 546 [ + + ]: 215 : else if (strcmp(defel->defname, "escape") == 0)
547 : : {
1238 heikki.linnakangas@i 548 [ + + ]: 38 : if (opts_out->escape)
1004 dean.a.rasheed@gmail 549 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 550 : 35 : opts_out->escape = defGetString(defel);
551 : : }
7298 bruce@momjian.us 552 [ + + ]: 177 : else if (strcmp(defel->defname, "force_quote") == 0)
553 : : {
1238 heikki.linnakangas@i 554 [ + + - + ]: 33 : if (opts_out->force_quote || opts_out->force_quote_all)
1004 dean.a.rasheed@gmail 555 : 3 : errorConflictingDefElem(defel, pstate);
5377 tgl@sss.pgh.pa.us 556 [ + - + + ]: 30 : if (defel->arg && IsA(defel->arg, A_Star))
1238 heikki.linnakangas@i 557 : 9 : opts_out->force_quote_all = true;
5319 tgl@sss.pgh.pa.us 558 [ + - + - ]: 21 : else if (defel->arg && IsA(defel->arg, List))
1238 heikki.linnakangas@i 559 : 21 : opts_out->force_quote = castNode(List, defel->arg);
560 : : else
5319 tgl@sss.pgh.pa.us 561 [ # # ]:UBC 0 : ereport(ERROR,
562 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
563 : : errmsg("argument to option \"%s\" must be a list of column names",
564 : : defel->defname),
565 : : parser_errposition(pstate, defel->location)));
566 : : }
5319 tgl@sss.pgh.pa.us 567 [ + + ]:CBC 144 : else if (strcmp(defel->defname, "force_not_null") == 0)
568 : : {
197 andrew@dunslane.net 569 [ + + + + ]:GNC 38 : if (opts_out->force_notnull || opts_out->force_notnull_all)
1004 dean.a.rasheed@gmail 570 :CBC 6 : errorConflictingDefElem(defel, pstate);
197 andrew@dunslane.net 571 [ + - + + ]:GNC 32 : if (defel->arg && IsA(defel->arg, A_Star))
572 : 9 : opts_out->force_notnull_all = true;
573 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1238 heikki.linnakangas@i 574 :CBC 23 : opts_out->force_notnull = castNode(List, defel->arg);
575 : : else
5319 tgl@sss.pgh.pa.us 576 [ # # ]:UBC 0 : ereport(ERROR,
577 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
578 : : errmsg("argument to option \"%s\" must be a list of column names",
579 : : defel->defname),
580 : : parser_errposition(pstate, defel->location)));
581 : : }
3694 andrew@dunslane.net 582 [ + + ]:CBC 106 : else if (strcmp(defel->defname, "force_null") == 0)
583 : : {
197 andrew@dunslane.net 584 [ + + + + ]:GNC 38 : if (opts_out->force_null || opts_out->force_null_all)
1004 dean.a.rasheed@gmail 585 :CBC 6 : errorConflictingDefElem(defel, pstate);
197 andrew@dunslane.net 586 [ + - + + ]:GNC 32 : if (defel->arg && IsA(defel->arg, A_Star))
587 : 9 : opts_out->force_null_all = true;
588 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1238 heikki.linnakangas@i 589 :CBC 23 : opts_out->force_null = castNode(List, defel->arg);
590 : : else
3694 andrew@dunslane.net 591 [ # # ]:UBC 0 : ereport(ERROR,
592 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
593 : : errmsg("argument to option \"%s\" must be a list of column names",
594 : : defel->defname),
595 : : parser_errposition(pstate, defel->location)));
596 : : }
4294 tgl@sss.pgh.pa.us 597 [ + + ]:CBC 68 : else if (strcmp(defel->defname, "convert_selectively") == 0)
598 : : {
599 : : /*
600 : : * Undocumented, not-accessible-from-SQL option: convert only the
601 : : * named columns to binary form, storing the rest as NULLs. It's
602 : : * allowed for the column list to be NIL.
603 : : */
1238 heikki.linnakangas@i 604 [ + + ]: 8 : if (opts_out->convert_selectively)
1004 dean.a.rasheed@gmail 605 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 606 : 5 : opts_out->convert_selectively = true;
4294 tgl@sss.pgh.pa.us 607 [ + - + - ]: 5 : if (defel->arg == NULL || IsA(defel->arg, List))
1238 heikki.linnakangas@i 608 : 5 : opts_out->convert_select = castNode(List, defel->arg);
609 : : else
4294 tgl@sss.pgh.pa.us 610 [ # # ]:UBC 0 : ereport(ERROR,
611 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
612 : : errmsg("argument to option \"%s\" must be a list of column names",
613 : : defel->defname),
614 : : parser_errposition(pstate, defel->location)));
615 : : }
4801 itagaki.takahiro@gma 616 [ + + ]:CBC 60 : else if (strcmp(defel->defname, "encoding") == 0)
617 : : {
1238 heikki.linnakangas@i 618 [ + + ]: 12 : if (opts_out->file_encoding >= 0)
1004 dean.a.rasheed@gmail 619 : 3 : errorConflictingDefElem(defel, pstate);
1238 heikki.linnakangas@i 620 : 9 : opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
621 [ - + ]: 9 : if (opts_out->file_encoding < 0)
4801 itagaki.takahiro@gma 622 [ # # ]:UBC 0 : ereport(ERROR,
623 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
624 : : errmsg("argument to option \"%s\" must be a valid encoding name",
625 : : defel->defname),
626 : : parser_errposition(pstate, defel->location)));
627 : : }
86 akorotkov@postgresql 628 [ + + ]:GNC 48 : else if (strcmp(defel->defname, "on_error") == 0)
629 : : {
630 [ + + ]: 33 : if (on_error_specified)
89 631 : 3 : errorConflictingDefElem(defel, pstate);
86 632 : 30 : on_error_specified = true;
633 : 30 : opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
634 : : }
13 msawada@postgresql.o 635 [ + - ]: 15 : else if (strcmp(defel->defname, "log_verbosity") == 0)
636 : : {
637 [ + + ]: 15 : if (log_verbosity_specified)
638 : 3 : errorConflictingDefElem(defel, pstate);
639 : 12 : log_verbosity_specified = true;
640 : 12 : opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
641 : : }
642 : : else
5319 tgl@sss.pgh.pa.us 643 [ # # ]:UBC 0 : ereport(ERROR,
644 : : (errcode(ERRCODE_SYNTAX_ERROR),
645 : : errmsg("option \"%s\" not recognized",
646 : : defel->defname),
647 : : parser_errposition(pstate, defel->location)));
648 : : }
649 : :
650 : : /*
651 : : * Check for incompatible options (must do these two before inserting
652 : : * defaults)
653 : : */
1238 heikki.linnakangas@i 654 [ + + + + ]:CBC 4888 : if (opts_out->binary && opts_out->delim)
7574 tgl@sss.pgh.pa.us 655 [ + - ]: 3 : ereport(ERROR,
656 : : (errcode(ERRCODE_SYNTAX_ERROR),
657 : : errmsg("cannot specify DELIMITER in BINARY mode")));
658 : :
1238 heikki.linnakangas@i 659 [ + + + + ]: 4885 : if (opts_out->binary && opts_out->null_print)
7574 tgl@sss.pgh.pa.us 660 [ + - ]: 3 : ereport(ERROR,
661 : : (errcode(ERRCODE_SYNTAX_ERROR),
662 : : errmsg("cannot specify NULL in BINARY mode")));
663 : :
398 andrew@dunslane.net 664 [ + + + + ]: 4882 : if (opts_out->binary && opts_out->default_print)
665 [ + - ]: 3 : ereport(ERROR,
666 : : (errcode(ERRCODE_SYNTAX_ERROR),
667 : : errmsg("cannot specify DEFAULT in BINARY mode")));
668 : :
86 akorotkov@postgresql 669 [ + + + + ]:GNC 4879 : if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
89 670 [ + - ]: 3 : ereport(ERROR,
671 : : (errcode(ERRCODE_SYNTAX_ERROR),
672 : : errmsg("only ON_ERROR STOP is allowed in BINARY mode")));
673 : :
674 : : /* Set defaults for omitted options */
1238 heikki.linnakangas@i 675 [ + + ]:CBC 4876 : if (!opts_out->delim)
676 [ + + ]: 4753 : opts_out->delim = opts_out->csv_mode ? "," : "\t";
677 : :
678 [ + + ]: 4876 : if (!opts_out->null_print)
679 [ + + ]: 4822 : opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
680 : 4876 : opts_out->null_print_len = strlen(opts_out->null_print);
681 : :
682 [ + + ]: 4876 : if (opts_out->csv_mode)
683 : : {
684 [ + + ]: 188 : if (!opts_out->quote)
685 : 154 : opts_out->quote = "\"";
686 [ + + ]: 188 : if (!opts_out->escape)
687 : 159 : opts_out->escape = opts_out->quote;
688 : : }
689 : :
690 : : /* Only single-byte delimiter strings are supported. */
691 [ + + ]: 4876 : if (strlen(opts_out->delim) != 1)
7300 bruce@momjian.us 692 [ + - ]: 1 : ereport(ERROR,
693 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
694 : : errmsg("COPY delimiter must be a single one-byte character")));
695 : :
696 : : /* Disallow end-of-line characters */
1238 heikki.linnakangas@i 697 [ + - ]: 4875 : if (strchr(opts_out->delim, '\r') != NULL ||
698 [ + + ]: 4875 : strchr(opts_out->delim, '\n') != NULL)
6645 bruce@momjian.us 699 [ + - ]: 1 : ereport(ERROR,
700 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
701 : : errmsg("COPY delimiter cannot be newline or carriage return")));
702 : :
1238 heikki.linnakangas@i 703 [ + - ]: 4874 : if (strchr(opts_out->null_print, '\r') != NULL ||
704 [ + + ]: 4874 : strchr(opts_out->null_print, '\n') != NULL)
6645 bruce@momjian.us 705 [ + - ]: 1 : ereport(ERROR,
706 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
707 : : errmsg("COPY null representation cannot use newline or carriage return")));
708 : :
398 andrew@dunslane.net 709 [ + + ]: 4873 : if (opts_out->default_print)
710 : : {
711 : 42 : opts_out->default_print_len = strlen(opts_out->default_print);
712 : :
713 [ + + ]: 42 : if (strchr(opts_out->default_print, '\r') != NULL ||
714 [ + + ]: 39 : strchr(opts_out->default_print, '\n') != NULL)
715 [ + - ]: 6 : ereport(ERROR,
716 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
717 : : errmsg("COPY default representation cannot use newline or carriage return")));
718 : : }
719 : :
720 : : /*
721 : : * Disallow unsafe delimiter characters in non-CSV mode. We can't allow
722 : : * backslash because it would be ambiguous. We can't allow the other
723 : : * cases because data characters matching the delimiter must be
724 : : * backslashed, and certain backslash combinations are interpreted
725 : : * non-literally by COPY IN. Disallowing all lower case ASCII letters is
726 : : * more than strictly necessary, but seems best for consistency and
727 : : * future-proofing. Likewise we disallow all digits though only octal
728 : : * digits are actually dangerous.
729 : : */
1238 heikki.linnakangas@i 730 [ + + ]: 4867 : if (!opts_out->csv_mode &&
5953 tgl@sss.pgh.pa.us 731 : 4682 : strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
1238 heikki.linnakangas@i 732 [ + + ]: 4682 : opts_out->delim[0]) != NULL)
6645 bruce@momjian.us 733 [ + - ]: 5 : ereport(ERROR,
734 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
735 : : errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
736 : :
737 : : /* Check header */
807 peter@eisentraut.org 738 [ + + + + ]: 4862 : if (opts_out->binary && opts_out->header_line)
6917 bruce@momjian.us 739 [ + - ]: 1 : ereport(ERROR,
740 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
741 : : errmsg("cannot specify HEADER in BINARY mode")));
742 : :
743 : : /* Check quote */
1238 heikki.linnakangas@i 744 [ + + + + ]: 4861 : if (!opts_out->csv_mode && opts_out->quote != NULL)
7300 bruce@momjian.us 745 [ + - ]: 2 : ereport(ERROR,
746 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
747 : : errmsg("COPY QUOTE requires CSV mode")));
748 : :
1238 heikki.linnakangas@i 749 [ + + + + ]: 4859 : if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
7300 bruce@momjian.us 750 [ + - ]: 1 : ereport(ERROR,
751 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
752 : : errmsg("COPY quote must be a single one-byte character")));
753 : :
1238 heikki.linnakangas@i 754 [ + + + + ]: 4858 : if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
5950 andrew@dunslane.net 755 [ + - ]: 1 : ereport(ERROR,
756 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
757 : : errmsg("COPY delimiter and quote must be different")));
758 : :
759 : : /* Check escape */
1238 heikki.linnakangas@i 760 [ + + + + ]: 4857 : if (!opts_out->csv_mode && opts_out->escape != NULL)
7300 bruce@momjian.us 761 [ + - ]: 3 : ereport(ERROR,
762 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
763 : : errmsg("COPY ESCAPE requires CSV mode")));
764 : :
1238 heikki.linnakangas@i 765 [ + + + + ]: 4854 : if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
7300 bruce@momjian.us 766 [ + - ]: 1 : ereport(ERROR,
767 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
768 : : errmsg("COPY escape must be a single one-byte character")));
769 : :
770 : : /* Check force_quote */
1238 heikki.linnakangas@i 771 [ + + + + : 4853 : if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
- + ]
7300 bruce@momjian.us 772 [ + - ]: 3 : ereport(ERROR,
773 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
774 : : errmsg("COPY FORCE_QUOTE requires CSV mode")));
1238 heikki.linnakangas@i 775 [ + + + + : 4850 : if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
+ + ]
7300 bruce@momjian.us 776 [ + - ]: 3 : ereport(ERROR,
777 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
778 : : errmsg("COPY FORCE_QUOTE cannot be used with COPY FROM")));
779 : :
780 : : /* Check force_notnull */
1238 heikki.linnakangas@i 781 [ + + + + ]: 4847 : if (!opts_out->csv_mode && opts_out->force_notnull != NIL)
7300 bruce@momjian.us 782 [ + - ]: 4 : ereport(ERROR,
783 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
784 : : errmsg("COPY FORCE_NOT_NULL requires CSV mode")));
1238 heikki.linnakangas@i 785 [ + + + + ]: 4843 : if (opts_out->force_notnull != NIL && !is_from)
7300 bruce@momjian.us 786 [ + - ]: 3 : ereport(ERROR,
787 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
788 : : errmsg("COPY FORCE_NOT_NULL cannot be used with COPY TO")));
789 : :
790 : : /* Check force_null */
1238 heikki.linnakangas@i 791 [ + + + + ]: 4840 : if (!opts_out->csv_mode && opts_out->force_null != NIL)
3694 andrew@dunslane.net 792 [ + - ]: 3 : ereport(ERROR,
793 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
794 : : errmsg("COPY FORCE_NULL requires CSV mode")));
795 : :
1238 heikki.linnakangas@i 796 [ + + + + ]: 4837 : if (opts_out->force_null != NIL && !is_from)
3694 andrew@dunslane.net 797 [ + - ]: 3 : ereport(ERROR,
798 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
799 : : errmsg("COPY FORCE_NULL cannot be used with COPY TO")));
800 : :
801 : : /* Don't allow the delimiter to appear in the null string. */
1238 heikki.linnakangas@i 802 [ + + ]: 4834 : if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
7300 bruce@momjian.us 803 [ + - ]: 1 : ereport(ERROR,
804 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
805 : : errmsg("COPY delimiter character must not appear in the NULL specification")));
806 : :
807 : : /* Don't allow the CSV quote char to appear in the null string. */
1238 heikki.linnakangas@i 808 [ + + ]: 4833 : if (opts_out->csv_mode &&
809 [ + + ]: 172 : strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
7300 bruce@momjian.us 810 [ + - ]: 1 : ereport(ERROR,
811 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
812 : : errmsg("CSV quote character must not appear in the NULL specification")));
813 : :
814 : : /* Check freeze */
153 bruce@momjian.us 815 [ + + - + ]:GNC 4832 : if (opts_out->freeze && !is_from)
153 bruce@momjian.us 816 [ # # ]:UNC 0 : ereport(ERROR,
817 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
818 : : errmsg("COPY FREEZE cannot be used with COPY TO")));
819 : :
398 andrew@dunslane.net 820 [ + + ]:CBC 4832 : if (opts_out->default_print)
821 : : {
822 [ + + ]: 36 : if (!is_from)
823 [ + - ]: 3 : ereport(ERROR,
824 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
825 : : errmsg("COPY DEFAULT only available using COPY FROM")));
826 : :
827 : : /* Don't allow the delimiter to appear in the default string. */
828 [ + + ]: 33 : if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
829 [ + - ]: 3 : ereport(ERROR,
830 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
831 : : errmsg("COPY delimiter must not appear in the DEFAULT specification")));
832 : :
833 : : /* Don't allow the CSV quote char to appear in the default string. */
834 [ + + ]: 30 : if (opts_out->csv_mode &&
835 [ + + ]: 15 : strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
836 [ + - ]: 3 : ereport(ERROR,
837 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
838 : : errmsg("CSV quote character must not appear in the DEFAULT specification")));
839 : :
840 : : /* Don't allow the NULL and DEFAULT string to be the same */
841 [ + + ]: 27 : if (opts_out->null_print_len == opts_out->default_print_len &&
842 : 12 : strncmp(opts_out->null_print, opts_out->default_print,
843 [ + + ]: 12 : opts_out->null_print_len) == 0)
844 [ + - ]: 3 : ereport(ERROR,
845 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
846 : : errmsg("NULL specification and DEFAULT specification cannot be the same")));
847 : : }
4802 tgl@sss.pgh.pa.us 848 : 4820 : }
849 : :
850 : : /*
851 : : * CopyGetAttnums - build an integer list of attnums to be copied
852 : : *
853 : : * The input attnamelist is either the user-specified column list,
854 : : * or NIL if there was none (in which case we want all the non-dropped
855 : : * columns).
856 : : *
857 : : * We don't include generated columns in the generated full list and we don't
858 : : * allow them to be specified explicitly. They don't make sense for COPY
859 : : * FROM, but we could possibly allow them for COPY TO. But this way it's at
860 : : * least ensured that whatever we copy out can be copied back in.
861 : : *
862 : : * rel can be NULL ... it's only used for error reports.
863 : : */
864 : : List *
1238 heikki.linnakangas@i 865 : 9375 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
866 : : {
867 : 9375 : List *attnums = NIL;
868 : :
869 [ + + ]: 9375 : if (attnamelist == NIL)
870 : : {
871 : : /* Generate default column list */
7893 bruce@momjian.us 872 : 1668 : int attr_count = tupDesc->natts;
873 : : int i;
874 : :
7929 875 [ + + ]: 5675 : for (i = 0; i < attr_count; i++)
876 : : {
2429 andres@anarazel.de 877 [ + + ]: 4007 : if (TupleDescAttr(tupDesc, i)->attisdropped)
7926 tgl@sss.pgh.pa.us 878 : 98 : continue;
1842 peter@eisentraut.org 879 [ + + ]: 3909 : if (TupleDescAttr(tupDesc, i)->attgenerated)
880 : 27 : continue;
7263 neilc@samurai.com 881 : 3882 : attnums = lappend_int(attnums, i + 1);
882 : : }
883 : : }
884 : : else
885 : : {
886 : : /* Validate the user-supplied list and extract attnums */
887 : : ListCell *l;
888 : :
7926 tgl@sss.pgh.pa.us 889 [ + - + + : 35686 : foreach(l, attnamelist)
+ + ]
890 : : {
7909 891 : 28009 : char *name = strVal(lfirst(l));
892 : : int attnum;
893 : : int i;
894 : :
895 : : /* Lookup column name */
6437 896 : 28009 : attnum = InvalidAttrNumber;
897 [ + + ]: 4937780 : for (i = 0; i < tupDesc->natts; i++)
898 : : {
2429 andres@anarazel.de 899 : 4937765 : Form_pg_attribute att = TupleDescAttr(tupDesc, i);
900 : :
901 [ + + ]: 4937765 : if (att->attisdropped)
6437 tgl@sss.pgh.pa.us 902 : 386 : continue;
2429 andres@anarazel.de 903 [ + + ]: 4937379 : if (namestrcmp(&(att->attname), name) == 0)
904 : : {
1842 peter@eisentraut.org 905 [ + + ]: 27994 : if (att->attgenerated)
906 [ + - ]: 12 : ereport(ERROR,
907 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
908 : : errmsg("column \"%s\" is a generated column",
909 : : name),
910 : : errdetail("Generated columns cannot be used in COPY.")));
2429 andres@anarazel.de 911 : 27982 : attnum = att->attnum;
6437 tgl@sss.pgh.pa.us 912 : 27982 : break;
913 : : }
914 : : }
6597 915 [ + + ]: 27997 : if (attnum == InvalidAttrNumber)
916 : : {
6437 917 [ + - ]: 15 : if (rel != NULL)
918 [ + - ]: 15 : ereport(ERROR,
919 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
920 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
921 : : name, RelationGetRelationName(rel))));
922 : : else
6437 tgl@sss.pgh.pa.us 923 [ # # ]:UBC 0 : ereport(ERROR,
924 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
925 : : errmsg("column \"%s\" does not exist",
926 : : name)));
927 : : }
928 : : /* Check for duplicates */
7263 neilc@samurai.com 929 [ + + ]:CBC 27982 : if (list_member_int(attnums, attnum))
7574 tgl@sss.pgh.pa.us 930 [ + - ]: 3 : ereport(ERROR,
931 : : (errcode(ERRCODE_DUPLICATE_COLUMN),
932 : : errmsg("column \"%s\" specified more than once",
933 : : name)));
7263 neilc@samurai.com 934 : 27979 : attnums = lappend_int(attnums, attnum);
935 : : }
936 : : }
937 : :
7926 tgl@sss.pgh.pa.us 938 : 9345 : return attnums;
939 : : }
|