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