LCOV - code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 151 271 55.7 %
Date: 2024-03-29 14:11:41 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * psql - the PostgreSQL interactive terminal
       3             :  *
       4             :  * Copyright (c) 2000-2024, 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
      65         162 : free_copy_options(struct copy_options *ptr)
      66             : {
      67         162 :     if (!ptr)
      68           0 :         return;
      69         162 :     free(ptr->before_tofrom);
      70         162 :     free(ptr->after_tofrom);
      71         162 :     free(ptr->file);
      72         162 :     free(ptr);
      73             : }
      74             : 
      75             : 
      76             : /* concatenate "more" onto "var", freeing the original value of *var */
      77             : static void
      78        1008 : xstrcat(char **var, const char *more)
      79             : {
      80             :     char       *newvar;
      81             : 
      82        1008 :     newvar = psprintf("%s%s", *var, more);
      83        1008 :     free(*var);
      84        1008 :     *var = newvar;
      85        1008 : }
      86             : 
      87             : 
      88             : static struct copy_options *
      89         162 : parse_slash_copy(const char *args)
      90             : {
      91             :     struct copy_options *result;
      92             :     char       *token;
      93         162 :     const char *whitespace = " \t\n\r";
      94         162 :     char        nonstd_backslash = standard_strings() ? 0 : '\\';
      95             : 
      96         162 :     if (!args)
      97             :     {
      98           0 :         pg_log_error("\\copy: arguments required");
      99           0 :         return NULL;
     100             :     }
     101             : 
     102         162 :     result = pg_malloc0(sizeof(struct copy_options));
     103             : 
     104         162 :     result->before_tofrom = pg_strdup(""); /* initialize for appending */
     105             : 
     106         162 :     token = strtokx(args, whitespace, ".,()", "\"",
     107             :                     0, false, false, pset.encoding);
     108         162 :     if (!token)
     109           0 :         goto error;
     110             : 
     111             :     /* The following can be removed when we drop 7.3 syntax support */
     112         162 :     if (pg_strcasecmp(token, "binary") == 0)
     113             :     {
     114           0 :         xstrcat(&result->before_tofrom, token);
     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 */
     122         162 :     if (token[0] == '(')
     123             :     {
     124          24 :         int         parens = 1;
     125             : 
     126         366 :         while (parens > 0)
     127             :         {
     128         342 :             xstrcat(&result->before_tofrom, " ");
     129         342 :             xstrcat(&result->before_tofrom, token);
     130         342 :             token = strtokx(NULL, whitespace, "()", "\"'",
     131             :                             nonstd_backslash, true, false, pset.encoding);
     132         342 :             if (!token)
     133           0 :                 goto error;
     134         342 :             if (token[0] == '(')
     135          18 :                 parens++;
     136         324 :             else if (token[0] == ')')
     137          42 :                 parens--;
     138             :         }
     139             :     }
     140             : 
     141         162 :     xstrcat(&result->before_tofrom, " ");
     142         162 :     xstrcat(&result->before_tofrom, token);
     143         162 :     token = strtokx(NULL, whitespace, ".,()", "\"",
     144             :                     0, false, false, pset.encoding);
     145         162 :     if (!token)
     146           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             :      */
     152         162 :     if (token[0] == '.')
     153             :     {
     154             :         /* handle schema . table */
     155           0 :         xstrcat(&result->before_tofrom, token);
     156           0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
     157             :                         0, false, false, pset.encoding);
     158           0 :         if (!token)
     159           0 :             goto error;
     160           0 :         xstrcat(&result->before_tofrom, token);
     161           0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
     162             :                         0, false, false, pset.encoding);
     163           0 :         if (!token)
     164           0 :             goto error;
     165             :     }
     166             : 
     167         162 :     if (token[0] == '(')
     168             :     {
     169             :         /* handle parenthesized column list */
     170             :         for (;;)
     171             :         {
     172           0 :             xstrcat(&result->before_tofrom, " ");
     173           0 :             xstrcat(&result->before_tofrom, token);
     174           0 :             token = strtokx(NULL, whitespace, "()", "\"",
     175             :                             0, false, false, pset.encoding);
     176           0 :             if (!token)
     177           0 :                 goto error;
     178           0 :             if (token[0] == ')')
     179           0 :                 break;
     180             :         }
     181           0 :         xstrcat(&result->before_tofrom, " ");
     182           0 :         xstrcat(&result->before_tofrom, token);
     183           0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
     184             :                         0, false, false, pset.encoding);
     185           0 :         if (!token)
     186           0 :             goto error;
     187             :     }
     188             : 
     189         162 :     if (pg_strcasecmp(token, "from") == 0)
     190         102 :         result->from = true;
     191          60 :     else if (pg_strcasecmp(token, "to") == 0)
     192          60 :         result->from = false;
     193             :     else
     194           0 :         goto error;
     195             : 
     196             :     /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
     197         162 :     token = strtokx(NULL, whitespace, ";", "'",
     198             :                     0, false, false, pset.encoding);
     199         162 :     if (!token)
     200           0 :         goto error;
     201             : 
     202         162 :     if (pg_strcasecmp(token, "program") == 0)
     203             :     {
     204             :         int         toklen;
     205             : 
     206           0 :         token = strtokx(NULL, whitespace, ";", "'",
     207             :                         0, false, false, pset.encoding);
     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             :     }
     224         314 :     else if (pg_strcasecmp(token, "stdin") == 0 ||
     225         152 :              pg_strcasecmp(token, "stdout") == 0)
     226             :     {
     227          70 :         result->file = NULL;
     228             :     }
     229         184 :     else if (pg_strcasecmp(token, "pstdin") == 0 ||
     230          92 :              pg_strcasecmp(token, "pstdout") == 0)
     231             :     {
     232           0 :         result->psql_inout = true;
     233           0 :         result->file = NULL;
     234             :     }
     235             :     else
     236             :     {
     237             :         /* filename can be optionally quoted */
     238          92 :         strip_quotes(token, '\'', 0, pset.encoding);
     239          92 :         result->file = pg_strdup(token);
     240          92 :         expand_tilde(&result->file);
     241             :     }
     242             : 
     243             :     /* Collect the rest of the line (COPY options) */
     244         162 :     token = strtokx(NULL, "", NULL, NULL,
     245             :                     0, false, false, pset.encoding);
     246         162 :     if (token)
     247          50 :         result->after_tofrom = pg_strdup(token);
     248             : 
     249         162 :     return result;
     250             : 
     251           0 : error:
     252           0 :     if (token)
     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");
     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
     268         162 : 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         162 :     options = parse_slash_copy(args);
     277             : 
     278         162 :     if (!options)
     279           0 :         return false;
     280             : 
     281             :     /* prepare to read or write the target file */
     282         162 :     if (options->file && !options->program)
     283          92 :         canonicalize_path(options->file);
     284             : 
     285         162 :     if (options->from)
     286             :     {
     287         102 :         if (options->file)
     288             :         {
     289          92 :             if (options->program)
     290             :             {
     291           0 :                 fflush(NULL);
     292           0 :                 errno = 0;
     293           0 :                 copystream = popen(options->file, PG_BINARY_R);
     294             :             }
     295             :             else
     296          92 :                 copystream = fopen(options->file, PG_BINARY_R);
     297             :         }
     298          10 :         else if (!options->psql_inout)
     299          10 :             copystream = pset.cur_cmd_source;
     300             :         else
     301           0 :             copystream = stdin;
     302             :     }
     303             :     else
     304             :     {
     305          60 :         if (options->file)
     306             :         {
     307           0 :             if (options->program)
     308             :             {
     309           0 :                 fflush(NULL);
     310           0 :                 disable_sigpipe_trap();
     311           0 :                 errno = 0;
     312           0 :                 copystream = popen(options->file, PG_BINARY_W);
     313             :             }
     314             :             else
     315           0 :                 copystream = fopen(options->file, PG_BINARY_W);
     316             :         }
     317          60 :         else if (!options->psql_inout)
     318          60 :             copystream = pset.queryFout;
     319             :         else
     320           0 :             copystream = stdout;
     321             :     }
     322             : 
     323         162 :     if (!copystream)
     324             :     {
     325          10 :         if (options->program)
     326           0 :             pg_log_error("could not execute command \"%s\": %m",
     327             :                          options->file);
     328             :         else
     329          10 :             pg_log_error("%s: %m",
     330             :                          options->file);
     331          10 :         free_copy_options(options);
     332          10 :         return false;
     333             :     }
     334             : 
     335         152 :     if (!options->program)
     336             :     {
     337             :         struct stat st;
     338             :         int         result;
     339             : 
     340             :         /* make sure the specified file is not a directory */
     341         152 :         if ((result = fstat(fileno(copystream), &st)) < 0)
     342           0 :             pg_log_error("could not stat file \"%s\": %m",
     343             :                          options->file);
     344             : 
     345         152 :         if (result == 0 && S_ISDIR(st.st_mode))
     346           0 :             pg_log_error("%s: cannot copy from/to a directory",
     347             :                          options->file);
     348             : 
     349         152 :         if (result < 0 || S_ISDIR(st.st_mode))
     350             :         {
     351           0 :             fclose(copystream);
     352           0 :             free_copy_options(options);
     353           0 :             return false;
     354             :         }
     355             :     }
     356             : 
     357             :     /* build the command we will send to the backend */
     358         152 :     initPQExpBuffer(&query);
     359         152 :     printfPQExpBuffer(&query, "COPY ");
     360         152 :     appendPQExpBufferStr(&query, options->before_tofrom);
     361         152 :     if (options->from)
     362          92 :         appendPQExpBufferStr(&query, " FROM STDIN ");
     363             :     else
     364          60 :         appendPQExpBufferStr(&query, " TO STDOUT ");
     365         152 :     if (options->after_tofrom)
     366          44 :         appendPQExpBufferStr(&query, options->after_tofrom);
     367             : 
     368             :     /* run it like a user command, but with copystream as data source/sink */
     369         152 :     pset.copyStream = copystream;
     370         152 :     success = SendQuery(query.data);
     371         152 :     pset.copyStream = NULL;
     372         152 :     termPQExpBuffer(&query);
     373             : 
     374         152 :     if (options->file != NULL)
     375             :     {
     376          82 :         if (options->program)
     377             :         {
     378           0 :             int         pclose_rc = pclose(copystream);
     379             : 
     380           0 :             if (pclose_rc != 0)
     381             :             {
     382           0 :                 if (pclose_rc < 0)
     383           0 :                     pg_log_error("could not close pipe to external command: %m");
     384             :                 else
     385             :                 {
     386           0 :                     char       *reason = wait_result_to_str(pclose_rc);
     387             : 
     388           0 :                     pg_log_error("%s: %s", options->file,
     389             :                                  reason ? reason : "");
     390           0 :                     free(reason);
     391             :                 }
     392           0 :                 success = false;
     393             :             }
     394           0 :             SetShellResultVariables(pclose_rc);
     395           0 :             restore_sigpipe_trap();
     396             :         }
     397             :         else
     398             :         {
     399          82 :             if (fclose(copystream) != 0)
     400             :             {
     401           0 :                 pg_log_error("%s: %m", options->file);
     402           0 :                 success = false;
     403             :             }
     404             :         }
     405             :     }
     406         152 :     free_copy_options(options);
     407         152 :     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
     434         498 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
     435             : {
     436         498 :     bool        OK = true;
     437             :     char       *buf;
     438             :     int         ret;
     439             : 
     440             :     for (;;)
     441             :     {
     442        2256 :         ret = PQgetCopyData(conn, &buf, 0);
     443             : 
     444        2256 :         if (ret < 0)
     445         498 :             break;              /* done or server/connection error */
     446             : 
     447        1758 :         if (buf)
     448             :         {
     449        1758 :             if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret)
     450             :             {
     451           0 :                 pg_log_error("could not write COPY data: %m");
     452             :                 /* complain only once, keep reading data from server */
     453           0 :                 OK = false;
     454             :             }
     455        1758 :             PQfreemem(buf);
     456             :         }
     457             :     }
     458             : 
     459         498 :     if (OK && copystream && fflush(copystream))
     460             :     {
     461           0 :         pg_log_error("could not write COPY data: %m");
     462           0 :         OK = false;
     463             :     }
     464             : 
     465         498 :     if (ret == -2)
     466             :     {
     467           0 :         pg_log_error("COPY data transfer failed: %s", PQerrorMessage(conn));
     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             :      */
     483         498 :     *res = PQgetResult(conn);
     484         498 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     485             :     {
     486           2 :         pg_log_info("%s", PQerrorMessage(conn));
     487           2 :         OK = false;
     488             :     }
     489             : 
     490         498 :     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
     511         854 : 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             :      */
     521         854 :     if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
     522             :     {
     523             :         /* got here with longjmp */
     524             : 
     525             :         /* Terminate data transfer */
     526           0 :         PQputCopyEnd(conn,
     527           0 :                      (PQprotocolVersion(conn) < 3) ? NULL :
     528           0 :                      _("canceled by user"));
     529             : 
     530           0 :         OK = false;
     531           0 :         goto copyin_cleanup;
     532             :     }
     533             : 
     534             :     /* Prompt if interactive input */
     535         854 :     if (isatty(fileno(copystream)))
     536             :     {
     537           0 :         showprompt = true;
     538           0 :         if (!pset.quiet)
     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
     543         854 :         showprompt = false;
     544             : 
     545         854 :     OK = true;
     546             : 
     547         854 :     if (isbinary)
     548             :     {
     549             :         /* interactive input probably silly, but give one prompt anyway */
     550           0 :         if (showprompt)
     551             :         {
     552           0 :             const char *prompt = get_prompt(PROMPT_COPY, NULL);
     553             : 
     554           0 :             fputs(prompt, stdout);
     555           0 :             fflush(stdout);
     556             :         }
     557             : 
     558             :         for (;;)
     559           0 :         {
     560             :             int         buflen;
     561             : 
     562             :             /* enable longjmp while waiting for input */
     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             : 
     572           0 :             if (PQputCopyData(conn, buf, buflen) <= 0)
     573             :             {
     574           0 :                 OK = false;
     575           0 :                 break;
     576             :             }
     577             :         }
     578             :     }
     579             :     else
     580             :     {
     581         854 :         bool        copydone = false;
     582             :         int         buflen;
     583         854 :         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         854 :         buflen = 0;
     592       73090 :         while (!copydone)
     593             :         {
     594             :             char       *fgresult;
     595             : 
     596       72236 :             if (at_line_begin && showprompt)
     597             :             {
     598           0 :                 const char *prompt = get_prompt(PROMPT_COPY, NULL);
     599             : 
     600           0 :                 fputs(prompt, stdout);
     601           0 :                 fflush(stdout);
     602             :             }
     603             : 
     604             :             /* enable longjmp while waiting for input */
     605       72236 :             sigint_interrupt_enabled = true;
     606             : 
     607       72236 :             fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
     608             : 
     609       72236 :             sigint_interrupt_enabled = false;
     610             : 
     611       72236 :             if (!fgresult)
     612          80 :                 copydone = true;
     613             :             else
     614             :             {
     615             :                 int         linelen;
     616             : 
     617       72156 :                 linelen = strlen(fgresult);
     618       72156 :                 buflen += linelen;
     619             : 
     620             :                 /* current line is done? */
     621       72156 :                 if (buf[buflen - 1] == '\n')
     622             :                 {
     623             :                     /* check for EOF marker, but not on a partial line */
     624       72032 :                     if (at_line_begin)
     625             :                     {
     626             :                         /*
     627             :                          * This code erroneously assumes '\.' on a line alone
     628             :                          * inside a quoted CSV string terminates the \copy.
     629             :                          * https://www.postgresql.org/message-id/E1TdNVQ-0001ju-GO@wrigleys.postgresql.org
     630             :                          *
     631             :                          * https://www.postgresql.org/message-id/bfcd57e4-8f23-4c3e-a5db-2571d09208e2@beta.fastmail.com
     632             :                          */
     633       71916 :                         if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
     634         676 :                             (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
     635             :                         {
     636         774 :                             copydone = true;
     637             :                         }
     638             :                     }
     639             : 
     640       72032 :                     if (copystream == pset.cur_cmd_source)
     641             :                     {
     642        3272 :                         pset.lineno++;
     643        3272 :                         pset.stmt_lineno++;
     644             :                     }
     645       72032 :                     at_line_begin = true;
     646             :                 }
     647             :                 else
     648         124 :                     at_line_begin = false;
     649             :             }
     650             : 
     651             :             /*
     652             :              * If the buffer is full, or we've reached the EOF, flush it.
     653             :              *
     654             :              * Make sure there's always space for four more bytes in the
     655             :              * buffer, plus a NUL terminator.  That way, an EOF marker is
     656             :              * never split across two fgets() calls, which simplifies the
     657             :              * logic.
     658             :              */
     659       72236 :             if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
     660             :             {
     661        1010 :                 if (PQputCopyData(conn, buf, buflen) <= 0)
     662             :                 {
     663           0 :                     OK = false;
     664           0 :                     break;
     665             :                 }
     666             : 
     667        1010 :                 buflen = 0;
     668             :             }
     669             :         }
     670             :     }
     671             : 
     672             :     /* Check for read error */
     673         854 :     if (ferror(copystream))
     674           0 :         OK = false;
     675             : 
     676             :     /*
     677             :      * Terminate data transfer.  We can't send an error message if we're using
     678             :      * protocol version 2.  (libpq no longer supports protocol version 2, but
     679             :      * keep the version checks just in case you're using a pre-v14 libpq.so at
     680             :      * runtime)
     681             :      */
     682         854 :     if (PQputCopyEnd(conn,
     683           0 :                      (OK || PQprotocolVersion(conn) < 3) ? NULL :
     684           0 :                      _("aborted because of read failure")) <= 0)
     685           0 :         OK = false;
     686             : 
     687         854 : copyin_cleanup:
     688             : 
     689             :     /*
     690             :      * Clear the EOF flag on the stream, in case copying ended due to an EOF
     691             :      * signal.  This allows an interactive TTY session to perform another COPY
     692             :      * FROM STDIN later.  (In non-STDIN cases, we're about to close the file
     693             :      * anyway, so it doesn't matter.)  Although we don't ever test the flag
     694             :      * with feof(), some fread() implementations won't read more data if it's
     695             :      * set.  This also clears the error flag, but we already checked that.
     696             :      */
     697         854 :     clearerr(copystream);
     698             : 
     699             :     /*
     700             :      * Check command status and return to normal libpq state.
     701             :      *
     702             :      * We do not want to return with the status still PGRES_COPY_IN: our
     703             :      * caller would be unable to distinguish that situation from reaching the
     704             :      * next COPY in a command string that happened to contain two consecutive
     705             :      * COPY FROM STDIN commands.  We keep trying PQputCopyEnd() in the hope
     706             :      * it'll work eventually.  (What's actually likely to happen is that in
     707             :      * attempting to flush the data, libpq will eventually realize that the
     708             :      * connection is lost.  But that's fine; it will get us out of COPY_IN
     709             :      * state, which is what we need.)
     710             :      */
     711         854 :     while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
     712             :     {
     713           0 :         OK = false;
     714           0 :         PQclear(*res);
     715             :         /* We can't send an error message if we're using protocol version 2 */
     716           0 :         PQputCopyEnd(conn,
     717           0 :                      (PQprotocolVersion(conn) < 3) ? NULL :
     718           0 :                      _("trying to exit copy mode"));
     719             :     }
     720         854 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     721             :     {
     722         180 :         pg_log_info("%s", PQerrorMessage(conn));
     723         180 :         OK = false;
     724             :     }
     725             : 
     726         854 :     return OK;
     727             : }

Generated by: LCOV version 1.14