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