Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

copy.h File Reference

#include "libpq-fe.h"
Include dependency graph for copy.h:
This graph shows which files directly or indirectly include this file:

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)

Function Documentation

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;
}

bool handleCopyIn ( PGconn conn,
FILE *  copystream,
bool  isbinary 
)

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;
}

bool handleCopyOut ( PGconn conn,
FILE *  copystream 
)

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;
}