Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * copyto.c
4 : : * COPY <table> TO file/program/client
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/copyto.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/table.h"
22 : : #include "access/tableam.h"
23 : : #include "access/tupconvert.h"
24 : : #include "catalog/pg_inherits.h"
25 : : #include "commands/copyapi.h"
26 : : #include "commands/progress.h"
27 : : #include "executor/execdesc.h"
28 : : #include "executor/executor.h"
29 : : #include "executor/tuptable.h"
30 : : #include "funcapi.h"
31 : : #include "libpq/libpq.h"
32 : : #include "libpq/pqformat.h"
33 : : #include "mb/pg_wchar.h"
34 : : #include "miscadmin.h"
35 : : #include "pgstat.h"
36 : : #include "storage/fd.h"
37 : : #include "tcop/tcopprot.h"
38 : : #include "utils/json.h"
39 : : #include "utils/lsyscache.h"
40 : : #include "utils/memutils.h"
41 : : #include "utils/rel.h"
42 : : #include "utils/snapmgr.h"
43 : : #include "utils/wait_event.h"
44 : :
45 : : /*
46 : : * Represents the different dest cases we need to worry about at
47 : : * the bottom level
48 : : */
49 : : typedef enum CopyDest
50 : : {
51 : : COPY_FILE, /* to file (or a piped program) */
52 : : COPY_FRONTEND, /* to frontend */
53 : : COPY_CALLBACK, /* to callback function */
54 : : } CopyDest;
55 : :
56 : : /*
57 : : * This struct contains all the state variables used throughout a COPY TO
58 : : * operation.
59 : : *
60 : : * Multi-byte encodings: all supported client-side encodings encode multi-byte
61 : : * characters by having the first byte's high bit set. Subsequent bytes of the
62 : : * character can have the high bit not set. When scanning data in such an
63 : : * encoding to look for a match to a single-byte (ie ASCII) character, we must
64 : : * use the full pg_encoding_mblen() machinery to skip over multibyte
65 : : * characters, else we might find a false match to a trailing byte. In
66 : : * supported server encodings, there is no possibility of a false match, and
67 : : * it's faster to make useless comparisons to trailing bytes than it is to
68 : : * invoke pg_encoding_mblen() to skip over them. encoding_embeds_ascii is true
69 : : * when we have to do it the hard way.
70 : : */
71 : : typedef struct CopyToStateData
72 : : {
73 : : /* format-specific routines */
74 : : const CopyToRoutine *routine;
75 : :
76 : : /* low-level state data */
77 : : CopyDest copy_dest; /* type of copy source/destination */
78 : : FILE *copy_file; /* used if copy_dest == COPY_FILE */
79 : : StringInfo fe_msgbuf; /* used for all dests during COPY TO */
80 : :
81 : : int file_encoding; /* file or remote side's character encoding */
82 : : bool need_transcoding; /* file encoding diff from server? */
83 : : bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
84 : :
85 : : /* parameters from the COPY command */
86 : : Relation rel; /* relation to copy to */
87 : : QueryDesc *queryDesc; /* executable query to copy from */
88 : : List *attnumlist; /* integer list of attnums to copy */
89 : : char *filename; /* filename, or NULL for STDOUT */
90 : : bool is_program; /* is 'filename' a program to popen? */
91 : : bool json_row_delim_needed; /* need delimiter before next row */
92 : : StringInfo json_buf; /* reusable buffer for JSON output,
93 : : * initialized in BeginCopyTo */
94 : : TupleDesc tupDesc; /* Descriptor for JSON output; for a column
95 : : * list this is a projected descriptor */
96 : : Datum *json_projvalues; /* pre-allocated projection values, or
97 : : * NULL */
98 : : bool *json_projnulls; /* pre-allocated projection nulls, or NULL */
99 : : copy_data_dest_cb data_dest_cb; /* function for writing data */
100 : :
101 : : CopyFormatOptions opts;
102 : : Node *whereClause; /* WHERE condition (or NULL) */
103 : : List *partitions; /* OID list of partitions to copy data from */
104 : :
105 : : /*
106 : : * Working state
107 : : */
108 : : MemoryContext copycontext; /* per-copy execution context */
109 : :
110 : : FmgrInfo *out_functions; /* lookup info for output functions */
111 : : MemoryContext rowcontext; /* per-row evaluation context */
112 : : uint64 bytes_processed; /* number of bytes processed so far */
113 : : } CopyToStateData;
114 : :
115 : : /* DestReceiver for COPY (query) TO */
116 : : typedef struct
117 : : {
118 : : DestReceiver pub; /* publicly-known function pointers */
119 : : CopyToState cstate; /* CopyToStateData for the command */
120 : : uint64 processed; /* # of tuples processed */
121 : : } DR_copy;
122 : :
123 : : /* NOTE: there's a copy of this in copyfromparse.c */
124 : : static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
125 : :
126 : :
127 : : /* non-export function prototypes */
128 : : static void EndCopy(CopyToState cstate);
129 : : static void ClosePipeToProgram(CopyToState cstate);
130 : : static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot);
131 : : static void CopyAttributeOutText(CopyToState cstate, const char *string);
132 : : static void CopyAttributeOutCSV(CopyToState cstate, const char *string,
133 : : bool use_quote);
134 : : static void CopyRelationTo(CopyToState cstate, Relation rel, Relation root_rel,
135 : : uint64 *processed);
136 : :
137 : : /* built-in format-specific routines */
138 : : static void CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc);
139 : : static void CopyToTextLikeOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo);
140 : : static void CopyToTextOneRow(CopyToState cstate, TupleTableSlot *slot);
141 : : static void CopyToCSVOneRow(CopyToState cstate, TupleTableSlot *slot);
142 : : static void CopyToTextLikeOneRow(CopyToState cstate, TupleTableSlot *slot,
143 : : bool is_csv);
144 : : static void CopyToTextLikeEnd(CopyToState cstate);
145 : : static void CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot);
146 : : static void CopyToJsonEnd(CopyToState cstate);
147 : : static void CopyToBinaryStart(CopyToState cstate, TupleDesc tupDesc);
148 : : static void CopyToBinaryOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo);
149 : : static void CopyToBinaryOneRow(CopyToState cstate, TupleTableSlot *slot);
150 : : static void CopyToBinaryEnd(CopyToState cstate);
151 : :
152 : : /* Low-level communications functions */
153 : : static void SendCopyBegin(CopyToState cstate);
154 : : static void SendCopyEnd(CopyToState cstate);
155 : : static void CopySendData(CopyToState cstate, const void *databuf, int datasize);
156 : : static void CopySendString(CopyToState cstate, const char *str);
157 : : static void CopySendChar(CopyToState cstate, char c);
158 : : static void CopySendEndOfRow(CopyToState cstate);
159 : : static void CopySendTextLikeEndOfRow(CopyToState cstate);
160 : : static void CopySendInt32(CopyToState cstate, int32 val);
161 : : static void CopySendInt16(CopyToState cstate, int16 val);
162 : :
163 : : /*
164 : : * COPY TO routines for built-in formats.
165 : : */
166 : :
167 : : /* text format */
168 : : static const CopyToRoutine CopyToRoutineText = {
169 : : .CopyToStart = CopyToTextLikeStart,
170 : : .CopyToOutFunc = CopyToTextLikeOutFunc,
171 : : .CopyToOneRow = CopyToTextOneRow,
172 : : .CopyToEnd = CopyToTextLikeEnd,
173 : : };
174 : :
175 : : /* CSV format */
176 : : static const CopyToRoutine CopyToRoutineCSV = {
177 : : .CopyToStart = CopyToTextLikeStart,
178 : : .CopyToOutFunc = CopyToTextLikeOutFunc,
179 : : .CopyToOneRow = CopyToCSVOneRow,
180 : : .CopyToEnd = CopyToTextLikeEnd,
181 : : };
182 : :
183 : : /* json format */
184 : : static const CopyToRoutine CopyToRoutineJson = {
185 : : .CopyToStart = CopyToTextLikeStart,
186 : : .CopyToOutFunc = CopyToTextLikeOutFunc,
187 : : .CopyToOneRow = CopyToJsonOneRow,
188 : : .CopyToEnd = CopyToJsonEnd,
189 : : };
190 : :
191 : : /* binary format */
192 : : static const CopyToRoutine CopyToRoutineBinary = {
193 : : .CopyToStart = CopyToBinaryStart,
194 : : .CopyToOutFunc = CopyToBinaryOutFunc,
195 : : .CopyToOneRow = CopyToBinaryOneRow,
196 : : .CopyToEnd = CopyToBinaryEnd,
197 : : };
198 : :
199 : : /* Return a COPY TO routine for the given options */
200 : : static const CopyToRoutine *
587 msawada@postgresql.o 201 :CBC 5147 : CopyToGetRoutine(const CopyFormatOptions *opts)
202 : : {
210 andrew@dunslane.net 203 [ + + ]: 5147 : if (opts->format == COPY_FORMAT_CSV)
592 msawada@postgresql.o 204 : 92 : return &CopyToRoutineCSV;
210 andrew@dunslane.net 205 [ + + ]: 5055 : else if (opts->format == COPY_FORMAT_BINARY)
592 msawada@postgresql.o 206 : 9 : return &CopyToRoutineBinary;
210 andrew@dunslane.net 207 [ + + ]: 5046 : else if (opts->format == COPY_FORMAT_JSON)
208 : 116 : return &CopyToRoutineJson;
209 : :
210 : : /* default is text */
592 msawada@postgresql.o 211 : 4930 : return &CopyToRoutineText;
212 : : }
213 : :
214 : : /* Implementation of the start callback for text, CSV, and json formats */
215 : : static void
216 : 5049 : CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc)
217 : : {
218 : : /*
219 : : * For non-binary copy, we need to convert null_print to file encoding,
220 : : * because it will be sent directly with CopySendString.
221 : : */
222 [ + + ]: 5049 : if (cstate->need_transcoding)
223 : 17 : cstate->opts.null_print_client = pg_server_to_any(cstate->opts.null_print,
224 : : cstate->opts.null_print_len,
225 : : cstate->file_encoding);
226 : :
227 : : /* if a header has been requested send the line */
466 fujii@postgresql.org 228 [ + + ]: 5049 : if (cstate->opts.header_line == COPY_HEADER_TRUE)
229 : : {
230 : : ListCell *cur;
592 msawada@postgresql.o 231 : 28 : bool hdr_delim = false;
232 : :
210 andrew@dunslane.net 233 [ - + ]: 28 : Assert(cstate->opts.format != COPY_FORMAT_JSON);
234 : :
592 msawada@postgresql.o 235 [ + - + + : 76 : foreach(cur, cstate->attnumlist)
+ + ]
236 : : {
237 : 48 : int attnum = lfirst_int(cur);
238 : : char *colname;
239 : :
240 [ + + ]: 48 : if (hdr_delim)
241 : 20 : CopySendChar(cstate, cstate->opts.delim[0]);
242 : 48 : hdr_delim = true;
243 : :
244 : 48 : colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname);
245 : :
210 andrew@dunslane.net 246 [ + + ]: 48 : if (cstate->opts.format == COPY_FORMAT_CSV)
592 msawada@postgresql.o 247 : 16 : CopyAttributeOutCSV(cstate, colname, false);
248 : : else
249 : 32 : CopyAttributeOutText(cstate, colname);
250 : : }
251 : :
252 : 28 : CopySendTextLikeEndOfRow(cstate);
253 : : }
254 : :
255 : : /*
256 : : * If FORCE_ARRAY has been specified, send the opening bracket.
257 : : */
210 andrew@dunslane.net 258 [ + + + + ]: 5049 : if (cstate->opts.format == COPY_FORMAT_JSON && cstate->opts.force_array)
259 : : {
260 : 20 : CopySendChar(cstate, '[');
261 : 20 : CopySendTextLikeEndOfRow(cstate);
262 : : }
592 msawada@postgresql.o 263 : 5049 : }
264 : :
265 : : /*
266 : : * Implementation of the outfunc callback for text, CSV, and json formats. Assign
267 : : * the output function data to the given *finfo.
268 : : */
269 : : static void
270 : 16937 : CopyToTextLikeOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo)
271 : : {
272 : : Oid func_oid;
273 : : bool is_varlena;
274 : :
275 : : /* Set output function for an attribute */
276 : 16937 : getTypeOutputInfo(atttypid, &func_oid, &is_varlena);
277 : 16937 : fmgr_info(func_oid, finfo);
278 : 16937 : }
279 : :
280 : : /* Implementation of the per-row callback for text format */
281 : : static void
282 : 1840602 : CopyToTextOneRow(CopyToState cstate, TupleTableSlot *slot)
283 : : {
284 : 1840602 : CopyToTextLikeOneRow(cstate, slot, false);
285 : 1840602 : }
286 : :
287 : : /* Implementation of the per-row callback for CSV format */
288 : : static void
289 : 228 : CopyToCSVOneRow(CopyToState cstate, TupleTableSlot *slot)
290 : : {
291 : 228 : CopyToTextLikeOneRow(cstate, slot, true);
292 : 228 : }
293 : :
294 : : /*
295 : : * Workhorse for CopyToTextOneRow() and CopyToCSVOneRow().
296 : : *
297 : : * We use pg_always_inline to reduce function call overhead
298 : : * and to help compilers to optimize away the 'is_csv' condition.
299 : : */
300 : : static pg_always_inline void
301 : 1840830 : CopyToTextLikeOneRow(CopyToState cstate,
302 : : TupleTableSlot *slot,
303 : : bool is_csv)
304 : : {
305 : 1840830 : bool need_delim = false;
306 : 1840830 : FmgrInfo *out_functions = cstate->out_functions;
307 : :
308 [ + + + + : 10834961 : foreach_int(attnum, cstate->attnumlist)
+ + ]
309 : : {
310 : 7153301 : Datum value = slot->tts_values[attnum - 1];
311 : 7153301 : bool isnull = slot->tts_isnull[attnum - 1];
312 : :
313 [ + + ]: 7153301 : if (need_delim)
314 : 5312540 : CopySendChar(cstate, cstate->opts.delim[0]);
315 : 7153301 : need_delim = true;
316 : :
317 [ + + ]: 7153301 : if (isnull)
318 : : {
319 : 601598 : CopySendString(cstate, cstate->opts.null_print_client);
320 : : }
321 : : else
322 : : {
323 : : char *string;
324 : :
325 : 6551703 : string = OutputFunctionCall(&out_functions[attnum - 1],
326 : : value);
327 : :
328 [ + + ]: 6551703 : if (is_csv)
329 : 412 : CopyAttributeOutCSV(cstate, string,
330 : 412 : cstate->opts.force_quote_flags[attnum - 1]);
331 : : else
332 : 6551291 : CopyAttributeOutText(cstate, string);
333 : : }
334 : : }
335 : :
336 : 1840830 : CopySendTextLikeEndOfRow(cstate);
337 : 1840830 : }
338 : :
339 : : /* Implementation of the end callback for text and CSV formats */
340 : : static void
341 : 4932 : CopyToTextLikeEnd(CopyToState cstate)
342 : : {
343 : : /* Nothing to do here */
344 : 4932 : }
345 : :
346 : : /* Implementation of the end callback for json format */
347 : : static void
210 andrew@dunslane.net 348 : 116 : CopyToJsonEnd(CopyToState cstate)
349 : : {
350 [ + + ]: 116 : if (cstate->opts.force_array)
351 : : {
352 : 20 : CopySendChar(cstate, ']');
353 : 20 : CopySendTextLikeEndOfRow(cstate);
354 : : }
355 : 116 : }
356 : :
357 : : /* Implementation of per-row callback for json format */
358 : : static void
359 : 348 : CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot)
360 : : {
361 : : Datum rowdata;
362 : :
363 : 348 : resetStringInfo(cstate->json_buf);
364 : :
365 [ + + ]: 348 : if (cstate->json_projvalues != NULL)
366 : : {
367 : : /*
368 : : * Column list case: project selected column values into sequential
369 : : * positions matching the custom TupleDesc, then form a new tuple.
370 : : */
371 : : HeapTuple tup;
372 : 140 : int i = 0;
373 : :
374 [ + - + + : 560 : foreach_int(attnum, cstate->attnumlist)
+ + ]
375 : : {
376 : 280 : cstate->json_projvalues[i] = slot->tts_values[attnum - 1];
377 : 280 : cstate->json_projnulls[i] = slot->tts_isnull[attnum - 1];
378 : 280 : i++;
379 : : }
380 : :
381 : 140 : tup = heap_form_tuple(cstate->tupDesc,
382 : 140 : cstate->json_projvalues,
383 : 140 : cstate->json_projnulls);
384 : :
385 : : /*
386 : : * heap_form_tuple already stamps the datum-length, type-id, and
387 : : * type-mod fields on t_data, so we can use it directly as a composite
388 : : * Datum without the extra pallocmemcpy that heap_copy_tuple_as_datum
389 : : * would do. Any TOAST pointers in the projected values will be
390 : : * detoasted by the per-column output functions called from
391 : : * composite_to_json.
392 : : */
393 : 140 : rowdata = HeapTupleGetDatum(tup);
394 : : }
395 : : else
396 : : {
397 : : /*
398 : : * Full table or query without column list. For a query, the slot's
399 : : * descriptor is either an unregistered RECORD type, which
400 : : * composite_to_json's lookup_rowtype_tupdesc() cannot look up, or,
401 : : * when the top plan node does not project, the row type of a scanned
402 : : * table, whose column names need not match the query's. Either way,
403 : : * the datum must be stamped with the query's blessed descriptor.
404 : : *
405 : : * A virtual slot has no physical tuple, so form one directly.
406 : : * Otherwise copy the slot's tuple and stamp the copy with the query's
407 : : * descriptor. That is safe because the tuple's physical layout
408 : : * matches the query's result descriptor: a scan returns its scan
409 : : * tuple unprojected only if tlist_matches_tupdesc() holds, which
410 : : * rules out dropped columns and columns with missing values.
411 : : */
7 412 [ + + ]: 208 : if (cstate->rel)
413 : 128 : rowdata = ExecFetchSlotHeapTupleDatum(slot);
414 [ + + ]: 80 : else if (TTS_IS_VIRTUAL(slot))
415 : 52 : rowdata = HeapTupleGetDatum(heap_form_tuple(cstate->tupDesc,
416 : 52 : slot->tts_values,
417 : 52 : slot->tts_isnull));
418 : : else
419 : : {
420 : : HeapTuple tup;
421 : : bool shouldFree;
422 : :
423 [ - + ]: 28 : Assert(slot->tts_tupleDescriptor->natts == cstate->tupDesc->natts);
424 : :
425 : 28 : tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
426 : 28 : rowdata = heap_copy_tuple_as_datum(tup, cstate->tupDesc);
427 [ - + ]: 28 : if (shouldFree)
7 andrew@dunslane.net 428 :UBC 0 : heap_freetuple(tup);
429 : : }
430 : : }
431 : :
210 andrew@dunslane.net 432 :CBC 348 : composite_to_json(rowdata, cstate->json_buf, false);
433 : :
434 [ + + ]: 348 : if (cstate->opts.force_array)
435 : : {
436 [ + + ]: 48 : if (cstate->json_row_delim_needed)
437 : 32 : CopySendChar(cstate, ',');
438 : : else
439 : : {
440 : : /* first row needs no delimiter */
441 : 16 : CopySendChar(cstate, ' ');
442 : 16 : cstate->json_row_delim_needed = true;
443 : : }
444 : : }
445 : :
446 : : /*
447 : : * Convert the JSON output to the target encoding if needed. Unlike the
448 : : * text and CSV paths which convert per-attribute via CopyAttributeOut*,
449 : : * composite_to_json() emits the whole row as one buffer, so we transcode
450 : : * it here in a single call before sending.
451 : : */
175 452 [ + + ]: 348 : if (cstate->need_transcoding)
453 : : {
454 : : char *converted;
455 : :
456 : 8 : converted = pg_server_to_any(cstate->json_buf->data,
457 : 8 : cstate->json_buf->len,
458 : : cstate->file_encoding);
459 : 8 : CopySendData(cstate, converted, strlen(converted));
460 [ + - ]: 8 : if (converted != cstate->json_buf->data)
461 : 8 : pfree(converted);
462 : : }
463 : : else
464 : 340 : CopySendData(cstate, cstate->json_buf->data, cstate->json_buf->len);
465 : :
210 466 : 348 : CopySendTextLikeEndOfRow(cstate);
467 : 348 : }
468 : :
469 : : /*
470 : : * Implementation of the start callback for binary format. Send a header
471 : : * for a binary copy.
472 : : */
473 : : static void
592 msawada@postgresql.o 474 : 8 : CopyToBinaryStart(CopyToState cstate, TupleDesc tupDesc)
475 : : {
476 : : int32 tmp;
477 : :
478 : : /* Signature */
479 : 8 : CopySendData(cstate, BinarySignature, 11);
480 : : /* Flags field */
481 : 8 : tmp = 0;
482 : 8 : CopySendInt32(cstate, tmp);
483 : : /* No header extension */
484 : 8 : tmp = 0;
485 : 8 : CopySendInt32(cstate, tmp);
486 : 8 : }
487 : :
488 : : /*
489 : : * Implementation of the outfunc callback for binary format. Assign
490 : : * the binary output function to the given *finfo.
491 : : */
492 : : static void
493 : 38 : CopyToBinaryOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo)
494 : : {
495 : : Oid func_oid;
496 : : bool is_varlena;
497 : :
498 : : /* Set output function for an attribute */
499 : 38 : getTypeBinaryOutputInfo(atttypid, &func_oid, &is_varlena);
500 : 37 : fmgr_info(func_oid, finfo);
501 : 37 : }
502 : :
503 : : /* Implementation of the per-row callback for binary format */
504 : : static void
505 : 19 : CopyToBinaryOneRow(CopyToState cstate, TupleTableSlot *slot)
506 : : {
507 : 19 : FmgrInfo *out_functions = cstate->out_functions;
508 : :
509 : : /* Binary per-tuple header */
510 : 19 : CopySendInt16(cstate, list_length(cstate->attnumlist));
511 : :
512 [ + - + + : 139 : foreach_int(attnum, cstate->attnumlist)
+ + ]
513 : : {
514 : 101 : Datum value = slot->tts_values[attnum - 1];
515 : 101 : bool isnull = slot->tts_isnull[attnum - 1];
516 : :
517 [ + + ]: 101 : if (isnull)
518 : : {
519 : 20 : CopySendInt32(cstate, -1);
520 : : }
521 : : else
522 : : {
523 : : bytea *outputbytes;
524 : :
525 : 81 : outputbytes = SendFunctionCall(&out_functions[attnum - 1],
526 : : value);
527 : 81 : CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
528 : 81 : CopySendData(cstate, VARDATA(outputbytes),
529 : 81 : VARSIZE(outputbytes) - VARHDRSZ);
530 : : }
531 : : }
532 : :
533 : 19 : CopySendEndOfRow(cstate);
534 : 19 : }
535 : :
536 : : /* Implementation of the end callback for binary format */
537 : : static void
538 : 8 : CopyToBinaryEnd(CopyToState cstate)
539 : : {
540 : : /* Generate trailer for a binary copy */
541 : 8 : CopySendInt16(cstate, -1);
542 : : /* Need to flush out the trailer */
543 : 8 : CopySendEndOfRow(cstate);
544 : 8 : }
545 : :
546 : : /*
547 : : * Send copy start/stop messages for frontend copies. These have changed
548 : : * in past protocol redesigns.
549 : : */
550 : : static void
2149 heikki.linnakangas@i 551 : 5000 : SendCopyBegin(CopyToState cstate)
552 : : {
553 : : StringInfoData buf;
2048 554 : 5000 : int natts = list_length(cstate->attnumlist);
210 andrew@dunslane.net 555 : 5000 : int16 format = (cstate->opts.format == COPY_FORMAT_BINARY ? 1 : 0);
556 : : int i;
557 : :
1147 nathan@postgresql.or 558 : 5000 : pq_beginmessage(&buf, PqMsg_CopyOutResponse);
2048 heikki.linnakangas@i 559 : 5000 : pq_sendbyte(&buf, format); /* overall format */
210 andrew@dunslane.net 560 [ + + ]: 5000 : if (cstate->opts.format != COPY_FORMAT_JSON)
561 : : {
562 : 4892 : pq_sendint16(&buf, natts);
563 [ + + ]: 21438 : for (i = 0; i < natts; i++)
564 : 16546 : pq_sendint16(&buf, format); /* per-column formats */
565 : : }
566 : : else
567 : : {
568 : : /*
569 : : * For JSON format, report one text-format column. Each CopyData
570 : : * message contains one complete JSON object, not individual column
571 : : * values, so the per-column count is always 1.
572 : : */
573 : 108 : pq_sendint16(&buf, 1);
574 : 108 : pq_sendint16(&buf, 0);
575 : : }
576 : :
2048 heikki.linnakangas@i 577 : 5000 : pq_endmessage(&buf);
578 : 5000 : cstate->copy_dest = COPY_FRONTEND;
2149 579 : 5000 : }
580 : :
581 : : static void
582 : 4998 : SendCopyEnd(CopyToState cstate)
583 : : {
584 : : /* Shouldn't have any unsent data */
2048 585 [ - + ]: 4998 : Assert(cstate->fe_msgbuf->len == 0);
586 : : /* Send Copy Done message */
1147 nathan@postgresql.or 587 : 4998 : pq_putemptymessage(PqMsg_CopyDone);
2149 heikki.linnakangas@i 588 : 4998 : }
589 : :
590 : : /*----------
591 : : * CopySendData sends output data to the destination (file or frontend)
592 : : * CopySendString does the same for null-terminated strings
593 : : * CopySendChar does the same for single characters
594 : : * CopySendEndOfRow does the appropriate thing at end of each data row
595 : : * (data is not actually flushed except by CopySendEndOfRow)
596 : : *
597 : : * NB: no data conversion is applied by these functions
598 : : *----------
599 : : */
600 : : static void
601 : 6450017 : CopySendData(CopyToState cstate, const void *databuf, int datasize)
602 : : {
603 : 6450017 : appendBinaryStringInfo(cstate->fe_msgbuf, databuf, datasize);
604 : 6450017 : }
605 : :
606 : : static void
607 : 601806 : CopySendString(CopyToState cstate, const char *str)
608 : : {
609 : 601806 : appendBinaryStringInfo(cstate->fe_msgbuf, str, strlen(str));
610 : 601806 : }
611 : :
612 : : static void
613 : 7170722 : CopySendChar(CopyToState cstate, char c)
614 : : {
615 [ + + ]: 7170722 : appendStringInfoCharMacro(cstate->fe_msgbuf, c);
616 : 7170722 : }
617 : :
618 : : static void
619 : 1841273 : CopySendEndOfRow(CopyToState cstate)
620 : : {
621 : 1841273 : StringInfo fe_msgbuf = cstate->fe_msgbuf;
622 : :
623 [ + + + - ]: 1841273 : switch (cstate->copy_dest)
624 : : {
625 : 8203 : case COPY_FILE:
251 michael@paquier.xyz 626 : 8203 : pgstat_report_wait_start(WAIT_EVENT_COPY_TO_WRITE);
2149 heikki.linnakangas@i 627 [ + - ]: 8203 : if (fwrite(fe_msgbuf->data, fe_msgbuf->len, 1,
628 [ - + ]: 8203 : cstate->copy_file) != 1 ||
629 : 8203 : ferror(cstate->copy_file))
630 : : {
2149 heikki.linnakangas@i 631 [ # # ]:UBC 0 : if (cstate->is_program)
632 : : {
633 [ # # ]: 0 : if (errno == EPIPE)
634 : : {
635 : : /*
636 : : * The pipe will be closed automatically on error at
637 : : * the end of transaction, but we might get a better
638 : : * error message from the subprocess' exit code than
639 : : * just "Broken Pipe"
640 : : */
641 : 0 : ClosePipeToProgram(cstate);
642 : :
643 : : /*
644 : : * If ClosePipeToProgram() didn't throw an error, the
645 : : * program terminated normally, but closed the pipe
646 : : * first. Restore errno, and throw an error.
647 : : */
648 : 0 : errno = EPIPE;
649 : : }
650 [ # # ]: 0 : ereport(ERROR,
651 : : (errcode_for_file_access(),
652 : : errmsg("could not write to COPY program: %m")));
653 : : }
654 : : else
655 [ # # ]: 0 : ereport(ERROR,
656 : : (errcode_for_file_access(),
657 : : errmsg("could not write to COPY file: %m")));
658 : : }
251 michael@paquier.xyz 659 :CBC 8203 : pgstat_report_wait_end();
2149 heikki.linnakangas@i 660 : 8203 : break;
2048 661 : 1833067 : case COPY_FRONTEND:
662 : : /* Dump the accumulated row as one CopyData message */
1147 nathan@postgresql.or 663 : 1833067 : (void) pq_putmessage(PqMsg_CopyData, fe_msgbuf->data, fe_msgbuf->len);
2149 heikki.linnakangas@i 664 : 1833067 : break;
1462 michael@paquier.xyz 665 : 3 : case COPY_CALLBACK:
666 : 3 : cstate->data_dest_cb(fe_msgbuf->data, fe_msgbuf->len);
667 : 3 : break;
668 : : }
669 : :
670 : : /* Update the progress */
2105 tomas.vondra@postgre 671 : 1841273 : cstate->bytes_processed += fe_msgbuf->len;
672 : 1841273 : pgstat_progress_update_param(PROGRESS_COPY_BYTES_PROCESSED, cstate->bytes_processed);
673 : :
2149 heikki.linnakangas@i 674 : 1841273 : resetStringInfo(fe_msgbuf);
675 : 1841273 : }
676 : :
677 : : /*
678 : : * Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
679 : : * line termination and do common appropriate things for the end of row.
680 : : */
681 : : static inline void
592 msawada@postgresql.o 682 : 1841246 : CopySendTextLikeEndOfRow(CopyToState cstate)
683 : : {
684 [ + + + ]: 1841246 : switch (cstate->copy_dest)
685 : : {
686 : 8187 : case COPY_FILE:
687 : : /* Default line termination depends on platform */
688 : : #ifndef WIN32
689 : 8187 : CopySendChar(cstate, '\n');
690 : : #else
691 : : CopySendString(cstate, "\r\n");
692 : : #endif
693 : 8187 : break;
694 : 1833056 : case COPY_FRONTEND:
695 : : /* The FE/BE protocol uses \n as newline for all platforms */
696 : 1833056 : CopySendChar(cstate, '\n');
697 : 1833056 : break;
698 : 3 : default:
699 : 3 : break;
700 : : }
701 : :
702 : : /* Now take the actions related to the end of a row */
703 : 1841246 : CopySendEndOfRow(cstate);
704 : 1841246 : }
705 : :
706 : : /*
707 : : * These functions do apply some data conversion
708 : : */
709 : :
710 : : /*
711 : : * CopySendInt32 sends an int32 in network byte order
712 : : */
713 : : static inline void
2149 heikki.linnakangas@i 714 : 117 : CopySendInt32(CopyToState cstate, int32 val)
715 : : {
716 : : uint32 buf;
717 : :
718 : 117 : buf = pg_hton32((uint32) val);
719 : 117 : CopySendData(cstate, &buf, sizeof(buf));
720 : 117 : }
721 : :
722 : : /*
723 : : * CopySendInt16 sends an int16 in network byte order
724 : : */
725 : : static inline void
726 : 27 : CopySendInt16(CopyToState cstate, int16 val)
727 : : {
728 : : uint16 buf;
729 : :
730 : 27 : buf = pg_hton16((uint16) val);
731 : 27 : CopySendData(cstate, &buf, sizeof(buf));
732 : 27 : }
733 : :
734 : : /*
735 : : * Closes the pipe to an external program, checking the pclose() return code.
736 : : */
737 : : static void
2149 heikki.linnakangas@i 738 :UBC 0 : ClosePipeToProgram(CopyToState cstate)
739 : : {
740 : : int pclose_rc;
741 : :
742 [ # # ]: 0 : Assert(cstate->is_program);
743 : :
744 : 0 : pclose_rc = ClosePipeStream(cstate->copy_file);
745 [ # # ]: 0 : if (pclose_rc == -1)
746 [ # # ]: 0 : ereport(ERROR,
747 : : (errcode_for_file_access(),
748 : : errmsg("could not close pipe to external command: %m")));
749 [ # # ]: 0 : else if (pclose_rc != 0)
750 : : {
751 [ # # ]: 0 : ereport(ERROR,
752 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
753 : : errmsg("program \"%s\" failed",
754 : : cstate->filename),
755 : : errdetail_internal("%s", wait_result_to_str(pclose_rc))));
756 : : }
757 : 0 : }
758 : :
759 : : /*
760 : : * Release resources allocated in a cstate for COPY TO.
761 : : */
762 : : static void
2149 heikki.linnakangas@i 763 :CBC 5056 : EndCopy(CopyToState cstate)
764 : : {
765 [ - + ]: 5056 : if (cstate->is_program)
766 : : {
2149 heikki.linnakangas@i 767 :UBC 0 : ClosePipeToProgram(cstate);
768 : : }
769 : : else
770 : : {
2149 heikki.linnakangas@i 771 [ + + - + ]:CBC 5056 : if (cstate->filename != NULL && FreeFile(cstate->copy_file))
2149 heikki.linnakangas@i 772 [ # # ]:UBC 0 : ereport(ERROR,
773 : : (errcode_for_file_access(),
774 : : errmsg("could not close file \"%s\": %m",
775 : : cstate->filename)));
776 : : }
777 : :
2105 tomas.vondra@postgre 778 :CBC 5056 : pgstat_progress_end_command();
779 : :
2149 heikki.linnakangas@i 780 : 5056 : MemoryContextDelete(cstate->copycontext);
781 : :
357 msawada@postgresql.o 782 [ + + ]: 5056 : if (cstate->partitions)
783 : 22 : list_free(cstate->partitions);
784 : :
2149 heikki.linnakangas@i 785 : 5056 : pfree(cstate);
786 : 5056 : }
787 : :
788 : : /*
789 : : * Setup CopyToState to read tuples from a table or a query for COPY TO.
790 : : *
791 : : * 'rel': Relation to be copied
792 : : * 'raw_query': Query whose results are to be copied
793 : : * 'queryRelId': OID of base relation to convert to a query (for RLS)
794 : : * 'filename': Name of server-local file to write, NULL for STDOUT
795 : : * 'is_program': true if 'filename' is program to execute
796 : : * 'data_dest_cb': Callback that processes the output data
797 : : * 'attnamelist': List of char *, columns to include. NIL selects all cols.
798 : : * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
799 : : *
800 : : * Returns a CopyToState, to be passed to DoCopyTo() and related functions.
801 : : */
802 : : CopyToState
803 : 5256 : BeginCopyTo(ParseState *pstate,
804 : : Relation rel,
805 : : RawStmt *raw_query,
806 : : Oid queryRelId,
807 : : const char *filename,
808 : : bool is_program,
809 : : copy_data_dest_cb data_dest_cb,
810 : : List *attnamelist,
811 : : List *options)
812 : : {
813 : : CopyToState cstate;
1462 michael@paquier.xyz 814 [ + + + + ]: 5256 : bool pipe = (filename == NULL && data_dest_cb == NULL);
815 : : TupleDesc tupDesc;
816 : : int num_phys_attrs;
817 : : MemoryContext oldcontext;
2043 818 : 5256 : const int progress_cols[] = {
819 : : PROGRESS_COPY_COMMAND,
820 : : PROGRESS_COPY_TYPE
821 : : };
822 : 5256 : int64 progress_vals[] = {
823 : : PROGRESS_COPY_COMMAND_TO,
824 : : 0
825 : : };
357 msawada@postgresql.o 826 : 5256 : List *children = NIL;
827 : :
2149 heikki.linnakangas@i 828 [ + + + + ]: 5256 : if (rel != NULL && rel->rd_rel->relkind != RELKIND_RELATION)
829 : : {
830 [ + + ]: 39 : if (rel->rd_rel->relkind == RELKIND_VIEW)
831 [ + - ]: 8 : ereport(ERROR,
832 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
833 : : errmsg("cannot copy from view \"%s\"",
834 : : RelationGetRelationName(rel)),
835 : : errhint("Try the COPY (SELECT ...) TO variant.")));
836 [ + + ]: 31 : else if (rel->rd_rel->relkind == RELKIND_MATVIEW)
837 : : {
556 fujii@postgresql.org 838 [ + + ]: 8 : if (!RelationIsPopulated(rel))
839 [ + - ]: 4 : ereport(ERROR,
840 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
841 : : errmsg("cannot copy from unpopulated materialized view \"%s\"",
842 : : RelationGetRelationName(rel)),
843 : : errhint("Use the REFRESH MATERIALIZED VIEW command."));
844 : : }
2149 heikki.linnakangas@i 845 [ - + ]: 23 : else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2149 heikki.linnakangas@i 846 [ # # ]:UBC 0 : ereport(ERROR,
847 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
848 : : errmsg("cannot copy from foreign table \"%s\"",
849 : : RelationGetRelationName(rel)),
850 : : errhint("Try the COPY (SELECT ...) TO variant.")));
2149 heikki.linnakangas@i 851 [ - + ]:CBC 23 : else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
2149 heikki.linnakangas@i 852 [ # # ]:UBC 0 : ereport(ERROR,
853 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
854 : : errmsg("cannot copy from sequence \"%s\"",
855 : : RelationGetRelationName(rel))));
2149 heikki.linnakangas@i 856 [ + - ]:CBC 23 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
857 : : {
858 : : /*
859 : : * Collect OIDs of relation containing data, so that later
860 : : * DoCopyTo can copy the data from them.
861 : : */
357 msawada@postgresql.o 862 : 23 : children = find_all_inheritors(RelationGetRelid(rel), AccessShareLock, NULL);
863 : :
864 [ + - + + : 114 : foreach_oid(child, children)
+ + ]
865 : : {
866 : 70 : char relkind = get_rel_relkind(child);
867 : :
868 [ + + ]: 70 : if (relkind == RELKIND_FOREIGN_TABLE)
869 : : {
870 : 1 : char *relation_name = get_rel_name(child);
871 : :
872 [ + - ]: 1 : ereport(ERROR,
873 : : errcode(ERRCODE_WRONG_OBJECT_TYPE),
874 : : errmsg("cannot copy from foreign table \"%s\"", relation_name),
875 : : errdetail("Partition \"%s\" is a foreign table in partitioned table \"%s\".",
876 : : relation_name, RelationGetRelationName(rel)),
877 : : errhint("Try the COPY (SELECT ...) TO variant."));
878 : : }
879 : :
880 : : /* Exclude tables with no data */
881 [ + + - + ]: 69 : if (RELKIND_HAS_PARTITIONS(relkind))
882 : 33 : children = foreach_delete_current(children, child);
883 : : }
884 : : }
885 : : else
2149 heikki.linnakangas@i 886 [ # # ]:UBC 0 : ereport(ERROR,
887 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
888 : : errmsg("cannot copy from non-table relation \"%s\"",
889 : : RelationGetRelationName(rel))));
890 : : }
891 : :
892 : :
893 : : /* Allocate workspace and zero all fields */
306 michael@paquier.xyz 894 :CBC 5243 : cstate = palloc0_object(CopyToStateData);
895 : :
896 : : /*
897 : : * We allocate everything used by a cstate in a new memory context. This
898 : : * avoids memory leaks during repeated use of COPY in a query.
899 : : */
2149 heikki.linnakangas@i 900 : 5243 : cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
901 : : "COPY",
902 : : ALLOCSET_DEFAULT_SIZES);
903 : :
904 : 5243 : oldcontext = MemoryContextSwitchTo(cstate->copycontext);
905 : :
906 : : /* Extract options from the statement node tree */
1979 tgl@sss.pgh.pa.us 907 : 5243 : ProcessCopyOptions(pstate, &cstate->opts, false /* is_from */ , options);
908 : :
909 : : /* Set format routine */
587 msawada@postgresql.o 910 : 5147 : cstate->routine = CopyToGetRoutine(&cstate->opts);
911 : :
912 : : /* Process the source/target relation or query */
2149 heikki.linnakangas@i 913 [ + + ]: 5147 : if (rel)
914 : : {
915 [ - + ]: 4726 : Assert(!raw_query);
916 : :
917 : 4726 : cstate->rel = rel;
918 : :
919 : 4726 : tupDesc = RelationGetDescr(cstate->rel);
357 msawada@postgresql.o 920 : 4726 : cstate->partitions = children;
210 andrew@dunslane.net 921 : 4726 : cstate->tupDesc = tupDesc;
922 : : }
923 : : else
924 : : {
925 : : List *rewritten;
926 : : Query *query;
927 : : PlannedStmt *plan;
928 : : DestReceiver *dest;
929 : :
2149 heikki.linnakangas@i 930 : 421 : cstate->rel = NULL;
357 msawada@postgresql.o 931 : 421 : cstate->partitions = NIL;
932 : :
933 : : /*
934 : : * Run parse analysis and rewrite. Note this also acquires sufficient
935 : : * locks on the source table(s).
936 : : */
1683 peter@eisentraut.org 937 : 421 : rewritten = pg_analyze_and_rewrite_fixedparams(raw_query,
938 : : pstate->p_sourcetext, NULL, 0,
939 : : NULL);
940 : :
941 : : /* check that we got back something we can work with */
2149 heikki.linnakangas@i 942 [ + + ]: 413 : if (rewritten == NIL)
943 : : {
944 [ + - ]: 12 : ereport(ERROR,
945 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
946 : : errmsg("DO INSTEAD NOTHING rules are not supported for COPY")));
947 : : }
948 [ + + ]: 401 : else if (list_length(rewritten) > 1)
949 : : {
950 : : ListCell *lc;
951 : :
952 : : /* examine queries to determine which error message to issue */
953 [ + - + + : 68 : foreach(lc, rewritten)
+ + ]
954 : : {
955 : 56 : Query *q = lfirst_node(Query, lc);
956 : :
957 [ + + ]: 56 : if (q->querySource == QSRC_QUAL_INSTEAD_RULE)
958 [ + - ]: 12 : ereport(ERROR,
959 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
960 : : errmsg("conditional DO INSTEAD rules are not supported for COPY")));
961 [ + + ]: 44 : if (q->querySource == QSRC_NON_INSTEAD_RULE)
962 [ + - ]: 12 : ereport(ERROR,
963 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
964 : : errmsg("DO ALSO rules are not supported for COPY")));
965 : : }
966 : :
967 [ + - ]: 12 : ereport(ERROR,
968 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
969 : : errmsg("multi-statement DO INSTEAD rules are not supported for COPY")));
970 : : }
971 : :
972 : 365 : query = linitial_node(Query, rewritten);
973 : :
974 : : /* The grammar allows SELECT INTO, but we don't support that */
975 [ + + ]: 365 : if (query->utilityStmt != NULL &&
976 [ + + ]: 12 : IsA(query->utilityStmt, CreateTableAsStmt))
977 [ + - ]: 8 : ereport(ERROR,
978 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
979 : : errmsg("COPY (SELECT INTO) is not supported")));
980 : :
981 : : /* The only other utility command we could see is NOTIFY */
721 tgl@sss.pgh.pa.us 982 [ + + ]: 357 : if (query->utilityStmt != NULL)
983 [ + - ]: 4 : ereport(ERROR,
984 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
985 : : errmsg("COPY query must not be a utility command")));
986 : :
987 : : /*
988 : : * Similarly the grammar doesn't enforce the presence of a RETURNING
989 : : * clause, but this is required here.
990 : : */
2149 heikki.linnakangas@i 991 [ + + ]: 353 : if (query->commandType != CMD_SELECT &&
992 [ + + ]: 69 : query->returningList == NIL)
993 : : {
994 [ + + + + : 16 : Assert(query->commandType == CMD_INSERT ||
+ + - + ]
995 : : query->commandType == CMD_UPDATE ||
996 : : query->commandType == CMD_DELETE ||
997 : : query->commandType == CMD_MERGE);
998 : :
999 [ + - ]: 16 : ereport(ERROR,
1000 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1001 : : errmsg("COPY query must have a RETURNING clause")));
1002 : : }
1003 : :
1004 : : /* plan the query */
1005 : 337 : plan = pg_plan_query(query, pstate->p_sourcetext,
1006 : : CURSOR_OPT_PARALLEL_OK, NULL, NULL);
1007 : :
1008 : : /*
1009 : : * With row-level security and a user using "COPY relation TO", we
1010 : : * have to convert the "COPY relation TO" to a query-based COPY (eg:
1011 : : * "COPY (SELECT * FROM ONLY relation) TO"), to allow the rewriter to
1012 : : * add in any RLS clauses.
1013 : : *
1014 : : * When this happens, we are passed in the relid of the originally
1015 : : * found relation (which we have locked). As the planner will look up
1016 : : * the relation again, we double-check here to make sure it found the
1017 : : * same one that we have locked.
1018 : : */
1019 [ + + ]: 336 : if (queryRelId != InvalidOid)
1020 : : {
1021 : : /*
1022 : : * Note that with RLS involved there may be multiple relations,
1023 : : * and while the one we need is almost certainly first, we don't
1024 : : * make any guarantees of that in the planner, so check the whole
1025 : : * list and make sure we find the original relation.
1026 : : */
1027 [ - + ]: 52 : if (!list_member_oid(plan->relationOids, queryRelId))
2149 heikki.linnakangas@i 1028 [ # # ]:UBC 0 : ereport(ERROR,
1029 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1030 : : errmsg("relation referenced by COPY statement has changed")));
1031 : : }
1032 : :
1033 : : /*
1034 : : * Use a snapshot with an updated command ID to ensure this query sees
1035 : : * results of any previously executed queries.
1036 : : */
2149 heikki.linnakangas@i 1037 :CBC 336 : PushCopiedSnapshot(GetActiveSnapshot());
1038 : 336 : UpdateActiveSnapshotCommandId();
1039 : :
1040 : : /* Create dest receiver for COPY OUT */
1041 : 336 : dest = CreateDestReceiver(DestCopyOut);
1042 : 336 : ((DR_copy *) dest)->cstate = cstate;
1043 : :
1044 : : /* Create a QueryDesc requesting no output */
508 amitlan@postgresql.o 1045 : 336 : cstate->queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
1046 : : GetActiveSnapshot(),
1047 : : InvalidSnapshot,
1048 : : dest, NULL, NULL, 0);
1049 : :
1050 : : /*
1051 : : * Call ExecutorStart to prepare the plan for execution.
1052 : : *
1053 : : * ExecutorStart computes a result tupdesc for us
1054 : : */
1055 : 336 : ExecutorStart(cstate->queryDesc, 0);
1056 : :
2149 heikki.linnakangas@i 1057 : 332 : tupDesc = cstate->queryDesc->tupDesc;
210 andrew@dunslane.net 1058 : 332 : tupDesc = BlessTupleDesc(tupDesc);
1059 : 332 : cstate->tupDesc = tupDesc;
1060 : : }
1061 : :
1062 : : /* Generate or convert list of attributes to process */
2149 heikki.linnakangas@i 1063 : 5058 : cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
1064 : :
1065 : : /* Set up JSON-specific state */
210 andrew@dunslane.net 1066 [ + + ]: 5058 : if (cstate->opts.format == COPY_FORMAT_JSON)
1067 : : {
1068 : 116 : cstate->json_buf = makeStringInfo();
1069 : :
1070 : : /*
1071 : : * Build a projected TupleDesc describing only the selected columns so
1072 : : * that composite_to_json() emits the right column names and types;
1073 : : * needed when an explicit column list was given (possibly with a
1074 : : * different column order) or when generated columns are excluded from
1075 : : * the output.
1076 : : */
107 1077 [ + + + + ]: 116 : if (rel && (attnamelist != NIL ||
1078 [ + + ]: 32 : list_length(cstate->attnumlist) < tupDesc->natts))
1079 : : {
210 1080 : 44 : int natts = list_length(cstate->attnumlist);
1081 : : TupleDesc resultDesc;
1082 : :
1083 : 44 : resultDesc = CreateTemplateTupleDesc(natts);
1084 : :
1085 [ + - + + : 180 : foreach_int(attnum, cstate->attnumlist)
+ + ]
1086 : : {
1087 : 92 : Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1);
1088 : :
1089 : 92 : TupleDescInitEntry(resultDesc,
1090 : 92 : foreach_current_index(attnum) + 1,
1091 : 92 : NameStr(attr->attname),
1092 : : attr->atttypid,
1093 : : attr->atttypmod,
1094 : 92 : attr->attndims);
1095 : : }
1096 : :
1097 : 44 : TupleDescFinalize(resultDesc);
1098 : 44 : cstate->tupDesc = BlessTupleDesc(resultDesc);
1099 : :
1100 : : /*
1101 : : * Pre-allocate arrays for projecting selected column values into
1102 : : * sequential positions matching the custom TupleDesc.
1103 : : */
1104 : 44 : cstate->json_projvalues = palloc_array(Datum, natts);
1105 : 44 : cstate->json_projnulls = palloc_array(bool, natts);
1106 : : }
1107 : : }
1108 : :
2149 heikki.linnakangas@i 1109 : 5058 : num_phys_attrs = tupDesc->natts;
1110 : :
1111 : : /* Convert FORCE_QUOTE name list to per-column flags, check validity */
56 michael@paquier.xyz 1112 :GNC 5058 : cstate->opts.force_quote_flags = palloc0_array(bool, num_phys_attrs);
2149 heikki.linnakangas@i 1113 [ + + ]:CBC 5058 : if (cstate->opts.force_quote_all)
1114 : : {
1108 andrew@dunslane.net 1115 [ + - - + : 12 : MemSet(cstate->opts.force_quote_flags, true, num_phys_attrs * sizeof(bool));
- - - - -
- ]
1116 : : }
2149 heikki.linnakangas@i 1117 [ + + ]: 5046 : else if (cstate->opts.force_quote)
1118 : : {
1119 : : List *attnums;
1120 : : ListCell *cur;
1121 : :
1122 : 16 : attnums = CopyGetAttnums(tupDesc, cstate->rel, cstate->opts.force_quote);
1123 : :
1124 [ + - + + : 32 : foreach(cur, attnums)
+ + ]
1125 : : {
1126 : 16 : int attnum = lfirst_int(cur);
1127 : 16 : Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1);
1128 : :
1129 [ - + ]: 16 : if (!list_member_int(cstate->attnumlist, attnum))
2149 heikki.linnakangas@i 1130 [ # # ]:UBC 0 : ereport(ERROR,
1131 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1132 : : /*- translator: %s is the name of a COPY option, e.g. FORCE_NOT_NULL */
1133 : : errmsg("%s column \"%s\" not referenced by COPY",
1134 : : "FORCE_QUOTE", NameStr(attr->attname))));
2149 heikki.linnakangas@i 1135 :CBC 16 : cstate->opts.force_quote_flags[attnum - 1] = true;
1136 : : }
1137 : : }
1138 : :
1139 : : /* Use client encoding when ENCODING option is not specified. */
1140 [ + + ]: 5058 : if (cstate->opts.file_encoding < 0)
1141 : 5034 : cstate->file_encoding = pg_get_client_encoding();
1142 : : else
1143 : 24 : cstate->file_encoding = cstate->opts.file_encoding;
1144 : :
1145 : : /*
1146 : : * Set up encoding conversion info if the file and server encodings differ
1147 : : * (see also pg_server_to_any).
1148 : : */
976 michael@paquier.xyz 1149 [ + + ]: 5058 : if (cstate->file_encoding == GetDatabaseEncoding() ||
1150 [ + + ]: 21 : cstate->file_encoding == PG_SQL_ASCII)
1151 : 5041 : cstate->need_transcoding = false;
1152 : : else
1153 : 17 : cstate->need_transcoding = true;
1154 : :
1155 : : /* See Multibyte encoding comment above */
2149 heikki.linnakangas@i 1156 [ + + + - ]: 5058 : cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
1157 : :
1158 : 5058 : cstate->copy_dest = COPY_FILE; /* default */
1159 : :
1462 michael@paquier.xyz 1160 [ + + ]: 5058 : if (data_dest_cb)
1161 : : {
1162 : 1 : progress_vals[1] = PROGRESS_COPY_TYPE_CALLBACK;
1163 : 1 : cstate->copy_dest = COPY_CALLBACK;
1164 : 1 : cstate->data_dest_cb = data_dest_cb;
1165 : : }
1166 [ + + ]: 5057 : else if (pipe)
1167 : : {
2043 1168 : 5000 : progress_vals[1] = PROGRESS_COPY_TYPE_PIPE;
1169 : :
2149 heikki.linnakangas@i 1170 [ - + ]: 5000 : Assert(!is_program); /* the grammar does not allow this */
1171 [ - + ]: 5000 : if (whereToSendOutput != DestRemote)
2149 heikki.linnakangas@i 1172 :UBC 0 : cstate->copy_file = stdout;
1173 : : }
1174 : : else
1175 : : {
2149 heikki.linnakangas@i 1176 :CBC 57 : cstate->filename = pstrdup(filename);
1177 : 57 : cstate->is_program = is_program;
1178 : :
1179 [ - + ]: 57 : if (is_program)
1180 : : {
2043 michael@paquier.xyz 1181 :UBC 0 : progress_vals[1] = PROGRESS_COPY_TYPE_PROGRAM;
2149 heikki.linnakangas@i 1182 : 0 : cstate->copy_file = OpenPipeStream(cstate->filename, PG_BINARY_W);
1183 [ # # ]: 0 : if (cstate->copy_file == NULL)
1184 [ # # ]: 0 : ereport(ERROR,
1185 : : (errcode_for_file_access(),
1186 : : errmsg("could not execute command \"%s\": %m",
1187 : : cstate->filename)));
1188 : : }
1189 : : else
1190 : : {
1191 : : mode_t oumask; /* Pre-existing umask value */
1192 : : struct stat st;
1193 : :
2043 michael@paquier.xyz 1194 :CBC 57 : progress_vals[1] = PROGRESS_COPY_TYPE_FILE;
1195 : :
1196 : : /*
1197 : : * Prevent write to relative path ... too easy to shoot oneself in
1198 : : * the foot by overwriting a database file ...
1199 : : */
2149 heikki.linnakangas@i 1200 [ - + ]: 57 : if (!is_absolute_path(filename))
2149 heikki.linnakangas@i 1201 [ # # ]:UBC 0 : ereport(ERROR,
1202 : : (errcode(ERRCODE_INVALID_NAME),
1203 : : errmsg("relative path not allowed for COPY to file")));
1204 : :
2149 heikki.linnakangas@i 1205 :CBC 57 : oumask = umask(S_IWGRP | S_IWOTH);
1206 [ + - ]: 57 : PG_TRY();
1207 : : {
1208 : 57 : cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W);
1209 : : }
2149 heikki.linnakangas@i 1210 :UBC 0 : PG_FINALLY();
1211 : : {
2149 heikki.linnakangas@i 1212 :CBC 57 : umask(oumask);
1213 : : }
1214 [ - + ]: 57 : PG_END_TRY();
1215 [ - + ]: 57 : if (cstate->copy_file == NULL)
1216 : : {
1217 : : /* copy errno because ereport subfunctions might change it */
2149 heikki.linnakangas@i 1218 :UBC 0 : int save_errno = errno;
1219 : :
1220 [ # # # # : 0 : ereport(ERROR,
# # ]
1221 : : (errcode_for_file_access(),
1222 : : errmsg("could not open file \"%s\" for writing: %m",
1223 : : cstate->filename),
1224 : : (save_errno == ENOENT || save_errno == EACCES) ?
1225 : : errhint("COPY TO instructs the PostgreSQL server process to write a file. "
1226 : : "You may want a client-side facility such as psql's \\copy.") : 0));
1227 : : }
1228 : :
2149 heikki.linnakangas@i 1229 [ - + ]:CBC 57 : if (fstat(fileno(cstate->copy_file), &st))
2149 heikki.linnakangas@i 1230 [ # # ]:UBC 0 : ereport(ERROR,
1231 : : (errcode_for_file_access(),
1232 : : errmsg("could not stat file \"%s\": %m",
1233 : : cstate->filename)));
1234 : :
2149 heikki.linnakangas@i 1235 [ - + ]:CBC 57 : if (S_ISDIR(st.st_mode))
2149 heikki.linnakangas@i 1236 [ # # ]:UBC 0 : ereport(ERROR,
1237 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1238 : : errmsg("\"%s\" is a directory", cstate->filename)));
1239 : : }
1240 : : }
1241 : :
1242 : : /* initialize progress */
2105 tomas.vondra@postgre 1243 :CBC 5058 : pgstat_progress_start_command(PROGRESS_COMMAND_COPY,
1244 [ + + ]: 5058 : cstate->rel ? RelationGetRelid(cstate->rel) : InvalidOid);
2043 michael@paquier.xyz 1245 : 5058 : pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
1246 : :
2105 tomas.vondra@postgre 1247 : 5058 : cstate->bytes_processed = 0;
1248 : :
2149 heikki.linnakangas@i 1249 : 5058 : MemoryContextSwitchTo(oldcontext);
1250 : :
1251 : 5058 : return cstate;
1252 : : }
1253 : :
1254 : : /*
1255 : : * Clean up storage and release resources for COPY TO.
1256 : : */
1257 : : void
1258 : 5056 : EndCopyTo(CopyToState cstate)
1259 : : {
1260 [ + + ]: 5056 : if (cstate->queryDesc != NULL)
1261 : : {
1262 : : /* Close down the query and free resources. */
1263 : 332 : ExecutorFinish(cstate->queryDesc);
1264 : 332 : ExecutorEnd(cstate->queryDesc);
1265 : 332 : FreeQueryDesc(cstate->queryDesc);
1266 : 332 : PopActiveSnapshot();
1267 : : }
1268 : :
1269 : : /* Clean up storage */
1270 : 5056 : EndCopy(cstate);
1271 : 5056 : }
1272 : :
1273 : : /*
1274 : : * Copy from relation or query TO file.
1275 : : *
1276 : : * Returns the number of rows processed.
1277 : : */
1278 : : uint64
2048 1279 : 5058 : DoCopyTo(CopyToState cstate)
1280 : : {
1462 michael@paquier.xyz 1281 [ + + + + ]: 5058 : bool pipe = (cstate->filename == NULL && cstate->data_dest_cb == NULL);
2048 heikki.linnakangas@i 1282 [ + + + - ]: 5058 : bool fe_copy = (pipe && whereToSendOutput == DestRemote);
1283 : : TupleDesc tupDesc;
1284 : : int num_phys_attrs;
1285 : : ListCell *cur;
357 msawada@postgresql.o 1286 : 5058 : uint64 processed = 0;
1287 : :
2048 heikki.linnakangas@i 1288 [ + + ]: 5058 : if (fe_copy)
1289 : 5000 : SendCopyBegin(cstate);
1290 : :
2149 1291 [ + + ]: 5058 : if (cstate->rel)
1292 : 4726 : tupDesc = RelationGetDescr(cstate->rel);
1293 : : else
1294 : 332 : tupDesc = cstate->queryDesc->tupDesc;
1295 : 5058 : num_phys_attrs = tupDesc->natts;
1979 tgl@sss.pgh.pa.us 1296 : 5058 : cstate->opts.null_print_client = cstate->opts.null_print; /* default */
1297 : :
1298 : : /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
2149 heikki.linnakangas@i 1299 : 5058 : cstate->fe_msgbuf = makeStringInfo();
1300 : :
1301 : : /* Get info about the columns we need to process. */
56 michael@paquier.xyz 1302 :GNC 5058 : cstate->out_functions = palloc_array(FmgrInfo, num_phys_attrs);
2149 heikki.linnakangas@i 1303 [ + + + + :CBC 22032 : foreach(cur, cstate->attnumlist)
+ + ]
1304 : : {
1305 : 16975 : int attnum = lfirst_int(cur);
1306 : 16975 : Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1);
1307 : :
592 msawada@postgresql.o 1308 : 16975 : cstate->routine->CopyToOutFunc(cstate, attr->atttypid,
1309 : 16975 : &cstate->out_functions[attnum - 1]);
1310 : : }
1311 : :
1312 : : /*
1313 : : * Create a temporary memory context that we can reset once per row to
1314 : : * recover palloc'd memory. This avoids any problems with leaks inside
1315 : : * datatype output routines, and should be faster than retail pfree's
1316 : : * anyway. (We don't need a whole econtext as CopyFrom does.)
1317 : : */
2149 heikki.linnakangas@i 1318 : 5057 : cstate->rowcontext = AllocSetContextCreate(CurrentMemoryContext,
1319 : : "COPY TO",
1320 : : ALLOCSET_DEFAULT_SIZES);
1321 : :
592 msawada@postgresql.o 1322 : 5057 : cstate->routine->CopyToStart(cstate, tupDesc);
1323 : :
2149 heikki.linnakangas@i 1324 [ + + ]: 5057 : if (cstate->rel)
1325 : : {
1326 : : /*
1327 : : * If COPY TO source table is a partitioned table, then open each
1328 : : * partition and process each individual partition.
1329 : : */
357 msawada@postgresql.o 1330 [ + + ]: 4725 : if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1331 : : {
1332 [ + - + + : 80 : foreach_oid(child, cstate->partitions)
+ + ]
1333 : : {
1334 : : Relation scan_rel;
1335 : :
1336 : : /* We already got the lock in BeginCopyTo */
1337 : 36 : scan_rel = table_open(child, NoLock);
1338 : 36 : CopyRelationTo(cstate, scan_rel, cstate->rel, &processed);
1339 : 36 : table_close(scan_rel, NoLock);
1340 : : }
1341 : : }
1342 : : else
1343 : 4703 : CopyRelationTo(cstate, cstate->rel, NULL, &processed);
1344 : : }
1345 : : else
1346 : : {
1347 : : /* run the plan --- the dest receiver will send tuples */
672 tgl@sss.pgh.pa.us 1348 : 332 : ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0);
2149 heikki.linnakangas@i 1349 : 332 : processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
1350 : : }
1351 : :
592 msawada@postgresql.o 1352 : 5056 : cstate->routine->CopyToEnd(cstate);
1353 : :
2149 heikki.linnakangas@i 1354 : 5056 : MemoryContextDelete(cstate->rowcontext);
1355 : :
2048 1356 [ + + ]: 5056 : if (fe_copy)
1357 : 4998 : SendCopyEnd(cstate);
1358 : :
2149 1359 : 5056 : return processed;
1360 : : }
1361 : :
1362 : : /*
1363 : : * Scans a single table and exports its rows to the COPY destination.
1364 : : *
1365 : : * root_rel can be set to the root table of rel if rel is a partition
1366 : : * table so that we can send tuples in root_rel's rowtype, which might
1367 : : * differ from individual partitions.
1368 : : */
1369 : : static void
357 msawada@postgresql.o 1370 : 4739 : CopyRelationTo(CopyToState cstate, Relation rel, Relation root_rel, uint64 *processed)
1371 : : {
1372 : : TupleTableSlot *slot;
1373 : : TableScanDesc scandesc;
1374 : 4739 : AttrMap *map = NULL;
1375 : 4739 : TupleTableSlot *root_slot = NULL;
1376 : :
196 melanieplageman@gmai 1377 : 4739 : scandesc = table_beginscan(rel, GetActiveSnapshot(), 0, NULL,
1378 : : SO_NONE);
357 msawada@postgresql.o 1379 : 4738 : slot = table_slot_create(rel, NULL);
1380 : :
1381 : : /*
1382 : : * If we are exporting partition data here, we check if converting tuples
1383 : : * to the root table's rowtype, because a partition might have column
1384 : : * order different than its root table.
1385 : : */
1386 [ + + ]: 4738 : if (root_rel != NULL)
1387 : : {
1388 : 36 : root_slot = table_slot_create(root_rel, NULL);
151 1389 : 36 : map = build_attrmap_by_name_if_req(RelationGetDescr(rel),
1390 : : RelationGetDescr(root_rel),
1391 : : false);
1392 : : }
1393 : :
357 1394 [ + + ]: 1841137 : while (table_scan_getnextslot(scandesc, ForwardScanDirection, slot))
1395 : : {
1396 : : TupleTableSlot *copyslot;
1397 : :
1398 [ - + ]: 1836399 : CHECK_FOR_INTERRUPTS();
1399 : :
1400 [ + + ]: 1836399 : if (map != NULL)
1401 : 26 : copyslot = execute_attr_map_slot(map, slot, root_slot);
1402 : : else
1403 : : {
1404 : : /* Deconstruct the tuple */
1405 : 1836373 : slot_getallattrs(slot);
1406 : 1836373 : copyslot = slot;
1407 : : }
1408 : :
1409 : : /* Format and send the data */
1410 : 1836399 : CopyOneRowTo(cstate, copyslot);
1411 : :
1412 : : /*
1413 : : * Increment the number of processed tuples, and report the progress.
1414 : : */
1415 : 1836399 : pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED,
1416 : 1836399 : ++(*processed));
1417 : : }
1418 : :
1419 : 4738 : ExecDropSingleTupleTableSlot(slot);
1420 : :
1421 [ + + ]: 4738 : if (root_slot != NULL)
1422 : 36 : ExecDropSingleTupleTableSlot(root_slot);
1423 : :
1424 [ + + ]: 4738 : if (map != NULL)
1425 : 11 : free_attrmap(map);
1426 : :
1427 : 4738 : table_endscan(scandesc);
1428 : 4738 : }
1429 : :
1430 : : /*
1431 : : * Emit one row during DoCopyTo().
1432 : : */
1433 : : static inline void
2149 heikki.linnakangas@i 1434 : 1841197 : CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot)
1435 : : {
1436 : : MemoryContext oldcontext;
1437 : :
1438 : 1841197 : MemoryContextReset(cstate->rowcontext);
1439 : 1841197 : oldcontext = MemoryContextSwitchTo(cstate->rowcontext);
1440 : :
1441 : : /* Make sure the tuple is fully deconstructed */
1442 : 1841197 : slot_getallattrs(slot);
1443 : :
592 msawada@postgresql.o 1444 : 1841197 : cstate->routine->CopyToOneRow(cstate, slot);
1445 : :
2149 heikki.linnakangas@i 1446 : 1841197 : MemoryContextSwitchTo(oldcontext);
1447 : 1841197 : }
1448 : :
1449 : : /*
1450 : : * Send text representation of one attribute, with conversion and escaping
1451 : : */
1452 : : #define DUMPSOFAR() \
1453 : : do { \
1454 : : if (ptr > start) \
1455 : : CopySendData(cstate, start, ptr - start); \
1456 : : } while (0)
1457 : :
1458 : : static void
978 michael@paquier.xyz 1459 : 6551323 : CopyAttributeOutText(CopyToState cstate, const char *string)
1460 : : {
1461 : : const char *ptr;
1462 : : const char *start;
1463 : : char c;
2149 heikki.linnakangas@i 1464 : 6551323 : char delimc = cstate->opts.delim[0];
1465 : :
1466 [ + + ]: 6551323 : if (cstate->need_transcoding)
1467 : 4 : ptr = pg_server_to_any(string, strlen(string), cstate->file_encoding);
1468 : : else
1469 : 6551319 : ptr = string;
1470 : :
1471 : : /*
1472 : : * We have to grovel through the string searching for control characters
1473 : : * and instances of the delimiter character. In most cases, though, these
1474 : : * are infrequent. To avoid overhead from calling CopySendData once per
1475 : : * character, we dump out all characters between escaped characters in a
1476 : : * single call. The loop invariant is that the data from "start" to "ptr"
1477 : : * can be sent literally, but hasn't yet been.
1478 : : *
1479 : : * We can skip pg_encoding_mblen() overhead when encoding is safe, because
1480 : : * in valid backend encodings, extra bytes of a multibyte character never
1481 : : * look like ASCII. This loop is sufficiently performance-critical that
1482 : : * it's worth making two copies of it to get the IS_HIGHBIT_SET() test out
1483 : : * of the normal safe-encoding path.
1484 : : */
1485 [ + + ]: 6551323 : if (cstate->encoding_embeds_ascii)
1486 : : {
1487 : 4 : start = ptr;
1488 [ + + ]: 12 : while ((c = *ptr) != '\0')
1489 : : {
1490 [ - + ]: 8 : if ((unsigned char) c < (unsigned char) 0x20)
1491 : : {
1492 : : /*
1493 : : * \r and \n must be escaped, the others are traditional. We
1494 : : * prefer to dump these using the C-like notation, rather than
1495 : : * a backslash and the literal character, because it makes the
1496 : : * dump file a bit more proof against Microsoftish data
1497 : : * mangling.
1498 : : */
2149 heikki.linnakangas@i 1499 [ # # # # :UBC 0 : switch (c)
# # # ]
1500 : : {
1501 : 0 : case '\b':
1502 : 0 : c = 'b';
1503 : 0 : break;
1504 : 0 : case '\f':
1505 : 0 : c = 'f';
1506 : 0 : break;
1507 : 0 : case '\n':
1508 : 0 : c = 'n';
1509 : 0 : break;
1510 : 0 : case '\r':
1511 : 0 : c = 'r';
1512 : 0 : break;
1513 : 0 : case '\t':
1514 : 0 : c = 't';
1515 : 0 : break;
1516 : 0 : case '\v':
1517 : 0 : c = 'v';
1518 : 0 : break;
1519 : 0 : default:
1520 : : /* If it's the delimiter, must backslash it */
1521 [ # # ]: 0 : if (c == delimc)
1522 : 0 : break;
1523 : : /* All ASCII control chars are length 1 */
1524 : 0 : ptr++;
1525 : 0 : continue; /* fall to end of loop */
1526 : : }
1527 : : /* if we get here, we need to convert the control char */
1528 [ # # ]: 0 : DUMPSOFAR();
1529 : 0 : CopySendChar(cstate, '\\');
1530 : 0 : CopySendChar(cstate, c);
1531 : 0 : start = ++ptr; /* do not include char in next run */
1532 : : }
2149 heikki.linnakangas@i 1533 [ + - - + ]:CBC 8 : else if (c == '\\' || c == delimc)
1534 : : {
2149 heikki.linnakangas@i 1535 [ # # ]:UBC 0 : DUMPSOFAR();
1536 : 0 : CopySendChar(cstate, '\\');
1537 : 0 : start = ptr++; /* we include char in next run */
1538 : : }
2149 heikki.linnakangas@i 1539 [ + + ]:CBC 8 : else if (IS_HIGHBIT_SET(c))
1540 : 4 : ptr += pg_encoding_mblen(cstate->file_encoding, ptr);
1541 : : else
1542 : 4 : ptr++;
1543 : : }
1544 : : }
1545 : : else
1546 : : {
1547 : 6551319 : start = ptr;
1548 [ + + ]: 77229563 : while ((c = *ptr) != '\0')
1549 : : {
1550 [ + + ]: 70678244 : if ((unsigned char) c < (unsigned char) 0x20)
1551 : : {
1552 : : /*
1553 : : * \r and \n must be escaped, the others are traditional. We
1554 : : * prefer to dump these using the C-like notation, rather than
1555 : : * a backslash and the literal character, because it makes the
1556 : : * dump file a bit more proof against Microsoftish data
1557 : : * mangling.
1558 : : */
1559 [ - - + - : 6975 : switch (c)
+ - - ]
1560 : : {
2149 heikki.linnakangas@i 1561 :UBC 0 : case '\b':
1562 : 0 : c = 'b';
1563 : 0 : break;
1564 : 0 : case '\f':
1565 : 0 : c = 'f';
1566 : 0 : break;
2149 heikki.linnakangas@i 1567 :CBC 5901 : case '\n':
1568 : 5901 : c = 'n';
1569 : 5901 : break;
2149 heikki.linnakangas@i 1570 :UBC 0 : case '\r':
1571 : 0 : c = 'r';
1572 : 0 : break;
2149 heikki.linnakangas@i 1573 :CBC 1074 : case '\t':
1574 : 1074 : c = 't';
1575 : 1074 : break;
2149 heikki.linnakangas@i 1576 :UBC 0 : case '\v':
1577 : 0 : c = 'v';
1578 : 0 : break;
1579 : 0 : default:
1580 : : /* If it's the delimiter, must backslash it */
1581 [ # # ]: 0 : if (c == delimc)
1582 : 0 : break;
1583 : : /* All ASCII control chars are length 1 */
1584 : 0 : ptr++;
1585 : 0 : continue; /* fall to end of loop */
1586 : : }
1587 : : /* if we get here, we need to convert the control char */
2149 heikki.linnakangas@i 1588 [ + + ]:CBC 6975 : DUMPSOFAR();
1589 : 6975 : CopySendChar(cstate, '\\');
1590 : 6975 : CopySendChar(cstate, c);
1591 : 6975 : start = ++ptr; /* do not include char in next run */
1592 : : }
1593 [ + + - + ]: 70671269 : else if (c == '\\' || c == delimc)
1594 : : {
1595 [ + + ]: 2337 : DUMPSOFAR();
1596 : 2337 : CopySendChar(cstate, '\\');
1597 : 2337 : start = ptr++; /* we include char in next run */
1598 : : }
1599 : : else
1600 : 70668932 : ptr++;
1601 : : }
1602 : : }
1603 : :
1604 [ + + ]: 6551323 : DUMPSOFAR();
1605 : 6551323 : }
1606 : :
1607 : : /*
1608 : : * Send text representation of one attribute, with conversion and
1609 : : * CSV-style escaping
1610 : : */
1611 : : static void
1718 peter@eisentraut.org 1612 : 428 : CopyAttributeOutCSV(CopyToState cstate, const char *string,
1613 : : bool use_quote)
1614 : : {
1615 : : const char *ptr;
1616 : : const char *start;
1617 : : char c;
2149 heikki.linnakangas@i 1618 : 428 : char delimc = cstate->opts.delim[0];
1619 : 428 : char quotec = cstate->opts.quote[0];
1620 : 428 : char escapec = cstate->opts.escape[0];
978 michael@paquier.xyz 1621 : 428 : bool single_attr = (list_length(cstate->attnumlist) == 1);
1622 : :
1623 : : /* force quoting if it matches null_print (before conversion!) */
2149 heikki.linnakangas@i 1624 [ + + + + ]: 428 : if (!use_quote && strcmp(string, cstate->opts.null_print) == 0)
1625 : 36 : use_quote = true;
1626 : :
1627 [ + + ]: 428 : if (cstate->need_transcoding)
1628 : 4 : ptr = pg_server_to_any(string, strlen(string), cstate->file_encoding);
1629 : : else
1630 : 424 : ptr = string;
1631 : :
1632 : : /*
1633 : : * Make a preliminary pass to discover if it needs quoting
1634 : : */
1635 [ + + ]: 428 : if (!use_quote)
1636 : : {
1637 : : /*
1638 : : * Quote '\.' if it appears alone on a line, so that it will not be
1639 : : * interpreted as an end-of-data marker. (PG 18 and up will not
1640 : : * interpret '\.' in CSV that way, except in embedded-in-SQL data; but
1641 : : * we want the data to be loadable by older versions too. Also, this
1642 : : * avoids breaking clients that are still using PQgetline().)
1643 : : */
1644 [ + + + + ]: 304 : if (single_attr && strcmp(ptr, "\\.") == 0)
1645 : 4 : use_quote = true;
1646 : : else
1647 : : {
1718 peter@eisentraut.org 1648 : 300 : const char *tptr = ptr;
1649 : :
2149 heikki.linnakangas@i 1650 [ + + ]: 1504 : while ((c = *tptr) != '\0')
1651 : : {
1652 [ + + + + : 1296 : if (c == delimc || c == quotec || c == '\n' || c == '\r')
+ + + + ]
1653 : : {
1654 : 92 : use_quote = true;
1655 : 92 : break;
1656 : : }
1657 [ + + + + ]: 1204 : if (IS_HIGHBIT_SET(c) && cstate->encoding_embeds_ascii)
1658 : 4 : tptr += pg_encoding_mblen(cstate->file_encoding, tptr);
1659 : : else
1660 : 1200 : tptr++;
1661 : : }
1662 : : }
1663 : : }
1664 : :
1665 [ + + ]: 428 : if (use_quote)
1666 : : {
1667 : 220 : CopySendChar(cstate, quotec);
1668 : :
1669 : : /*
1670 : : * We adopt the same optimization strategy as in CopyAttributeOutText
1671 : : */
1672 : 220 : start = ptr;
1673 [ + + ]: 1704 : while ((c = *ptr) != '\0')
1674 : : {
1675 [ + + + + ]: 1484 : if (c == quotec || c == escapec)
1676 : : {
1677 [ + + ]: 104 : DUMPSOFAR();
1678 : 104 : CopySendChar(cstate, escapec);
1679 : 104 : start = ptr; /* we include char in next run */
1680 : : }
1681 [ + + + - ]: 1484 : if (IS_HIGHBIT_SET(c) && cstate->encoding_embeds_ascii)
1682 : 4 : ptr += pg_encoding_mblen(cstate->file_encoding, ptr);
1683 : : else
1684 : 1480 : ptr++;
1685 : : }
1686 [ + + ]: 220 : DUMPSOFAR();
1687 : :
1688 : 220 : CopySendChar(cstate, quotec);
1689 : : }
1690 : : else
1691 : : {
1692 : : /* If it doesn't need quoting, we can just dump it as-is */
1693 : 208 : CopySendString(cstate, ptr);
1694 : : }
1695 : 428 : }
1696 : :
1697 : : /*
1698 : : * copy_dest_startup --- executor startup
1699 : : */
1700 : : static void
1701 : 332 : copy_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
1702 : : {
1703 : : /* no-op */
1704 : 332 : }
1705 : :
1706 : : /*
1707 : : * copy_dest_receive --- receive one tuple
1708 : : */
1709 : : static bool
1710 : 4798 : copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
1711 : : {
1712 : 4798 : DR_copy *myState = (DR_copy *) self;
1979 tgl@sss.pgh.pa.us 1713 : 4798 : CopyToState cstate = myState->cstate;
1714 : :
1715 : : /* Send the data */
2149 heikki.linnakangas@i 1716 : 4798 : CopyOneRowTo(cstate, slot);
1717 : :
1718 : : /* Increment the number of processed tuples, and report the progress */
2043 michael@paquier.xyz 1719 : 4798 : pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED,
1720 : 4798 : ++myState->processed);
1721 : :
2149 heikki.linnakangas@i 1722 : 4798 : return true;
1723 : : }
1724 : :
1725 : : /*
1726 : : * copy_dest_shutdown --- executor end
1727 : : */
1728 : : static void
1729 : 332 : copy_dest_shutdown(DestReceiver *self)
1730 : : {
1731 : : /* no-op */
1732 : 332 : }
1733 : :
1734 : : /*
1735 : : * copy_dest_destroy --- release DestReceiver object
1736 : : */
1737 : : static void
2149 heikki.linnakangas@i 1738 :UBC 0 : copy_dest_destroy(DestReceiver *self)
1739 : : {
1740 : 0 : pfree(self);
1741 : 0 : }
1742 : :
1743 : : /*
1744 : : * CreateCopyDestReceiver -- create a suitable DestReceiver object
1745 : : */
1746 : : DestReceiver *
2149 heikki.linnakangas@i 1747 :CBC 336 : CreateCopyDestReceiver(void)
1748 : : {
306 michael@paquier.xyz 1749 : 336 : DR_copy *self = palloc_object(DR_copy);
1750 : :
2149 heikki.linnakangas@i 1751 : 336 : self->pub.receiveSlot = copy_dest_receive;
1752 : 336 : self->pub.rStartup = copy_dest_startup;
1753 : 336 : self->pub.rShutdown = copy_dest_shutdown;
1754 : 336 : self->pub.rDestroy = copy_dest_destroy;
1755 : 336 : self->pub.mydest = DestCopyOut;
1756 : :
1757 : 336 : self->cstate = NULL; /* will be set later */
1758 : 336 : self->processed = 0;
1759 : :
1760 : 336 : return (DestReceiver *) self;
1761 : : }
|