Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * psql - the PostgreSQL interactive terminal
3 : : *
4 : : * Copyright (c) 2000-2025, 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 | pstdout | 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
2999 tgl@sss.pgh.pa.us 65 :GIC 83 : free_copy_options(struct copy_options *ptr)
66 : : {
9438 bruce@momjian.us 67 [ - + ]: 83 : if (!ptr)
9438 bruce@momjian.us 68 :UIC 0 : return;
5831 tgl@sss.pgh.pa.us 69 :GIC 83 : free(ptr->before_tofrom);
70 : 83 : free(ptr->after_tofrom);
9438 bruce@momjian.us 71 : 83 : free(ptr->file);
72 : 83 : free(ptr);
73 : : }
74 : :
75 : :
76 : : /* concatenate "more" onto "var", freeing the original value of *var */
77 : : static void
8358 tgl@sss.pgh.pa.us 78 : 508 : xstrcat(char **var, const char *more)
79 : : {
80 : : char *newvar;
81 : :
4337 82 : 508 : newvar = psprintf("%s%s", *var, more);
8358 83 : 508 : free(*var);
84 : 508 : *var = newvar;
85 : 508 : }
86 : :
87 : :
88 : : static struct copy_options *
9363 peter_e@gmx.net 89 : 83 : parse_slash_copy(const char *args)
90 : : {
91 : : struct copy_options *result;
92 : : char *token;
8358 tgl@sss.pgh.pa.us 93 : 83 : const char *whitespace = " \t\n\r";
7037 94 [ + - ]: 83 : char nonstd_backslash = standard_strings() ? 0 : '\\';
95 : :
5831 96 [ - + ]: 83 : if (!args)
97 : : {
2350 peter@eisentraut.org 98 :UIC 0 : pg_log_error("\\copy: arguments required");
9274 peter_e@gmx.net 99 : 0 : return NULL;
100 : : }
101 : :
4722 tgl@sss.pgh.pa.us 102 :GIC 83 : result = pg_malloc0(sizeof(struct copy_options));
103 : :
2999 104 : 83 : result->before_tofrom = pg_strdup(""); /* initialize for appending */
105 : :
5831 106 : 83 : token = strtokx(args, whitespace, ".,()", "\"",
107 : : 0, false, false, pset.encoding);
9438 bruce@momjian.us 108 [ - + ]: 83 : if (!token)
8358 tgl@sss.pgh.pa.us 109 :UIC 0 : goto error;
110 : :
111 : : /* The following can be removed when we drop 7.3 syntax support */
7792 tgl@sss.pgh.pa.us 112 [ - + ]:GIC 83 : if (pg_strcasecmp(token, "binary") == 0)
113 : : {
5831 tgl@sss.pgh.pa.us 114 :UIC 0 : xstrcat(&result->before_tofrom, token);
8358 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 */
6947 tgl@sss.pgh.pa.us 122 [ + + ]:GIC 83 : if (token[0] == '(')
123 : : {
6912 bruce@momjian.us 124 : 12 : int parens = 1;
125 : :
6947 tgl@sss.pgh.pa.us 126 [ + + ]: 183 : while (parens > 0)
127 : : {
5831 128 : 171 : xstrcat(&result->before_tofrom, " ");
129 : 171 : xstrcat(&result->before_tofrom, token);
130 : 171 : token = strtokx(NULL, whitespace, "()", "\"'",
131 : : nonstd_backslash, true, false, pset.encoding);
6947 132 [ - + ]: 171 : if (!token)
6947 tgl@sss.pgh.pa.us 133 :UIC 0 : goto error;
6947 tgl@sss.pgh.pa.us 134 [ + + ]:GIC 171 : if (token[0] == '(')
135 : 9 : parens++;
136 [ + + ]: 162 : else if (token[0] == ')')
137 : 21 : parens--;
138 : : }
139 : : }
140 : :
5831 141 : 83 : xstrcat(&result->before_tofrom, " ");
142 : 83 : xstrcat(&result->before_tofrom, token);
8358 143 : 83 : token = strtokx(NULL, whitespace, ".,()", "\"",
144 : : 0, false, false, pset.encoding);
145 [ - + ]: 83 : if (!token)
8358 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 : : */
8358 tgl@sss.pgh.pa.us 152 [ - + ]:GIC 83 : if (token[0] == '.')
153 : : {
154 : : /* handle schema . table */
5831 tgl@sss.pgh.pa.us 155 :UIC 0 : xstrcat(&result->before_tofrom, token);
8358 156 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
157 : : 0, false, false, pset.encoding);
9438 bruce@momjian.us 158 [ # # ]: 0 : if (!token)
8358 tgl@sss.pgh.pa.us 159 : 0 : goto error;
5831 160 : 0 : xstrcat(&result->before_tofrom, token);
8358 161 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
162 : : 0, false, false, pset.encoding);
163 [ # # ]: 0 : if (!token)
164 : 0 : goto error;
165 : : }
166 : :
8358 tgl@sss.pgh.pa.us 167 [ - + ]:GIC 83 : if (token[0] == '(')
168 : : {
169 : : /* handle parenthesized column list */
170 : : for (;;)
171 : : {
5831 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);
8358 176 [ # # ]: 0 : if (!token)
177 : 0 : goto error;
178 [ # # ]: 0 : if (token[0] == ')')
179 : 0 : break;
180 : : }
5831 181 : 0 : xstrcat(&result->before_tofrom, " ");
182 : 0 : xstrcat(&result->before_tofrom, token);
8358 183 : 0 : token = strtokx(NULL, whitespace, ".,()", "\"",
184 : : 0, false, false, pset.encoding);
185 [ # # ]: 0 : if (!token)
186 : 0 : goto error;
187 : : }
188 : :
7792 tgl@sss.pgh.pa.us 189 [ + + ]:GIC 83 : if (pg_strcasecmp(token, "from") == 0)
8358 190 : 52 : result->from = true;
7792 191 [ + - ]: 31 : else if (pg_strcasecmp(token, "to") == 0)
8358 192 : 31 : result->from = false;
193 : : else
8358 tgl@sss.pgh.pa.us 194 :UIC 0 : goto error;
195 : :
196 : : /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
4379 bruce@momjian.us 197 :GIC 83 : token = strtokx(NULL, whitespace, ";", "'",
198 : : 0, false, false, pset.encoding);
8358 tgl@sss.pgh.pa.us 199 [ - + ]: 83 : if (!token)
8358 tgl@sss.pgh.pa.us 200 :UIC 0 : goto error;
201 : :
4574 heikki.linnakangas@i 202 [ - + ]:GIC 83 : if (pg_strcasecmp(token, "program") == 0)
203 : : {
204 : : int toklen;
205 : :
4379 bruce@momjian.us 206 :UIC 0 : token = strtokx(NULL, whitespace, ";", "'",
207 : : 0, false, false, pset.encoding);
4574 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 : : }
4574 heikki.linnakangas@i 224 [ + + + + ]:GIC 160 : else if (pg_strcasecmp(token, "stdin") == 0 ||
225 : 77 : pg_strcasecmp(token, "stdout") == 0)
226 : : {
7900 tgl@sss.pgh.pa.us 227 : 37 : result->file = NULL;
228 : : }
7792 229 [ + - - + ]: 92 : else if (pg_strcasecmp(token, "pstdin") == 0 ||
7678 bruce@momjian.us 230 : 46 : pg_strcasecmp(token, "pstdout") == 0)
231 : : {
7817 bruce@momjian.us 232 :UIC 0 : result->psql_inout = true;
8358 tgl@sss.pgh.pa.us 233 : 0 : result->file = NULL;
234 : : }
235 : : else
236 : : {
237 : : /* filename can be optionally quoted */
4574 heikki.linnakangas@i 238 :GIC 46 : strip_quotes(token, '\'', 0, pset.encoding);
7895 neilc@samurai.com 239 : 46 : result->file = pg_strdup(token);
7900 tgl@sss.pgh.pa.us 240 : 46 : expand_tilde(&result->file);
241 : : }
242 : :
243 : : /* Collect the rest of the line (COPY options) */
5831 244 : 83 : token = strtokx(NULL, "", NULL, NULL,
245 : : 0, false, false, pset.encoding);
8358 246 [ + + ]: 83 : if (token)
5831 247 : 27 : result->after_tofrom = pg_strdup(token);
248 : :
8358 249 : 83 : return result;
250 : :
8358 tgl@sss.pgh.pa.us 251 :UIC 0 : error:
252 [ # # ]: 0 : if (token)
2350 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");
8358 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
9363 peter_e@gmx.net 268 :GIC 83 : 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 : 83 : options = parse_slash_copy(args);
277 : :
9438 bruce@momjian.us 278 [ - + ]: 83 : if (!options)
9438 bruce@momjian.us 279 :UIC 0 : return false;
280 : :
281 : : /* prepare to read or write the target file */
4574 heikki.linnakangas@i 282 [ + + + - ]:GIC 83 : if (options->file && !options->program)
220 tgl@sss.pgh.pa.us 283 : 46 : canonicalize_path_enc(options->file, pset.encoding);
284 : :
9438 bruce@momjian.us 285 [ + + ]: 83 : if (options->from)
286 : : {
9278 287 [ + + ]: 52 : if (options->file)
288 : : {
4574 heikki.linnakangas@i 289 [ - + ]: 46 : if (options->program)
290 : : {
1104 tgl@sss.pgh.pa.us 291 :UIC 0 : fflush(NULL);
4574 heikki.linnakangas@i 292 : 0 : errno = 0;
293 : 0 : copystream = popen(options->file, PG_BINARY_R);
294 : : }
295 : : else
4574 heikki.linnakangas@i 296 :GIC 46 : copystream = fopen(options->file, PG_BINARY_R);
297 : : }
7817 bruce@momjian.us 298 [ + - ]: 6 : else if (!options->psql_inout)
7678 299 : 6 : copystream = pset.cur_cmd_source;
300 : : else
7678 bruce@momjian.us 301 :UIC 0 : copystream = stdin;
302 : : }
303 : : else
304 : : {
9278 bruce@momjian.us 305 [ - + ]:GIC 31 : if (options->file)
306 : : {
4574 heikki.linnakangas@i 307 [ # # ]:UIC 0 : if (options->program)
308 : : {
1104 tgl@sss.pgh.pa.us 309 : 0 : fflush(NULL);
3565 310 : 0 : disable_sigpipe_trap();
1104 311 : 0 : errno = 0;
4574 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 : : }
7817 bruce@momjian.us 317 [ + - ]:GIC 31 : else if (!options->psql_inout)
7678 318 : 31 : copystream = pset.queryFout;
319 : : else
9278 bruce@momjian.us 320 :UIC 0 : copystream = stdout;
321 : : }
322 : :
9438 bruce@momjian.us 323 [ + + ]:GIC 83 : if (!copystream)
324 : : {
4574 heikki.linnakangas@i 325 [ - + ]: 5 : if (options->program)
2350 peter@eisentraut.org 326 :UIC 0 : pg_log_error("could not execute command \"%s\": %m",
327 : : options->file);
328 : : else
2350 peter@eisentraut.org 329 :GIC 5 : pg_log_error("%s: %m",
330 : : options->file);
9438 bruce@momjian.us 331 : 5 : free_copy_options(options);
332 : 5 : return false;
333 : : }
334 : :
4574 heikki.linnakangas@i 335 [ + - ]: 78 : if (!options->program)
336 : : {
337 : : struct stat st;
338 : : int result;
339 : :
340 : : /* make sure the specified file is not a directory */
4207 sfrost@snowman.net 341 [ - + ]: 78 : if ((result = fstat(fileno(copystream), &st)) < 0)
2350 peter@eisentraut.org 342 :UIC 0 : pg_log_error("could not stat file \"%s\": %m",
343 : : options->file);
344 : :
4207 sfrost@snowman.net 345 [ + - - + ]:GIC 78 : if (result == 0 && S_ISDIR(st.st_mode))
2350 peter@eisentraut.org 346 :UIC 0 : pg_log_error("%s: cannot copy from/to a directory",
347 : : options->file);
348 : :
4207 sfrost@snowman.net 349 [ + - - + ]:GIC 78 : if (result < 0 || S_ISDIR(st.st_mode))
350 : : {
4207 sfrost@snowman.net 351 :UIC 0 : fclose(copystream);
4574 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 */
5831 tgl@sss.pgh.pa.us 358 :GIC 78 : initPQExpBuffer(&query);
359 : 78 : printfPQExpBuffer(&query, "COPY ");
360 : 78 : appendPQExpBufferStr(&query, options->before_tofrom);
361 [ + + ]: 78 : if (options->from)
4310 heikki.linnakangas@i 362 : 47 : appendPQExpBufferStr(&query, " FROM STDIN ");
363 : : else
364 : 31 : appendPQExpBufferStr(&query, " TO STDOUT ");
5831 tgl@sss.pgh.pa.us 365 [ + + ]: 78 : if (options->after_tofrom)
366 : 24 : appendPQExpBufferStr(&query, options->after_tofrom);
367 : :
368 : : /* run it like a user command, but with copystream as data source/sink */
4198 369 : 78 : pset.copyStream = copystream;
4973 alvherre@alvh.no-ip. 370 : 78 : success = SendQuery(query.data);
4198 tgl@sss.pgh.pa.us 371 : 78 : pset.copyStream = NULL;
8536 peter_e@gmx.net 372 : 78 : termPQExpBuffer(&query);
373 : :
7678 bruce@momjian.us 374 [ + + ]: 78 : if (options->file != NULL)
375 : : {
4574 heikki.linnakangas@i 376 [ - + ]: 41 : if (options->program)
377 : : {
4483 bruce@momjian.us 378 :UIC 0 : int pclose_rc = pclose(copystream);
379 : :
4574 heikki.linnakangas@i 380 [ # # ]: 0 : if (pclose_rc != 0)
381 : : {
382 [ # # ]: 0 : if (pclose_rc < 0)
2350 peter@eisentraut.org 383 : 0 : pg_log_error("could not close pipe to external command: %m");
384 : : else
385 : : {
4483 bruce@momjian.us 386 : 0 : char *reason = wait_result_to_str(pclose_rc);
387 : :
2350 peter@eisentraut.org 388 [ # # ]: 0 : pg_log_error("%s: %s", options->file,
389 : : reason ? reason : "");
1178 390 : 0 : free(reason);
391 : : }
4574 heikki.linnakangas@i 392 : 0 : success = false;
393 : : }
884 tgl@sss.pgh.pa.us 394 : 0 : SetShellResultVariables(pclose_rc);
3565 395 : 0 : restore_sigpipe_trap();
396 : : }
397 : : else
398 : : {
4574 heikki.linnakangas@i 399 [ - + ]:GIC 41 : if (fclose(copystream) != 0)
400 : : {
2350 peter@eisentraut.org 401 :UIC 0 : pg_log_error("%s: %m", options->file);
4574 heikki.linnakangas@i 402 : 0 : success = false;
403 : : }
404 : : }
405 : : }
9438 bruce@momjian.us 406 :GIC 78 : free_copy_options(options);
407 : 78 : 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
4195 tgl@sss.pgh.pa.us 434 : 273 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
435 : : {
6912 bruce@momjian.us 436 : 273 : bool OK = true;
437 : : char *buf;
438 : : int ret;
439 : :
440 : : for (;;)
441 : : {
7127 tgl@sss.pgh.pa.us 442 : 1193 : ret = PQgetCopyData(conn, &buf, 0);
443 : :
444 [ + + ]: 1193 : if (ret < 0)
4223 445 : 273 : break; /* done or server/connection error */
446 : :
7127 447 [ + - ]: 920 : if (buf)
448 : : {
2415 449 [ + - + - : 920 : if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret)
- + ]
450 : : {
2350 peter@eisentraut.org 451 :UIC 0 : pg_log_error("could not write COPY data: %m");
452 : : /* complain only once, keep reading data from server */
7043 tgl@sss.pgh.pa.us 453 : 0 : OK = false;
454 : : }
7127 tgl@sss.pgh.pa.us 455 :GIC 920 : PQfreemem(buf);
456 : : }
457 : : }
458 : :
2415 459 [ + - + - : 273 : if (OK && copystream && fflush(copystream))
- + ]
460 : : {
2350 peter@eisentraut.org 461 :UIC 0 : pg_log_error("could not write COPY data: %m");
7043 tgl@sss.pgh.pa.us 462 : 0 : OK = false;
463 : : }
464 : :
7127 tgl@sss.pgh.pa.us 465 [ - + ]:GIC 273 : if (ret == -2)
466 : : {
2350 peter@eisentraut.org 467 :UIC 0 : pg_log_error("COPY data transfer failed: %s", PQerrorMessage(conn));
7127 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 : : */
4195 tgl@sss.pgh.pa.us 483 :GIC 273 : *res = PQgetResult(conn);
484 [ + + ]: 273 : if (PQresultStatus(*res) != PGRES_COMMAND_OK)
485 : : {
2350 peter@eisentraut.org 486 : 1 : pg_log_info("%s", PQerrorMessage(conn));
7127 tgl@sss.pgh.pa.us 487 : 1 : OK = false;
488 : : }
489 : :
490 : 273 : 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.
499 : : * copystream is the file stream to read the data from.
500 : : * isbinary can be set from PQbinaryTuples().
501 : : * The final status for the COPY is returned into *res (but note
502 : : * we already reported the error, if it's not a success result).
503 : : *
504 : : * result is true if successful, false if not.
505 : : */
506 : :
507 : : /* read chunk size for COPY IN - size is not critical */
508 : : #define COPYBUFSIZ 8192
509 : :
510 : : bool
4195 511 : 469 : handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res)
512 : : {
513 : : bool OK;
514 : : char buf[COPYBUFSIZ];
515 : : bool showprompt;
516 : :
517 : : /*
518 : : * Establish longjmp destination for exiting from wait-for-input. (This is
519 : : * only effective while sigint_interrupt_enabled is TRUE.)
520 : : */
7024 521 [ - + ]: 469 : if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
522 : : {
523 : : /* got here with longjmp */
524 : :
525 : : /* Terminate data transfer */
4223 tgl@sss.pgh.pa.us 526 :UIC 0 : PQputCopyEnd(conn,
527 [ # # ]: 0 : (PQprotocolVersion(conn) < 3) ? NULL :
528 : 0 : _("canceled by user"));
529 : :
4973 alvherre@alvh.no-ip. 530 : 0 : OK = false;
531 : 0 : goto copyin_cleanup;
532 : : }
533 : :
534 : : /* Prompt if interactive input */
7900 tgl@sss.pgh.pa.us 535 [ - + ]:GIC 469 : if (isatty(fileno(copystream)))
536 : : {
4022 andres@anarazel.de 537 :UIC 0 : showprompt = true;
6948 tgl@sss.pgh.pa.us 538 [ # # ]: 0 : if (!pset.quiet)
7501 bruce@momjian.us 539 : 0 : puts(_("Enter data to be copied followed by a newline.\n"
540 : : "End with a backslash and a period on a line by itself, or an EOF signal."));
541 : : }
542 : : else
4005 andres@anarazel.de 543 :GIC 469 : showprompt = false;
544 : :
7024 tgl@sss.pgh.pa.us 545 : 469 : OK = true;
546 : :
7043 547 [ - + ]: 469 : if (isbinary)
548 : : {
549 : : /* interactive input probably silly, but give one prompt anyway */
4022 andres@anarazel.de 550 [ # # ]:UIC 0 : if (showprompt)
551 : : {
3082 tgl@sss.pgh.pa.us 552 : 0 : const char *prompt = get_prompt(PROMPT_COPY, NULL);
553 : :
9438 bruce@momjian.us 554 : 0 : fputs(prompt, stdout);
555 : 0 : fflush(stdout);
556 : : }
557 : :
558 : : for (;;)
7043 tgl@sss.pgh.pa.us 559 : 0 : {
560 : : int buflen;
561 : :
562 : : /* enable longjmp while waiting for input */
7024 563 : 0 : sigint_interrupt_enabled = true;
564 : :
565 : 0 : buflen = fread(buf, 1, COPYBUFSIZ, copystream);
566 : :
567 : 0 : sigint_interrupt_enabled = false;
568 : :
569 [ # # ]: 0 : if (buflen <= 0)
570 : 0 : break;
571 : :
7043 572 [ # # ]: 0 : if (PQputCopyData(conn, buf, buflen) <= 0)
573 : : {
574 : 0 : OK = false;
9438 bruce@momjian.us 575 : 0 : break;
576 : : }
577 : : }
578 : : }
579 : : else
580 : : {
7043 tgl@sss.pgh.pa.us 581 :GIC 469 : bool copydone = false;
582 : : int buflen;
1515 heikki.linnakangas@i 583 : 469 : bool at_line_begin = true;
584 : :
585 : : /*
586 : : * In text mode, we have to read the input one line at a time, so that
587 : : * we can stop reading at the EOF marker (\.). We mustn't read beyond
588 : : * the EOF marker, because if the data was inlined in a SQL script, we
589 : : * would eat up the commands after the EOF marker.
590 : : */
591 : 469 : buflen = 0;
7043 tgl@sss.pgh.pa.us 592 [ + + ]: 136764 : while (!copydone)
593 : : {
594 : : char *fgresult;
595 : :
1515 heikki.linnakangas@i 596 [ + + - + ]: 136295 : if (at_line_begin && showprompt)
597 : : {
3082 tgl@sss.pgh.pa.us 598 :UIC 0 : const char *prompt = get_prompt(PROMPT_COPY, NULL);
599 : :
7043 600 : 0 : fputs(prompt, stdout);
601 : 0 : fflush(stdout);
602 : : }
603 : :
604 : : /* enable longjmp while waiting for input */
1515 heikki.linnakangas@i 605 :GIC 136295 : sigint_interrupt_enabled = true;
606 : :
607 : 136295 : fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
608 : :
609 : 136295 : sigint_interrupt_enabled = false;
610 : :
611 [ + + ]: 136295 : if (!fgresult)
612 : 40 : copydone = true;
613 : : else
614 : : {
615 : : int linelen;
616 : :
617 : 136255 : linelen = strlen(fgresult);
618 : 136255 : buflen += linelen;
619 : :
620 : : /* current line is done? */
621 [ + + ]: 136255 : if (buf[buflen - 1] == '\n')
622 : : {
623 : : /*
624 : : * When at the beginning of the line and the data is
625 : : * inlined, check for EOF marker. If the marker is found,
626 : : * we must stop at this point. If not, the \. line can be
627 : : * sent to the server, and we let it decide whether it's
628 : : * an EOF or not depending on the format: in TEXT mode, \.
629 : : * will be interpreted as an EOF, in CSV, it will not.
630 : : */
341 tgl@sss.pgh.pa.us 631 [ + + + + ]: 136192 : if (at_line_begin && copystream == pset.cur_cmd_source)
632 : : {
1515 heikki.linnakangas@i 633 [ + + + + : 101802 : if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
+ + ]
634 [ - + ]: 936 : (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
635 : : {
636 : 429 : copydone = true;
637 : :
638 : : /*
639 : : * Remove the EOF marker from the data sent. In
640 : : * CSV mode, the EOF marker must be removed,
641 : : * otherwise it would be interpreted by the server
642 : : * as valid data.
643 : : */
341 tgl@sss.pgh.pa.us 644 : 429 : *fgresult = '\0';
645 : 429 : buflen -= linelen;
646 : : }
647 : : }
648 : :
1515 heikki.linnakangas@i 649 [ + + ]: 136192 : if (copystream == pset.cur_cmd_source)
650 : : {
651 : 101812 : pset.lineno++;
652 : 101812 : pset.stmt_lineno++;
653 : : }
654 : 136192 : at_line_begin = true;
655 : : }
656 : : else
657 : 63 : at_line_begin = false;
658 : : }
659 : :
660 : : /*
661 : : * If the buffer is full, or we've reached the EOF, flush it.
662 : : *
663 : : * Make sure there's always space for four more bytes in the
664 : : * buffer, plus a NUL terminator. That way, an EOF marker is
665 : : * never split across two fgets() calls, which simplifies the
666 : : * logic.
667 : : */
668 [ + + + + : 136295 : if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
+ + ]
669 : : {
670 [ - + ]: 611 : if (PQputCopyData(conn, buf, buflen) <= 0)
671 : : {
7043 tgl@sss.pgh.pa.us 672 :UIC 0 : OK = false;
673 : 0 : break;
674 : : }
675 : :
1515 heikki.linnakangas@i 676 :GIC 611 : buflen = 0;
677 : : }
678 : : }
679 : : }
680 : :
681 : : /* Check for read error */
7043 tgl@sss.pgh.pa.us 682 [ - + ]: 469 : if (ferror(copystream))
7043 tgl@sss.pgh.pa.us 683 :UIC 0 : OK = false;
684 : :
685 : : /*
686 : : * Terminate data transfer. We can't send an error message if we're using
687 : : * protocol version 2. (libpq no longer supports protocol version 2, but
688 : : * keep the version checks just in case you're using a pre-v14 libpq.so at
689 : : * runtime)
690 : : */
7127 tgl@sss.pgh.pa.us 691 [ - + + - ]:GIC 469 : if (PQputCopyEnd(conn,
4223 tgl@sss.pgh.pa.us 692 [ # # ]:UIC 0 : (OK || PQprotocolVersion(conn) < 3) ? NULL :
693 : 0 : _("aborted because of read failure")) <= 0)
7127 694 : 0 : OK = false;
695 : :
4973 alvherre@alvh.no-ip. 696 :GIC 469 : copyin_cleanup:
697 : :
698 : : /*
699 : : * Clear the EOF flag on the stream, in case copying ended due to an EOF
700 : : * signal. This allows an interactive TTY session to perform another COPY
701 : : * FROM STDIN later. (In non-STDIN cases, we're about to close the file
702 : : * anyway, so it doesn't matter.) Although we don't ever test the flag
703 : : * with feof(), some fread() implementations won't read more data if it's
704 : : * set. This also clears the error flag, but we already checked that.
705 : : */
3034 tgl@sss.pgh.pa.us 706 : 469 : clearerr(copystream);
707 : :
708 : : /*
709 : : * Check command status and return to normal libpq state.
710 : : *
711 : : * We do not want to return with the status still PGRES_COPY_IN: our
712 : : * caller would be unable to distinguish that situation from reaching the
713 : : * next COPY in a command string that happened to contain two consecutive
714 : : * COPY FROM STDIN commands. We keep trying PQputCopyEnd() in the hope
715 : : * it'll work eventually. (What's actually likely to happen is that in
716 : : * attempting to flush the data, libpq will eventually realize that the
717 : : * connection is lost. But that's fine; it will get us out of COPY_IN
718 : : * state, which is what we need.)
719 : : */
4195 720 [ - + ]: 469 : while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
721 : : {
4973 alvherre@alvh.no-ip. 722 :UIC 0 : OK = false;
4195 tgl@sss.pgh.pa.us 723 : 0 : PQclear(*res);
724 : : /* We can't send an error message if we're using protocol version 2 */
4223 725 : 0 : PQputCopyEnd(conn,
726 [ # # ]: 0 : (PQprotocolVersion(conn) < 3) ? NULL :
727 : 0 : _("trying to exit copy mode"));
728 : : }
4195 tgl@sss.pgh.pa.us 729 [ + + ]:GIC 469 : if (PQresultStatus(*res) != PGRES_COMMAND_OK)
730 : : {
2350 peter@eisentraut.org 731 : 104 : pg_log_info("%s", PQerrorMessage(conn));
7127 tgl@sss.pgh.pa.us 732 : 104 : OK = false;
733 : : }
734 : :
735 : 469 : return OK;
736 : : }
|