Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * psql - the PostgreSQL interactive terminal
3 : : *
4 : : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
5 : : *
6 : : * src/bin/psql/copy.c
7 : : */
8 : : #include "postgres_fe.h"
9 : :
10 : : #include <signal.h>
11 : : #include <sys/stat.h>
12 : : #ifndef WIN32
13 : : #include <unistd.h> /* for isatty */
14 : : #else
15 : : #include <io.h> /* I think */
16 : : #endif
17 : :
18 : : #include "common.h"
19 : : #include "common/logging.h"
20 : : #include "copy.h"
21 : : #include "libpq-fe.h"
22 : : #include "pqexpbuffer.h"
23 : : #include "prompt.h"
24 : : #include "settings.h"
25 : : #include "stringutils.h"
26 : :
27 : : /*
28 : : * parse_slash_copy
29 : : * -- parses \copy command line
30 : : *
31 : : * The documented syntax is:
32 : : * \copy tablename [(columnlist)] from|to filename [options]
33 : : * \copy ( query stmt ) to filename [options]
34 : : *
35 : : * where 'filename' can be one of the following:
36 : : * '<file path>' | PROGRAM '<command>' | stdin | stdout | pstdin | pstdout
37 : : * and 'query' can be one of the following:
38 : : * SELECT | UPDATE | INSERT | DELETE
39 : : *
40 : : * An undocumented fact is that you can still write BINARY before the
41 : : * tablename; this is a hangover from the pre-7.3 syntax. The options
42 : : * syntax varies across backend versions, but we avoid all that mess
43 : : * by just transmitting the stuff after the filename literally.
44 : : *
45 : : * table name can be double-quoted and can have a schema part.
46 : : * column names can be double-quoted.
47 : : * filename can be single-quoted like SQL literals.
48 : : * command must be single-quoted like SQL literals.
49 : : *
50 : : * returns a malloc'ed structure with the options, or NULL on parsing error
51 : : */
52 : :
53 : : struct copy_options
54 : : {
55 : : char *before_tofrom; /* COPY string before TO/FROM */
56 : : char *after_tofrom; /* COPY string after TO/FROM filename */
57 : : char *file; /* NULL = stdin/stdout */
58 : : bool program; /* is 'file' a program to popen? */
59 : : bool psql_inout; /* true = use psql stdin/stdout */
60 : : bool from; /* true = FROM, false = TO */
61 : : };
62 : :
63 : :
64 : : static void
3354 tgl@sss.pgh.pa.us 65 :GIC 99 : free_copy_options(struct copy_options *ptr)
66 : : {
9793 bruce@momjian.us 67 [ - + ]: 99 : if (!ptr)
9793 bruce@momjian.us 68 :UIC 0 : return;
6186 tgl@sss.pgh.pa.us 69 :GIC 99 : free(ptr->before_tofrom);
70 : 99 : free(ptr->after_tofrom);
9793 bruce@momjian.us 71 : 99 : free(ptr->file);
72 : 99 : free(ptr);
73 : : }
74 : :
75 : :
76 : : /* concatenate "more" onto "var", freeing the original value of *var */
77 : : static void
8713 tgl@sss.pgh.pa.us 78 : 654 : xstrcat(char **var, const char *more)
79 : : {
80 : : char *newvar;
81 : :
4692 82 : 654 : newvar = psprintf("%s%s", *var, more);
8713 83 : 654 : free(*var);
84 : 654 : *var = newvar;
85 : 654 : }
86 : :
87 : :
88 : : static struct copy_options *
9718 peter_e@gmx.net 89 : 99 : parse_slash_copy(const char *args)
90 : : {
91 : : struct copy_options *result;
92 : : char *token;
8713 tgl@sss.pgh.pa.us 93 : 99 : const char *whitespace = " \t\n\r";
7392 94 [ + - ]: 99 : char nonstd_backslash = standard_strings() ? 0 : '\\';
95 : :
6186 96 [ - + ]: 99 : if (!args)
97 : : {
2705 peter@eisentraut.org 98 :UIC 0 : pg_log_error("\\copy: arguments required");
9629 peter_e@gmx.net 99 : 0 : return NULL;
100 : : }
101 : :
181 michael@paquier.xyz 102 :GIC 99 : result = pg_malloc0_object(struct copy_options);
103 : :
3354 tgl@sss.pgh.pa.us 104 : 99 : result->before_tofrom = pg_strdup(""); /* initialize for appending */
105 : :
6186 106 : 99 : token = strtokx(args, whitespace, ".,()", "\"",
107 : : 0, false, false, pset.encoding);
9793 bruce@momjian.us 108 [ - + ]: 99 : if (!token)
8713 tgl@sss.pgh.pa.us 109 :UIC 0 : goto error;
110 : :
111 : : /* The following can be removed when we drop 7.3 syntax support */
8147 tgl@sss.pgh.pa.us 112 [ - + ]:GIC 99 : if (pg_strcasecmp(token, "binary") == 0)
113 : : {
6186 tgl@sss.pgh.pa.us 114 :UIC 0 : xstrcat(&result->before_tofrom, token);
8713 115 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
116 : : 0, false, false, pset.encoding);
117 [ # # ]: 0 : if (!token)
118 : 0 : goto error;
119 : : }
120 : :
121 : : /* Handle COPY (query) case */
7302 tgl@sss.pgh.pa.us 122 [ + + ]:GIC 99 : if (token[0] == '(')
123 : : {
7267 bruce@momjian.us 124 : 16 : int parens = 1;
125 : :
7302 tgl@sss.pgh.pa.us 126 [ + + ]: 244 : while (parens > 0)
127 : : {
6186 128 : 228 : xstrcat(&result->before_tofrom, " ");
129 : 228 : xstrcat(&result->before_tofrom, token);
130 : 228 : token = strtokx(NULL, whitespace, "()", "\"'",
131 : : nonstd_backslash, true, false, pset.encoding);
7302 132 [ - + ]: 228 : if (!token)
7302 tgl@sss.pgh.pa.us 133 :UIC 0 : goto error;
7302 tgl@sss.pgh.pa.us 134 [ + + ]:GIC 228 : if (token[0] == '(')
135 : 12 : parens++;
136 [ + + ]: 216 : else if (token[0] == ')')
137 : 28 : parens--;
138 : : }
139 : : }
140 : :
6186 141 : 99 : xstrcat(&result->before_tofrom, " ");
142 : 99 : xstrcat(&result->before_tofrom, token);
8713 143 : 99 : token = strtokx(NULL, whitespace, ".,()", "\"",
144 : : 0, false, false, pset.encoding);
145 [ - + ]: 99 : if (!token)
8713 tgl@sss.pgh.pa.us 146 :UIC 0 : goto error;
147 : :
148 : : /*
149 : : * strtokx() will not have returned a multi-character token starting with
150 : : * '.', so we don't need strcmp() here. Likewise for '(', etc, below.
151 : : */
8713 tgl@sss.pgh.pa.us 152 [ - + ]:GIC 99 : if (token[0] == '.')
153 : : {
154 : : /* handle schema . table */
6186 tgl@sss.pgh.pa.us 155 :UIC 0 : xstrcat(&result->before_tofrom, token);
8713 156 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
157 : : 0, false, false, pset.encoding);
9793 bruce@momjian.us 158 [ # # ]: 0 : if (!token)
8713 tgl@sss.pgh.pa.us 159 : 0 : goto error;
6186 160 : 0 : xstrcat(&result->before_tofrom, token);
8713 161 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
162 : : 0, false, false, pset.encoding);
163 [ # # ]: 0 : if (!token)
164 : 0 : goto error;
165 : : }
166 : :
8713 tgl@sss.pgh.pa.us 167 [ - + ]:GIC 99 : if (token[0] == '(')
168 : : {
169 : : /* handle parenthesized column list */
170 : : for (;;)
171 : : {
6186 tgl@sss.pgh.pa.us 172 :UIC 0 : xstrcat(&result->before_tofrom, " ");
173 : 0 : xstrcat(&result->before_tofrom, token);
174 : 0 : token = strtokx(NULL, whitespace, "()", "\"",
175 : : 0, false, false, pset.encoding);
8713 176 [ # # ]: 0 : if (!token)
177 : 0 : goto error;
178 [ # # ]: 0 : if (token[0] == ')')
179 : 0 : break;
180 : : }
6186 181 : 0 : xstrcat(&result->before_tofrom, " ");
182 : 0 : xstrcat(&result->before_tofrom, token);
8713 183 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
184 : : 0, false, false, pset.encoding);
185 [ # # ]: 0 : if (!token)
186 : 0 : goto error;
187 : : }
188 : :
8147 tgl@sss.pgh.pa.us 189 [ + + ]:GIC 99 : if (pg_strcasecmp(token, "from") == 0)
8713 190 : 58 : result->from = true;
8147 191 [ + - ]: 41 : else if (pg_strcasecmp(token, "to") == 0)
8713 192 : 41 : result->from = false;
193 : : else
8713 tgl@sss.pgh.pa.us 194 :UIC 0 : goto error;
195 : :
196 : : /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
4734 bruce@momjian.us 197 :GIC 99 : token = strtokx(NULL, whitespace, ";", "'",
198 : : 0, false, false, pset.encoding);
8713 tgl@sss.pgh.pa.us 199 [ - + ]: 99 : if (!token)
8713 tgl@sss.pgh.pa.us 200 :UIC 0 : goto error;
201 : :
4929 heikki.linnakangas@i 202 [ - + ]:GIC 99 : if (pg_strcasecmp(token, "program") == 0)
203 : : {
204 : : int toklen;
205 : :
4734 bruce@momjian.us 206 :UIC 0 : token = strtokx(NULL, whitespace, ";", "'",
207 : : 0, false, false, pset.encoding);
4929 heikki.linnakangas@i 208 [ # # ]: 0 : if (!token)
209 : 0 : goto error;
210 : :
211 : : /*
212 : : * The shell command must be quoted. This isn't fool-proof, but
213 : : * catches most quoting errors.
214 : : */
215 : 0 : toklen = strlen(token);
216 [ # # # # : 0 : if (token[0] != '\'' || toklen < 2 || token[toklen - 1] != '\'')
# # ]
217 : 0 : goto error;
218 : :
219 : 0 : strip_quotes(token, '\'', 0, pset.encoding);
220 : :
221 : 0 : result->program = true;
222 : 0 : result->file = pg_strdup(token);
223 : : }
4929 heikki.linnakangas@i 224 [ + + + + ]:GIC 187 : else if (pg_strcasecmp(token, "stdin") == 0 ||
225 : 88 : pg_strcasecmp(token, "stdout") == 0)
226 : : {
8255 tgl@sss.pgh.pa.us 227 : 52 : result->file = NULL;
228 : : }
8147 229 [ + - - + ]: 94 : else if (pg_strcasecmp(token, "pstdin") == 0 ||
8033 bruce@momjian.us 230 : 47 : pg_strcasecmp(token, "pstdout") == 0)
231 : : {
8172 bruce@momjian.us 232 :UIC 0 : result->psql_inout = true;
8713 tgl@sss.pgh.pa.us 233 : 0 : result->file = NULL;
234 : : }
235 : : else
236 : : {
237 : : /* filename can be optionally quoted */
4929 heikki.linnakangas@i 238 :GIC 47 : strip_quotes(token, '\'', 0, pset.encoding);
8250 neilc@samurai.com 239 : 47 : result->file = pg_strdup(token);
8255 tgl@sss.pgh.pa.us 240 : 47 : expand_tilde(&result->file);
241 : : }
242 : :
243 : : /* Collect the rest of the line (COPY options) */
6186 244 : 99 : token = strtokx(NULL, "", NULL, NULL,
245 : : 0, false, false, pset.encoding);
8713 246 [ + + ]: 99 : if (token)
6186 247 : 34 : result->after_tofrom = pg_strdup(token);
248 : :
8713 249 : 99 : return result;
250 : :
8713 tgl@sss.pgh.pa.us 251 :UIC 0 : error:
252 [ # # ]: 0 : if (token)
2705 peter@eisentraut.org 253 : 0 : pg_log_error("\\copy: parse error at \"%s\"", token);
254 : : else
255 : 0 : pg_log_error("\\copy: parse error at end of line");
8713 tgl@sss.pgh.pa.us 256 : 0 : free_copy_options(result);
257 : :
258 : 0 : return NULL;
259 : : }
260 : :
261 : :
262 : : /*
263 : : * Execute a \copy command (frontend copy). We have to open a file (or execute
264 : : * a command), then submit a COPY query to the backend and either feed it data
265 : : * from the file or route its response into the file.
266 : : */
267 : : bool
9718 peter_e@gmx.net 268 :GIC 99 : do_copy(const char *args)
269 : : {
270 : : PQExpBufferData query;
271 : : FILE *copystream;
272 : : struct copy_options *options;
273 : : bool success;
274 : :
275 : : /* parse options */
276 : 99 : options = parse_slash_copy(args);
277 : :
9793 bruce@momjian.us 278 [ - + ]: 99 : if (!options)
9793 bruce@momjian.us 279 :UIC 0 : return false;
280 : :
281 : : /* prepare to read or write the target file */
4929 heikki.linnakangas@i 282 [ + + + - ]:GIC 99 : if (options->file && !options->program)
575 tgl@sss.pgh.pa.us 283 : 47 : canonicalize_path_enc(options->file, pset.encoding);
284 : :
9793 bruce@momjian.us 285 [ + + ]: 99 : if (options->from)
286 : : {
9633 287 [ + + ]: 58 : if (options->file)
288 : : {
4929 heikki.linnakangas@i 289 [ - + ]: 47 : if (options->program)
290 : : {
1459 tgl@sss.pgh.pa.us 291 :UIC 0 : fflush(NULL);
4929 heikki.linnakangas@i 292 : 0 : errno = 0;
293 : 0 : copystream = popen(options->file, PG_BINARY_R);
294 : : }
295 : : else
4929 heikki.linnakangas@i 296 :GIC 47 : copystream = fopen(options->file, PG_BINARY_R);
297 : : }
8172 bruce@momjian.us 298 [ + - ]: 11 : else if (!options->psql_inout)
8033 299 : 11 : copystream = pset.cur_cmd_source;
300 : : else
8033 bruce@momjian.us 301 :UIC 0 : copystream = stdin;
302 : : }
303 : : else
304 : : {
9633 bruce@momjian.us 305 [ - + ]:GIC 41 : if (options->file)
306 : : {
4929 heikki.linnakangas@i 307 [ # # ]:UIC 0 : if (options->program)
308 : : {
1459 tgl@sss.pgh.pa.us 309 : 0 : fflush(NULL);
3920 310 : 0 : disable_sigpipe_trap();
1459 311 : 0 : errno = 0;
4929 heikki.linnakangas@i 312 : 0 : copystream = popen(options->file, PG_BINARY_W);
313 : : }
314 : : else
315 : 0 : copystream = fopen(options->file, PG_BINARY_W);
316 : : }
8172 bruce@momjian.us 317 [ + - ]:GIC 41 : else if (!options->psql_inout)
8033 318 : 41 : copystream = pset.queryFout;
319 : : else
9633 bruce@momjian.us 320 :UIC 0 : copystream = stdout;
321 : : }
322 : :
9793 bruce@momjian.us 323 [ + + ]:GIC 99 : if (!copystream)
324 : : {
4929 heikki.linnakangas@i 325 [ - + ]: 5 : if (options->program)
2705 peter@eisentraut.org 326 :UIC 0 : pg_log_error("could not execute command \"%s\": %m",
327 : : options->file);
328 : : else
2705 peter@eisentraut.org 329 :GIC 5 : pg_log_error("%s: %m",
330 : : options->file);
9793 bruce@momjian.us 331 : 5 : free_copy_options(options);
332 : 5 : return false;
333 : : }
334 : :
4929 heikki.linnakangas@i 335 [ + - ]: 94 : if (!options->program)
336 : : {
337 : : struct stat st;
338 : : int result;
339 : :
340 : : /* make sure the specified file is not a directory */
4562 sfrost@snowman.net 341 [ - + ]: 94 : if ((result = fstat(fileno(copystream), &st)) < 0)
2705 peter@eisentraut.org 342 :UIC 0 : pg_log_error("could not stat file \"%s\": %m",
343 : : options->file);
344 : :
4562 sfrost@snowman.net 345 [ + - - + ]:GIC 94 : if (result == 0 && S_ISDIR(st.st_mode))
2705 peter@eisentraut.org 346 :UIC 0 : pg_log_error("%s: cannot copy from/to a directory",
347 : : options->file);
348 : :
4562 sfrost@snowman.net 349 [ + - - + ]:GIC 94 : if (result < 0 || S_ISDIR(st.st_mode))
350 : : {
4562 sfrost@snowman.net 351 :UIC 0 : fclose(copystream);
4929 heikki.linnakangas@i 352 : 0 : free_copy_options(options);
353 : 0 : return false;
354 : : }
355 : : }
356 : :
357 : : /* build the command we will send to the backend */
6186 tgl@sss.pgh.pa.us 358 :GIC 94 : initPQExpBuffer(&query);
359 : 94 : printfPQExpBuffer(&query, "COPY ");
360 : 94 : appendPQExpBufferStr(&query, options->before_tofrom);
361 [ + + ]: 94 : if (options->from)
4665 heikki.linnakangas@i 362 : 53 : appendPQExpBufferStr(&query, " FROM STDIN ");
363 : : else
364 : 41 : appendPQExpBufferStr(&query, " TO STDOUT ");
6186 tgl@sss.pgh.pa.us 365 [ + + ]: 94 : if (options->after_tofrom)
366 : 31 : appendPQExpBufferStr(&query, options->after_tofrom);
367 : :
368 : : /* run it like a user command, but with copystream as data source/sink */
4553 369 : 94 : pset.copyStream = copystream;
17 370 : 94 : success = SendQuery(query.data, options->from ? 1 : 0);
4553 371 : 94 : pset.copyStream = NULL;
8891 peter_e@gmx.net 372 : 94 : termPQExpBuffer(&query);
373 : :
8033 bruce@momjian.us 374 [ + + ]: 94 : if (options->file != NULL)
375 : : {
4929 heikki.linnakangas@i 376 [ - + ]: 42 : if (options->program)
377 : : {
4838 bruce@momjian.us 378 :UIC 0 : int pclose_rc = pclose(copystream);
379 : :
4929 heikki.linnakangas@i 380 [ # # ]: 0 : if (pclose_rc != 0)
381 : : {
382 [ # # ]: 0 : if (pclose_rc < 0)
2705 peter@eisentraut.org 383 : 0 : pg_log_error("could not close pipe to external command: %m");
384 : : else
385 : : {
4838 bruce@momjian.us 386 : 0 : char *reason = wait_result_to_str(pclose_rc);
387 : :
2705 peter@eisentraut.org 388 [ # # ]: 0 : pg_log_error("%s: %s", options->file,
389 : : reason ? reason : "");
1533 390 : 0 : free(reason);
391 : : }
4929 heikki.linnakangas@i 392 : 0 : success = false;
393 : : }
1239 tgl@sss.pgh.pa.us 394 : 0 : SetShellResultVariables(pclose_rc);
3920 395 : 0 : restore_sigpipe_trap();
396 : : }
397 : : else
398 : : {
4929 heikki.linnakangas@i 399 [ - + ]:GIC 42 : if (fclose(copystream) != 0)
400 : : {
2705 peter@eisentraut.org 401 :UIC 0 : pg_log_error("%s: %m", options->file);
4929 heikki.linnakangas@i 402 : 0 : success = false;
403 : : }
404 : : }
405 : : }
9793 bruce@momjian.us 406 :GIC 94 : free_copy_options(options);
407 : 94 : return success;
408 : : }
409 : :
410 : :
411 : : /*
412 : : * Functions for handling COPY IN/OUT data transfer.
413 : : *
414 : : * If you want to use COPY TO STDOUT/FROM STDIN in your application,
415 : : * this is the code to steal ;)
416 : : */
417 : :
418 : : /*
419 : : * handleCopyOut
420 : : * receives data as a result of a COPY ... TO STDOUT command
421 : : *
422 : : * conn should be a database connection that you just issued COPY TO on
423 : : * and got back a PGRES_COPY_OUT result.
424 : : *
425 : : * copystream is the file stream for the data to go to.
426 : : * copystream can be NULL to eat the data without writing it anywhere.
427 : : *
428 : : * The final status for the COPY is returned into *res (but note
429 : : * we already reported the error, if it's not a success result).
430 : : *
431 : : * result is true if successful, false if not.
432 : : */
433 : : bool
4550 tgl@sss.pgh.pa.us 434 : 486 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
435 : : {
7267 bruce@momjian.us 436 : 486 : bool OK = true;
437 : : char *buf;
438 : : int ret;
439 : :
440 : : for (;;)
441 : : {
7482 tgl@sss.pgh.pa.us 442 : 2193 : ret = PQgetCopyData(conn, &buf, 0);
443 : :
444 [ + + ]: 2193 : if (ret < 0)
4578 445 : 486 : break; /* done or server/connection error */
446 : :
7482 447 [ + - ]: 1707 : if (buf)
448 : : {
2770 449 [ + - + - : 1707 : if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret)
- + ]
450 : : {
2705 peter@eisentraut.org 451 :UIC 0 : pg_log_error("could not write COPY data: %m");
452 : : /* complain only once, keep reading data from server */
7398 tgl@sss.pgh.pa.us 453 : 0 : OK = false;
454 : : }
7482 tgl@sss.pgh.pa.us 455 :GIC 1707 : PQfreemem(buf);
456 : : }
457 : : }
458 : :
2770 459 [ + - + - : 486 : if (OK && copystream && fflush(copystream))
- + ]
460 : : {
2705 peter@eisentraut.org 461 :UIC 0 : pg_log_error("could not write COPY data: %m");
7398 tgl@sss.pgh.pa.us 462 : 0 : OK = false;
463 : : }
464 : :
7482 tgl@sss.pgh.pa.us 465 [ - + ]:GIC 486 : if (ret == -2)
466 : : {
2705 peter@eisentraut.org 467 :UIC 0 : pg_log_error("COPY data transfer failed: %s", PQerrorMessage(conn));
7482 tgl@sss.pgh.pa.us 468 : 0 : OK = false;
469 : : }
470 : :
471 : : /*
472 : : * Check command status and return to normal libpq state.
473 : : *
474 : : * If for some reason libpq is still reporting PGRES_COPY_OUT state, we
475 : : * would like to forcibly exit that state, since our caller would be
476 : : * unable to distinguish that situation from reaching the next COPY in a
477 : : * command string that happened to contain two consecutive COPY TO STDOUT
478 : : * commands. However, libpq provides no API for doing that, and in
479 : : * principle it's a libpq bug anyway if PQgetCopyData() returns -1 or -2
480 : : * but hasn't exited COPY_OUT state internally. So we ignore the
481 : : * possibility here.
482 : : */
4550 tgl@sss.pgh.pa.us 483 :GIC 486 : *res = PQgetResult(conn);
484 [ + + ]: 486 : if (PQresultStatus(*res) != PGRES_COMMAND_OK)
485 : : {
2705 peter@eisentraut.org 486 : 2 : pg_log_info("%s", PQerrorMessage(conn));
7482 tgl@sss.pgh.pa.us 487 : 2 : OK = false;
488 : : }
489 : :
490 : 486 : return OK;
491 : : }
492 : :
493 : : /*
494 : : * handleCopyIn
495 : : * sends data to complete a COPY ... FROM STDIN command
496 : : *
497 : : * conn should be a database connection that you just issued COPY FROM on
498 : : * and got back a PGRES_COPY_IN result. Alternatively, if conn is NULL,
499 : : * we read and discard the appropriate amount of data from copystream.
500 : : * copystream is the file stream to read the data from.
501 : : * isbinary can be set from PQbinaryTuples().
502 : : * The final status for the COPY is returned into *res; but note
503 : : * we already reported the error, if it's not a success result.
504 : : * Also, if conn is NULL then *res is not touched.
505 : : *
506 : : * result is true if successful, false if not.
507 : : */
508 : :
509 : : /* read chunk size for COPY IN - size is not critical */
510 : : #define COPYBUFSIZ 8192
511 : :
512 : : bool
4550 513 : 945 : handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res)
514 : : {
515 : : bool OK;
516 : : char buf[COPYBUFSIZ];
517 : : bool showprompt;
518 : :
519 : : /* We want to prompt if interactive input ... */
17 520 : 945 : showprompt = isatty(fileno(copystream));
521 : : /* ... but if we're just discarding data, don't bother the user at all */
522 [ - + - - ]: 945 : if (showprompt && !conn)
17 tgl@sss.pgh.pa.us 523 :UIC 0 : return true;
524 : :
525 : : /*
526 : : * Establish longjmp destination for exiting from wait-for-input. (This is
527 : : * only effective while sigint_interrupt_enabled is TRUE.)
528 : : */
7379 tgl@sss.pgh.pa.us 529 [ - + ]:GIC 945 : if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
530 : : {
531 : : /* got here with longjmp */
532 : :
533 : : /* Terminate data transfer */
17 tgl@sss.pgh.pa.us 534 [ # # ]:UIC 0 : if (conn)
535 : 0 : PQputCopyEnd(conn,
536 [ # # ]: 0 : (PQprotocolVersion(conn) < 3) ? NULL :
537 : 0 : _("canceled by user"));
538 : :
5328 alvherre@alvh.no-ip. 539 : 0 : OK = false;
540 : 0 : goto copyin_cleanup;
541 : : }
542 : :
543 : : /* Issue initial prompt if interactive input */
17 tgl@sss.pgh.pa.us 544 [ - + - - ]:GIC 945 : if (showprompt && !pset.quiet)
17 tgl@sss.pgh.pa.us 545 :UIC 0 : puts(_("Enter data to be copied followed by a newline.\n"
546 : : "End with a backslash and a period on a line by itself, or an EOF signal."));
547 : :
7379 tgl@sss.pgh.pa.us 548 :GIC 945 : OK = true;
549 : :
7398 550 [ - + ]: 945 : if (isbinary)
551 : : {
552 : : /* interactive input probably silly, but give one prompt anyway */
4377 andres@anarazel.de 553 [ # # ]:UIC 0 : if (showprompt)
554 : : {
3437 tgl@sss.pgh.pa.us 555 : 0 : const char *prompt = get_prompt(PROMPT_COPY, NULL);
556 : :
9793 bruce@momjian.us 557 : 0 : fputs(prompt, stdout);
558 : 0 : fflush(stdout);
559 : : }
560 : :
561 : : for (;;)
7398 tgl@sss.pgh.pa.us 562 : 0 : {
563 : : int buflen;
564 : :
565 : : /* enable longjmp while waiting for input */
7379 566 : 0 : sigint_interrupt_enabled = true;
567 : :
568 : 0 : buflen = fread(buf, 1, COPYBUFSIZ, copystream);
569 : :
570 : 0 : sigint_interrupt_enabled = false;
571 : :
572 [ # # ]: 0 : if (buflen <= 0)
573 : 0 : break;
574 : :
17 575 [ # # # # ]: 0 : if (conn && PQputCopyData(conn, buf, buflen) <= 0)
576 : : {
7398 577 : 0 : OK = false;
9793 bruce@momjian.us 578 : 0 : break;
579 : : }
580 : : }
581 : : }
582 : : else
583 : : {
7398 tgl@sss.pgh.pa.us 584 :GIC 945 : bool copydone = false;
585 : : int buflen;
1870 heikki.linnakangas@i 586 : 945 : bool at_line_begin = true;
587 : :
588 : : /*
589 : : * In text mode, we have to read the input one line at a time, so that
590 : : * we can stop reading at the EOF marker (\.). We mustn't read beyond
591 : : * the EOF marker, because if the data was inlined in a SQL script, we
592 : : * would eat up the commands after the EOF marker.
593 : : */
594 : 945 : buflen = 0;
7398 tgl@sss.pgh.pa.us 595 [ + + ]: 38204 : while (!copydone)
596 : : {
597 : : char *fgresult;
598 : :
1870 heikki.linnakangas@i 599 [ + + - + ]: 37259 : if (at_line_begin && showprompt)
600 : : {
3437 tgl@sss.pgh.pa.us 601 :UIC 0 : const char *prompt = get_prompt(PROMPT_COPY, NULL);
602 : :
7398 603 : 0 : fputs(prompt, stdout);
604 : 0 : fflush(stdout);
605 : : }
606 : :
607 : : /* enable longjmp while waiting for input */
1870 heikki.linnakangas@i 608 :GIC 37259 : sigint_interrupt_enabled = true;
609 : :
610 : 37259 : fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
611 : :
612 : 37259 : sigint_interrupt_enabled = false;
613 : :
614 [ + + ]: 37259 : if (!fgresult)
615 : 42 : copydone = true;
616 : : else
617 : : {
618 : : int linelen;
619 : :
620 : 37217 : linelen = strlen(fgresult);
621 : 37217 : buflen += linelen;
622 : :
623 : : /* current line is done? */
624 [ + + ]: 37217 : if (buf[buflen - 1] == '\n')
625 : : {
626 : : /*
627 : : * When at the beginning of the line and the data is
628 : : * inlined, check for EOF marker. If the marker is found,
629 : : * we must stop at this point. If not, the \. line can be
630 : : * sent to the server, and we let it decide whether it's
631 : : * an EOF or not depending on the format: in TEXT mode, \.
632 : : * will be interpreted as an EOF, in CSV, it will not.
633 : : */
696 tgl@sss.pgh.pa.us 634 [ + + + + ]: 37155 : if (at_line_begin && copystream == pset.cur_cmd_source)
635 : : {
1870 heikki.linnakangas@i 636 [ + + + + : 2707 : if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
+ + ]
637 [ - + ]: 60 : (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
638 : : {
639 : 903 : copydone = true;
640 : :
641 : : /*
642 : : * Remove the EOF marker from the data sent. In
643 : : * CSV mode, the EOF marker must be removed,
644 : : * otherwise it would be interpreted by the server
645 : : * as valid data.
646 : : */
696 tgl@sss.pgh.pa.us 647 : 903 : *fgresult = '\0';
648 : 903 : buflen -= linelen;
649 : : }
650 : : }
651 : :
1870 heikki.linnakangas@i 652 [ + + ]: 37155 : if (copystream == pset.cur_cmd_source)
653 : : {
654 : 2716 : pset.lineno++;
655 : 2716 : pset.stmt_lineno++;
656 : : }
657 : 37155 : at_line_begin = true;
658 : : }
659 : : else
660 : 62 : at_line_begin = false;
661 : : }
662 : :
663 : : /*
664 : : * If the buffer is full, or we've reached the EOF, flush it.
665 : : *
666 : : * Make sure there's always space for four more bytes in the
667 : : * buffer, plus a NUL terminator. That way, an EOF marker is
668 : : * never split across two fgets() calls, which simplifies the
669 : : * logic.
670 : : */
671 [ + + + + : 37259 : if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
+ + ]
672 : : {
17 tgl@sss.pgh.pa.us 673 [ + + - + ]: 715 : if (conn && PQputCopyData(conn, buf, buflen) <= 0)
674 : : {
7398 tgl@sss.pgh.pa.us 675 :UIC 0 : OK = false;
676 : 0 : break;
677 : : }
678 : :
1870 heikki.linnakangas@i 679 :GIC 715 : buflen = 0;
680 : : }
681 : : }
682 : : }
683 : :
684 : : /* Check for read error */
7398 tgl@sss.pgh.pa.us 685 [ - + ]: 945 : if (ferror(copystream))
7398 tgl@sss.pgh.pa.us 686 :UIC 0 : OK = false;
687 : :
688 : : /*
689 : : * Terminate data transfer. We can't send an error message if we're using
690 : : * protocol version 2. (libpq no longer supports protocol version 2, but
691 : : * keep the version checks just in case you're using a pre-v14 libpq.so at
692 : : * runtime)
693 : : */
17 tgl@sss.pgh.pa.us 694 [ + + ]:GIC 945 : if (conn &&
695 [ - + + - ]: 641 : PQputCopyEnd(conn,
4578 tgl@sss.pgh.pa.us 696 [ # # ]:UIC 0 : (OK || PQprotocolVersion(conn) < 3) ? NULL :
697 : 0 : _("aborted because of read failure")) <= 0)
7482 698 : 0 : OK = false;
699 : :
5328 alvherre@alvh.no-ip. 700 :GIC 945 : copyin_cleanup:
701 : :
702 : : /*
703 : : * Clear the EOF flag on the stream, in case copying ended due to an EOF
704 : : * signal. This allows an interactive TTY session to perform another COPY
705 : : * FROM STDIN later. (In non-STDIN cases, we're about to close the file
706 : : * anyway, so it doesn't matter.) Although we don't ever test the flag
707 : : * with feof(), some fread() implementations won't read more data if it's
708 : : * set. This also clears the error flag, but we already checked that.
709 : : */
3389 tgl@sss.pgh.pa.us 710 : 945 : clearerr(copystream);
711 : :
712 : : /* Done if we don't have a connection to clean up */
17 713 [ + + ]: 945 : if (!conn)
714 : 304 : return OK;
715 : :
716 : : /*
717 : : * Check command status and return to normal libpq state.
718 : : *
719 : : * We do not want to return with the status still PGRES_COPY_IN: our
720 : : * caller would be unable to distinguish that situation from reaching the
721 : : * next COPY in a command string that happened to contain two consecutive
722 : : * COPY FROM STDIN commands. We keep trying PQputCopyEnd() in the hope
723 : : * it'll work eventually. (What's actually likely to happen is that in
724 : : * attempting to flush the data, libpq will eventually realize that the
725 : : * connection is lost. But that's fine; it will get us out of COPY_IN
726 : : * state, which is what we need.)
727 : : */
4550 728 [ - + ]: 641 : while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
729 : : {
5328 alvherre@alvh.no-ip. 730 :UIC 0 : OK = false;
4550 tgl@sss.pgh.pa.us 731 : 0 : PQclear(*res);
732 : : /* We can't send an error message if we're using protocol version 2 */
4578 733 : 0 : PQputCopyEnd(conn,
734 [ # # ]: 0 : (PQprotocolVersion(conn) < 3) ? NULL :
735 : 0 : _("trying to exit copy mode"));
736 : : }
4550 tgl@sss.pgh.pa.us 737 [ + + ]:GIC 641 : if (PQresultStatus(*res) != PGRES_COMMAND_OK)
738 : : {
2705 peter@eisentraut.org 739 : 161 : pg_log_info("%s", PQerrorMessage(conn));
7482 tgl@sss.pgh.pa.us 740 : 161 : OK = false;
741 : : }
742 : :
743 : 641 : return OK;
744 : : }
|