#include "libpq-fe.h"
Go to the source code of this file.
Functions | |
bool | do_copy (const char *args) |
bool | handleCopyOut (PGconn *conn, FILE *copystream) |
bool | handleCopyIn (PGconn *conn, FILE *copystream, bool isbinary) |
bool do_copy | ( | const char * | args | ) |
Definition at line 270 of file copy.c.
References appendPQExpBuffer(), appendPQExpBufferStr(), canonicalize_path(), _psqlSettings::cur_cmd_source, PQExpBufferData::data, free, free_copy_options(), initPQExpBuffer(), NULL, parse_slash_copy(), PG_BINARY_R, PG_BINARY_W, pqsignal(), printfPQExpBuffer(), pset, psql_error(), _psqlSettings::queryFout, SendQuery(), SIG_DFL, SIG_IGN, SIGPIPE, strerror(), termPQExpBuffer(), and wait_result_to_str().
Referenced by exec_command().
{ PQExpBufferData query; FILE *copystream; FILE *save_file; FILE **override_file; struct copy_options *options; bool success; struct stat st; /* parse options */ options = parse_slash_copy(args); if (!options) return false; /* prepare to read or write the target file */ if (options->file && !options->program) canonicalize_path(options->file); if (options->from) { override_file = &pset.cur_cmd_source; if (options->file) { if (options->program) { fflush(stdout); fflush(stderr); errno = 0; copystream = popen(options->file, PG_BINARY_R); } else copystream = fopen(options->file, PG_BINARY_R); } else if (!options->psql_inout) copystream = pset.cur_cmd_source; else copystream = stdin; } else { override_file = &pset.queryFout; if (options->file) { if (options->program) { fflush(stdout); fflush(stderr); errno = 0; #ifndef WIN32 pqsignal(SIGPIPE, SIG_IGN); #endif copystream = popen(options->file, PG_BINARY_W); } else copystream = fopen(options->file, PG_BINARY_W); } else if (!options->psql_inout) copystream = pset.queryFout; else copystream = stdout; } if (!copystream) { if (options->program) psql_error("could not execute command \"%s\": %s\n", options->file, strerror(errno)); else psql_error("%s: %s\n", options->file, strerror(errno)); free_copy_options(options); return false; } if (!options->program) { /* make sure the specified file is not a directory */ fstat(fileno(copystream), &st); if (S_ISDIR(st.st_mode)) { fclose(copystream); psql_error("%s: cannot copy from/to a directory\n", options->file); free_copy_options(options); return false; } } /* build the command we will send to the backend */ initPQExpBuffer(&query); printfPQExpBuffer(&query, "COPY "); appendPQExpBufferStr(&query, options->before_tofrom); if (options->from) appendPQExpBuffer(&query, " FROM STDIN "); else appendPQExpBuffer(&query, " TO STDOUT "); if (options->after_tofrom) appendPQExpBufferStr(&query, options->after_tofrom); /* Run it like a user command, interposing the data source or sink. */ save_file = *override_file; *override_file = copystream; success = SendQuery(query.data); *override_file = save_file; termPQExpBuffer(&query); if (options->file != NULL) { if (options->program) { int pclose_rc = pclose(copystream); if (pclose_rc != 0) { if (pclose_rc < 0) psql_error("could not close pipe to external command: %s\n", strerror(errno)); else { char *reason = wait_result_to_str(pclose_rc); psql_error("%s: %s\n", options->file, reason ? reason : ""); if (reason) free(reason); } success = false; } #ifndef WIN32 pqsignal(SIGPIPE, SIG_DFL); #endif } else { if (fclose(copystream) != 0) { psql_error("%s: %s\n", options->file, strerror(errno)); success = false; } } } free_copy_options(options); return success; }
Definition at line 526 of file copy.c.
References _, buf, COPYBUFSIZ, _psqlSettings::cur_cmd_source, _psqlSettings::db, get_prompt(), _psqlSettings::lineno, NULL, PGRES_COMMAND_OK, PGRES_COPY_IN, PQclear(), PQerrorMessage(), PQgetResult(), PQputCopyData(), PQputCopyEnd(), PQresultStatus(), PROMPT_COPY, pset, psql_error(), _psqlSettings::quiet, sigint_interrupt_enabled, sigint_interrupt_jmp, and sigsetjmp.
Referenced by ProcessResult().
{ bool OK; const char *prompt; char buf[COPYBUFSIZ]; PGresult *res; /* * Establish longjmp destination for exiting from wait-for-input. (This is * only effective while sigint_interrupt_enabled is TRUE.) */ if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) { /* got here with longjmp */ /* Terminate data transfer */ PQputCopyEnd(conn, _("canceled by user")); OK = false; goto copyin_cleanup; } /* Prompt if interactive input */ if (isatty(fileno(copystream))) { if (!pset.quiet) puts(_("Enter data to be copied followed by a newline.\n" "End with a backslash and a period on a line by itself.")); prompt = get_prompt(PROMPT_COPY); } else prompt = NULL; OK = true; if (isbinary) { /* interactive input probably silly, but give one prompt anyway */ if (prompt) { fputs(prompt, stdout); fflush(stdout); } for (;;) { int buflen; /* enable longjmp while waiting for input */ sigint_interrupt_enabled = true; buflen = fread(buf, 1, COPYBUFSIZ, copystream); sigint_interrupt_enabled = false; if (buflen <= 0) break; if (PQputCopyData(conn, buf, buflen) <= 0) { OK = false; break; } } } else { bool copydone = false; while (!copydone) { /* for each input line ... */ bool firstload; bool linedone; if (prompt) { fputs(prompt, stdout); fflush(stdout); } firstload = true; linedone = false; while (!linedone) { /* for each bufferload in line ... */ int linelen; char *fgresult; /* enable longjmp while waiting for input */ sigint_interrupt_enabled = true; fgresult = fgets(buf, sizeof(buf), copystream); sigint_interrupt_enabled = false; if (!fgresult) { copydone = true; break; } linelen = strlen(buf); /* current line is done? */ if (linelen > 0 && buf[linelen - 1] == '\n') linedone = true; /* check for EOF marker, but not on a partial line */ if (firstload) { if (strcmp(buf, "\\.\n") == 0 || strcmp(buf, "\\.\r\n") == 0) { copydone = true; break; } firstload = false; } if (PQputCopyData(conn, buf, linelen) <= 0) { OK = false; copydone = true; break; } } if (copystream == pset.cur_cmd_source) pset.lineno++; } } /* Check for read error */ if (ferror(copystream)) OK = false; /* Terminate data transfer */ if (PQputCopyEnd(conn, OK ? NULL : _("aborted because of read failure")) <= 0) OK = false; copyin_cleanup: /* * Check command status and return to normal libpq state * * We must not ever return with the status still PGRES_COPY_IN. Our * caller is unable to distinguish that situation from reaching the next * COPY in a command string that happened to contain two consecutive COPY * FROM STDIN commands. XXX if something makes PQputCopyEnd() fail * indefinitely while retaining status PGRES_COPY_IN, we get an infinite * loop. This is more realistic than handleCopyOut()'s counterpart risk. */ while (res = PQgetResult(conn), PQresultStatus(res) == PGRES_COPY_IN) { OK = false; PQclear(res); PQputCopyEnd(pset.db, _("trying to exit copy mode")); } if (PQresultStatus(res) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(conn)); OK = false; } PQclear(res); return OK; }
Definition at line 436 of file copy.c.
References buf, PGRES_COMMAND_OK, PGRES_COPY_OUT, PQclear(), PQerrorMessage(), PQexec(), PQfreemem(), PQgetCopyData(), PQgetResult(), PQresultStatus(), psql_error(), and strerror().
Referenced by ProcessResult().
{ bool OK = true; char *buf; int ret; PGresult *res; for (;;) { ret = PQgetCopyData(conn, &buf, 0); if (ret < 0) break; /* done or error */ if (buf) { if (fwrite(buf, 1, ret, copystream) != ret) { if (OK) /* complain only once, keep reading data */ psql_error("could not write COPY data: %s\n", strerror(errno)); OK = false; } PQfreemem(buf); } } if (OK && fflush(copystream)) { psql_error("could not write COPY data: %s\n", strerror(errno)); OK = false; } if (ret == -2) { psql_error("COPY data transfer failed: %s", PQerrorMessage(conn)); OK = false; } /* * Check command status and return to normal libpq state. After a * client-side error, the server will remain ready to deliver data. The * cleanest thing is to fully drain and discard that data. If the * client-side error happened early in a large file, this takes a long * time. Instead, take advantage of the fact that PQexec() will silently * end any ongoing PGRES_COPY_OUT state. This does cause us to lose the * results of any commands following the COPY in a single command string. * It also only works for protocol version 3. XXX should we clean up * using the slow way when the connection is using protocol version 2? * * We must not ever return with the status still PGRES_COPY_OUT. Our * caller is unable to distinguish that situation from reaching the next * COPY in a command string that happened to contain two consecutive COPY * TO STDOUT commands. We trust that no condition can make PQexec() fail * indefinitely while retaining status PGRES_COPY_OUT. */ while (res = PQgetResult(conn), PQresultStatus(res) == PGRES_COPY_OUT) { OK = false; PQclear(res); PQexec(conn, "-- clear PGRES_COPY_OUT state"); } if (PQresultStatus(res) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(conn)); OK = false; } PQclear(res); return OK; }