LCOV - differential code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB
Current: Differential Code Coverage HEAD vs 15 Lines: 55.7 % 271 151 5 4 39 72 12 47 92 30 55 6
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 6 6 2 1 3 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a