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
3258 tgl@sss.pgh.pa.us 62 :CBC 5680 : DoCopy(ParseState *pstate, const CopyStmt *stmt,
63 : : int stmt_location, int stmt_len,
64 : : uint64 *processed)
65 : : {
5417 itagaki.takahiro@gma 66 : 5680 : bool is_from = stmt->is_from;
67 : 5680 : bool pipe = (stmt->filename == NULL);
68 : : Relation rel;
69 : : Oid relid;
3258 tgl@sss.pgh.pa.us 70 : 5680 : RawStmt *query = NULL;
2448 andres@anarazel.de 71 : 5680 : Node *whereClause = NULL;
72 : :
73 : : /*
74 : : * Disallow COPY to/from file or program except to users with the
75 : : * appropriate role.
76 : : */
2811 sfrost@snowman.net 77 [ + + ]: 5680 : if (!pipe)
78 : : {
4675 heikki.linnakangas@i 79 [ - + ]: 224 : if (stmt->is_program)
80 : : {
1359 mail@joeconway.com 81 [ # # ]:UBC 0 : if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM))
2811 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 : : {
1359 mail@joeconway.com 92 [ + + - + ]:CBC 224 : if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
2811 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 : :
1359 mail@joeconway.com 101 [ + + - + ]:CBC 224 : if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
2811 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 : :
5417 itagaki.takahiro@gma 112 [ + + ]:CBC 5680 : if (stmt->relation)
113 : : {
2634 tgl@sss.pgh.pa.us 114 [ + + ]: 5389 : LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock;
115 : : ParseNamespaceItem *nsitem;
116 : : RTEPermissionInfo *perminfo;
117 : : TupleDesc tupDesc;
118 : : List *attnums;
119 : : ListCell *cur;
120 : :
5417 itagaki.takahiro@gma 121 [ - + ]: 5389 : Assert(!stmt->query);
122 : :
123 : : /* Open and lock the relation, using the appropriate lock type. */
2521 andres@anarazel.de 124 : 5389 : rel = table_openrv(stmt->relation, lockmode);
125 : :
4735 rhaas@postgresql.org 126 : 5388 : relid = RelationGetRelid(rel);
127 : :
2175 tgl@sss.pgh.pa.us 128 : 5388 : nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
129 : : NULL, false, false);
130 : :
1106 alvherre@alvh.no-ip. 131 : 5388 : perminfo = nsitem->p_perminfo;
132 [ + + ]: 5388 : perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
133 : :
2523 tomas.vondra@postgre 134 [ + + ]: 5388 : if (stmt->whereClause)
135 : : {
40 peter@eisentraut.org 136 : 36 : Bitmapset *expr_attrs = NULL;
137 : : int i;
138 : :
139 : : /* add nsitem to query namespace */
2175 tgl@sss.pgh.pa.us 140 : 36 : addNSItemToQuery(pstate, nsitem, false, true, true);
141 : :
142 : : /* Transform the raw expression tree */
2523 tomas.vondra@postgre 143 : 36 : whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
144 : :
145 : : /* Make sure it yields a boolean result. */
146 : 21 : whereClause = coerce_to_boolean(pstate, whereClause, "WHERE");
147 : :
148 : : /* we have to fix its collations too */
149 : 21 : assign_expr_collations(pstate, whereClause);
150 : :
151 : : /*
152 : : * Examine all the columns in the WHERE clause expression. When
153 : : * the whole-row reference is present, examine all the columns of
154 : : * the table.
155 : : */
40 peter@eisentraut.org 156 : 21 : pull_varattnos(whereClause, 1, &expr_attrs);
157 [ + + ]: 21 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
158 : : {
159 : 12 : expr_attrs = bms_add_range(expr_attrs,
160 : : 1 - FirstLowInvalidHeapAttributeNumber,
161 : 6 : RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
162 : 6 : expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
163 : : }
164 : :
165 : 21 : i = -1;
166 [ + + ]: 36 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
167 : : {
168 : 27 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
169 : :
170 [ - + ]: 27 : Assert(attno != 0);
171 : :
172 : : /*
173 : : * Prohibit generated columns in the WHERE clause. Stored
174 : : * generated columns are not yet computed when the filtering
175 : : * happens. Virtual generated columns could probably work (we
176 : : * would need to expand them somewhere around here), but for
177 : : * now we keep them consistent with the stored variant.
178 : : */
179 [ + + ]: 27 : if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
180 [ + - ]: 12 : ereport(ERROR,
181 : : errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
182 : : errmsg("generated columns are not supported in COPY FROM WHERE conditions"),
183 : : errdetail("Column \"%s\" is a generated column.",
184 : : get_attname(RelationGetRelid(rel), attno, false)));
185 : : }
186 : :
2523 tomas.vondra@postgre 187 : 9 : whereClause = eval_const_expressions(NULL, whereClause);
188 : :
189 : 9 : whereClause = (Node *) canonicalize_qual((Expr *) whereClause, false);
190 : 9 : whereClause = (Node *) make_ands_implicit((Expr *) whereClause);
191 : : }
192 : :
5417 itagaki.takahiro@gma 193 : 5361 : tupDesc = RelationGetDescr(rel);
194 : 5361 : attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
5413 tgl@sss.pgh.pa.us 195 [ + + + + : 23681 : foreach(cur, attnums)
+ + ]
196 : : {
197 : : int attno;
198 : : Bitmapset **bms;
199 : :
1106 alvherre@alvh.no-ip. 200 : 18362 : attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
201 [ + + ]: 18362 : bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
202 : :
203 : 18362 : *bms = bms_add_member(*bms, attno);
204 : : }
205 : 5319 : ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true);
206 : :
207 : : /*
208 : : * Permission check for row security policies.
209 : : *
210 : : * check_enable_rls will ereport(ERROR) if the user has requested
211 : : * something invalid and will otherwise indicate if we should enable
212 : : * RLS (returns RLS_ENABLED) or not for this COPY statement.
213 : : *
214 : : * If the relation has a row security policy and we are to apply it
215 : : * then perform a "query" copy and allow the normal query processing
216 : : * to handle the policies.
217 : : *
218 : : * If RLS is not enabled for this, then just fall through to the
219 : : * normal non-filtering relation handling.
220 : : */
221 [ + + ]: 5277 : if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED)
222 : : {
223 : : SelectStmt *select;
224 : : ColumnRef *cr;
225 : : ResTarget *target;
226 : : RangeVar *from;
3361 sfrost@snowman.net 227 : 42 : List *targetList = NIL;
228 : :
4106 229 [ + + ]: 42 : if (is_from)
230 [ + - ]: 3 : ereport(ERROR,
231 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
232 : : errmsg("COPY FROM not supported with row-level security"),
233 : : errhint("Use INSERT statements instead.")));
234 : :
235 : : /*
236 : : * Build target list
237 : : *
238 : : * If no columns are specified in the attribute list of the COPY
239 : : * command, then the target list is 'all' columns. Therefore, '*'
240 : : * should be used as the target list for the resulting SELECT
241 : : * statement.
242 : : *
243 : : * In the case that columns are specified in the attribute list,
244 : : * create a ColumnRef and ResTarget for each column and add them
245 : : * to the target list for the resulting SELECT statement.
246 : : */
247 [ + + ]: 39 : if (!stmt->attlist)
248 : : {
3361 249 : 21 : cr = makeNode(ColumnRef);
4106 250 : 21 : cr->fields = list_make1(makeNode(A_Star));
3361 251 : 21 : cr->location = -1;
252 : :
253 : 21 : target = makeNode(ResTarget);
254 : 21 : target->name = NULL;
255 : 21 : target->indirection = NIL;
256 : 21 : target->val = (Node *) cr;
257 : 21 : target->location = -1;
258 : :
259 : 21 : targetList = list_make1(target);
260 : : }
261 : : else
262 : : {
263 : : ListCell *lc;
264 : :
265 [ + - + + : 51 : foreach(lc, stmt->attlist)
+ + ]
266 : : {
267 : : /*
268 : : * Build the ColumnRef for each column. The ColumnRef
269 : : * 'fields' property is a String node that corresponds to
270 : : * the column name respectively.
271 : : */
272 : 33 : cr = makeNode(ColumnRef);
273 : 33 : cr->fields = list_make1(lfirst(lc));
274 : 33 : cr->location = -1;
275 : :
276 : : /* Build the ResTarget and add the ColumnRef to it. */
277 : 33 : target = makeNode(ResTarget);
278 : 33 : target->name = NULL;
279 : 33 : target->indirection = NIL;
280 : 33 : target->val = (Node *) cr;
281 : 33 : target->location = -1;
282 : :
283 : : /* Add each column to the SELECT statement's target list */
284 : 33 : targetList = lappend(targetList, target);
285 : : }
286 : : }
287 : :
288 : : /*
289 : : * Build RangeVar for from clause, fully qualified based on the
290 : : * relation which we have opened and locked. Use "ONLY" so that
291 : : * COPY retrieves rows from only the target table not any
292 : : * inheritance children, the same as when RLS doesn't apply.
293 : : *
294 : : * However, when copying data from a partitioned table, we don't
295 : : * use "ONLY", since we need to retrieve rows from its descendant
296 : : * tables too.
297 : : */
3795 298 : 39 : from = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
3207 tgl@sss.pgh.pa.us 299 : 39 : pstrdup(RelationGetRelationName(rel)),
300 : : -1);
57 msawada@postgresql.o 301 :GNC 39 : from->inh = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
302 : :
303 : : /* Build query */
4106 sfrost@snowman.net 304 :CBC 39 : select = makeNode(SelectStmt);
3361 305 : 39 : select->targetList = targetList;
4106 306 : 39 : select->fromClause = list_make1(from);
307 : :
3258 tgl@sss.pgh.pa.us 308 : 39 : query = makeNode(RawStmt);
309 : 39 : query->stmt = (Node *) select;
310 : 39 : query->stmt_location = stmt_location;
311 : 39 : query->stmt_len = stmt_len;
312 : :
313 : : /*
314 : : * Close the relation for now, but keep the lock on it to prevent
315 : : * changes between now and when we start the query-based COPY.
316 : : *
317 : : * We'll reopen it later as part of the query-based COPY.
318 : : */
2521 andres@anarazel.de 319 : 39 : table_close(rel, NoLock);
4106 sfrost@snowman.net 320 : 39 : rel = NULL;
321 : : }
322 : : }
323 : : else
324 : : {
5417 itagaki.takahiro@gma 325 [ - + ]: 291 : Assert(stmt->query);
326 : :
3258 tgl@sss.pgh.pa.us 327 : 291 : query = makeNode(RawStmt);
328 : 291 : query->stmt = stmt->query;
329 : 291 : query->stmt_location = stmt_location;
330 : 291 : query->stmt_len = stmt_len;
331 : :
4733 peter_e@gmx.net 332 : 291 : relid = InvalidOid;
5417 itagaki.takahiro@gma 333 : 291 : rel = NULL;
334 : : }
335 : :
336 [ + + ]: 5556 : if (is_from)
337 : : {
338 : : CopyFromState cstate;
339 : :
5025 peter_e@gmx.net 340 [ - + ]: 869 : Assert(rel);
341 : :
342 : : /* check read-only transaction and parallel mode */
4747 tgl@sss.pgh.pa.us 343 [ - + - - ]: 869 : if (XactReadOnly && !rel->rd_islocaltemp)
5417 itagaki.takahiro@gma 344 :UBC 0 : PreventCommandIfReadOnly("COPY FROM");
345 : :
1849 heikki.linnakangas@i 346 :CBC 869 : cstate = BeginCopyFrom(pstate, rel, whereClause,
347 : 869 : stmt->filename, stmt->is_program,
3190 peter_e@gmx.net 348 : 869 : NULL, stmt->attlist, stmt->options);
4735 rhaas@postgresql.org 349 : 737 : *processed = CopyFrom(cstate); /* copy from file to database */
5417 itagaki.takahiro@gma 350 : 618 : EndCopyFrom(cstate);
351 : : }
352 : : else
353 : : {
354 : : CopyToState cstate;
355 : :
3388 peter_e@gmx.net 356 : 4687 : cstate = BeginCopyTo(pstate, rel, query, relid,
4675 heikki.linnakangas@i 357 : 4687 : stmt->filename, stmt->is_program,
1162 michael@paquier.xyz 358 : 4687 : NULL, stmt->attlist, stmt->options);
4735 rhaas@postgresql.org 359 : 4586 : *processed = DoCopyTo(cstate); /* copy from database to file */
5417 itagaki.takahiro@gma 360 : 4585 : EndCopyTo(cstate);
361 : : }
362 : :
363 [ + + ]: 5203 : if (rel != NULL)
2041 akapila@postgresql.o 364 : 4943 : table_close(rel, NoLock);
5417 itagaki.takahiro@gma 365 : 5203 : }
366 : :
367 : : /*
368 : : * Extract the CopyFormatOptions.header_line value from a DefElem.
369 : : *
370 : : * Parses the HEADER option for COPY, which can be a boolean, a non-negative
371 : : * integer (number of lines to skip), or the special value "match".
372 : : */
373 : : static int
166 fujii@postgresql.org 374 :GNC 114 : defGetCopyHeaderOption(DefElem *def, bool is_from)
375 : : {
376 : : /*
377 : : * If no parameter value given, assume "true" is meant.
378 : : */
1357 peter@eisentraut.org 379 [ + + ]:CBC 114 : if (def->arg == NULL)
380 : 15 : return COPY_HEADER_TRUE;
381 : :
382 : : /*
383 : : * Allow 0, 1, "true", "false", "on", "off", a non-negative integer, or
384 : : * "match".
385 : : */
386 [ + + ]: 99 : switch (nodeTag(def->arg))
387 : : {
1357 peter@eisentraut.org 388 :GBC 15 : case T_Integer:
389 : : {
166 fujii@postgresql.org 390 :GNC 15 : int ival = intVal(def->arg);
391 : :
392 [ + + ]: 15 : if (ival < 0)
393 [ + - ]: 3 : ereport(ERROR,
394 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
395 : : errmsg("a negative integer value cannot be "
396 : : "specified for %s", def->defname)));
397 : :
398 [ + + + - ]: 12 : if (!is_from && ival > 1)
399 [ + - ]: 3 : ereport(ERROR,
400 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
401 : : errmsg("cannot use multi-line header in COPY TO")));
402 : :
403 : 9 : return ival;
404 : : }
1357 peter@eisentraut.org 405 :EUB : break;
1357 peter@eisentraut.org 406 :CBC 84 : default:
407 : : {
1314 tgl@sss.pgh.pa.us 408 : 84 : char *sval = defGetString(def);
409 : :
410 : : /*
411 : : * The set of strings accepted here should match up with the
412 : : * grammar's opt_boolean_or_string production.
413 : : */
1357 peter@eisentraut.org 414 [ + + ]: 84 : if (pg_strcasecmp(sval, "true") == 0)
415 : 32 : return COPY_HEADER_TRUE;
416 [ - + ]: 52 : if (pg_strcasecmp(sval, "false") == 0)
1357 peter@eisentraut.org 417 :UBC 0 : return COPY_HEADER_FALSE;
1357 peter@eisentraut.org 418 [ - + ]:CBC 52 : if (pg_strcasecmp(sval, "on") == 0)
1357 peter@eisentraut.org 419 :UBC 0 : return COPY_HEADER_TRUE;
1357 peter@eisentraut.org 420 [ + + ]:CBC 52 : if (pg_strcasecmp(sval, "off") == 0)
421 : 3 : return COPY_HEADER_FALSE;
422 [ + + ]: 49 : if (pg_strcasecmp(sval, "match") == 0)
423 : : {
1272 michael@paquier.xyz 424 [ + + ]: 43 : if (!is_from)
425 [ + - ]: 3 : ereport(ERROR,
426 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
427 : : errmsg("cannot use \"%s\" with HEADER in COPY TO",
428 : : sval)));
1357 peter@eisentraut.org 429 : 40 : return COPY_HEADER_MATCH;
430 : : }
431 : : }
432 : 6 : break;
433 : : }
434 [ + - ]: 6 : ereport(ERROR,
435 : : (errcode(ERRCODE_SYNTAX_ERROR),
436 : : errmsg("%s requires a Boolean value, a non-negative integer, "
437 : : "or the string \"match\"",
438 : : def->defname)));
439 : : return COPY_HEADER_FALSE; /* keep compiler quiet */
440 : : }
441 : :
442 : : /*
443 : : * Extract a CopyOnErrorChoice value from a DefElem.
444 : : */
445 : : static CopyOnErrorChoice
697 akorotkov@postgresql 446 : 57 : defGetCopyOnErrorChoice(DefElem *def, ParseState *pstate, bool is_from)
447 : : {
608 msawada@postgresql.o 448 : 57 : char *sval = defGetString(def);
449 : :
700 akorotkov@postgresql 450 [ + + ]: 57 : if (!is_from)
451 [ + - ]: 3 : ereport(ERROR,
452 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
453 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
454 : : second %s is a COPY with direction, e.g. COPY TO */
455 : : errmsg("COPY %s cannot be used with %s", "ON_ERROR", "COPY TO"),
456 : : parser_errposition(pstate, def->location)));
457 : :
458 : : /*
459 : : * Allow "stop", or "ignore" values.
460 : : */
697 461 [ + + ]: 54 : if (pg_strcasecmp(sval, "stop") == 0)
462 : 3 : return COPY_ON_ERROR_STOP;
463 [ + + ]: 51 : if (pg_strcasecmp(sval, "ignore") == 0)
464 : 47 : return COPY_ON_ERROR_IGNORE;
465 : :
700 466 [ + - ]: 4 : ereport(ERROR,
467 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
468 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
469 : : errmsg("COPY %s \"%s\" not recognized", "ON_ERROR", sval),
470 : : parser_errposition(pstate, def->location)));
471 : : return COPY_ON_ERROR_STOP; /* keep compiler quiet */
472 : : }
473 : :
474 : : /*
475 : : * Extract REJECT_LIMIT value from a DefElem.
476 : : *
477 : : * REJECT_LIMIT can be specified in two ways: as an int64 for the COPY command
478 : : * option or as a single-quoted string for the foreign table option using
479 : : * file_fdw. Therefore this function needs to handle both formats.
480 : : */
481 : : static int64
434 fujii@postgresql.org 482 : 19 : defGetCopyRejectLimitOption(DefElem *def)
483 : : {
484 : : int64 reject_limit;
485 : :
391 486 [ - + ]: 19 : if (def->arg == NULL)
391 fujii@postgresql.org 487 [ # # ]:UBC 0 : ereport(ERROR,
488 : : (errcode(ERRCODE_SYNTAX_ERROR),
489 : : errmsg("%s requires a numeric value",
490 : : def->defname)));
391 fujii@postgresql.org 491 [ + + ]:CBC 19 : else if (nodeTag(def->arg) == T_String)
492 : 7 : reject_limit = pg_strtoint64(strVal(def->arg));
493 : : else
494 : 12 : reject_limit = defGetInt64(def);
495 : :
434 496 [ + + ]: 19 : if (reject_limit <= 0)
497 [ + - ]: 4 : ereport(ERROR,
498 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
499 : : errmsg("REJECT_LIMIT (%" PRId64 ") must be greater than zero",
500 : : reject_limit)));
501 : :
502 : 15 : return reject_limit;
503 : : }
504 : :
505 : : /*
506 : : * Extract a CopyLogVerbosityChoice value from a DefElem.
507 : : */
508 : : static CopyLogVerbosityChoice
624 msawada@postgresql.o 509 : 23 : defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate)
510 : : {
511 : : char *sval;
512 : :
513 : : /*
514 : : * Allow "silent", "default", or "verbose" values.
515 : : */
516 : 23 : sval = defGetString(def);
439 fujii@postgresql.org 517 [ + + ]: 23 : if (pg_strcasecmp(sval, "silent") == 0)
518 : 10 : return COPY_LOG_VERBOSITY_SILENT;
624 msawada@postgresql.o 519 [ + + ]: 13 : if (pg_strcasecmp(sval, "default") == 0)
520 : 3 : return COPY_LOG_VERBOSITY_DEFAULT;
521 [ + + ]: 10 : if (pg_strcasecmp(sval, "verbose") == 0)
522 : 6 : return COPY_LOG_VERBOSITY_VERBOSE;
523 : :
524 [ + - ]: 4 : ereport(ERROR,
525 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
526 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR */
527 : : errmsg("COPY %s \"%s\" not recognized", "LOG_VERBOSITY", sval),
528 : : parser_errposition(pstate, def->location)));
529 : : return COPY_LOG_VERBOSITY_DEFAULT; /* keep compiler quiet */
530 : : }
531 : :
532 : : /*
533 : : * Process the statement option list for COPY.
534 : : *
535 : : * Scan the options list (a list of DefElem) and transpose the information
536 : : * into *opts_out, applying appropriate error checking.
537 : : *
538 : : * If 'opts_out' is not NULL, it is assumed to be filled with zeroes initially.
539 : : *
540 : : * This is exported so that external users of the COPY API can sanity-check
541 : : * a list of options. In that usage, 'opts_out' can be passed as NULL and
542 : : * the collected data is just leaked until CurrentMemoryContext is reset.
543 : : *
544 : : * Note that additional checking, such as whether column names listed in FORCE
545 : : * QUOTE actually exist, has to be applied later. This just checks for
546 : : * self-consistency of the options list.
547 : : */
548 : : void
3388 peter_e@gmx.net 549 : 5831 : ProcessCopyOptions(ParseState *pstate,
550 : : CopyFormatOptions *opts_out,
551 : : bool is_from,
552 : : List *options)
553 : : {
5930 tgl@sss.pgh.pa.us 554 : 5831 : bool format_specified = false;
1898 michael@paquier.xyz 555 : 5831 : bool freeze_specified = false;
556 : 5831 : bool header_specified = false;
697 akorotkov@postgresql 557 : 5831 : bool on_error_specified = false;
624 msawada@postgresql.o 558 : 5831 : bool log_verbosity_specified = false;
434 fujii@postgresql.org 559 : 5831 : bool reject_limit_specified = false;
560 : : ListCell *option;
561 : :
562 : : /* Support external use for option sanity checking */
1849 heikki.linnakangas@i 563 [ + + ]: 5831 : if (opts_out == NULL)
6 michael@paquier.xyz 564 :GNC 52 : opts_out = palloc0_object(CopyFormatOptions);
565 : :
1849 heikki.linnakangas@i 566 :CBC 5831 : opts_out->file_encoding = -1;
567 : :
568 : : /* Extract options from the statement node tree */
5417 itagaki.takahiro@gma 569 [ + + + + : 6868 : foreach(option, options)
+ + ]
570 : : {
3172 tgl@sss.pgh.pa.us 571 : 1116 : DefElem *defel = lfirst_node(DefElem, option);
572 : :
5930 573 [ + + ]: 1116 : if (strcmp(defel->defname, "format") == 0)
574 : : {
5772 bruce@momjian.us 575 : 328 : char *fmt = defGetString(defel);
576 : :
5930 tgl@sss.pgh.pa.us 577 [ + + ]: 328 : if (format_specified)
1615 dean.a.rasheed@gmail 578 : 3 : errorConflictingDefElem(defel, pstate);
5930 tgl@sss.pgh.pa.us 579 : 325 : format_specified = true;
580 [ + + ]: 325 : if (strcmp(fmt, "text") == 0)
581 : : /* default format */ ;
582 [ + + ]: 287 : else if (strcmp(fmt, "csv") == 0)
1849 heikki.linnakangas@i 583 : 251 : opts_out->csv_mode = true;
5930 tgl@sss.pgh.pa.us 584 [ + + ]: 36 : else if (strcmp(fmt, "binary") == 0)
1849 heikki.linnakangas@i 585 : 35 : opts_out->binary = true;
586 : : else
5930 tgl@sss.pgh.pa.us 587 [ + - ]: 1 : ereport(ERROR,
588 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
589 : : errmsg("COPY format \"%s\" not recognized", fmt),
590 : : parser_errposition(pstate, defel->location)));
591 : : }
4763 simon@2ndQuadrant.co 592 [ + + ]: 788 : else if (strcmp(defel->defname, "freeze") == 0)
593 : : {
1898 michael@paquier.xyz 594 [ + + ]: 42 : if (freeze_specified)
1615 dean.a.rasheed@gmail 595 : 3 : errorConflictingDefElem(defel, pstate);
1898 michael@paquier.xyz 596 : 39 : freeze_specified = true;
1849 heikki.linnakangas@i 597 : 39 : opts_out->freeze = defGetBoolean(defel);
598 : : }
8580 bruce@momjian.us 599 [ + + ]: 746 : else if (strcmp(defel->defname, "delimiter") == 0)
600 : : {
1849 heikki.linnakangas@i 601 [ + + ]: 150 : if (opts_out->delim)
1615 dean.a.rasheed@gmail 602 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 603 : 147 : opts_out->delim = defGetString(defel);
604 : : }
8580 bruce@momjian.us 605 [ + + ]: 596 : else if (strcmp(defel->defname, "null") == 0)
606 : : {
1849 heikki.linnakangas@i 607 [ + + ]: 72 : if (opts_out->null_print)
1615 dean.a.rasheed@gmail 608 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 609 : 69 : opts_out->null_print = defGetString(defel);
610 : : }
1009 andrew@dunslane.net 611 [ + + ]: 524 : else if (strcmp(defel->defname, "default") == 0)
612 : : {
613 [ - + ]: 45 : if (opts_out->default_print)
1009 andrew@dunslane.net 614 :UBC 0 : errorConflictingDefElem(defel, pstate);
1009 andrew@dunslane.net 615 :CBC 45 : opts_out->default_print = defGetString(defel);
616 : : }
7528 bruce@momjian.us 617 [ + + ]: 479 : else if (strcmp(defel->defname, "header") == 0)
618 : : {
1898 michael@paquier.xyz 619 [ + + ]: 117 : if (header_specified)
1615 dean.a.rasheed@gmail 620 : 3 : errorConflictingDefElem(defel, pstate);
1898 michael@paquier.xyz 621 : 114 : header_specified = true;
166 fujii@postgresql.org 622 :GNC 114 : opts_out->header_line = defGetCopyHeaderOption(defel, is_from);
623 : : }
7911 bruce@momjian.us 624 [ + + ]:CBC 362 : else if (strcmp(defel->defname, "quote") == 0)
625 : : {
1849 heikki.linnakangas@i 626 [ + + ]: 51 : if (opts_out->quote)
1615 dean.a.rasheed@gmail 627 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 628 : 48 : opts_out->quote = defGetString(defel);
629 : : }
7911 bruce@momjian.us 630 [ + + ]: 311 : else if (strcmp(defel->defname, "escape") == 0)
631 : : {
1849 heikki.linnakangas@i 632 [ + + ]: 47 : if (opts_out->escape)
1615 dean.a.rasheed@gmail 633 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 634 : 44 : opts_out->escape = defGetString(defel);
635 : : }
7909 bruce@momjian.us 636 [ + + ]: 264 : else if (strcmp(defel->defname, "force_quote") == 0)
637 : : {
1849 heikki.linnakangas@i 638 [ + + - + ]: 39 : if (opts_out->force_quote || opts_out->force_quote_all)
1615 dean.a.rasheed@gmail 639 : 3 : errorConflictingDefElem(defel, pstate);
5988 tgl@sss.pgh.pa.us 640 [ + - + + ]: 36 : if (defel->arg && IsA(defel->arg, A_Star))
1849 heikki.linnakangas@i 641 : 15 : opts_out->force_quote_all = true;
5930 tgl@sss.pgh.pa.us 642 [ + - + - ]: 21 : else if (defel->arg && IsA(defel->arg, List))
1849 heikki.linnakangas@i 643 : 21 : opts_out->force_quote = castNode(List, defel->arg);
644 : : else
5930 tgl@sss.pgh.pa.us 645 [ # # ]:UBC 0 : ereport(ERROR,
646 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
647 : : errmsg("argument to option \"%s\" must be a list of column names",
648 : : defel->defname),
649 : : parser_errposition(pstate, defel->location)));
650 : : }
5930 tgl@sss.pgh.pa.us 651 [ + + ]:CBC 225 : else if (strcmp(defel->defname, "force_not_null") == 0)
652 : : {
808 andrew@dunslane.net 653 [ + + + + ]: 44 : if (opts_out->force_notnull || opts_out->force_notnull_all)
1615 dean.a.rasheed@gmail 654 : 6 : errorConflictingDefElem(defel, pstate);
808 andrew@dunslane.net 655 [ + - + + ]: 38 : if (defel->arg && IsA(defel->arg, A_Star))
656 : 15 : opts_out->force_notnull_all = true;
657 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1849 heikki.linnakangas@i 658 : 23 : opts_out->force_notnull = castNode(List, defel->arg);
659 : : else
5930 tgl@sss.pgh.pa.us 660 [ # # ]:UBC 0 : ereport(ERROR,
661 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
662 : : errmsg("argument to option \"%s\" must be a list of column names",
663 : : defel->defname),
664 : : parser_errposition(pstate, defel->location)));
665 : : }
4305 andrew@dunslane.net 666 [ + + ]:CBC 181 : else if (strcmp(defel->defname, "force_null") == 0)
667 : : {
808 668 [ + + + + ]: 44 : if (opts_out->force_null || opts_out->force_null_all)
1615 dean.a.rasheed@gmail 669 : 6 : errorConflictingDefElem(defel, pstate);
808 andrew@dunslane.net 670 [ + - + + ]: 38 : if (defel->arg && IsA(defel->arg, A_Star))
671 : 15 : opts_out->force_null_all = true;
672 [ + - + - ]: 23 : else if (defel->arg && IsA(defel->arg, List))
1849 heikki.linnakangas@i 673 : 23 : opts_out->force_null = castNode(List, defel->arg);
674 : : else
4305 andrew@dunslane.net 675 [ # # ]:UBC 0 : ereport(ERROR,
676 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
677 : : errmsg("argument to option \"%s\" must be a list of column names",
678 : : defel->defname),
679 : : parser_errposition(pstate, defel->location)));
680 : : }
4905 tgl@sss.pgh.pa.us 681 [ + + ]:CBC 137 : else if (strcmp(defel->defname, "convert_selectively") == 0)
682 : : {
683 : : /*
684 : : * Undocumented, not-accessible-from-SQL option: convert only the
685 : : * named columns to binary form, storing the rest as NULLs. It's
686 : : * allowed for the column list to be NIL.
687 : : */
1849 heikki.linnakangas@i 688 [ + + ]: 8 : if (opts_out->convert_selectively)
1615 dean.a.rasheed@gmail 689 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 690 : 5 : opts_out->convert_selectively = true;
4905 tgl@sss.pgh.pa.us 691 [ + - + - ]: 5 : if (defel->arg == NULL || IsA(defel->arg, List))
1849 heikki.linnakangas@i 692 : 5 : opts_out->convert_select = castNode(List, defel->arg);
693 : : else
4905 tgl@sss.pgh.pa.us 694 [ # # ]:UBC 0 : ereport(ERROR,
695 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
696 : : errmsg("argument to option \"%s\" must be a list of column names",
697 : : defel->defname),
698 : : parser_errposition(pstate, defel->location)));
699 : : }
5412 itagaki.takahiro@gma 700 [ + + ]:CBC 129 : else if (strcmp(defel->defname, "encoding") == 0)
701 : : {
1849 heikki.linnakangas@i 702 [ + + ]: 24 : if (opts_out->file_encoding >= 0)
1615 dean.a.rasheed@gmail 703 : 3 : errorConflictingDefElem(defel, pstate);
1849 heikki.linnakangas@i 704 : 21 : opts_out->file_encoding = pg_char_to_encoding(defGetString(defel));
705 [ - + ]: 21 : if (opts_out->file_encoding < 0)
5412 itagaki.takahiro@gma 706 [ # # ]:UBC 0 : ereport(ERROR,
707 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
708 : : errmsg("argument to option \"%s\" must be a valid encoding name",
709 : : defel->defname),
710 : : parser_errposition(pstate, defel->location)));
711 : : }
697 akorotkov@postgresql 712 [ + + ]:CBC 105 : else if (strcmp(defel->defname, "on_error") == 0)
713 : : {
714 [ + + ]: 60 : if (on_error_specified)
700 715 : 3 : errorConflictingDefElem(defel, pstate);
697 716 : 57 : on_error_specified = true;
717 : 57 : opts_out->on_error = defGetCopyOnErrorChoice(defel, pstate, is_from);
718 : : }
624 msawada@postgresql.o 719 [ + + ]: 45 : else if (strcmp(defel->defname, "log_verbosity") == 0)
720 : : {
721 [ + + ]: 26 : if (log_verbosity_specified)
722 : 3 : errorConflictingDefElem(defel, pstate);
723 : 23 : log_verbosity_specified = true;
724 : 23 : opts_out->log_verbosity = defGetCopyLogVerbosityChoice(defel, pstate);
725 : : }
434 fujii@postgresql.org 726 [ + - ]: 19 : else if (strcmp(defel->defname, "reject_limit") == 0)
727 : : {
728 [ - + ]: 19 : if (reject_limit_specified)
434 fujii@postgresql.org 729 :UBC 0 : errorConflictingDefElem(defel, pstate);
434 fujii@postgresql.org 730 :CBC 19 : reject_limit_specified = true;
731 : 19 : opts_out->reject_limit = defGetCopyRejectLimitOption(defel);
732 : : }
733 : : else
5930 tgl@sss.pgh.pa.us 734 [ # # ]:UBC 0 : ereport(ERROR,
735 : : (errcode(ERRCODE_SYNTAX_ERROR),
736 : : errmsg("option \"%s\" not recognized",
737 : : defel->defname),
738 : : parser_errposition(pstate, defel->location)));
739 : : }
740 : :
741 : : /*
742 : : * Check for incompatible options (must do these three before inserting
743 : : * defaults)
744 : : */
1849 heikki.linnakangas@i 745 [ + + + + ]:CBC 5752 : if (opts_out->binary && opts_out->delim)
8185 tgl@sss.pgh.pa.us 746 [ + - ]: 3 : ereport(ERROR,
747 : : (errcode(ERRCODE_SYNTAX_ERROR),
748 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
749 : : errmsg("cannot specify %s in BINARY mode", "DELIMITER")));
750 : :
1849 heikki.linnakangas@i 751 [ + + + + ]: 5749 : if (opts_out->binary && opts_out->null_print)
8185 tgl@sss.pgh.pa.us 752 [ + - ]: 3 : ereport(ERROR,
753 : : (errcode(ERRCODE_SYNTAX_ERROR),
754 : : errmsg("cannot specify %s in BINARY mode", "NULL")));
755 : :
1009 andrew@dunslane.net 756 [ + + + + ]: 5746 : if (opts_out->binary && opts_out->default_print)
757 [ + - ]: 3 : ereport(ERROR,
758 : : (errcode(ERRCODE_SYNTAX_ERROR),
759 : : errmsg("cannot specify %s in BINARY mode", "DEFAULT")));
760 : :
761 : : /* Set defaults for omitted options */
1849 heikki.linnakangas@i 762 [ + + ]: 5743 : if (!opts_out->delim)
763 [ + + ]: 5602 : opts_out->delim = opts_out->csv_mode ? "," : "\t";
764 : :
765 [ + + ]: 5743 : if (!opts_out->null_print)
766 [ + + ]: 5680 : opts_out->null_print = opts_out->csv_mode ? "" : "\\N";
767 : 5743 : opts_out->null_print_len = strlen(opts_out->null_print);
768 : :
769 [ + + ]: 5743 : if (opts_out->csv_mode)
770 : : {
771 [ + + ]: 242 : if (!opts_out->quote)
772 : 199 : opts_out->quote = "\"";
773 [ + + ]: 242 : if (!opts_out->escape)
774 : 204 : opts_out->escape = opts_out->quote;
775 : : }
776 : :
777 : : /* Only single-byte delimiter strings are supported. */
778 [ + + ]: 5743 : if (strlen(opts_out->delim) != 1)
7911 bruce@momjian.us 779 [ + - ]: 1 : ereport(ERROR,
780 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
781 : : errmsg("COPY delimiter must be a single one-byte character")));
782 : :
783 : : /* Disallow end-of-line characters */
1849 heikki.linnakangas@i 784 [ + - ]: 5742 : if (strchr(opts_out->delim, '\r') != NULL ||
785 [ + + ]: 5742 : strchr(opts_out->delim, '\n') != NULL)
7256 bruce@momjian.us 786 [ + - ]: 1 : ereport(ERROR,
787 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
788 : : errmsg("COPY delimiter cannot be newline or carriage return")));
789 : :
1849 heikki.linnakangas@i 790 [ + - ]: 5741 : if (strchr(opts_out->null_print, '\r') != NULL ||
791 [ + + ]: 5741 : strchr(opts_out->null_print, '\n') != NULL)
7256 bruce@momjian.us 792 [ + - ]: 1 : ereport(ERROR,
793 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
794 : : errmsg("COPY null representation cannot use newline or carriage return")));
795 : :
1009 andrew@dunslane.net 796 [ + + ]: 5740 : if (opts_out->default_print)
797 : : {
798 : 42 : opts_out->default_print_len = strlen(opts_out->default_print);
799 : :
800 [ + + ]: 42 : if (strchr(opts_out->default_print, '\r') != NULL ||
801 [ + + ]: 39 : strchr(opts_out->default_print, '\n') != NULL)
802 [ + - ]: 6 : ereport(ERROR,
803 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
804 : : errmsg("COPY default representation cannot use newline or carriage return")));
805 : : }
806 : :
807 : : /*
808 : : * Disallow unsafe delimiter characters in non-CSV mode. We can't allow
809 : : * backslash because it would be ambiguous. We can't allow the other
810 : : * cases because data characters matching the delimiter must be
811 : : * backslashed, and certain backslash combinations are interpreted
812 : : * non-literally by COPY IN. Disallowing all lower case ASCII letters is
813 : : * more than strictly necessary, but seems best for consistency and
814 : : * future-proofing. Likewise we disallow all digits though only octal
815 : : * digits are actually dangerous.
816 : : */
1849 heikki.linnakangas@i 817 [ + + ]: 5734 : if (!opts_out->csv_mode &&
6564 tgl@sss.pgh.pa.us 818 : 5495 : strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
1849 heikki.linnakangas@i 819 [ + + ]: 5495 : opts_out->delim[0]) != NULL)
7256 bruce@momjian.us 820 [ + - ]: 5 : ereport(ERROR,
821 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
822 : : errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim)));
823 : :
824 : : /* Check header */
166 fujii@postgresql.org 825 [ + + + + ]:GNC 5729 : if (opts_out->binary && opts_out->header_line != COPY_HEADER_FALSE)
7528 bruce@momjian.us 826 [ + - ]:CBC 1 : ereport(ERROR,
827 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
828 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
829 : : errmsg("cannot specify %s in BINARY mode", "HEADER")));
830 : :
831 : : /* Check quote */
1849 heikki.linnakangas@i 832 [ + + + + ]: 5728 : if (!opts_out->csv_mode && opts_out->quote != NULL)
7911 bruce@momjian.us 833 [ + - ]: 2 : ereport(ERROR,
834 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
835 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
836 : : errmsg("COPY %s requires CSV mode", "QUOTE")));
837 : :
1849 heikki.linnakangas@i 838 [ + + + + ]: 5726 : if (opts_out->csv_mode && strlen(opts_out->quote) != 1)
7911 bruce@momjian.us 839 [ + - ]: 1 : ereport(ERROR,
840 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
841 : : errmsg("COPY quote must be a single one-byte character")));
842 : :
1849 heikki.linnakangas@i 843 [ + + + + ]: 5725 : if (opts_out->csv_mode && opts_out->delim[0] == opts_out->quote[0])
6561 andrew@dunslane.net 844 [ + - ]: 1 : ereport(ERROR,
845 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
846 : : errmsg("COPY delimiter and quote must be different")));
847 : :
848 : : /* Check escape */
1849 heikki.linnakangas@i 849 [ + + + + ]: 5724 : if (!opts_out->csv_mode && opts_out->escape != NULL)
7911 bruce@momjian.us 850 [ + - ]: 3 : 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", "ESCAPE")));
854 : :
1849 heikki.linnakangas@i 855 [ + + + + ]: 5721 : if (opts_out->csv_mode && strlen(opts_out->escape) != 1)
7911 bruce@momjian.us 856 [ + - ]: 1 : ereport(ERROR,
857 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
858 : : errmsg("COPY escape must be a single one-byte character")));
859 : :
860 : : /* Check force_quote */
1849 heikki.linnakangas@i 861 [ + + + + : 5720 : if (!opts_out->csv_mode && (opts_out->force_quote || opts_out->force_quote_all))
+ + ]
7911 bruce@momjian.us 862 [ + - ]: 6 : ereport(ERROR,
863 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
864 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
865 : : errmsg("COPY %s requires CSV mode", "FORCE_QUOTE")));
1849 heikki.linnakangas@i 866 [ + + + + : 5714 : if ((opts_out->force_quote || opts_out->force_quote_all) && is_from)
+ + ]
7911 bruce@momjian.us 867 [ + - ]: 6 : ereport(ERROR,
868 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
869 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
870 : : second %s is a COPY with direction, e.g. COPY TO */
871 : : errmsg("COPY %s cannot be used with %s", "FORCE_QUOTE",
872 : : "COPY FROM")));
873 : :
874 : : /* Check force_notnull */
425 michael@paquier.xyz 875 [ + + + + ]: 5708 : if (!opts_out->csv_mode && (opts_out->force_notnull != NIL ||
876 [ + + ]: 5474 : opts_out->force_notnull_all))
7911 bruce@momjian.us 877 [ + - ]: 7 : ereport(ERROR,
878 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
879 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
880 : : errmsg("COPY %s requires CSV mode", "FORCE_NOT_NULL")));
425 michael@paquier.xyz 881 [ + + + + ]: 5701 : if ((opts_out->force_notnull != NIL || opts_out->force_notnull_all) &&
882 [ + + ]: 25 : !is_from)
7911 bruce@momjian.us 883 [ + - ]: 6 : 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", "FORCE_NOT_NULL",
888 : : "COPY TO")));
889 : :
890 : : /* Check force_null */
425 michael@paquier.xyz 891 [ + + + + ]: 5695 : if (!opts_out->csv_mode && (opts_out->force_null != NIL ||
892 [ + + ]: 5468 : opts_out->force_null_all))
4305 andrew@dunslane.net 893 [ + - ]: 6 : ereport(ERROR,
894 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
895 : : /*- translator: %s is the name of a COPY option, e.g. ON_ERROR */
896 : : errmsg("COPY %s requires CSV mode", "FORCE_NULL")));
897 : :
425 michael@paquier.xyz 898 [ + + + + ]: 5689 : if ((opts_out->force_null != NIL || opts_out->force_null_all) &&
899 [ + + ]: 25 : !is_from)
4305 andrew@dunslane.net 900 [ + - ]: 6 : ereport(ERROR,
901 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
902 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
903 : : second %s is a COPY with direction, e.g. COPY TO */
904 : : errmsg("COPY %s cannot be used with %s", "FORCE_NULL",
905 : : "COPY TO")));
906 : :
907 : : /* Don't allow the delimiter to appear in the null string. */
1849 heikki.linnakangas@i 908 [ + + ]: 5683 : if (strchr(opts_out->null_print, opts_out->delim[0]) != NULL)
7911 bruce@momjian.us 909 [ + - ]: 1 : ereport(ERROR,
910 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
911 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
912 : : errmsg("COPY delimiter character must not appear in the %s specification",
913 : : "NULL")));
914 : :
915 : : /* Don't allow the CSV quote char to appear in the null string. */
1849 heikki.linnakangas@i 916 [ + + ]: 5682 : if (opts_out->csv_mode &&
917 [ + + ]: 217 : strchr(opts_out->null_print, opts_out->quote[0]) != NULL)
7911 bruce@momjian.us 918 [ + - ]: 1 : ereport(ERROR,
919 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
920 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
921 : : errmsg("CSV quote character must not appear in the %s specification",
922 : : "NULL")));
923 : :
924 : : /* Check freeze */
764 925 [ + + - + ]: 5681 : if (opts_out->freeze && !is_from)
764 bruce@momjian.us 926 [ # # ]:UBC 0 : ereport(ERROR,
927 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
928 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
929 : : second %s is a COPY with direction, e.g. COPY TO */
930 : : errmsg("COPY %s cannot be used with %s", "FREEZE",
931 : : "COPY TO")));
932 : :
1009 andrew@dunslane.net 933 [ + + ]:CBC 5681 : if (opts_out->default_print)
934 : : {
935 [ + + ]: 36 : if (!is_from)
936 [ + - ]: 3 : ereport(ERROR,
937 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
938 : : /*- translator: first %s is the name of a COPY option, e.g. ON_ERROR,
939 : : second %s is a COPY with direction, e.g. COPY TO */
940 : : errmsg("COPY %s cannot be used with %s", "DEFAULT",
941 : : "COPY TO")));
942 : :
943 : : /* Don't allow the delimiter to appear in the default string. */
944 [ + + ]: 33 : if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
945 [ + - ]: 3 : ereport(ERROR,
946 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
947 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
948 : : errmsg("COPY delimiter character must not appear in the %s specification",
949 : : "DEFAULT")));
950 : :
951 : : /* Don't allow the CSV quote char to appear in the default string. */
952 [ + + ]: 30 : if (opts_out->csv_mode &&
953 [ + + ]: 15 : strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
954 [ + - ]: 3 : ereport(ERROR,
955 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
956 : : /*- translator: %s is the name of a COPY option, e.g. NULL */
957 : : errmsg("CSV quote character must not appear in the %s specification",
958 : : "DEFAULT")));
959 : :
960 : : /* Don't allow the NULL and DEFAULT string to be the same */
961 [ + + ]: 27 : if (opts_out->null_print_len == opts_out->default_print_len &&
962 : 12 : strncmp(opts_out->null_print, opts_out->default_print,
963 [ + + ]: 12 : opts_out->null_print_len) == 0)
964 [ + - ]: 3 : ereport(ERROR,
965 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
966 : : errmsg("NULL specification and DEFAULT specification cannot be the same")));
967 : : }
968 : : /* Check on_error */
434 fujii@postgresql.org 969 [ + + + + ]: 5669 : if (opts_out->binary && opts_out->on_error != COPY_ON_ERROR_STOP)
970 [ + - ]: 4 : ereport(ERROR,
971 : : (errcode(ERRCODE_SYNTAX_ERROR),
972 : : errmsg("only ON_ERROR STOP is allowed in BINARY mode")));
973 : :
974 [ + + + + ]: 5665 : if (opts_out->reject_limit && !opts_out->on_error)
975 [ + - ]: 4 : ereport(ERROR,
976 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
977 : : /*- translator: first and second %s are the names of COPY option, e.g.
978 : : * ON_ERROR, third is the value of the COPY option, e.g. IGNORE */
979 : : errmsg("COPY %s requires %s to be set to %s",
980 : : "REJECT_LIMIT", "ON_ERROR", "IGNORE")));
5413 tgl@sss.pgh.pa.us 981 : 5661 : }
982 : :
983 : : /*
984 : : * CopyGetAttnums - build an integer list of attnums to be copied
985 : : *
986 : : * The input attnamelist is either the user-specified column list,
987 : : * or NIL if there was none (in which case we want all the non-dropped
988 : : * columns).
989 : : *
990 : : * We don't include generated columns in the generated full list and we don't
991 : : * allow them to be specified explicitly. They don't make sense for COPY
992 : : * FROM, but we could possibly allow them for COPY TO. But this way it's at
993 : : * least ensured that whatever we copy out can be copied back in.
994 : : *
995 : : * rel can be NULL ... it's only used for error reports.
996 : : */
997 : : List *
1849 heikki.linnakangas@i 998 : 10968 : CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
999 : : {
1000 : 10968 : List *attnums = NIL;
1001 : :
1002 [ + + ]: 10968 : if (attnamelist == NIL)
1003 : : {
1004 : : /* Generate default column list */
8504 bruce@momjian.us 1005 : 1928 : int attr_count = tupDesc->natts;
1006 : : int i;
1007 : :
8540 1008 [ + + ]: 6825 : for (i = 0; i < attr_count; i++)
1009 : : {
361 drowley@postgresql.o 1010 : 4897 : CompactAttribute *attr = TupleDescCompactAttr(tupDesc, i);
1011 : :
1012 [ + + + + ]: 4897 : if (attr->attisdropped || attr->attgenerated)
2453 peter@eisentraut.org 1013 : 151 : continue;
7874 neilc@samurai.com 1014 : 4746 : attnums = lappend_int(attnums, i + 1);
1015 : : }
1016 : : }
1017 : : else
1018 : : {
1019 : : /* Validate the user-supplied list and extract attnums */
1020 : : ListCell *l;
1021 : :
8537 tgl@sss.pgh.pa.us 1022 [ + - + + : 41327 : foreach(l, attnamelist)
+ + ]
1023 : : {
8520 1024 : 32329 : char *name = strVal(lfirst(l));
1025 : : int attnum;
1026 : : int i;
1027 : :
1028 : : /* Lookup column name */
7048 1029 : 32329 : attnum = InvalidAttrNumber;
1030 [ + + ]: 4963697 : for (i = 0; i < tupDesc->natts; i++)
1031 : : {
3040 andres@anarazel.de 1032 : 4963682 : Form_pg_attribute att = TupleDescAttr(tupDesc, i);
1033 : :
1034 [ + + ]: 4963682 : if (att->attisdropped)
7048 tgl@sss.pgh.pa.us 1035 : 396 : continue;
3040 andres@anarazel.de 1036 [ + + ]: 4963286 : if (namestrcmp(&(att->attname), name) == 0)
1037 : : {
2453 peter@eisentraut.org 1038 [ + + ]: 32314 : if (att->attgenerated)
1039 [ + - ]: 24 : ereport(ERROR,
1040 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1041 : : errmsg("column \"%s\" is a generated column",
1042 : : name),
1043 : : errdetail("Generated columns cannot be used in COPY.")));
3040 andres@anarazel.de 1044 : 32290 : attnum = att->attnum;
7048 tgl@sss.pgh.pa.us 1045 : 32290 : break;
1046 : : }
1047 : : }
7208 1048 [ + + ]: 32305 : if (attnum == InvalidAttrNumber)
1049 : : {
7048 1050 [ + - ]: 15 : if (rel != NULL)
1051 [ + - ]: 15 : ereport(ERROR,
1052 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1053 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
1054 : : name, RelationGetRelationName(rel))));
1055 : : else
7048 tgl@sss.pgh.pa.us 1056 [ # # ]:UBC 0 : ereport(ERROR,
1057 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1058 : : errmsg("column \"%s\" does not exist",
1059 : : name)));
1060 : : }
1061 : : /* Check for duplicates */
7874 neilc@samurai.com 1062 [ + + ]:CBC 32290 : if (list_member_int(attnums, attnum))
8185 tgl@sss.pgh.pa.us 1063 [ + - ]: 3 : ereport(ERROR,
1064 : : (errcode(ERRCODE_DUPLICATE_COLUMN),
1065 : : errmsg("column \"%s\" specified more than once",
1066 : : name)));
7874 neilc@samurai.com 1067 : 32287 : attnums = lappend_int(attnums, attnum);
1068 : : }
1069 : : }
1070 : :
8537 tgl@sss.pgh.pa.us 1071 : 10926 : return attnums;
1072 : : }
|