LCOV - code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 51.6 % 345 178
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 43.5 % 207 90

             Branch data     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                 :          11 : free_copy_options(struct copy_options *ptr)
      66                 :             : {
      67         [ +  - ]:          11 :         if (!ptr)
      68                 :           0 :                 return;
      69                 :          11 :         free(ptr->before_tofrom);
      70                 :          11 :         free(ptr->after_tofrom);
      71                 :          11 :         free(ptr->file);
      72                 :          11 :         free(ptr);
      73                 :          11 : }
      74                 :             : 
      75                 :             : 
      76                 :             : /* concatenate "more" onto "var", freeing the original value of *var */
      77                 :             : static void
      78                 :         136 : xstrcat(char **var, const char *more)
      79                 :             : {
      80                 :         136 :         char       *newvar;
      81                 :             : 
      82                 :         136 :         newvar = psprintf("%s%s", *var, more);
      83                 :         136 :         free(*var);
      84                 :         136 :         *var = newvar;
      85                 :         136 : }
      86                 :             : 
      87                 :             : 
      88                 :             : static struct copy_options *
      89                 :          11 : parse_slash_copy(const char *args)
      90                 :             : {
      91                 :          11 :         struct copy_options *result;
      92                 :          11 :         char       *token;
      93                 :          11 :         const char *whitespace = " \t\n\r";
      94                 :          11 :         char            nonstd_backslash = standard_strings() ? 0 : '\\';
      95                 :             : 
      96         [ +  - ]:          11 :         if (!args)
      97                 :             :         {
      98                 :           0 :                 pg_log_error("\\copy: arguments required");
      99                 :           0 :                 return NULL;
     100                 :             :         }
     101                 :             : 
     102                 :          11 :         result = pg_malloc0(sizeof(struct copy_options));
     103                 :             : 
     104                 :          11 :         result->before_tofrom = pg_strdup("");     /* initialize for appending */
     105                 :             : 
     106                 :          22 :         token = strtokx(args, whitespace, ".,()", "\"",
     107                 :          11 :                                         0, false, false, pset.encoding);
     108         [ +  - ]:          11 :         if (!token)
     109                 :           0 :                 goto error;
     110                 :             : 
     111                 :             :         /* The following can be removed when we drop 7.3 syntax support */
     112         [ +  - ]:          11 :         if (pg_strcasecmp(token, "binary") == 0)
     113                 :             :         {
     114                 :           0 :                 xstrcat(&result->before_tofrom, token);
     115                 :           0 :                 token = strtokx(NULL, whitespace, ".,()", "\"",
     116                 :           0 :                                                 0, false, false, pset.encoding);
     117         [ #  # ]:           0 :                 if (!token)
     118                 :           0 :                         goto error;
     119                 :           0 :         }
     120                 :             : 
     121                 :             :         /* Handle COPY (query) case */
     122         [ +  + ]:          11 :         if (token[0] == '(')
     123                 :             :         {
     124                 :           4 :                 int                     parens = 1;
     125                 :             : 
     126         [ +  + ]:          61 :                 while (parens > 0)
     127                 :             :                 {
     128                 :          57 :                         xstrcat(&result->before_tofrom, " ");
     129                 :          57 :                         xstrcat(&result->before_tofrom, token);
     130                 :         114 :                         token = strtokx(NULL, whitespace, "()", "\"'",
     131                 :          57 :                                                         nonstd_backslash, true, false, pset.encoding);
     132         [ -  + ]:          57 :                         if (!token)
     133                 :           0 :                                 goto error;
     134         [ +  + ]:          57 :                         if (token[0] == '(')
     135                 :           3 :                                 parens++;
     136         [ +  + ]:          54 :                         else if (token[0] == ')')
     137                 :           7 :                                 parens--;
     138                 :             :                 }
     139      [ -  -  + ]:           4 :         }
     140                 :             : 
     141                 :          11 :         xstrcat(&result->before_tofrom, " ");
     142                 :          11 :         xstrcat(&result->before_tofrom, token);
     143                 :          22 :         token = strtokx(NULL, whitespace, ".,()", "\"",
     144                 :          11 :                                         0, false, false, pset.encoding);
     145         [ -  + ]:          11 :         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         [ +  - ]:          11 :         if (token[0] == '.')
     153                 :             :         {
     154                 :             :                 /* handle schema . table */
     155                 :           0 :                 xstrcat(&result->before_tofrom, token);
     156                 :           0 :                 token = strtokx(NULL, whitespace, ".,()", "\"",
     157                 :           0 :                                                 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 :                                                 0, false, false, pset.encoding);
     163         [ #  # ]:           0 :                 if (!token)
     164                 :           0 :                         goto error;
     165                 :           0 :         }
     166                 :             : 
     167         [ +  - ]:          11 :         if (token[0] == '(')
     168                 :             :         {
     169                 :             :                 /* handle parenthesized column list */
     170                 :           0 :                 for (;;)
     171                 :             :                 {
     172                 :           0 :                         xstrcat(&result->before_tofrom, " ");
     173                 :           0 :                         xstrcat(&result->before_tofrom, token);
     174                 :           0 :                         token = strtokx(NULL, whitespace, "()", "\"",
     175                 :           0 :                                                         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 :                                                 0, false, false, pset.encoding);
     185         [ #  # ]:           0 :                 if (!token)
     186                 :           0 :                         goto error;
     187                 :           0 :         }
     188                 :             : 
     189         [ +  + ]:          11 :         if (pg_strcasecmp(token, "from") == 0)
     190                 :           1 :                 result->from = true;
     191         [ -  + ]:          10 :         else if (pg_strcasecmp(token, "to") == 0)
     192                 :          10 :                 result->from = false;
     193                 :             :         else
     194                 :           0 :                 goto error;
     195                 :             : 
     196                 :             :         /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
     197                 :          22 :         token = strtokx(NULL, whitespace, ";", "'",
     198                 :          11 :                                         0, false, false, pset.encoding);
     199         [ +  - ]:          11 :         if (!token)
     200                 :           0 :                 goto error;
     201                 :             : 
     202         [ +  - ]:          11 :         if (pg_strcasecmp(token, "program") == 0)
     203                 :             :         {
     204                 :           0 :                 int                     toklen;
     205                 :             : 
     206                 :           0 :                 token = strtokx(NULL, whitespace, ";", "'",
     207                 :           0 :                                                 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         [ #  # ]:           0 :         }
     224   [ +  +  +  - ]:          11 :         else if (pg_strcasecmp(token, "stdin") == 0 ||
     225                 :          10 :                          pg_strcasecmp(token, "stdout") == 0)
     226                 :             :         {
     227                 :          11 :                 result->file = NULL;
     228                 :          11 :         }
     229   [ #  #  #  # ]:           0 :         else if (pg_strcasecmp(token, "pstdin") == 0 ||
     230                 :           0 :                          pg_strcasecmp(token, "pstdout") == 0)
     231                 :             :         {
     232                 :           0 :                 result->psql_inout = true;
     233                 :           0 :                 result->file = NULL;
     234                 :           0 :         }
     235                 :             :         else
     236                 :             :         {
     237                 :             :                 /* filename can be optionally quoted */
     238                 :           0 :                 strip_quotes(token, '\'', 0, pset.encoding);
     239                 :           0 :                 result->file = pg_strdup(token);
     240                 :           0 :                 expand_tilde(&result->file);
     241                 :             :         }
     242                 :             : 
     243                 :             :         /* Collect the rest of the line (COPY options) */
     244                 :          11 :         token = strtokx(NULL, "", NULL, NULL,
     245                 :          11 :                                         0, false, false, pset.encoding);
     246         [ +  + ]:          11 :         if (token)
     247                 :           7 :                 result->after_tofrom = pg_strdup(token);
     248                 :             : 
     249                 :          11 :         return result;
     250                 :             : 
     251                 :             : 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                 :          11 : }
     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                 :          11 : do_copy(const char *args)
     269                 :             : {
     270                 :          11 :         PQExpBufferData query;
     271                 :          11 :         FILE       *copystream;
     272                 :          11 :         struct copy_options *options;
     273                 :          11 :         bool            success;
     274                 :             : 
     275                 :             :         /* parse options */
     276                 :          11 :         options = parse_slash_copy(args);
     277                 :             : 
     278         [ +  - ]:          11 :         if (!options)
     279                 :           0 :                 return false;
     280                 :             : 
     281                 :             :         /* prepare to read or write the target file */
     282   [ -  +  #  # ]:          11 :         if (options->file && !options->program)
     283                 :           0 :                 canonicalize_path_enc(options->file, pset.encoding);
     284                 :             : 
     285         [ +  + ]:          11 :         if (options->from)
     286                 :             :         {
     287         [ -  + ]:           1 :                 if (options->file)
     288                 :             :                 {
     289         [ #  # ]:           0 :                         if (options->program)
     290                 :             :                         {
     291                 :           0 :                                 fflush(NULL);
     292                 :           0 :                                 errno = 0;
     293                 :           0 :                                 copystream = popen(options->file, PG_BINARY_R);
     294                 :           0 :                         }
     295                 :             :                         else
     296                 :           0 :                                 copystream = fopen(options->file, PG_BINARY_R);
     297                 :           0 :                 }
     298         [ -  + ]:           1 :                 else if (!options->psql_inout)
     299                 :           1 :                         copystream = pset.cur_cmd_source;
     300                 :             :                 else
     301                 :           0 :                         copystream = stdin;
     302                 :           1 :         }
     303                 :             :         else
     304                 :             :         {
     305         [ -  + ]:          10 :                 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                 :           0 :                         }
     314                 :             :                         else
     315                 :           0 :                                 copystream = fopen(options->file, PG_BINARY_W);
     316                 :           0 :                 }
     317         [ -  + ]:          10 :                 else if (!options->psql_inout)
     318                 :          10 :                         copystream = pset.queryFout;
     319                 :             :                 else
     320                 :           0 :                         copystream = stdout;
     321                 :             :         }
     322                 :             : 
     323         [ +  - ]:          11 :         if (!copystream)
     324                 :             :         {
     325         [ #  # ]:           0 :                 if (options->program)
     326                 :           0 :                         pg_log_error("could not execute command \"%s\": %m",
     327                 :             :                                                  options->file);
     328                 :             :                 else
     329                 :           0 :                         pg_log_error("%s: %m",
     330                 :             :                                                  options->file);
     331                 :           0 :                 free_copy_options(options);
     332                 :           0 :                 return false;
     333                 :             :         }
     334                 :             : 
     335         [ -  + ]:          11 :         if (!options->program)
     336                 :             :         {
     337                 :          11 :                 struct stat st;
     338                 :          11 :                 int                     result;
     339                 :             : 
     340                 :             :                 /* make sure the specified file is not a directory */
     341         [ +  - ]:          11 :                 if ((result = fstat(fileno(copystream), &st)) < 0)
     342                 :           0 :                         pg_log_error("could not stat file \"%s\": %m",
     343                 :             :                                                  options->file);
     344                 :             : 
     345   [ +  -  +  - ]:          11 :                 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   [ +  -  -  + ]:          11 :                 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         [ -  + ]:          11 :         }
     356                 :             : 
     357                 :             :         /* build the command we will send to the backend */
     358                 :          11 :         initPQExpBuffer(&query);
     359                 :          11 :         printfPQExpBuffer(&query, "COPY ");
     360                 :          11 :         appendPQExpBufferStr(&query, options->before_tofrom);
     361         [ +  + ]:          11 :         if (options->from)
     362                 :           1 :                 appendPQExpBufferStr(&query, " FROM STDIN ");
     363                 :             :         else
     364                 :          10 :                 appendPQExpBufferStr(&query, " TO STDOUT ");
     365         [ +  + ]:          11 :         if (options->after_tofrom)
     366                 :           7 :                 appendPQExpBufferStr(&query, options->after_tofrom);
     367                 :             : 
     368                 :             :         /* run it like a user command, but with copystream as data source/sink */
     369                 :          11 :         pset.copyStream = copystream;
     370                 :          11 :         success = SendQuery(query.data);
     371                 :          11 :         pset.copyStream = NULL;
     372                 :          11 :         termPQExpBuffer(&query);
     373                 :             : 
     374         [ +  - ]:          11 :         if (options->file != NULL)
     375                 :             :         {
     376         [ #  # ]:           0 :                 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                 :           0 :                                 }
     392                 :           0 :                                 success = false;
     393                 :           0 :                         }
     394                 :           0 :                         SetShellResultVariables(pclose_rc);
     395                 :           0 :                         restore_sigpipe_trap();
     396                 :           0 :                 }
     397                 :             :                 else
     398                 :             :                 {
     399         [ #  # ]:           0 :                         if (fclose(copystream) != 0)
     400                 :             :                         {
     401                 :           0 :                                 pg_log_error("%s: %m", options->file);
     402                 :           0 :                                 success = false;
     403                 :           0 :                         }
     404                 :             :                 }
     405                 :           0 :         }
     406                 :          11 :         free_copy_options(options);
     407                 :          11 :         return success;
     408                 :          11 : }
     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                 :          89 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
     435                 :             : {
     436                 :          89 :         bool            OK = true;
     437                 :          89 :         char       *buf;
     438                 :          89 :         int                     ret;
     439                 :             : 
     440                 :         410 :         for (;;)
     441                 :             :         {
     442                 :         410 :                 ret = PQgetCopyData(conn, &buf, 0);
     443                 :             : 
     444         [ +  + ]:         410 :                 if (ret < 0)
     445                 :          89 :                         break;                          /* done or server/connection error */
     446                 :             : 
     447         [ -  + ]:         321 :                 if (buf)
     448                 :             :                 {
     449   [ +  -  +  -  :         321 :                         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                 :           0 :                         }
     455                 :         321 :                         PQfreemem(buf);
     456                 :         321 :                 }
     457                 :             :         }
     458                 :             : 
     459   [ +  -  +  -  :          89 :         if (OK && copystream && fflush(copystream))
                   +  - ]
     460                 :             :         {
     461                 :           0 :                 pg_log_error("could not write COPY data: %m");
     462                 :           0 :                 OK = false;
     463                 :           0 :         }
     464                 :             : 
     465         [ +  - ]:          89 :         if (ret == -2)
     466                 :             :         {
     467                 :           0 :                 pg_log_error("COPY data transfer failed: %s", PQerrorMessage(conn));
     468                 :           0 :                 OK = false;
     469                 :           0 :         }
     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                 :          89 :         *res = PQgetResult(conn);
     484         [ +  - ]:          89 :         if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     485                 :             :         {
     486                 :           0 :                 pg_log_info("%s", PQerrorMessage(conn));
     487                 :           0 :                 OK = false;
     488                 :           0 :         }
     489                 :             : 
     490                 :         178 :         return OK;
     491                 :          89 : }
     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                 :         132 : handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res)
     512                 :             : {
     513                 :         132 :         bool            OK;
     514                 :         132 :         char            buf[COPYBUFSIZ];
     515                 :         132 :         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         [ -  + ]:         132 :         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         [ -  + ]:         132 :         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                 :           0 :         }
     542                 :             :         else
     543                 :         132 :                 showprompt = false;
     544                 :             : 
     545                 :         132 :         OK = true;
     546                 :             : 
     547         [ -  + ]:         132 :         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                 :           0 :                 }
     557                 :             : 
     558                 :           0 :                 for (;;)
     559                 :             :                 {
     560                 :           0 :                         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         [ #  # ]:           0 :                 }
     578                 :           0 :         }
     579                 :             :         else
     580                 :             :         {
     581                 :         132 :                 bool            copydone = false;
     582                 :         132 :                 int                     buflen;
     583                 :         132 :                 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                 :         132 :                 buflen = 0;
     592         [ +  + ]:         624 :                 while (!copydone)
     593                 :             :                 {
     594                 :         492 :                         char       *fgresult;
     595                 :             : 
     596   [ +  -  +  - ]:         492 :                         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                 :           0 :                         }
     603                 :             : 
     604                 :             :                         /* enable longjmp while waiting for input */
     605                 :         492 :                         sigint_interrupt_enabled = true;
     606                 :             : 
     607                 :         492 :                         fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
     608                 :             : 
     609                 :         492 :                         sigint_interrupt_enabled = false;
     610                 :             : 
     611         [ +  - ]:         492 :                         if (!fgresult)
     612                 :           0 :                                 copydone = true;
     613                 :             :                         else
     614                 :             :                         {
     615                 :         492 :                                 int                     linelen;
     616                 :             : 
     617                 :         492 :                                 linelen = strlen(fgresult);
     618                 :         492 :                                 buflen += linelen;
     619                 :             : 
     620                 :             :                                 /* current line is done? */
     621         [ +  - ]:         492 :                                 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   [ +  -  -  + ]:         492 :                                         if (at_line_begin && copystream == pset.cur_cmd_source)
     632                 :             :                                         {
     633   [ +  +  +  + ]:         504 :                                                 if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
     634         [ +  + ]:         360 :                                                         (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
     635                 :             :                                                 {
     636                 :         132 :                                                         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                 :         132 :                                                         *fgresult = '\0';
     645                 :         132 :                                                         buflen -= linelen;
     646                 :         132 :                                                 }
     647                 :         492 :                                         }
     648                 :             : 
     649         [ -  + ]:         492 :                                         if (copystream == pset.cur_cmd_source)
     650                 :             :                                         {
     651                 :         492 :                                                 pset.lineno++;
     652                 :         492 :                                                 pset.stmt_lineno++;
     653                 :         492 :                                         }
     654                 :         492 :                                         at_line_begin = true;
     655                 :         492 :                                 }
     656                 :             :                                 else
     657                 :           0 :                                         at_line_begin = false;
     658                 :         492 :                         }
     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   [ +  -  +  +  :         492 :                         if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
                   +  + ]
     669                 :             :                         {
     670         [ -  + ]:         131 :                                 if (PQputCopyData(conn, buf, buflen) <= 0)
     671                 :             :                                 {
     672                 :           0 :                                         OK = false;
     673                 :           0 :                                         break;
     674                 :             :                                 }
     675                 :             : 
     676                 :         131 :                                 buflen = 0;
     677                 :         131 :                         }
     678         [ -  + ]:         492 :                 }
     679                 :         132 :         }
     680                 :             : 
     681                 :             :         /* Check for read error */
     682         [ +  - ]:         132 :         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                 :         264 :         if (PQputCopyEnd(conn,
     692   [ -  +  #  # ]:         132 :                                          (OK || PQprotocolVersion(conn) < 3) ? NULL :
     693         [ +  - ]:         132 :                                          _("aborted because of read failure")) <= 0)
     694                 :           0 :                 OK = false;
     695                 :             : 
     696                 :             : 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                 :         132 :         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         [ -  + ]:         132 :         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         [ +  + ]:         132 :         if (PQresultStatus(*res) != PGRES_COMMAND_OK)
     730                 :             :         {
     731                 :          33 :                 pg_log_info("%s", PQerrorMessage(conn));
     732                 :          33 :                 OK = false;
     733                 :          33 :         }
     734                 :             : 
     735                 :         264 :         return OK;
     736                 :         132 : }
        

Generated by: LCOV version 2.3.2-1