00001
00002
00003
00004
00005
00006
00007
00008 #include "postgres_fe.h"
00009 #include "copy.h"
00010
00011 #include <signal.h>
00012 #include <sys/stat.h>
00013 #ifndef WIN32
00014 #include <unistd.h>
00015 #else
00016 #include <io.h>
00017 #endif
00018
00019 #include "libpq-fe.h"
00020 #include "pqexpbuffer.h"
00021 #include "dumputils.h"
00022
00023 #include "settings.h"
00024 #include "common.h"
00025 #include "prompt.h"
00026 #include "stringutils.h"
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 struct copy_options
00054 {
00055 char *before_tofrom;
00056 char *after_tofrom;
00057 char *file;
00058 bool program;
00059 bool psql_inout;
00060 bool from;
00061 };
00062
00063
00064 static void
00065 free_copy_options(struct copy_options * ptr)
00066 {
00067 if (!ptr)
00068 return;
00069 free(ptr->before_tofrom);
00070 free(ptr->after_tofrom);
00071 free(ptr->file);
00072 free(ptr);
00073 }
00074
00075
00076
00077 static void
00078 xstrcat(char **var, const char *more)
00079 {
00080 char *newvar;
00081
00082 newvar = pg_malloc(strlen(*var) + strlen(more) + 1);
00083 strcpy(newvar, *var);
00084 strcat(newvar, more);
00085 free(*var);
00086 *var = newvar;
00087 }
00088
00089
00090 static struct copy_options *
00091 parse_slash_copy(const char *args)
00092 {
00093 struct copy_options *result;
00094 char *token;
00095 const char *whitespace = " \t\n\r";
00096 char nonstd_backslash = standard_strings() ? 0 : '\\';
00097
00098 if (!args)
00099 {
00100 psql_error("\\copy: arguments required\n");
00101 return NULL;
00102 }
00103
00104 result = pg_malloc0(sizeof(struct copy_options));
00105
00106 result->before_tofrom = pg_strdup("");
00107
00108 token = strtokx(args, whitespace, ".,()", "\"",
00109 0, false, false, pset.encoding);
00110 if (!token)
00111 goto error;
00112
00113
00114 if (pg_strcasecmp(token, "binary") == 0)
00115 {
00116 xstrcat(&result->before_tofrom, token);
00117 token = strtokx(NULL, whitespace, ".,()", "\"",
00118 0, false, false, pset.encoding);
00119 if (!token)
00120 goto error;
00121 }
00122
00123
00124 if (token[0] == '(')
00125 {
00126 int parens = 1;
00127
00128 while (parens > 0)
00129 {
00130 xstrcat(&result->before_tofrom, " ");
00131 xstrcat(&result->before_tofrom, token);
00132 token = strtokx(NULL, whitespace, "()", "\"'",
00133 nonstd_backslash, true, false, pset.encoding);
00134 if (!token)
00135 goto error;
00136 if (token[0] == '(')
00137 parens++;
00138 else if (token[0] == ')')
00139 parens--;
00140 }
00141 }
00142
00143 xstrcat(&result->before_tofrom, " ");
00144 xstrcat(&result->before_tofrom, token);
00145 token = strtokx(NULL, whitespace, ".,()", "\"",
00146 0, false, false, pset.encoding);
00147 if (!token)
00148 goto error;
00149
00150
00151
00152
00153
00154 if (token[0] == '.')
00155 {
00156
00157 xstrcat(&result->before_tofrom, token);
00158 token = strtokx(NULL, whitespace, ".,()", "\"",
00159 0, false, false, pset.encoding);
00160 if (!token)
00161 goto error;
00162 xstrcat(&result->before_tofrom, token);
00163 token = strtokx(NULL, whitespace, ".,()", "\"",
00164 0, false, false, pset.encoding);
00165 if (!token)
00166 goto error;
00167 }
00168
00169 if (token[0] == '(')
00170 {
00171
00172 for (;;)
00173 {
00174 xstrcat(&result->before_tofrom, " ");
00175 xstrcat(&result->before_tofrom, token);
00176 token = strtokx(NULL, whitespace, "()", "\"",
00177 0, false, false, pset.encoding);
00178 if (!token)
00179 goto error;
00180 if (token[0] == ')')
00181 break;
00182 }
00183 xstrcat(&result->before_tofrom, " ");
00184 xstrcat(&result->before_tofrom, token);
00185 token = strtokx(NULL, whitespace, ".,()", "\"",
00186 0, false, false, pset.encoding);
00187 if (!token)
00188 goto error;
00189 }
00190
00191 if (pg_strcasecmp(token, "from") == 0)
00192 result->from = true;
00193 else if (pg_strcasecmp(token, "to") == 0)
00194 result->from = false;
00195 else
00196 goto error;
00197
00198
00199 token = strtokx(NULL, whitespace, NULL, "'",
00200 0, false, false, pset.encoding);
00201 if (!token)
00202 goto error;
00203
00204 if (pg_strcasecmp(token, "program") == 0)
00205 {
00206 int toklen;
00207
00208 token = strtokx(NULL, whitespace, NULL, "'",
00209 0, false, false, pset.encoding);
00210 if (!token)
00211 goto error;
00212
00213
00214
00215
00216
00217 toklen = strlen(token);
00218 if (token[0] != '\'' || toklen < 2 || token[toklen - 1] != '\'')
00219 goto error;
00220
00221 strip_quotes(token, '\'', 0, pset.encoding);
00222
00223 result->program = true;
00224 result->file = pg_strdup(token);
00225 }
00226 else if (pg_strcasecmp(token, "stdin") == 0 ||
00227 pg_strcasecmp(token, "stdout") == 0)
00228 {
00229 result->file = NULL;
00230 }
00231 else if (pg_strcasecmp(token, "pstdin") == 0 ||
00232 pg_strcasecmp(token, "pstdout") == 0)
00233 {
00234 result->psql_inout = true;
00235 result->file = NULL;
00236 }
00237 else
00238 {
00239
00240 strip_quotes(token, '\'', 0, pset.encoding);
00241 result->file = pg_strdup(token);
00242 expand_tilde(&result->file);
00243 }
00244
00245
00246 token = strtokx(NULL, "", NULL, NULL,
00247 0, false, false, pset.encoding);
00248 if (token)
00249 result->after_tofrom = pg_strdup(token);
00250
00251 return result;
00252
00253 error:
00254 if (token)
00255 psql_error("\\copy: parse error at \"%s\"\n", token);
00256 else
00257 psql_error("\\copy: parse error at end of line\n");
00258 free_copy_options(result);
00259
00260 return NULL;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 bool
00270 do_copy(const char *args)
00271 {
00272 PQExpBufferData query;
00273 FILE *copystream;
00274 FILE *save_file;
00275 FILE **override_file;
00276 struct copy_options *options;
00277 bool success;
00278 struct stat st;
00279
00280
00281 options = parse_slash_copy(args);
00282
00283 if (!options)
00284 return false;
00285
00286
00287 if (options->file && !options->program)
00288 canonicalize_path(options->file);
00289
00290 if (options->from)
00291 {
00292 override_file = &pset.cur_cmd_source;
00293
00294 if (options->file)
00295 {
00296 if (options->program)
00297 {
00298 fflush(stdout);
00299 fflush(stderr);
00300 errno = 0;
00301 copystream = popen(options->file, PG_BINARY_R);
00302 }
00303 else
00304 copystream = fopen(options->file, PG_BINARY_R);
00305 }
00306 else if (!options->psql_inout)
00307 copystream = pset.cur_cmd_source;
00308 else
00309 copystream = stdin;
00310 }
00311 else
00312 {
00313 override_file = &pset.queryFout;
00314
00315 if (options->file)
00316 {
00317 if (options->program)
00318 {
00319 fflush(stdout);
00320 fflush(stderr);
00321 errno = 0;
00322 #ifndef WIN32
00323 pqsignal(SIGPIPE, SIG_IGN);
00324 #endif
00325 copystream = popen(options->file, PG_BINARY_W);
00326 }
00327 else
00328 copystream = fopen(options->file, PG_BINARY_W);
00329 }
00330 else if (!options->psql_inout)
00331 copystream = pset.queryFout;
00332 else
00333 copystream = stdout;
00334 }
00335
00336 if (!copystream)
00337 {
00338 if (options->program)
00339 psql_error("could not execute command \"%s\": %s\n",
00340 options->file, strerror(errno));
00341 else
00342 psql_error("%s: %s\n",
00343 options->file, strerror(errno));
00344 free_copy_options(options);
00345 return false;
00346 }
00347
00348 if (!options->program)
00349 {
00350
00351 fstat(fileno(copystream), &st);
00352 if (S_ISDIR(st.st_mode))
00353 {
00354 fclose(copystream);
00355 psql_error("%s: cannot copy from/to a directory\n",
00356 options->file);
00357 free_copy_options(options);
00358 return false;
00359 }
00360 }
00361
00362
00363 initPQExpBuffer(&query);
00364 printfPQExpBuffer(&query, "COPY ");
00365 appendPQExpBufferStr(&query, options->before_tofrom);
00366 if (options->from)
00367 appendPQExpBuffer(&query, " FROM STDIN ");
00368 else
00369 appendPQExpBuffer(&query, " TO STDOUT ");
00370 if (options->after_tofrom)
00371 appendPQExpBufferStr(&query, options->after_tofrom);
00372
00373
00374 save_file = *override_file;
00375 *override_file = copystream;
00376 success = SendQuery(query.data);
00377 *override_file = save_file;
00378 termPQExpBuffer(&query);
00379
00380 if (options->file != NULL)
00381 {
00382 if (options->program)
00383 {
00384 int pclose_rc = pclose(copystream);
00385 if (pclose_rc != 0)
00386 {
00387 if (pclose_rc < 0)
00388 psql_error("could not close pipe to external command: %s\n",
00389 strerror(errno));
00390 else
00391 {
00392 char *reason = wait_result_to_str(pclose_rc);
00393 psql_error("%s: %s\n", options->file,
00394 reason ? reason : "");
00395 if (reason)
00396 free(reason);
00397 }
00398 success = false;
00399 }
00400 #ifndef WIN32
00401 pqsignal(SIGPIPE, SIG_DFL);
00402 #endif
00403 }
00404 else
00405 {
00406 if (fclose(copystream) != 0)
00407 {
00408 psql_error("%s: %s\n", options->file, strerror(errno));
00409 success = false;
00410 }
00411 }
00412 }
00413 free_copy_options(options);
00414 return success;
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 bool
00436 handleCopyOut(PGconn *conn, FILE *copystream)
00437 {
00438 bool OK = true;
00439 char *buf;
00440 int ret;
00441 PGresult *res;
00442
00443 for (;;)
00444 {
00445 ret = PQgetCopyData(conn, &buf, 0);
00446
00447 if (ret < 0)
00448 break;
00449
00450 if (buf)
00451 {
00452 if (fwrite(buf, 1, ret, copystream) != ret)
00453 {
00454 if (OK)
00455 psql_error("could not write COPY data: %s\n",
00456 strerror(errno));
00457 OK = false;
00458 }
00459 PQfreemem(buf);
00460 }
00461 }
00462
00463 if (OK && fflush(copystream))
00464 {
00465 psql_error("could not write COPY data: %s\n",
00466 strerror(errno));
00467 OK = false;
00468 }
00469
00470 if (ret == -2)
00471 {
00472 psql_error("COPY data transfer failed: %s", PQerrorMessage(conn));
00473 OK = false;
00474 }
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 while (res = PQgetResult(conn), PQresultStatus(res) == PGRES_COPY_OUT)
00494 {
00495 OK = false;
00496 PQclear(res);
00497
00498 PQexec(conn, "-- clear PGRES_COPY_OUT state");
00499 }
00500 if (PQresultStatus(res) != PGRES_COMMAND_OK)
00501 {
00502 psql_error("%s", PQerrorMessage(conn));
00503 OK = false;
00504 }
00505 PQclear(res);
00506
00507 return OK;
00508 }
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523 #define COPYBUFSIZ 8192
00524
00525 bool
00526 handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
00527 {
00528 bool OK;
00529 const char *prompt;
00530 char buf[COPYBUFSIZ];
00531 PGresult *res;
00532
00533
00534
00535
00536
00537 if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
00538 {
00539
00540
00541
00542 PQputCopyEnd(conn, _("canceled by user"));
00543
00544 OK = false;
00545 goto copyin_cleanup;
00546 }
00547
00548
00549 if (isatty(fileno(copystream)))
00550 {
00551 if (!pset.quiet)
00552 puts(_("Enter data to be copied followed by a newline.\n"
00553 "End with a backslash and a period on a line by itself."));
00554 prompt = get_prompt(PROMPT_COPY);
00555 }
00556 else
00557 prompt = NULL;
00558
00559 OK = true;
00560
00561 if (isbinary)
00562 {
00563
00564 if (prompt)
00565 {
00566 fputs(prompt, stdout);
00567 fflush(stdout);
00568 }
00569
00570 for (;;)
00571 {
00572 int buflen;
00573
00574
00575 sigint_interrupt_enabled = true;
00576
00577 buflen = fread(buf, 1, COPYBUFSIZ, copystream);
00578
00579 sigint_interrupt_enabled = false;
00580
00581 if (buflen <= 0)
00582 break;
00583
00584 if (PQputCopyData(conn, buf, buflen) <= 0)
00585 {
00586 OK = false;
00587 break;
00588 }
00589 }
00590 }
00591 else
00592 {
00593 bool copydone = false;
00594
00595 while (!copydone)
00596 {
00597 bool firstload;
00598 bool linedone;
00599
00600 if (prompt)
00601 {
00602 fputs(prompt, stdout);
00603 fflush(stdout);
00604 }
00605
00606 firstload = true;
00607 linedone = false;
00608
00609 while (!linedone)
00610 {
00611 int linelen;
00612 char *fgresult;
00613
00614
00615 sigint_interrupt_enabled = true;
00616
00617 fgresult = fgets(buf, sizeof(buf), copystream);
00618
00619 sigint_interrupt_enabled = false;
00620
00621 if (!fgresult)
00622 {
00623 copydone = true;
00624 break;
00625 }
00626
00627 linelen = strlen(buf);
00628
00629
00630 if (linelen > 0 && buf[linelen - 1] == '\n')
00631 linedone = true;
00632
00633
00634 if (firstload)
00635 {
00636 if (strcmp(buf, "\\.\n") == 0 ||
00637 strcmp(buf, "\\.\r\n") == 0)
00638 {
00639 copydone = true;
00640 break;
00641 }
00642
00643 firstload = false;
00644 }
00645
00646 if (PQputCopyData(conn, buf, linelen) <= 0)
00647 {
00648 OK = false;
00649 copydone = true;
00650 break;
00651 }
00652 }
00653
00654 if (copystream == pset.cur_cmd_source)
00655 pset.lineno++;
00656 }
00657 }
00658
00659
00660 if (ferror(copystream))
00661 OK = false;
00662
00663
00664 if (PQputCopyEnd(conn,
00665 OK ? NULL : _("aborted because of read failure")) <= 0)
00666 OK = false;
00667
00668 copyin_cleanup:
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680 while (res = PQgetResult(conn), PQresultStatus(res) == PGRES_COPY_IN)
00681 {
00682 OK = false;
00683 PQclear(res);
00684
00685 PQputCopyEnd(pset.db, _("trying to exit copy mode"));
00686 }
00687 if (PQresultStatus(res) != PGRES_COMMAND_OK)
00688 {
00689 psql_error("%s", PQerrorMessage(conn));
00690 OK = false;
00691 }
00692 PQclear(res);
00693
00694 return OK;
00695 }