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