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