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