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-2025, 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
3208 tgl@sss.pgh.pa.us 62 :CBC 5646 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
63 : : int stmt_location, int stmt_len,
64 : : uint64 *processed)
65 : : {
5367 itagaki.takahiro@gma 66 : 5646 : bool is_from = stmt->is_from;
67 : 5646 : bool pipe = (stmt->filename == NULL);
68 : : Relation rel;
69 : : Oid relid;
3208 tgl@sss.pgh.pa.us 70 : 5646 : RawStmt *query = NULL;
2398 andres@anarazel.de 71 : 5646 : Node *whereClause = NULL;
72 : :
73 : : /*
74 : : * Disallow COPY to/from file or program except to users with the
75 : : * appropriate role.
76 : : */
2761 sfrost@snowman.net 77 [ + + ]: 5646 : if (!pipe)
78 : : {
4625 heikki.linnakangas@i 79 [ - + ]: 223 : if (stmt->is_program)
80 : : {
1309 mail@joeconway.com 81 [ # # ]:UBC 0 : if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM))
2761 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 : : {
1309 mail@joeconway.com 92 [ + + - + ]:CBC 223 : if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
2761 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 : :
1309 mail@joeconway.com 101 [ + + - + ]:CBC 223 : if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
2761 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 : :
5367 itagaki.takahiro@gma 112 [ + + ]:CBC 5646 : if (stmt->relation)
113 : : {
2584 tgl@sss.pgh.pa.us 114 [ + + ]: 5345 : LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock;
115 : : ParseNamespaceItem *nsitem;
116 : : RTEPermissionInfo *perminfo;
117 : : TupleDesc tupDesc;
118 : : List *attnums;
119 : : ListCell *cur;
120 : :
5367 itagaki.takahiro@gma 121 [ - + ]: 5345 : Assert(!stmt->query);
122 : :
123 : : /* Open and lock the relation, using the appropriate lock type. */
2471 andres@anarazel.de 124 : 5345 : rel = table_openrv(stmt->relation, lockmode);
125 : :
4685 rhaas@postgresql.org 126 : 5344 : relid = RelationGetRelid(rel);
127 : :
2125 tgl@sss.pgh.pa.us 128 : 5344 : nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
129 : : NULL, false, false);
130 : :
1056 alvherre@alvh.no-ip. 131 : 5344 : perminfo = nsitem->p_perminfo;
132 [ + + ]: 5344 : perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
133 : :
2473 tomas.vondra@postgre 134 [ + + ]: 5344 : if (stmt->whereClause)
135 : : {
136 : : /* add nsitem to query namespace */
2125 tgl@sss.pgh.pa.us 137 : 24 : addNSItemToQuery(pstate, nsitem, false, true, true);
138 : :
139 : : /* Transform the raw expression tree */
2473 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 : :
5367 itagaki.takahiro@gma 154 : 5329 : tupDesc = RelationGetDescr(rel);
155 : 5329 : attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
5363 tgl@sss.pgh.pa.us 156 [ + + + + : 23586 : foreach(cur, attnums)
+ + ]
157 : : {
158 : : int attno;
159 : : Bitmapset **bms;
160 : :
1056 alvherre@alvh.no-ip. 161 : 18299 : attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
162 [ + + ]: 18299 : bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
163 : :
164 : 18299 : *bms = bms_add_member(*bms, attno);
165 : : }
166 : 5287 : 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 [ + + ]: 5245 : if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
183 : : {
184 : : SelectStmt *select;
185 : : ColumnRef *cr;
186 : : ResTarget *target;
187 : : RangeVar *from;
3311 sfrost@snowman.net 188 : 39 : List *targetList = NIL;
189 : :
4056 190 [ + + ]: 39 : 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 [ + + ]: 36 : if (!stmt->attlist)
209 : : {
3311 210 : 18 : cr = makeNode(ColumnRef);
4056 211 : 18 : cr->fields = list_make1(makeNode(A_Star));
3311 212 : 18 : cr->location = -1;
213 : :
214 : 18 : target = makeNode(ResTarget);
215 : 18 : target->name = NULL;
216 : 18 : target->indirection = NIL;
217 : 18 : target->val = (Node *) cr;
218 : 18 : target->location = -1;
219 : :
220 : 18 : 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 : : *
255 : : * However, when copying data from a partitioned table, we don't
256 : : * use "ONLY", since we need to retrieve rows from its descendant
257 : : * tables too.
258 : : */
3745 259 : 36 : from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
3157 tgl@sss.pgh.pa.us 260 : 36 : pstrdup(RelationGetRelationName(rel)),
261 : : -1);
7 msawada@postgresql.o 262 :GNC 36 : from->inh = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
263 : :
264 : : /* Build query */
4056 sfrost@snowman.net 265 :CBC 36 : select = makeNode(SelectStmt);
3311 266 : 36 : select->targetList = targetList;
4056 267 : 36 : select->fromClause = list_make1(from);
268 : :
3208 tgl@sss.pgh.pa.us 269 : 36 : query = makeNode(RawStmt);
270 : 36 : query->stmt = (Node *) select;
271 : 36 : query->stmt_location = stmt_location;
272 : 36 : query->stmt_len = stmt_len;
273 : :
274 : : /*
275 : : * Close the relation for now, but keep the lock on it to prevent
276 : : * changes between now and when we start the query-based COPY.
277 : : *
278 : : * We'll reopen it later as part of the query-based COPY.
279 : : */
2471 andres@anarazel.de 280 : 36 : table_close(rel, NoLock);
4056 sfrost@snowman.net 281 : 36 : rel = NULL;
282 : : }
283 : : }
284 : : else
285 : : {
5367 itagaki.takahiro@gma 286 [ - + ]: 301 : Assert(stmt->query);
287 : :
3208 tgl@sss.pgh.pa.us 288 : 301 : query = makeNode(RawStmt);
289 : 301 : query->stmt = stmt->query;
290 : 301 : query->stmt_location = stmt_location;
291 : 301 : query->stmt_len = stmt_len;
292 : :
4683 peter_e@gmx.net 293 : 301 : relid = InvalidOid;
5367 itagaki.takahiro@gma 294 : 301 : rel = NULL;
295 : : }
296 : :
297 [ + + ]: 5534 : if (is_from)
298 : : {
299 : : CopyFromState cstate;
300 : :
4975 peter_e@gmx.net 301 [ - + ]: 863 : Assert(rel);
302 : :
303 : : /* check read-only transaction and parallel mode */
4697 tgl@sss.pgh.pa.us 304 [ - + - - ]: 863 : if (XactReadOnly && !rel->rd_islocaltemp)
5367 itagaki.takahiro@gma 305 :UBC 0 : PreventCommandIfReadOnly("COPY FROM");
306 : :
1799 heikki.linnakangas@i 307 :CBC 863 : cstate = BeginCopyFrom(pstate, rel, whereClause,
308 : 863 : stmt->filename, stmt->is_program,
3140 peter_e@gmx.net 309 : 863 : NULL, stmt->attlist, stmt->options);
4685 rhaas@postgresql.org 310 : 731 : *processed = CopyFrom(cstate); /* copy from file to database */
5367 itagaki.takahiro@gma 311 : 613 : EndCopyFrom(cstate);
312 : : }
313 : : else
314 : : {
315 : : CopyToState cstate;
316 : :
3338 peter_e@gmx.net 317 : 4671 : cstate = BeginCopyTo(pstate, rel, query, relid,
4625 heikki.linnakangas@i 318 : 4671 : stmt->filename, stmt->is_program,
1112 michael@paquier.xyz 319 : 4671 : NULL, stmt->attlist, stmt->options);
4685 rhaas@postgresql.org 320 : 4570 : *processed = DoCopyTo(cstate); /* copy from database to file */
5367 itagaki.takahiro@gma 321 : 4569 : EndCopyTo(cstate);
322 : : }
323 : :
324 [ + + ]: 5182 : if (rel != NULL)
1991 akapila@postgresql.o 325 : 4915 : table_close(rel, NoLock);
5367 itagaki.takahiro@gma 326 : 5182 : }
327 : :
328 : : /*
329 : : * Extract the CopyFormatOptions.header_line value from a DefElem.
330 : : *
331 : : * Parses the HEADER option for COPY, which can be a boolean, a non-negative
332 : : * integer (number of lines to skip), or the special value "match".
333 : : */
334 : : static int
116 fujii@postgresql.org 335 :GNC 114 : defGetCopyHeaderOption(DefElem *def, bool is_from)
336 : : {
337 : : /*
338 : : * If no parameter value given, assume "true" is meant.
339 : : */
1307 peter@eisentraut.org 340 [ + + ]:CBC 114 : if (def->arg == NULL)
341 : 15 : return COPY_HEADER_TRUE;
342 : :
343 : : /*
344 : : * Allow 0, 1, "true", "false", "on", "off", a non-negative integer, or
345 : : * "match".
346 : : */
347 [ + + ]: 99 : switch (nodeTag(def->arg))
348 : : {
1307 peter@eisentraut.org 349 :GBC 15 : case T_Integer:
350 : : {
116 fujii@postgresql.org 351 :GNC 15 : int ival = intVal(def->arg);
352 : :
353 [ + + ]: 15 : if (ival < 0)
354 [ + - ]: 3 : ereport(ERROR,
355 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
356 : : errmsg("a negative integer value cannot be "
357 : : "specified for %s", def->defname)));
358 : :
359 [ + + + - ]: 12 : if (!is_from && ival > 1)
360 [ + - ]: 3 : ereport(ERROR,
361 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
362 : : errmsg("cannot use multi-line header in COPY TO")));
363 : :
364 : 9 : return ival;
365 : : }
1307 peter@eisentraut.org 366 :EUB : break;
1307 peter@eisentraut.org 367 :CBC 84 : default:
368 : : {
1264 tgl@sss.pgh.pa.us 369 : 84 : char *sval = defGetString(def);
370 : :
371 : : /*
372 : : * The set of strings accepted here should match up with the
373 : : * grammar's opt_boolean_or_string production.
374 : : */
1307 peter@eisentraut.org 375 [ + + ]: 84 : if (pg_strcasecmp(sval, "true") == 0)
376 : 32 : return COPY_HEADER_TRUE;
377 [ - + ]: 52 : if (pg_strcasecmp(sval, "false") == 0)
1307 peter@eisentraut.org 378 :UBC 0 : return COPY_HEADER_FALSE;
1307 peter@eisentraut.org 379 [ - + ]:CBC 52 : if (pg_strcasecmp(sval, "on") == 0)
1307 peter@eisentraut.org 380 :UBC 0 : return COPY_HEADER_TRUE;
1307 peter@eisentraut.org 381 [ + + ]:CBC 52 : if (pg_strcasecmp(sval, "off") == 0)
382 : 3 : return COPY_HEADER_FALSE;
383 [ + + ]: 49 : if (pg_strcasecmp(sval, "match") == 0)
384 : : {
1222 michael@paquier.xyz 385 [ + + ]: 43 : if (!is_from)
386 [ + - ]: 3 : ereport(ERROR,
387 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
388 : : errmsg("cannot use \"%s\" with HEADER in COPY TO",
389 : : sval)));
1307 peter@eisentraut.org 390 : 40 : return COPY_HEADER_MATCH;
391 : : }
392 : : }
393 : 6 : break;
394 : : }
395 [ + - ]: 6 : ereport(ERROR,
396 : : (errcode(ERRCODE_SYNTAX_ERROR),
397 : : errmsg("%s requires a Boolean value, a non-negative integer, "
398 : : "or the string \"match\"",
399 : : def->defname)));
400 : : return COPY_HEADER_FALSE; /* keep compiler quiet */
401 : : }
402 : :
403 : : /*
404 : : * Extract a CopyOnErrorChoice value from a DefElem.
405 : : */
406 : : static CopyOnErrorChoice
647 akorotkov@postgresql 407 : 57 : defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
408 : : {
558 msawada@postgresql.o 409 : 57 : char *sval = defGetString(def);
410 : :
650 akorotkov@postgresql 411 [ + + ]: 57 : if (!is_from)
412 [ + - ]: 3 : ereport(ERROR,
413 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
414 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
415 : : second %s is a COPY with direction, e.g. COPY TO */
416 : : errmsg("COPY %s cannot be used with %s", "ON_ERROR", "COPY TO"),
417 : : parser_errposition(pstate, def->location)));
418 : :
419 : : /*
420 : : * Allow "stop", or "ignore" values.
421 : : */
647 422 [ + + ]: 54 : if (pg_strcasecmp(sval, "stop") == 0)
423 : 3 : return COPY_ON_ERROR_STOP;
424 [ + + ]: 51 : if (pg_strcasecmp(sval, "ignore") == 0)
425 : 47 : return COPY_ON_ERROR_IGNORE;
426 : :
650 427 [ + - ]: 4 : ereport(ERROR,
428 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
429 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
430 : : errmsg("COPY %s \"%s\" not recognized", "ON_ERROR", sval),
431 : : parser_errposition(pstate, def->location)));
432 : : return COPY_ON_ERROR_STOP; /* keep compiler quiet */
433 : : }
434 : :
435 : : /*
436 : : * Extract REJECT_LIMIT value from a DefElem.
437 : : *
438 : : * REJECT_LIMIT can be specified in two ways: as an int64 for the COPY command
439 : : * option or as a single-quoted string for the foreign table option using
440 : : * file_fdw. Therefore this function needs to handle both formats.
441 : : */
442 : : static int64
384 fujii@postgresql.org 443 : 19 : defGetCopyRejectLimitOption(DefElem *def)
444 : : {
445 : : int64 reject_limit;
446 : :
341 447 [ - + ]: 19 : if (def->arg == NULL)
341 fujii@postgresql.org 448 [ # # ]:UBC 0 : ereport(ERROR,
449 : : (errcode(ERRCODE_SYNTAX_ERROR),
450 : : errmsg("%s requires a numeric value",
451 : : def->defname)));
341 fujii@postgresql.org 452 [ + + ]:CBC 19 : else if (nodeTag(def->arg) == T_String)
453 : 7 : reject_limit = pg_strtoint64(strVal(def->arg));
454 : : else
455 : 12 : reject_limit = defGetInt64(def);
456 : :
384 457 [ + + ]: 19 : if (reject_limit <= 0)
458 [ + - ]: 4 : ereport(ERROR,
459 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
460 : : errmsg("REJECT_LIMIT (%" PRId64 ") must be greater than zero",
461 : : reject_limit)));
462 : :
463 : 15 : return reject_limit;
464 : : }
465 : :
466 : : /*
467 : : * Extract a CopyLogVerbosityChoice value from a DefElem.
468 : : */
469 : : static CopyLogVerbosityChoice
574 msawada@postgresql.o 470 : 23 : defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate)
471 : : {
472 : : char *sval;
473 : :
474 : : /*
475 : : * Allow "silent", "default", or "verbose" values.
476 : : */
477 : 23 : sval = defGetString(def);
389 fujii@postgresql.org 478 [ + + ]: 23 : if (pg_strcasecmp(sval, "silent") == 0)
479 : 10 : return COPY_LOG_VERBOSITY_SILENT;
574 msawada@postgresql.o 480 [ + + ]: 13 : if (pg_strcasecmp(sval, "default") == 0)
481 : 3 : return COPY_LOG_VERBOSITY_DEFAULT;
482 [ + + ]: 10 : if (pg_strcasecmp(sval, "verbose") == 0)
483 : 6 : return COPY_LOG_VERBOSITY_VERBOSE;
484 : :
485 [ + - ]: 4 : ereport(ERROR,
486 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
487 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
488 : : errmsg("COPY %s \"%s\" not recognized", "LOG_VERBOSITY", sval),
489 : : parser_errposition(pstate, def->location)));
490 : : return COPY_LOG_VERBOSITY_DEFAULT; /* keep compiler quiet */
491 : : }
492 : :
493 : : /*
494 : : * Process the statement option list for COPY.
495 : : *
496 : : * Scan the options list (a list of DefElem) and transpose the information
497 : : * into *opts_out, applying appropriate error checking.
498 : : *
499 : : * If 'opts_out' is not NULL, it is assumed to be filled with zeroes initially.
500 : : *
501 : : * This is exported so that external users of the COPY API can sanity-check
502 : : * a list of options. In that usage, 'opts_out' can be passed as NULL and
503 : : * the collected data is just leaked until CurrentMemoryContext is reset.
504 : : *
505 : : * Note that additional checking, such as whether column names listed in FORCE
506 : : * QUOTE actually exist, has to be applied later. This just checks for
507 : : * self-consistency of the options list.
508 : : */
509 : : void
3338 peter_e@gmx.net 510 : 5804 : ProcessCopyOptions(ParseState *pstate,
511 : : CopyFormatOptions *opts_out,
512 : : bool is_from,
513 : : List *options)
514 : : {
5880 tgl@sss.pgh.pa.us 515 : 5804 : bool format_specified = false;
1848 michael@paquier.xyz 516 : 5804 : bool freeze_specified = false;
517 : 5804 : bool header_specified = false;
647 akorotkov@postgresql 518 : 5804 : bool on_error_specified = false;
574 msawada@postgresql.o 519 : 5804 : bool log_verbosity_specified = false;
384 fujii@postgresql.org 520 : 5804 : bool reject_limit_specified = false;
521 : : ListCell *option;
522 : :
523 : : /* Support external use for option sanity checking */
1799 heikki.linnakangas@i 524 [ + + ]: 5804 : if (opts_out == NULL)
525 : 52 : opts_out = (CopyFormatOptions *) palloc0(sizeof(CopyFormatOptions));
526 : :
527 : 5804 : opts_out->file_encoding = -1;
528 : :
529 : : /* Extract options from the statement node tree */
5367 itagaki.takahiro@gma 530 [ + + + + : 6841 : foreach(option, options)
+ + ]
531 : : {
3122 tgl@sss.pgh.pa.us 532 : 1116 : DefElem *defel = lfirst_node(DefElem, option);
533 : :
5880 534 [ + + ]: 1116 : if (strcmp(defel->defname, "format") == 0)
535 : : {
5722 bruce@momjian.us 536 : 328 : char *fmt = defGetString(defel);
537 : :
5880 tgl@sss.pgh.pa.us 538 [ + + ]: 328 : if (format_specified)
1565 dean.a.rasheed@gmail 539 : 3 : errorConflictingDefElem(defel, pstate);
5880 tgl@sss.pgh.pa.us 540 : 325 : format_specified = true;
541 [ + + ]: 325 : if (strcmp(fmt, "text") == 0)
542 : : /* default format */ ;
543 [ + + ]: 287 : else if (strcmp(fmt, "csv") == 0)
1799 heikki.linnakangas@i 544 : 251 : opts_out->csv_mode = true;
5880 tgl@sss.pgh.pa.us 545 [ + + ]: 36 : else if (strcmp(fmt, "binary") == 0)
1799 heikki.linnakangas@i 546 : 35 : opts_out->binary = true;
547 : : else
5880 tgl@sss.pgh.pa.us 548 [ + - ]: 1 : ereport(ERROR,
549 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
550 : : errmsg("COPY format \"%s\" not recognized", fmt),
551 : : parser_errposition(pstate, defel->location)));
552 : : }
4713 simon@2ndQuadrant.co 553 [ + + ]: 788 : else if (strcmp(defel->defname, "freeze") == 0)
554 : : {
1848 michael@paquier.xyz 555 [ + + ]: 42 : if (freeze_specified)
1565 dean.a.rasheed@gmail 556 : 3 : errorConflictingDefElem(defel, pstate);
1848 michael@paquier.xyz 557 : 39 : freeze_specified = true;
1799 heikki.linnakangas@i 558 : 39 : opts_out->freeze = defGetBoolean(defel);
559 : : }
8530 bruce@momjian.us 560 [ + + ]: 746 : else if (strcmp(defel->defname, "delimiter") == 0)
561 : : {
1799 heikki.linnakangas@i 562 [ + + ]: 150 : if (opts_out->delim)
1565 dean.a.rasheed@gmail 563 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 564 : 147 : opts_out->delim = defGetString(defel);
565 : : }
8530 bruce@momjian.us 566 [ + + ]: 596 : else if (strcmp(defel->defname, "null") == 0)
567 : : {
1799 heikki.linnakangas@i 568 [ + + ]: 72 : if (opts_out->null_print)
1565 dean.a.rasheed@gmail 569 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 570 : 69 : opts_out->null_print = defGetString(defel);
571 : : }
959 andrew@dunslane.net 572 [ + + ]: 524 : else if (strcmp(defel->defname, "default") == 0)
573 : : {
574 [ - + ]: 45 : if (opts_out->default_print)
959 andrew@dunslane.net 575 :UBC 0 : errorConflictingDefElem(defel, pstate);
959 andrew@dunslane.net 576 :CBC 45 : opts_out->default_print = defGetString(defel);
577 : : }
7478 bruce@momjian.us 578 [ + + ]: 479 : else if (strcmp(defel->defname, "header") == 0)
579 : : {
1848 michael@paquier.xyz 580 [ + + ]: 117 : if (header_specified)
1565 dean.a.rasheed@gmail 581 : 3 : errorConflictingDefElem(defel, pstate);
1848 michael@paquier.xyz 582 : 114 : header_specified = true;
116 fujii@postgresql.org 583 :GNC 114 : opts_out->header_line = defGetCopyHeaderOption(defel, is_from);
584 : : }
7861 bruce@momjian.us 585 [ + + ]:CBC 362 : else if (strcmp(defel->defname, "quote") == 0)
586 : : {
1799 heikki.linnakangas@i 587 [ + + ]: 51 : if (opts_out->quote)
1565 dean.a.rasheed@gmail 588 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 589 : 48 : opts_out->quote = defGetString(defel);
590 : : }
7861 bruce@momjian.us 591 [ + + ]: 311 : else if (strcmp(defel->defname, "escape") == 0)
592 : : {
1799 heikki.linnakangas@i 593 [ + + ]: 47 : if (opts_out->escape)
1565 dean.a.rasheed@gmail 594 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 595 : 44 : opts_out->escape = defGetString(defel);
596 : : }
7859 bruce@momjian.us 597 [ + + ]: 264 : else if (strcmp(defel->defname, "force_quote") == 0)
598 : : {
1799 heikki.linnakangas@i 599 [ + + - + ]: 39 : if (opts_out->force_quote || opts_out->force_quote_all)
1565 dean.a.rasheed@gmail 600 : 3 : errorConflictingDefElem(defel, pstate);
5938 tgl@sss.pgh.pa.us 601 [ + - + + ]: 36 : if (defel->arg && IsA(defel->arg, A_Star))
1799 heikki.linnakangas@i 602 : 15 : opts_out->force_quote_all = true;
5880 tgl@sss.pgh.pa.us 603 [ + - + - ]: 21 : else if (defel->arg && IsA(defel->arg, List))
1799 heikki.linnakangas@i 604 : 21 : opts_out->force_quote = castNode(List, defel->arg);
605 : : else
5880 tgl@sss.pgh.pa.us 606 [ # # ]:UBC 0 : ereport(ERROR,
607 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
608 : : errmsg("argument to option \"%s\" must be a list of column names",
609 : : defel->defname),
610 : : parser_errposition(pstate, defel->location)));
611 : : }
5880 tgl@sss.pgh.pa.us 612 [ + + ]:CBC 225 : else if (strcmp(defel->defname, "force_not_null") == 0)
613 : : {
758 andrew@dunslane.net 614 [ + + + + ]: 44 : if (opts_out->force_notnull || opts_out->force_notnull_all)
1565 dean.a.rasheed@gmail 615 : 6 : errorConflictingDefElem(defel, pstate);
758 andrew@dunslane.net 616 [ + - + + ]: 38 : if (defel->arg && IsA(defel->arg, A_Star))
617 : 15 : opts_out->force_notnull_all = true;
618 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1799 heikki.linnakangas@i 619 : 23 : opts_out->force_notnull = castNode(List, defel->arg);
620 : : else
5880 tgl@sss.pgh.pa.us 621 [ # # ]:UBC 0 : ereport(ERROR,
622 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
623 : : errmsg("argument to option \"%s\" must be a list of column names",
624 : : defel->defname),
625 : : parser_errposition(pstate, defel->location)));
626 : : }
4255 andrew@dunslane.net 627 [ + + ]:CBC 181 : else if (strcmp(defel->defname, "force_null") == 0)
628 : : {
758 629 [ + + + + ]: 44 : if (opts_out->force_null || opts_out->force_null_all)
1565 dean.a.rasheed@gmail 630 : 6 : errorConflictingDefElem(defel, pstate);
758 andrew@dunslane.net 631 [ + - + + ]: 38 : if (defel->arg && IsA(defel->arg, A_Star))
632 : 15 : opts_out->force_null_all = true;
633 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1799 heikki.linnakangas@i 634 : 23 : opts_out->force_null = castNode(List, defel->arg);
635 : : else
4255 andrew@dunslane.net 636 [ # # ]:UBC 0 : ereport(ERROR,
637 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
638 : : errmsg("argument to option \"%s\" must be a list of column names",
639 : : defel->defname),
640 : : parser_errposition(pstate, defel->location)));
641 : : }
4855 tgl@sss.pgh.pa.us 642 [ + + ]:CBC 137 : else if (strcmp(defel->defname, "convert_selectively") == 0)
643 : : {
644 : : /*
645 : : * Undocumented, not-accessible-from-SQL option: convert only the
646 : : * named columns to binary form, storing the rest as NULLs. It's
647 : : * allowed for the column list to be NIL.
648 : : */
1799 heikki.linnakangas@i 649 [ + + ]: 8 : if (opts_out->convert_selectively)
1565 dean.a.rasheed@gmail 650 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 651 : 5 : opts_out->convert_selectively = true;
4855 tgl@sss.pgh.pa.us 652 [ + - + - ]: 5 : if (defel->arg == NULL || IsA(defel->arg, List))
1799 heikki.linnakangas@i 653 : 5 : opts_out->convert_select = castNode(List, defel->arg);
654 : : else
4855 tgl@sss.pgh.pa.us 655 [ # # ]:UBC 0 : ereport(ERROR,
656 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
657 : : errmsg("argument to option \"%s\" must be a list of column names",
658 : : defel->defname),
659 : : parser_errposition(pstate, defel->location)));
660 : : }
5362 itagaki.takahiro@gma 661 [ + + ]:CBC 129 : else if (strcmp(defel->defname, "encoding") == 0)
662 : : {
1799 heikki.linnakangas@i 663 [ + + ]: 24 : if (opts_out->file_encoding >= 0)
1565 dean.a.rasheed@gmail 664 : 3 : errorConflictingDefElem(defel, pstate);
1799 heikki.linnakangas@i 665 : 21 : opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
666 [ - + ]: 21 : if (opts_out->file_encoding < 0)
5362 itagaki.takahiro@gma 667 [ # # ]:UBC 0 : ereport(ERROR,
668 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
669 : : errmsg("argument to option \"%s\" must be a valid encoding name",
670 : : defel->defname),
671 : : parser_errposition(pstate, defel->location)));
672 : : }
647 akorotkov@postgresql 673 [ + + ]:CBC 105 : else if (strcmp(defel->defname, "on_error") == 0)
674 : : {
675 [ + + ]: 60 : if (on_error_specified)
650 676 : 3 : errorConflictingDefElem(defel, pstate);
647 677 : 57 : on_error_specified = true;
678 : 57 : opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
679 : : }
574 msawada@postgresql.o 680 [ + + ]: 45 : else if (strcmp(defel->defname, "log_verbosity") == 0)
681 : : {
682 [ + + ]: 26 : if (log_verbosity_specified)
683 : 3 : errorConflictingDefElem(defel, pstate);
684 : 23 : log_verbosity_specified = true;
685 : 23 : opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
686 : : }
384 fujii@postgresql.org 687 [ + - ]: 19 : else if (strcmp(defel->defname, "reject_limit") == 0)
688 : : {
689 [ - + ]: 19 : if (reject_limit_specified)
384 fujii@postgresql.org 690 :UBC 0 : errorConflictingDefElem(defel, pstate);
384 fujii@postgresql.org 691 :CBC 19 : reject_limit_specified = true;
692 : 19 : opts_out->reject_limit = defGetCopyRejectLimitOption(defel);
693 : : }
694 : : else
5880 tgl@sss.pgh.pa.us 695 [ # # ]:UBC 0 : ereport(ERROR,
696 : : (errcode(ERRCODE_SYNTAX_ERROR),
697 : : errmsg("option \"%s\" not recognized",
698 : : defel->defname),
699 : : parser_errposition(pstate, defel->location)));
700 : : }
701 : :
702 : : /*
703 : : * Check for incompatible options (must do these three before inserting
704 : : * defaults)
705 : : */
1799 heikki.linnakangas@i 706 [ + + + + ]:CBC 5725 : if (opts_out->binary && opts_out->delim)
8135 tgl@sss.pgh.pa.us 707 [ + - ]: 3 : ereport(ERROR,
708 : : (errcode(ERRCODE_SYNTAX_ERROR),
709 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
710 : : errmsg("cannot specify %s in BINARY mode", "DELIMITER")));
711 : :
1799 heikki.linnakangas@i 712 [ + + + + ]: 5722 : if (opts_out->binary && opts_out->null_print)
8135 tgl@sss.pgh.pa.us 713 [ + - ]: 3 : ereport(ERROR,
714 : : (errcode(ERRCODE_SYNTAX_ERROR),
715 : : errmsg("cannot specify %s in BINARY mode", "NULL")));
716 : :
959 andrew@dunslane.net 717 [ + + + + ]: 5719 : if (opts_out->binary && opts_out->default_print)
718 [ + - ]: 3 : ereport(ERROR,
719 : : (errcode(ERRCODE_SYNTAX_ERROR),
720 : : errmsg("cannot specify %s in BINARY mode", "DEFAULT")));
721 : :
722 : : /* Set defaults for omitted options */
1799 heikki.linnakangas@i 723 [ + + ]: 5716 : if (!opts_out->delim)
724 [ + + ]: 5575 : opts_out->delim = opts_out->csv_mode ? "," : "\t";
725 : :
726 [ + + ]: 5716 : if (!opts_out->null_print)
727 [ + + ]: 5653 : opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
728 : 5716 : opts_out->null_print_len = strlen(opts_out->null_print);
729 : :
730 [ + + ]: 5716 : if (opts_out->csv_mode)
731 : : {
732 [ + + ]: 242 : if (!opts_out->quote)
733 : 199 : opts_out->quote = "\"";
734 [ + + ]: 242 : if (!opts_out->escape)
735 : 204 : opts_out->escape = opts_out->quote;
736 : : }
737 : :
738 : : /* Only single-byte delimiter strings are supported. */
739 [ + + ]: 5716 : if (strlen(opts_out->delim) != 1)
7861 bruce@momjian.us 740 [ + - ]: 1 : ereport(ERROR,
741 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
742 : : errmsg("COPY delimiter must be a single one-byte character")));
743 : :
744 : : /* Disallow end-of-line characters */
1799 heikki.linnakangas@i 745 [ + - ]: 5715 : if (strchr(opts_out->delim, '\r') != NULL ||
746 [ + + ]: 5715 : strchr(opts_out->delim, '\n') != NULL)
7206 bruce@momjian.us 747 [ + - ]: 1 : ereport(ERROR,
748 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
749 : : errmsg("COPY delimiter cannot be newline or carriage return")));
750 : :
1799 heikki.linnakangas@i 751 [ + - ]: 5714 : if (strchr(opts_out->null_print, '\r') != NULL ||
752 [ + + ]: 5714 : strchr(opts_out->null_print, '\n') != NULL)
7206 bruce@momjian.us 753 [ + - ]: 1 : ereport(ERROR,
754 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
755 : : errmsg("COPY null representation cannot use newline or carriage return")));
756 : :
959 andrew@dunslane.net 757 [ + + ]: 5713 : if (opts_out->default_print)
758 : : {
759 : 42 : opts_out->default_print_len = strlen(opts_out->default_print);
760 : :
761 [ + + ]: 42 : if (strchr(opts_out->default_print, '\r') != NULL ||
762 [ + + ]: 39 : strchr(opts_out->default_print, '\n') != NULL)
763 [ + - ]: 6 : ereport(ERROR,
764 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
765 : : errmsg("COPY default representation cannot use newline or carriage return")));
766 : : }
767 : :
768 : : /*
769 : : * Disallow unsafe delimiter characters in non-CSV mode. We can't allow
770 : : * backslash because it would be ambiguous. We can't allow the other
771 : : * cases because data characters matching the delimiter must be
772 : : * backslashed, and certain backslash combinations are interpreted
773 : : * non-literally by COPY IN. Disallowing all lower case ASCII letters is
774 : : * more than strictly necessary, but seems best for consistency and
775 : : * future-proofing. Likewise we disallow all digits though only octal
776 : : * digits are actually dangerous.
777 : : */
1799 heikki.linnakangas@i 778 [ + + ]: 5707 : if (!opts_out->csv_mode &&
6514 tgl@sss.pgh.pa.us 779 : 5468 : strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
1799 heikki.linnakangas@i 780 [ + + ]: 5468 : opts_out->delim[0]) != NULL)
7206 bruce@momjian.us 781 [ + - ]: 5 : ereport(ERROR,
782 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
783 : : errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
784 : :
785 : : /* Check header */
116 fujii@postgresql.org 786 [ + + + + ]:GNC 5702 : if (opts_out->binary && opts_out->header_line != COPY_HEADER_FALSE)
7478 bruce@momjian.us 787 [ + - ]:CBC 1 : ereport(ERROR,
788 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
789 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
790 : : errmsg("cannot specify %s in BINARY mode", "HEADER")));
791 : :
792 : : /* Check quote */
1799 heikki.linnakangas@i 793 [ + + + + ]: 5701 : if (!opts_out->csv_mode && opts_out->quote != NULL)
7861 bruce@momjian.us 794 [ + - ]: 2 : ereport(ERROR,
795 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
796 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
797 : : errmsg("COPY %s requires CSV mode", "QUOTE")));
798 : :
1799 heikki.linnakangas@i 799 [ + + + + ]: 5699 : if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
7861 bruce@momjian.us 800 [ + - ]: 1 : ereport(ERROR,
801 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
802 : : errmsg("COPY quote must be a single one-byte character")));
803 : :
1799 heikki.linnakangas@i 804 [ + + + + ]: 5698 : if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
6511 andrew@dunslane.net 805 [ + - ]: 1 : ereport(ERROR,
806 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
807 : : errmsg("COPY delimiter and quote must be different")));
808 : :
809 : : /* Check escape */
1799 heikki.linnakangas@i 810 [ + + + + ]: 5697 : if (!opts_out->csv_mode && opts_out->escape != NULL)
7861 bruce@momjian.us 811 [ + - ]: 3 : ereport(ERROR,
812 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
813 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
814 : : errmsg("COPY %s requires CSV mode", "ESCAPE")));
815 : :
1799 heikki.linnakangas@i 816 [ + + + + ]: 5694 : if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
7861 bruce@momjian.us 817 [ + - ]: 1 : ereport(ERROR,
818 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 : : errmsg("COPY escape must be a single one-byte character")));
820 : :
821 : : /* Check force_quote */
1799 heikki.linnakangas@i 822 [ + + + + : 5693 : if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
+ + ]
7861 bruce@momjian.us 823 [ + - ]: 6 : ereport(ERROR,
824 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
825 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
826 : : errmsg("COPY %s requires CSV mode", "FORCE_QUOTE")));
1799 heikki.linnakangas@i 827 [ + + + + : 5687 : if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
+ + ]
7861 bruce@momjian.us 828 [ + - ]: 6 : ereport(ERROR,
829 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
830 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
831 : : second %s is a COPY with direction, e.g. COPY TO */
832 : : errmsg("COPY %s cannot be used with %s", "FORCE_QUOTE",
833 : : "COPY FROM")));
834 : :
835 : : /* Check force_notnull */
375 michael@paquier.xyz 836 [ + + + + ]: 5681 : if (!opts_out->csv_mode && (opts_out->force_notnull != NIL ||
837 [ + + ]: 5447 : opts_out->force_notnull_all))
7861 bruce@momjian.us 838 [ + - ]: 7 : ereport(ERROR,
839 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
840 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
841 : : errmsg("COPY %s requires CSV mode", "FORCE_NOT_NULL")));
375 michael@paquier.xyz 842 [ + + + + ]: 5674 : if ((opts_out->force_notnull != NIL || opts_out->force_notnull_all) &&
843 [ + + ]: 25 : !is_from)
7861 bruce@momjian.us 844 [ + - ]: 6 : ereport(ERROR,
845 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
846 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
847 : : second %s is a COPY with direction, e.g. COPY TO */
848 : : errmsg("COPY %s cannot be used with %s", "FORCE_NOT_NULL",
849 : : "COPY TO")));
850 : :
851 : : /* Check force_null */
375 michael@paquier.xyz 852 [ + + + + ]: 5668 : if (!opts_out->csv_mode && (opts_out->force_null != NIL ||
853 [ + + ]: 5441 : opts_out->force_null_all))
4255 andrew@dunslane.net 854 [ + - ]: 6 : ereport(ERROR,
855 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
856 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
857 : : errmsg("COPY %s requires CSV mode", "FORCE_NULL")));
858 : :
375 michael@paquier.xyz 859 [ + + + + ]: 5662 : if ((opts_out->force_null != NIL || opts_out->force_null_all) &&
860 [ + + ]: 25 : !is_from)
4255 andrew@dunslane.net 861 [ + - ]: 6 : ereport(ERROR,
862 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
863 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
864 : : second %s is a COPY with direction, e.g. COPY TO */
865 : : errmsg("COPY %s cannot be used with %s", "FORCE_NULL",
866 : : "COPY TO")));
867 : :
868 : : /* Don't allow the delimiter to appear in the null string. */
1799 heikki.linnakangas@i 869 [ + + ]: 5656 : if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
7861 bruce@momjian.us 870 [ + - ]: 1 : ereport(ERROR,
871 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
872 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
873 : : errmsg("COPY delimiter character must not appear in the %s specification",
874 : : "NULL")));
875 : :
876 : : /* Don't allow the CSV quote char to appear in the null string. */
1799 heikki.linnakangas@i 877 [ + + ]: 5655 : if (opts_out->csv_mode &&
878 [ + + ]: 217 : strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
7861 bruce@momjian.us 879 [ + - ]: 1 : ereport(ERROR,
880 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
881 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
882 : : errmsg("CSV quote character must not appear in the %s specification",
883 : : "NULL")));
884 : :
885 : : /* Check freeze */
714 886 [ + + - + ]: 5654 : if (opts_out->freeze && !is_from)
714 bruce@momjian.us 887 [ # # ]:UBC 0 : ereport(ERROR,
888 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
889 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
890 : : second %s is a COPY with direction, e.g. COPY TO */
891 : : errmsg("COPY %s cannot be used with %s", "FREEZE",
892 : : "COPY TO")));
893 : :
959 andrew@dunslane.net 894 [ + + ]:CBC 5654 : if (opts_out->default_print)
895 : : {
896 [ + + ]: 36 : if (!is_from)
897 [ + - ]: 3 : ereport(ERROR,
898 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
899 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
900 : : second %s is a COPY with direction, e.g. COPY TO */
901 : : errmsg("COPY %s cannot be used with %s", "DEFAULT",
902 : : "COPY TO")));
903 : :
904 : : /* Don't allow the delimiter to appear in the default string. */
905 [ + + ]: 33 : if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
906 [ + - ]: 3 : ereport(ERROR,
907 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
908 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
909 : : errmsg("COPY delimiter character must not appear in the %s specification",
910 : : "DEFAULT")));
911 : :
912 : : /* Don't allow the CSV quote char to appear in the default string. */
913 [ + + ]: 30 : if (opts_out->csv_mode &&
914 [ + + ]: 15 : strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
915 [ + - ]: 3 : ereport(ERROR,
916 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
917 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
918 : : errmsg("CSV quote character must not appear in the %s specification",
919 : : "DEFAULT")));
920 : :
921 : : /* Don't allow the NULL and DEFAULT string to be the same */
922 [ + + ]: 27 : if (opts_out->null_print_len == opts_out->default_print_len &&
923 : 12 : strncmp(opts_out->null_print, opts_out->default_print,
924 [ + + ]: 12 : opts_out->null_print_len) == 0)
925 [ + - ]: 3 : ereport(ERROR,
926 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
927 : : errmsg("NULL specification and DEFAULT specification cannot be the same")));
928 : : }
929 : : /* Check on_error */
384 fujii@postgresql.org 930 [ + + + + ]: 5642 : if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
931 [ + - ]: 4 : ereport(ERROR,
932 : : (errcode(ERRCODE_SYNTAX_ERROR),
933 : : errmsg("only ON_ERROR STOP is allowed in BINARY mode")));
934 : :
935 [ + + + + ]: 5638 : if (opts_out->reject_limit && !opts_out->on_error)
936 [ + - ]: 4 : ereport(ERROR,
937 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
938 : : /*- translator: first and second %s are the names of COPY option, e.g.
939 : : * ON_ERROR, third is the value of the COPY option, e.g. IGNORE */
940 : : errmsg("COPY %s requires %s to be set to %s",
941 : : "REJECT_LIMIT", "ON_ERROR", "IGNORE")));
5363 tgl@sss.pgh.pa.us 942 : 5634 : }
943 : :
944 : : /*
945 : : * CopyGetAttnums - build an integer list of attnums to be copied
946 : : *
947 : : * The input attnamelist is either the user-specified column list,
948 : : * or NIL if there was none (in which case we want all the non-dropped
949 : : * columns).
950 : : *
951 : : * We don't include generated columns in the generated full list and we don't
952 : : * allow them to be specified explicitly. They don't make sense for COPY
953 : : * FROM, but we could possibly allow them for COPY TO. But this way it's at
954 : : * least ensured that whatever we copy out can be copied back in.
955 : : *
956 : : * rel can be NULL ... it's only used for error reports.
957 : : */
958 : : List *
1799 heikki.linnakangas@i 959 : 10909 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
960 : : {
961 : 10909 : List *attnums = NIL;
962 : :
963 [ + + ]: 10909 : if (attnamelist == NIL)
964 : : {
965 : : /* Generate default column list */
8454 bruce@momjian.us 966 : 1930 : int attr_count = tupDesc->natts;
967 : : int i;
968 : :
8490 969 [ + + ]: 6816 : for (i = 0; i < attr_count; i++)
970 : : {
311 drowley@postgresql.o 971 : 4886 : CompactAttribute *attr = TupleDescCompactAttr(tupDesc, i);
972 : :
973 [ + + + + ]: 4886 : if (attr->attisdropped || attr->attgenerated)
2403 peter@eisentraut.org 974 : 151 : continue;
7824 neilc@samurai.com 975 : 4735 : attnums = lappend_int(attnums, i + 1);
976 : : }
977 : : }
978 : : else
979 : : {
980 : : /* Validate the user-supplied list and extract attnums */
981 : : ListCell *l;
982 : :
8487 tgl@sss.pgh.pa.us 983 [ + - + + : 41146 : foreach(l, attnamelist)
+ + ]
984 : : {
8470 985 : 32209 : char *name = strVal(lfirst(l));
986 : : int attnum;
987 : : int i;
988 : :
989 : : /* Lookup column name */
6998 990 : 32209 : attnum = InvalidAttrNumber;
991 [ + + ]: 4963432 : for (i = 0; i < tupDesc->natts; i++)
992 : : {
2990 andres@anarazel.de 993 : 4963417 : Form_pg_attribute att = TupleDescAttr(tupDesc, i);
994 : :
995 [ + + ]: 4963417 : if (att->attisdropped)
6998 tgl@sss.pgh.pa.us 996 : 396 : continue;
2990 andres@anarazel.de 997 [ + + ]: 4963021 : if (namestrcmp(&(att->attname), name) == 0)
998 : : {
2403 peter@eisentraut.org 999 [ + + ]: 32194 : if (att->attgenerated)
1000 [ + - ]: 24 : ereport(ERROR,
1001 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1002 : : errmsg("column \"%s\" is a generated column",
1003 : : name),
1004 : : errdetail("Generated columns cannot be used in COPY.")));
2990 andres@anarazel.de 1005 : 32170 : attnum = att->attnum;
6998 tgl@sss.pgh.pa.us 1006 : 32170 : break;
1007 : : }
1008 : : }
7158 1009 [ + + ]: 32185 : if (attnum == InvalidAttrNumber)
1010 : : {
6998 1011 [ + - ]: 15 : if (rel != NULL)
1012 [ + - ]: 15 : ereport(ERROR,
1013 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1014 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
1015 : : name, RelationGetRelationName(rel))));
1016 : : else
6998 tgl@sss.pgh.pa.us 1017 [ # # ]:UBC 0 : ereport(ERROR,
1018 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1019 : : errmsg("column \"%s\" does not exist",
1020 : : name)));
1021 : : }
1022 : : /* Check for duplicates */
7824 neilc@samurai.com 1023 [ + + ]:CBC 32170 : if (list_member_int(attnums, attnum))
8135 tgl@sss.pgh.pa.us 1024 [ + - ]: 3 : ereport(ERROR,
1025 : : (errcode(ERRCODE_DUPLICATE_COLUMN),
1026 : : errmsg("column \"%s\" specified more than once",
1027 : : name)));
7824 neilc@samurai.com 1028 : 32167 : attnums = lappend_int(attnums, attnum);
1029 : : }
1030 : : }
1031 : :
8487 tgl@sss.pgh.pa.us 1032 : 10867 : return attnums;
1033 : : }
|