LCOV - code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 153 273 56.0 %
Date: 2024-12-12 20:14:52 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         522 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
     435             : {
     436         522 :     bool        OK = true;
     437             :     char       *buf;
     438             :     int         ret;
     439             : 
     440             :     for (;;)
     441             :     {
     442        2316 :         ret = PQgetCopyData(conn, &buf, 0);
     443             : 
     444        2316 :         if (ret < 0)
     445         522 :             break;              /* done or server/connection error */
     446             : 
     447        1794 :         if (buf)
     448             :         {
     449        1794 :             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        1794 :             PQfreemem(buf);
     456             :         }
     457             :     }
     458             : 
     459         522 :     if (OK && copystream && fflush(copystream))
     460             :     {
     461           0 :         pg_log_error("could not write COPY data: %m");
     462           0 :         OK = false;
     463             :     }
     464             : 
     465         522 :     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         522 :     *res = PQgetResult(conn);
     484         522 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     485             :     {
     486           2 :         pg_log_info("%s", PQerrorMessage(conn));
     487           2 :         OK = false;
     488             :     }
     489             : 
     490         522 :     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         890 : 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         890 :     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         890 :     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         890 :         showprompt = false;
     544             : 
     545         890 :     OK = true;
     546             : 
     547         890 :     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         890 :         bool        copydone = false;
     582             :         int         buflen;
     583         890 :         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         890 :         buflen = 0;
     592       73306 :         while (!copydone)
     593             :         {
     594             :             char       *fgresult;
     595             : 
     596       72416 :             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       72416 :             sigint_interrupt_enabled = true;
     606             : 
     607       72416 :             fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
     608             : 
     609       72416 :             sigint_interrupt_enabled = false;
     610             : 
     611       72416 :             if (!fgresult)
     612          80 :                 copydone = true;
     613             :             else
     614             :             {
     615             :                 int         linelen;
     616             : 
     617       72336 :                 linelen = strlen(fgresult);
     618       72336 :                 buflen += linelen;
     619             : 
     620             :                 /* current line is done? */
     621       72336 :                 if (buf[buflen - 1] == '\n')
     622             :                 {
     623             :                     /*
     624             :                      * When at the beginning of the line and the data is
     625             :                      * inlined, check for EOF marker.  If the marker is found,
     626             :                      * we must stop at this point.  If not, the \. line can be
     627             :                      * sent to the server, and we let it decide whether it's
     628             :                      * an EOF or not depending on the format: in TEXT mode, \.
     629             :                      * will be interpreted as an EOF, in CSV, it will not.
     630             :                      */
     631       72212 :                     if (at_line_begin && copystream == pset.cur_cmd_source)
     632             :                     {
     633        3434 :                         if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
     634          72 :                             (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
     635             :                         {
     636         810 :                             copydone = true;
     637             : 
     638             :                             /*
     639             :                              * Remove the EOF marker from the data sent.  In
     640             :                              * CSV mode, the EOF marker must be removed,
     641             :                              * otherwise it would be interpreted by the server
     642             :                              * as valid data.
     643             :                              */
     644         810 :                             *fgresult = '\0';
     645         810 :                             buflen -= linelen;
     646             :                         }
     647             :                     }
     648             : 
     649       72212 :                     if (copystream == pset.cur_cmd_source)
     650             :                     {
     651        3452 :                         pset.lineno++;
     652        3452 :                         pset.stmt_lineno++;
     653             :                     }
     654       72212 :                     at_line_begin = true;
     655             :                 }
     656             :                 else
     657         124 :                     at_line_begin = false;
     658             :             }
     659             : 
     660             :             /*
     661             :              * If the buffer is full, or we've reached the EOF, flush it.
     662             :              *
     663             :              * Make sure there's always space for four more bytes in the
     664             :              * buffer, plus a NUL terminator.  That way, an EOF marker is
     665             :              * never split across two fgets() calls, which simplifies the
     666             :              * logic.
     667             :              */
     668       72416 :             if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
     669             :             {
     670        1032 :                 if (PQputCopyData(conn, buf, buflen) <= 0)
     671             :                 {
     672           0 :                     OK = false;
     673           0 :                     break;
     674             :                 }
     675             : 
     676        1032 :                 buflen = 0;
     677             :             }
     678             :         }
     679             :     }
     680             : 
     681             :     /* Check for read error */
     682         890 :     if (ferror(copystream))
     683           0 :         OK = false;
     684             : 
     685             :     /*
     686             :      * Terminate data transfer.  We can't send an error message if we're using
     687             :      * protocol version 2.  (libpq no longer supports protocol version 2, but
     688             :      * keep the version checks just in case you're using a pre-v14 libpq.so at
     689             :      * runtime)
     690             :      */
     691         890 :     if (PQputCopyEnd(conn,
     692           0 :                      (OK || PQprotocolVersion(conn) < 3) ? NULL :
     693           0 :                      _("aborted because of read failure")) <= 0)
     694           0 :         OK = false;
     695             : 
     696         890 : copyin_cleanup:
     697             : 
     698             :     /*
     699             :      * Clear the EOF flag on the stream, in case copying ended due to an EOF
     700             :      * signal.  This allows an interactive TTY session to perform another COPY
     701             :      * FROM STDIN later.  (In non-STDIN cases, we're about to close the file
     702             :      * anyway, so it doesn't matter.)  Although we don't ever test the flag
     703             :      * with feof(), some fread() implementations won't read more data if it's
     704             :      * set.  This also clears the error flag, but we already checked that.
     705             :      */
     706         890 :     clearerr(copystream);
     707             : 
     708             :     /*
     709             :      * Check command status and return to normal libpq state.
     710             :      *
     711             :      * We do not want to return with the status still PGRES_COPY_IN: our
     712             :      * caller would be unable to distinguish that situation from reaching the
     713             :      * next COPY in a command string that happened to contain two consecutive
     714             :      * COPY FROM STDIN commands.  We keep trying PQputCopyEnd() in the hope
     715             :      * it'll work eventually.  (What's actually likely to happen is that in
     716             :      * attempting to flush the data, libpq will eventually realize that the
     717             :      * connection is lost.  But that's fine; it will get us out of COPY_IN
     718             :      * state, which is what we need.)
     719             :      */
     720         890 :     while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
     721             :     {
     722           0 :         OK = false;
     723           0 :         PQclear(*res);
     724             :         /* We can't send an error message if we're using protocol version 2 */
     725           0 :         PQputCopyEnd(conn,
     726           0 :                      (PQprotocolVersion(conn) < 3) ? NULL :
     727           0 :                      _("trying to exit copy mode"));
     728             :     }
     729         890 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     730             :     {
     731         198 :         pg_log_info("%s", PQerrorMessage(conn));
     732         198 :         OK = false;
     733             :     }
     734             : 
     735         890 :     return OK;
     736             : }

Generated by: LCOV version 1.14