LCOV - code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 56.0 % 273 153
Test Date: 2026-03-19 06:16:26 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * psql - the PostgreSQL interactive terminal
       3              :  *
       4              :  * Copyright (c) 2000-2026, 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 | pstdin | 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           95 : free_copy_options(struct copy_options *ptr)
      66              : {
      67           95 :     if (!ptr)
      68            0 :         return;
      69           95 :     free(ptr->before_tofrom);
      70           95 :     free(ptr->after_tofrom);
      71           95 :     free(ptr->file);
      72           95 :     free(ptr);
      73              : }
      74              : 
      75              : 
      76              : /* concatenate "more" onto "var", freeing the original value of *var */
      77              : static void
      78          646 : xstrcat(char **var, const char *more)
      79              : {
      80              :     char       *newvar;
      81              : 
      82          646 :     newvar = psprintf("%s%s", *var, more);
      83          646 :     free(*var);
      84          646 :     *var = newvar;
      85          646 : }
      86              : 
      87              : 
      88              : static struct copy_options *
      89           95 : parse_slash_copy(const char *args)
      90              : {
      91              :     struct copy_options *result;
      92              :     char       *token;
      93           95 :     const char *whitespace = " \t\n\r";
      94           95 :     char        nonstd_backslash = standard_strings() ? 0 : '\\';
      95              : 
      96           95 :     if (!args)
      97              :     {
      98            0 :         pg_log_error("\\copy: arguments required");
      99            0 :         return NULL;
     100              :     }
     101              : 
     102           95 :     result = pg_malloc0_object(struct copy_options);
     103              : 
     104           95 :     result->before_tofrom = pg_strdup(""); /* initialize for appending */
     105              : 
     106           95 :     token = strtokx(args, whitespace, ".,()", "\"",
     107              :                     0, false, false, pset.encoding);
     108           95 :     if (!token)
     109            0 :         goto error;
     110              : 
     111              :     /* The following can be removed when we drop 7.3 syntax support */
     112           95 :     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           95 :     if (token[0] == '(')
     123              :     {
     124           16 :         int         parens = 1;
     125              : 
     126          244 :         while (parens > 0)
     127              :         {
     128          228 :             xstrcat(&result->before_tofrom, " ");
     129          228 :             xstrcat(&result->before_tofrom, token);
     130          228 :             token = strtokx(NULL, whitespace, "()", "\"'",
     131              :                             nonstd_backslash, true, false, pset.encoding);
     132          228 :             if (!token)
     133            0 :                 goto error;
     134          228 :             if (token[0] == '(')
     135           12 :                 parens++;
     136          216 :             else if (token[0] == ')')
     137           28 :                 parens--;
     138              :         }
     139              :     }
     140              : 
     141           95 :     xstrcat(&result->before_tofrom, " ");
     142           95 :     xstrcat(&result->before_tofrom, token);
     143           95 :     token = strtokx(NULL, whitespace, ".,()", "\"",
     144              :                     0, false, false, pset.encoding);
     145           95 :     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           95 :     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           95 :     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           95 :     if (pg_strcasecmp(token, "from") == 0)
     190           54 :         result->from = true;
     191           41 :     else if (pg_strcasecmp(token, "to") == 0)
     192           41 :         result->from = false;
     193              :     else
     194            0 :         goto error;
     195              : 
     196              :     /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
     197           95 :     token = strtokx(NULL, whitespace, ";", "'",
     198              :                     0, false, false, pset.encoding);
     199           95 :     if (!token)
     200            0 :         goto error;
     201              : 
     202           95 :     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          183 :     else if (pg_strcasecmp(token, "stdin") == 0 ||
     225           88 :              pg_strcasecmp(token, "stdout") == 0)
     226              :     {
     227           48 :         result->file = NULL;
     228              :     }
     229           94 :     else if (pg_strcasecmp(token, "pstdin") == 0 ||
     230           47 :              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           47 :         strip_quotes(token, '\'', 0, pset.encoding);
     239           47 :         result->file = pg_strdup(token);
     240           47 :         expand_tilde(&result->file);
     241              :     }
     242              : 
     243              :     /* Collect the rest of the line (COPY options) */
     244           95 :     token = strtokx(NULL, "", NULL, NULL,
     245              :                     0, false, false, pset.encoding);
     246           95 :     if (token)
     247           34 :         result->after_tofrom = pg_strdup(token);
     248              : 
     249           95 :     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           95 : 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           95 :     options = parse_slash_copy(args);
     277              : 
     278           95 :     if (!options)
     279            0 :         return false;
     280              : 
     281              :     /* prepare to read or write the target file */
     282           95 :     if (options->file && !options->program)
     283           47 :         canonicalize_path_enc(options->file, pset.encoding);
     284              : 
     285           95 :     if (options->from)
     286              :     {
     287           54 :         if (options->file)
     288              :         {
     289           47 :             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           47 :                 copystream = fopen(options->file, PG_BINARY_R);
     297              :         }
     298            7 :         else if (!options->psql_inout)
     299            7 :             copystream = pset.cur_cmd_source;
     300              :         else
     301            0 :             copystream = stdin;
     302              :     }
     303              :     else
     304              :     {
     305           41 :         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           41 :         else if (!options->psql_inout)
     318           41 :             copystream = pset.queryFout;
     319              :         else
     320            0 :             copystream = stdout;
     321              :     }
     322              : 
     323           95 :     if (!copystream)
     324              :     {
     325            5 :         if (options->program)
     326            0 :             pg_log_error("could not execute command \"%s\": %m",
     327              :                          options->file);
     328              :         else
     329            5 :             pg_log_error("%s: %m",
     330              :                          options->file);
     331            5 :         free_copy_options(options);
     332            5 :         return false;
     333              :     }
     334              : 
     335           90 :     if (!options->program)
     336              :     {
     337              :         struct stat st;
     338              :         int         result;
     339              : 
     340              :         /* make sure the specified file is not a directory */
     341           90 :         if ((result = fstat(fileno(copystream), &st)) < 0)
     342            0 :             pg_log_error("could not stat file \"%s\": %m",
     343              :                          options->file);
     344              : 
     345           90 :         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           90 :         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           90 :     initPQExpBuffer(&query);
     359           90 :     printfPQExpBuffer(&query, "COPY ");
     360           90 :     appendPQExpBufferStr(&query, options->before_tofrom);
     361           90 :     if (options->from)
     362           49 :         appendPQExpBufferStr(&query, " FROM STDIN ");
     363              :     else
     364           41 :         appendPQExpBufferStr(&query, " TO STDOUT ");
     365           90 :     if (options->after_tofrom)
     366           31 :         appendPQExpBufferStr(&query, options->after_tofrom);
     367              : 
     368              :     /* run it like a user command, but with copystream as data source/sink */
     369           90 :     pset.copyStream = copystream;
     370           90 :     success = SendQuery(query.data);
     371           90 :     pset.copyStream = NULL;
     372           90 :     termPQExpBuffer(&query);
     373              : 
     374           90 :     if (options->file != NULL)
     375              :     {
     376           42 :         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           42 :             if (fclose(copystream) != 0)
     400              :             {
     401            0 :                 pg_log_error("%s: %m", options->file);
     402            0 :                 success = false;
     403              :             }
     404              :         }
     405              :     }
     406           90 :     free_copy_options(options);
     407           90 :     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          377 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
     435              : {
     436          377 :     bool        OK = true;
     437              :     char       *buf;
     438              :     int         ret;
     439              : 
     440              :     for (;;)
     441              :     {
     442         1704 :         ret = PQgetCopyData(conn, &buf, 0);
     443              : 
     444         1704 :         if (ret < 0)
     445          377 :             break;              /* done or server/connection error */
     446              : 
     447         1327 :         if (buf)
     448              :         {
     449         1327 :             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         1327 :             PQfreemem(buf);
     456              :         }
     457              :     }
     458              : 
     459          377 :     if (OK && copystream && fflush(copystream))
     460              :     {
     461            0 :         pg_log_error("could not write COPY data: %m");
     462            0 :         OK = false;
     463              :     }
     464              : 
     465          377 :     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          377 :     *res = PQgetResult(conn);
     484          377 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     485              :     {
     486            1 :         pg_log_info("%s", PQerrorMessage(conn));
     487            1 :         OK = false;
     488              :     }
     489              : 
     490          377 :     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          631 : 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          631 :     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          631 :     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          631 :         showprompt = false;
     544              : 
     545          631 :     OK = true;
     546              : 
     547          631 :     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          631 :         bool        copydone = false;
     582              :         int         buflen;
     583          631 :         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          631 :         buflen = 0;
     592        37541 :         while (!copydone)
     593              :         {
     594              :             char       *fgresult;
     595              : 
     596        36910 :             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        36910 :             sigint_interrupt_enabled = true;
     606              : 
     607        36910 :             fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
     608              : 
     609        36910 :             sigint_interrupt_enabled = false;
     610              : 
     611        36910 :             if (!fgresult)
     612           41 :                 copydone = true;
     613              :             else
     614              :             {
     615              :                 int         linelen;
     616              : 
     617        36869 :                 linelen = strlen(fgresult);
     618        36869 :                 buflen += linelen;
     619              : 
     620              :                 /* current line is done? */
     621        36869 :                 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        36807 :                     if (at_line_begin && copystream == pset.cur_cmd_source)
     632              :                     {
     633         2368 :                         if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
     634           52 :                             (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
     635              :                         {
     636          590 :                             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          590 :                             *fgresult = '\0';
     645          590 :                             buflen -= linelen;
     646              :                         }
     647              :                     }
     648              : 
     649        36807 :                     if (copystream == pset.cur_cmd_source)
     650              :                     {
     651         2377 :                         pset.lineno++;
     652         2377 :                         pset.stmt_lineno++;
     653              :                     }
     654        36807 :                     at_line_begin = true;
     655              :                 }
     656              :                 else
     657           62 :                     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        36910 :             if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
     669              :             {
     670          701 :                 if (PQputCopyData(conn, buf, buflen) <= 0)
     671              :                 {
     672            0 :                     OK = false;
     673            0 :                     break;
     674              :                 }
     675              : 
     676          701 :                 buflen = 0;
     677              :             }
     678              :         }
     679              :     }
     680              : 
     681              :     /* Check for read error */
     682          631 :     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          631 :     if (PQputCopyEnd(conn,
     692            0 :                      (OK || PQprotocolVersion(conn) < 3) ? NULL :
     693            0 :                      _("aborted because of read failure")) <= 0)
     694            0 :         OK = false;
     695              : 
     696          631 : 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          631 :     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          631 :     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          631 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     730              :     {
     731          157 :         pg_log_info("%s", PQerrorMessage(conn));
     732          157 :         OK = false;
     733              :     }
     734              : 
     735          631 :     return OK;
     736              : }
        

Generated by: LCOV version 2.0-1