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