LCOV - differential code coverage report
Current view: top level - src/bin/psql - copy.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 55.7 % 271 151 120 151
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 6 6 6
Baseline: 16@8cea358b128 Branches: 53.5 % 198 106 92 106
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 55.7 % 271 151 120 151
Function coverage date bins:
(240..) days: 100.0 % 6 6 6
Branch coverage date bins:
(240..) days: 53.5 % 198 106 92 106

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * psql - the PostgreSQL interactive terminal
                                  3                 :                :  *
                                  4                 :                :  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
                                  5                 :                :  *
                                  6                 :                :  * src/bin/psql/copy.c
                                  7                 :                :  */
                                  8                 :                : #include "postgres_fe.h"
                                  9                 :                : 
                                 10                 :                : #include <signal.h>
                                 11                 :                : #include <sys/stat.h>
                                 12                 :                : #ifndef WIN32
                                 13                 :                : #include <unistd.h>               /* for isatty */
                                 14                 :                : #else
                                 15                 :                : #include <io.h>                   /* I think */
                                 16                 :                : #endif
                                 17                 :                : 
                                 18                 :                : #include "common.h"
                                 19                 :                : #include "common/logging.h"
                                 20                 :                : #include "copy.h"
                                 21                 :                : #include "libpq-fe.h"
                                 22                 :                : #include "pqexpbuffer.h"
                                 23                 :                : #include "prompt.h"
                                 24                 :                : #include "settings.h"
                                 25                 :                : #include "stringutils.h"
                                 26                 :                : 
                                 27                 :                : /*
                                 28                 :                :  * parse_slash_copy
                                 29                 :                :  * -- parses \copy command line
                                 30                 :                :  *
                                 31                 :                :  * The documented syntax is:
                                 32                 :                :  *  \copy tablename [(columnlist)] from|to filename [options]
                                 33                 :                :  *  \copy ( query stmt ) to filename [options]
                                 34                 :                :  *
                                 35                 :                :  * where 'filename' can be one of the following:
                                 36                 :                :  *  '<file path>' | PROGRAM '<command>' | stdin | stdout | pstdout | pstdout
                                 37                 :                :  * and 'query' can be one of the following:
                                 38                 :                :  *  SELECT | UPDATE | INSERT | DELETE
                                 39                 :                :  *
                                 40                 :                :  * An undocumented fact is that you can still write BINARY before the
                                 41                 :                :  * tablename; this is a hangover from the pre-7.3 syntax.  The options
                                 42                 :                :  * syntax varies across backend versions, but we avoid all that mess
                                 43                 :                :  * by just transmitting the stuff after the filename literally.
                                 44                 :                :  *
                                 45                 :                :  * table name can be double-quoted and can have a schema part.
                                 46                 :                :  * column names can be double-quoted.
                                 47                 :                :  * filename can be single-quoted like SQL literals.
                                 48                 :                :  * command must be single-quoted like SQL literals.
                                 49                 :                :  *
                                 50                 :                :  * returns a malloc'ed structure with the options, or NULL on parsing error
                                 51                 :                :  */
                                 52                 :                : 
                                 53                 :                : struct copy_options
                                 54                 :                : {
                                 55                 :                :     char       *before_tofrom;  /* COPY string before TO/FROM */
                                 56                 :                :     char       *after_tofrom;   /* COPY string after TO/FROM filename */
                                 57                 :                :     char       *file;           /* NULL = stdin/stdout */
                                 58                 :                :     bool        program;        /* is 'file' a program to popen? */
                                 59                 :                :     bool        psql_inout;     /* true = use psql stdin/stdout */
                                 60                 :                :     bool        from;           /* true = FROM, false = TO */
                                 61                 :                : };
                                 62                 :                : 
                                 63                 :                : 
                                 64                 :                : static void
 2489 tgl@sss.pgh.pa.us          65                 :CBC          81 : free_copy_options(struct copy_options *ptr)
                                 66                 :                : {
 8928 bruce@momjian.us           67         [ -  + ]:             81 :     if (!ptr)
 8928 bruce@momjian.us           68                 :UBC           0 :         return;
 5321 tgl@sss.pgh.pa.us          69                 :CBC          81 :     free(ptr->before_tofrom);
                                 70                 :             81 :     free(ptr->after_tofrom);
 8928 bruce@momjian.us           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
 7848 tgl@sss.pgh.pa.us          78                 :            504 : xstrcat(char **var, const char *more)
                                 79                 :                : {
                                 80                 :                :     char       *newvar;
                                 81                 :                : 
 3827                            82                 :            504 :     newvar = psprintf("%s%s", *var, more);
 7848                            83                 :            504 :     free(*var);
                                 84                 :            504 :     *var = newvar;
                                 85                 :            504 : }
                                 86                 :                : 
                                 87                 :                : 
                                 88                 :                : static struct copy_options *
 8853 peter_e@gmx.net            89                 :             81 : parse_slash_copy(const char *args)
                                 90                 :                : {
                                 91                 :                :     struct copy_options *result;
                                 92                 :                :     char       *token;
 7848 tgl@sss.pgh.pa.us          93                 :             81 :     const char *whitespace = " \t\n\r";
 6527                            94         [ +  - ]:             81 :     char        nonstd_backslash = standard_strings() ? 0 : '\\';
                                 95                 :                : 
 5321                            96         [ -  + ]:             81 :     if (!args)
                                 97                 :                :     {
 1840 peter@eisentraut.org       98                 :UBC           0 :         pg_log_error("\\copy: arguments required");
 8764 peter_e@gmx.net            99                 :              0 :         return NULL;
                                100                 :                :     }
                                101                 :                : 
 4212 tgl@sss.pgh.pa.us         102                 :CBC          81 :     result = pg_malloc0(sizeof(struct copy_options));
                                103                 :                : 
 2489                           104                 :             81 :     result->before_tofrom = pg_strdup(""); /* initialize for appending */
                                105                 :                : 
 5321                           106                 :             81 :     token = strtokx(args, whitespace, ".,()", "\"",
                                107                 :                :                     0, false, false, pset.encoding);
 8928 bruce@momjian.us          108         [ -  + ]:             81 :     if (!token)
 7848 tgl@sss.pgh.pa.us         109                 :UBC           0 :         goto error;
                                110                 :                : 
                                111                 :                :     /* The following can be removed when we drop 7.3 syntax support */
 7282 tgl@sss.pgh.pa.us         112         [ -  + ]:CBC          81 :     if (pg_strcasecmp(token, "binary") == 0)
                                113                 :                :     {
 5321 tgl@sss.pgh.pa.us         114                 :UBC           0 :         xstrcat(&result->before_tofrom, token);
 7848                           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 */
 6437 tgl@sss.pgh.pa.us         122         [ +  + ]:CBC          81 :     if (token[0] == '(')
                                123                 :                :     {
 6402 bruce@momjian.us          124                 :             12 :         int         parens = 1;
                                125                 :                : 
 6437 tgl@sss.pgh.pa.us         126         [ +  + ]:            183 :         while (parens > 0)
                                127                 :                :         {
 5321                           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);
 6437                           132         [ -  + ]:            171 :             if (!token)
 6437 tgl@sss.pgh.pa.us         133                 :UBC           0 :                 goto error;
 6437 tgl@sss.pgh.pa.us         134         [ +  + ]:CBC         171 :             if (token[0] == '(')
                                135                 :              9 :                 parens++;
                                136         [ +  + ]:            162 :             else if (token[0] == ')')
                                137                 :             21 :                 parens--;
                                138                 :                :         }
                                139                 :                :     }
                                140                 :                : 
 5321                           141                 :             81 :     xstrcat(&result->before_tofrom, " ");
                                142                 :             81 :     xstrcat(&result->before_tofrom, token);
 7848                           143                 :             81 :     token = strtokx(NULL, whitespace, ".,()", "\"",
                                144                 :                :                     0, false, false, pset.encoding);
                                145         [ -  + ]:             81 :     if (!token)
 7848 tgl@sss.pgh.pa.us         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                 :                :      */
 7848 tgl@sss.pgh.pa.us         152         [ -  + ]:CBC          81 :     if (token[0] == '.')
                                153                 :                :     {
                                154                 :                :         /* handle schema . table */
 5321 tgl@sss.pgh.pa.us         155                 :UBC           0 :         xstrcat(&result->before_tofrom, token);
 7848                           156                 :              0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
                                157                 :                :                         0, false, false, pset.encoding);
 8928 bruce@momjian.us          158         [ #  # ]:              0 :         if (!token)
 7848 tgl@sss.pgh.pa.us         159                 :              0 :             goto error;
 5321                           160                 :              0 :         xstrcat(&result->before_tofrom, token);
 7848                           161                 :              0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
                                162                 :                :                         0, false, false, pset.encoding);
                                163         [ #  # ]:              0 :         if (!token)
                                164                 :              0 :             goto error;
                                165                 :                :     }
                                166                 :                : 
 7848 tgl@sss.pgh.pa.us         167         [ -  + ]:CBC          81 :     if (token[0] == '(')
                                168                 :                :     {
                                169                 :                :         /* handle parenthesized column list */
                                170                 :                :         for (;;)
                                171                 :                :         {
 5321 tgl@sss.pgh.pa.us         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);
 7848                           176         [ #  # ]:              0 :             if (!token)
                                177                 :              0 :                 goto error;
                                178         [ #  # ]:              0 :             if (token[0] == ')')
                                179                 :              0 :                 break;
                                180                 :                :         }
 5321                           181                 :              0 :         xstrcat(&result->before_tofrom, " ");
                                182                 :              0 :         xstrcat(&result->before_tofrom, token);
 7848                           183                 :              0 :         token = strtokx(NULL, whitespace, ".,()", "\"",
                                184                 :                :                         0, false, false, pset.encoding);
                                185         [ #  # ]:              0 :         if (!token)
                                186                 :              0 :             goto error;
                                187                 :                :     }
                                188                 :                : 
 7282 tgl@sss.pgh.pa.us         189         [ +  + ]:CBC          81 :     if (pg_strcasecmp(token, "from") == 0)
 7848                           190                 :             51 :         result->from = true;
 7282                           191         [ +  - ]:             30 :     else if (pg_strcasecmp(token, "to") == 0)
 7848                           192                 :             30 :         result->from = false;
                                193                 :                :     else
 7848 tgl@sss.pgh.pa.us         194                 :UBC           0 :         goto error;
                                195                 :                : 
                                196                 :                :     /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
 3869 bruce@momjian.us          197                 :CBC          81 :     token = strtokx(NULL, whitespace, ";", "'",
                                198                 :                :                     0, false, false, pset.encoding);
 7848 tgl@sss.pgh.pa.us         199         [ -  + ]:             81 :     if (!token)
 7848 tgl@sss.pgh.pa.us         200                 :UBC           0 :         goto error;
                                201                 :                : 
 4064 heikki.linnakangas@i      202         [ -  + ]:CBC          81 :     if (pg_strcasecmp(token, "program") == 0)
                                203                 :                :     {
                                204                 :                :         int         toklen;
                                205                 :                : 
 3869 bruce@momjian.us          206                 :UBC           0 :         token = strtokx(NULL, whitespace, ";", "'",
                                207                 :                :                         0, false, false, pset.encoding);
 4064 heikki.linnakangas@i      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                 :                :     }
 4064 heikki.linnakangas@i      224   [ +  +  +  + ]:CBC         157 :     else if (pg_strcasecmp(token, "stdin") == 0 ||
                                225                 :             76 :              pg_strcasecmp(token, "stdout") == 0)
                                226                 :                :     {
 7390 tgl@sss.pgh.pa.us         227                 :             35 :         result->file = NULL;
                                228                 :                :     }
 7282                           229   [ +  -  -  + ]:             92 :     else if (pg_strcasecmp(token, "pstdin") == 0 ||
 7168 bruce@momjian.us          230                 :             46 :              pg_strcasecmp(token, "pstdout") == 0)
                                231                 :                :     {
 7307 bruce@momjian.us          232                 :UBC           0 :         result->psql_inout = true;
 7848 tgl@sss.pgh.pa.us         233                 :              0 :         result->file = NULL;
                                234                 :                :     }
                                235                 :                :     else
                                236                 :                :     {
                                237                 :                :         /* filename can be optionally quoted */
 4064 heikki.linnakangas@i      238                 :CBC          46 :         strip_quotes(token, '\'', 0, pset.encoding);
 7385 neilc@samurai.com         239                 :             46 :         result->file = pg_strdup(token);
 7390 tgl@sss.pgh.pa.us         240                 :             46 :         expand_tilde(&result->file);
                                241                 :                :     }
                                242                 :                : 
                                243                 :                :     /* Collect the rest of the line (COPY options) */
 5321                           244                 :             81 :     token = strtokx(NULL, "", NULL, NULL,
                                245                 :                :                     0, false, false, pset.encoding);
 7848                           246         [ +  + ]:             81 :     if (token)
 5321                           247                 :             25 :         result->after_tofrom = pg_strdup(token);
                                248                 :                : 
 7848                           249                 :             81 :     return result;
                                250                 :                : 
 7848 tgl@sss.pgh.pa.us         251                 :UBC           0 : error:
                                252         [ #  # ]:              0 :     if (token)
 1840 peter@eisentraut.org      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");
 7848 tgl@sss.pgh.pa.us         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
 8853 peter_e@gmx.net           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                 :                : 
 8928 bruce@momjian.us          278         [ -  + ]:             81 :     if (!options)
 8928 bruce@momjian.us          279                 :UBC           0 :         return false;
                                280                 :                : 
                                281                 :                :     /* prepare to read or write the target file */
 4064 heikki.linnakangas@i      282   [ +  +  +  - ]:CBC          81 :     if (options->file && !options->program)
 7184 tgl@sss.pgh.pa.us         283                 :             46 :         canonicalize_path(options->file);
                                284                 :                : 
 8928 bruce@momjian.us          285         [ +  + ]:             81 :     if (options->from)
                                286                 :                :     {
 8768                           287         [ +  + ]:             51 :         if (options->file)
                                288                 :                :         {
 4064 heikki.linnakangas@i      289         [ -  + ]:             46 :             if (options->program)
                                290                 :                :             {
  594 tgl@sss.pgh.pa.us         291                 :UBC           0 :                 fflush(NULL);
 4064 heikki.linnakangas@i      292                 :              0 :                 errno = 0;
                                293                 :              0 :                 copystream = popen(options->file, PG_BINARY_R);
                                294                 :                :             }
                                295                 :                :             else
 4064 heikki.linnakangas@i      296                 :CBC          46 :                 copystream = fopen(options->file, PG_BINARY_R);
                                297                 :                :         }
 7307 bruce@momjian.us          298         [ +  - ]:              5 :         else if (!options->psql_inout)
 7168                           299                 :              5 :             copystream = pset.cur_cmd_source;
                                300                 :                :         else
 7168 bruce@momjian.us          301                 :UBC           0 :             copystream = stdin;
                                302                 :                :     }
                                303                 :                :     else
                                304                 :                :     {
 8768 bruce@momjian.us          305         [ -  + ]:CBC          30 :         if (options->file)
                                306                 :                :         {
 4064 heikki.linnakangas@i      307         [ #  # ]:UBC           0 :             if (options->program)
                                308                 :                :             {
  594 tgl@sss.pgh.pa.us         309                 :              0 :                 fflush(NULL);
 3055                           310                 :              0 :                 disable_sigpipe_trap();
  594                           311                 :              0 :                 errno = 0;
 4064 heikki.linnakangas@i      312                 :              0 :                 copystream = popen(options->file, PG_BINARY_W);
                                313                 :                :             }
                                314                 :                :             else
                                315                 :              0 :                 copystream = fopen(options->file, PG_BINARY_W);
                                316                 :                :         }
 7307 bruce@momjian.us          317         [ +  - ]:CBC          30 :         else if (!options->psql_inout)
 7168                           318                 :             30 :             copystream = pset.queryFout;
                                319                 :                :         else
 8768 bruce@momjian.us          320                 :UBC           0 :             copystream = stdout;
                                321                 :                :     }
                                322                 :                : 
 8928 bruce@momjian.us          323         [ +  + ]:CBC          81 :     if (!copystream)
                                324                 :                :     {
 4064 heikki.linnakangas@i      325         [ -  + ]:              5 :         if (options->program)
 1840 peter@eisentraut.org      326                 :UBC           0 :             pg_log_error("could not execute command \"%s\": %m",
                                327                 :                :                          options->file);
                                328                 :                :         else
 1840 peter@eisentraut.org      329                 :CBC           5 :             pg_log_error("%s: %m",
                                330                 :                :                          options->file);
 8928 bruce@momjian.us          331                 :              5 :         free_copy_options(options);
                                332                 :              5 :         return false;
                                333                 :                :     }
                                334                 :                : 
 4064 heikki.linnakangas@i      335         [ +  - ]:             76 :     if (!options->program)
                                336                 :                :     {
                                337                 :                :         struct stat st;
                                338                 :                :         int         result;
                                339                 :                : 
                                340                 :                :         /* make sure the specified file is not a directory */
 3697 sfrost@snowman.net        341         [ -  + ]:             76 :         if ((result = fstat(fileno(copystream), &st)) < 0)
 1840 peter@eisentraut.org      342                 :UBC           0 :             pg_log_error("could not stat file \"%s\": %m",
                                343                 :                :                          options->file);
                                344                 :                : 
 3697 sfrost@snowman.net        345   [ +  -  -  + ]:CBC          76 :         if (result == 0 && S_ISDIR(st.st_mode))
 1840 peter@eisentraut.org      346                 :UBC           0 :             pg_log_error("%s: cannot copy from/to a directory",
                                347                 :                :                          options->file);
                                348                 :                : 
 3697 sfrost@snowman.net        349   [ +  -  -  + ]:CBC          76 :         if (result < 0 || S_ISDIR(st.st_mode))
                                350                 :                :         {
 3697 sfrost@snowman.net        351                 :UBC           0 :             fclose(copystream);
 4064 heikki.linnakangas@i      352                 :              0 :             free_copy_options(options);
                                353                 :              0 :             return false;
                                354                 :                :         }
                                355                 :                :     }
                                356                 :                : 
                                357                 :                :     /* build the command we will send to the backend */
 5321 tgl@sss.pgh.pa.us         358                 :CBC          76 :     initPQExpBuffer(&query);
                                359                 :             76 :     printfPQExpBuffer(&query, "COPY ");
                                360                 :             76 :     appendPQExpBufferStr(&query, options->before_tofrom);
                                361         [ +  + ]:             76 :     if (options->from)
 3800 heikki.linnakangas@i      362                 :             46 :         appendPQExpBufferStr(&query, " FROM STDIN ");
                                363                 :                :     else
                                364                 :             30 :         appendPQExpBufferStr(&query, " TO STDOUT ");
 5321 tgl@sss.pgh.pa.us         365         [ +  + ]:             76 :     if (options->after_tofrom)
                                366                 :             22 :         appendPQExpBufferStr(&query, options->after_tofrom);
                                367                 :                : 
                                368                 :                :     /* run it like a user command, but with copystream as data source/sink */
 3688                           369                 :             76 :     pset.copyStream = copystream;
 4463 alvherre@alvh.no-ip.      370                 :             76 :     success = SendQuery(query.data);
 3688 tgl@sss.pgh.pa.us         371                 :             76 :     pset.copyStream = NULL;
 8026 peter_e@gmx.net           372                 :             76 :     termPQExpBuffer(&query);
                                373                 :                : 
 7168 bruce@momjian.us          374         [ +  + ]:             76 :     if (options->file != NULL)
                                375                 :                :     {
 4064 heikki.linnakangas@i      376         [ -  + ]:             41 :         if (options->program)
                                377                 :                :         {
 3973 bruce@momjian.us          378                 :UBC           0 :             int         pclose_rc = pclose(copystream);
                                379                 :                : 
 4064 heikki.linnakangas@i      380         [ #  # ]:              0 :             if (pclose_rc != 0)
                                381                 :                :             {
                                382         [ #  # ]:              0 :                 if (pclose_rc < 0)
 1840 peter@eisentraut.org      383                 :              0 :                     pg_log_error("could not close pipe to external command: %m");
                                384                 :                :                 else
                                385                 :                :                 {
 3973 bruce@momjian.us          386                 :              0 :                     char       *reason = wait_result_to_str(pclose_rc);
                                387                 :                : 
 1840 peter@eisentraut.org      388         [ #  # ]:              0 :                     pg_log_error("%s: %s", options->file,
                                389                 :                :                                  reason ? reason : "");
  668                           390                 :              0 :                     free(reason);
                                391                 :                :                 }
 4064 heikki.linnakangas@i      392                 :              0 :                 success = false;
                                393                 :                :             }
  374 tgl@sss.pgh.pa.us         394                 :              0 :             SetShellResultVariables(pclose_rc);
 3055                           395                 :              0 :             restore_sigpipe_trap();
                                396                 :                :         }
                                397                 :                :         else
                                398                 :                :         {
 4064 heikki.linnakangas@i      399         [ -  + ]:CBC          41 :             if (fclose(copystream) != 0)
                                400                 :                :             {
 1840 peter@eisentraut.org      401                 :UBC           0 :                 pg_log_error("%s: %m", options->file);
 4064 heikki.linnakangas@i      402                 :              0 :                 success = false;
                                403                 :                :             }
                                404                 :                :         }
                                405                 :                :     }
 8928 bruce@momjian.us          406                 :CBC          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                 :                :  */
                                433                 :                : bool
 3685 tgl@sss.pgh.pa.us         434                 :            249 : handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
                                435                 :                : {
 6402 bruce@momjian.us          436                 :            249 :     bool        OK = true;
                                437                 :                :     char       *buf;
                                438                 :                :     int         ret;
                                439                 :                : 
                                440                 :                :     for (;;)
                                441                 :                :     {
 6617 tgl@sss.pgh.pa.us         442                 :           1126 :         ret = PQgetCopyData(conn, &buf, 0);
                                443                 :                : 
                                444         [ +  + ]:           1126 :         if (ret < 0)
 3713                           445                 :            249 :             break;              /* done or server/connection error */
                                446                 :                : 
 6617                           447         [ +  - ]:            877 :         if (buf)
                                448                 :                :         {
 1905                           449   [ +  -  +  -  :            877 :             if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret)
                                              -  + ]
                                450                 :                :             {
 1840 peter@eisentraut.org      451                 :UBC           0 :                 pg_log_error("could not write COPY data: %m");
                                452                 :                :                 /* complain only once, keep reading data from server */
 6533 tgl@sss.pgh.pa.us         453                 :              0 :                 OK = false;
                                454                 :                :             }
 6617 tgl@sss.pgh.pa.us         455                 :CBC         877 :             PQfreemem(buf);
                                456                 :                :         }
                                457                 :                :     }
                                458                 :                : 
 1905                           459   [ +  -  +  -  :            249 :     if (OK && copystream && fflush(copystream))
                                              -  + ]
                                460                 :                :     {
 1840 peter@eisentraut.org      461                 :UBC           0 :         pg_log_error("could not write COPY data: %m");
 6533 tgl@sss.pgh.pa.us         462                 :              0 :         OK = false;
                                463                 :                :     }
                                464                 :                : 
 6617 tgl@sss.pgh.pa.us         465         [ -  + ]:CBC         249 :     if (ret == -2)
                                466                 :                :     {
 1840 peter@eisentraut.org      467                 :UBC           0 :         pg_log_error("COPY data transfer failed: %s", PQerrorMessage(conn));
 6617 tgl@sss.pgh.pa.us         468                 :              0 :         OK = false;
                                469                 :                :     }
                                470                 :                : 
                                471                 :                :     /*
                                472                 :                :      * Check command status and return to normal libpq state.
                                473                 :                :      *
                                474                 :                :      * If for some reason libpq is still reporting PGRES_COPY_OUT state, we
                                475                 :                :      * would like to forcibly exit that state, since our caller would be
                                476                 :                :      * unable to distinguish that situation from reaching the next COPY in a
                                477                 :                :      * command string that happened to contain two consecutive COPY TO STDOUT
                                478                 :                :      * commands.  However, libpq provides no API for doing that, and in
                                479                 :                :      * principle it's a libpq bug anyway if PQgetCopyData() returns -1 or -2
                                480                 :                :      * but hasn't exited COPY_OUT state internally.  So we ignore the
                                481                 :                :      * possibility here.
                                482                 :                :      */
 3685 tgl@sss.pgh.pa.us         483                 :CBC         249 :     *res = PQgetResult(conn);
                                484         [ +  + ]:            249 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
                                485                 :                :     {
 1840 peter@eisentraut.org      486                 :              1 :         pg_log_info("%s", PQerrorMessage(conn));
 6617 tgl@sss.pgh.pa.us         487                 :              1 :         OK = false;
                                488                 :                :     }
                                489                 :                : 
                                490                 :            249 :     return OK;
                                491                 :                : }
                                492                 :                : 
                                493                 :                : /*
                                494                 :                :  * handleCopyIn
                                495                 :                :  * sends data to complete a COPY ... FROM STDIN command
                                496                 :                :  *
                                497                 :                :  * conn should be a database connection that you just issued COPY FROM on
                                498                 :                :  * and got back a PGRES_COPY_IN result.
                                499                 :                :  * copystream is the file stream to read the data from.
                                500                 :                :  * isbinary can be set from PQbinaryTuples().
                                501                 :                :  * The final status for the COPY is returned into *res (but note
                                502                 :                :  * we already reported the error, if it's not a success result).
                                503                 :                :  *
                                504                 :                :  * result is true if successful, false if not.
                                505                 :                :  */
                                506                 :                : 
                                507                 :                : /* read chunk size for COPY IN - size is not critical */
                                508                 :                : #define COPYBUFSIZ 8192
                                509                 :                : 
                                510                 :                : bool
 3685                           511                 :            431 : handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res)
                                512                 :                : {
                                513                 :                :     bool        OK;
                                514                 :                :     char        buf[COPYBUFSIZ];
                                515                 :                :     bool        showprompt;
                                516                 :                : 
                                517                 :                :     /*
                                518                 :                :      * Establish longjmp destination for exiting from wait-for-input. (This is
                                519                 :                :      * only effective while sigint_interrupt_enabled is TRUE.)
                                520                 :                :      */
 6514                           521         [ -  + ]:            431 :     if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
                                522                 :                :     {
                                523                 :                :         /* got here with longjmp */
                                524                 :                : 
                                525                 :                :         /* Terminate data transfer */
 3713 tgl@sss.pgh.pa.us         526                 :UBC           0 :         PQputCopyEnd(conn,
                                527         [ #  # ]:              0 :                      (PQprotocolVersion(conn) < 3) ? NULL :
                                528                 :              0 :                      _("canceled by user"));
                                529                 :                : 
 4463 alvherre@alvh.no-ip.      530                 :              0 :         OK = false;
                                531                 :              0 :         goto copyin_cleanup;
                                532                 :                :     }
                                533                 :                : 
                                534                 :                :     /* Prompt if interactive input */
 7390 tgl@sss.pgh.pa.us         535         [ -  + ]:CBC         431 :     if (isatty(fileno(copystream)))
                                536                 :                :     {
 3512 andres@anarazel.de        537                 :UBC           0 :         showprompt = true;
 6438 tgl@sss.pgh.pa.us         538         [ #  # ]:              0 :         if (!pset.quiet)
 6991 bruce@momjian.us          539                 :              0 :             puts(_("Enter data to be copied followed by a newline.\n"
                                540                 :                :                    "End with a backslash and a period on a line by itself, or an EOF signal."));
                                541                 :                :     }
                                542                 :                :     else
 3495 andres@anarazel.de        543                 :CBC         431 :         showprompt = false;
                                544                 :                : 
 6514 tgl@sss.pgh.pa.us         545                 :            431 :     OK = true;
                                546                 :                : 
 6533                           547         [ -  + ]:            431 :     if (isbinary)
                                548                 :                :     {
                                549                 :                :         /* interactive input probably silly, but give one prompt anyway */
 3512 andres@anarazel.de        550         [ #  # ]:UBC           0 :         if (showprompt)
                                551                 :                :         {
 2572 tgl@sss.pgh.pa.us         552                 :              0 :             const char *prompt = get_prompt(PROMPT_COPY, NULL);
                                553                 :                : 
 8928 bruce@momjian.us          554                 :              0 :             fputs(prompt, stdout);
                                555                 :              0 :             fflush(stdout);
                                556                 :                :         }
                                557                 :                : 
                                558                 :                :         for (;;)
 6533 tgl@sss.pgh.pa.us         559                 :              0 :         {
                                560                 :                :             int         buflen;
                                561                 :                : 
                                562                 :                :             /* enable longjmp while waiting for input */
 6514                           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                 :                : 
 6533                           572         [ #  # ]:              0 :             if (PQputCopyData(conn, buf, buflen) <= 0)
                                573                 :                :             {
                                574                 :              0 :                 OK = false;
 8928 bruce@momjian.us          575                 :              0 :                 break;
                                576                 :                :             }
                                577                 :                :         }
                                578                 :                :     }
                                579                 :                :     else
                                580                 :                :     {
 6533 tgl@sss.pgh.pa.us         581                 :CBC         431 :         bool        copydone = false;
                                582                 :                :         int         buflen;
 1005 heikki.linnakangas@i      583                 :            431 :         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                 :            431 :         buflen = 0;
 6533 tgl@sss.pgh.pa.us         592         [ +  + ]:         136560 :         while (!copydone)
                                593                 :                :         {
                                594                 :                :             char       *fgresult;
                                595                 :                : 
 1005 heikki.linnakangas@i      596   [ +  +  -  + ]:         136129 :             if (at_line_begin && showprompt)
                                597                 :                :             {
 2572 tgl@sss.pgh.pa.us         598                 :UBC           0 :                 const char *prompt = get_prompt(PROMPT_COPY, NULL);
                                599                 :                : 
 6533                           600                 :              0 :                 fputs(prompt, stdout);
                                601                 :              0 :                 fflush(stdout);
                                602                 :                :             }
                                603                 :                : 
                                604                 :                :             /* enable longjmp while waiting for input */
 1005 heikki.linnakangas@i      605                 :CBC      136129 :             sigint_interrupt_enabled = true;
                                606                 :                : 
                                607                 :         136129 :             fgresult = fgets(&buf[buflen], COPYBUFSIZ - buflen, copystream);
                                608                 :                : 
                                609                 :         136129 :             sigint_interrupt_enabled = false;
                                610                 :                : 
                                611         [ +  + ]:         136129 :             if (!fgresult)
                                612                 :             40 :                 copydone = true;
                                613                 :                :             else
                                614                 :                :             {
                                615                 :                :                 int         linelen;
                                616                 :                : 
                                617                 :         136089 :                 linelen = strlen(fgresult);
                                618                 :         136089 :                 buflen += linelen;
                                619                 :                : 
                                620                 :                :                 /* current line is done? */
                                621         [ +  + ]:         136089 :                 if (buf[buflen - 1] == '\n')
                                622                 :                :                 {
                                623                 :                :                     /* check for EOF marker, but not on a partial line */
                                624         [ +  + ]:         136026 :                     if (at_line_begin)
                                625                 :                :                     {
                                626                 :                :                         /*
                                627                 :                :                          * This code erroneously assumes '\.' on a line alone
                                628                 :                :                          * inside a quoted CSV string terminates the \copy.
                                629                 :                :                          * https://www.postgresql.org/message-id/E1TdNVQ-0001ju-GO@wrigleys.postgresql.org
                                630                 :                :                          *
                                631                 :                :                          * https://www.postgresql.org/message-id/bfcd57e4-8f23-4c3e-a5db-2571d09208e2@beta.fastmail.com
                                632                 :                :                          */
                                633   [ +  +  +  +  :         135967 :                         if ((linelen == 3 && memcmp(fgresult, "\\.\n", 3) == 0) ||
                                              +  + ]
                                634         [ -  + ]:           1238 :                             (linelen == 4 && memcmp(fgresult, "\\.\r\n", 4) == 0))
                                635                 :                :                         {
                                636                 :            391 :                             copydone = true;
                                637                 :                :                         }
                                638                 :                :                     }
                                639                 :                : 
                                640         [ +  + ]:         136026 :                     if (copystream == pset.cur_cmd_source)
                                641                 :                :                     {
                                642                 :         101646 :                         pset.lineno++;
                                643                 :         101646 :                         pset.stmt_lineno++;
                                644                 :                :                     }
                                645                 :         136026 :                     at_line_begin = true;
                                646                 :                :                 }
                                647                 :                :                 else
                                648                 :             63 :                     at_line_begin = false;
                                649                 :                :             }
                                650                 :                : 
                                651                 :                :             /*
                                652                 :                :              * If the buffer is full, or we've reached the EOF, flush it.
                                653                 :                :              *
                                654                 :                :              * Make sure there's always space for four more bytes in the
                                655                 :                :              * buffer, plus a NUL terminator.  That way, an EOF marker is
                                656                 :                :              * never split across two fgets() calls, which simplifies the
                                657                 :                :              * logic.
                                658                 :                :              */
                                659   [ +  +  +  +  :         136129 :             if (buflen >= COPYBUFSIZ - 5 || (copydone && buflen > 0))
                                              +  - ]
                                660                 :                :             {
                                661         [ -  + ]:            580 :                 if (PQputCopyData(conn, buf, buflen) <= 0)
                                662                 :                :                 {
 6533 tgl@sss.pgh.pa.us         663                 :UBC           0 :                     OK = false;
                                664                 :              0 :                     break;
                                665                 :                :                 }
                                666                 :                : 
 1005 heikki.linnakangas@i      667                 :CBC         580 :                 buflen = 0;
                                668                 :                :             }
                                669                 :                :         }
                                670                 :                :     }
                                671                 :                : 
                                672                 :                :     /* Check for read error */
 6533 tgl@sss.pgh.pa.us         673         [ -  + ]:            431 :     if (ferror(copystream))
 6533 tgl@sss.pgh.pa.us         674                 :UBC           0 :         OK = false;
                                675                 :                : 
                                676                 :                :     /*
                                677                 :                :      * Terminate data transfer.  We can't send an error message if we're using
                                678                 :                :      * protocol version 2.  (libpq no longer supports protocol version 2, but
                                679                 :                :      * keep the version checks just in case you're using a pre-v14 libpq.so at
                                680                 :                :      * runtime)
                                681                 :                :      */
 6617 tgl@sss.pgh.pa.us         682   [ -  +  +  - ]:CBC         431 :     if (PQputCopyEnd(conn,
 3713 tgl@sss.pgh.pa.us         683         [ #  # ]:UBC           0 :                      (OK || PQprotocolVersion(conn) < 3) ? NULL :
                                684                 :              0 :                      _("aborted because of read failure")) <= 0)
 6617                           685                 :              0 :         OK = false;
                                686                 :                : 
 4463 alvherre@alvh.no-ip.      687                 :CBC         431 : copyin_cleanup:
                                688                 :                : 
                                689                 :                :     /*
                                690                 :                :      * Clear the EOF flag on the stream, in case copying ended due to an EOF
                                691                 :                :      * signal.  This allows an interactive TTY session to perform another COPY
                                692                 :                :      * FROM STDIN later.  (In non-STDIN cases, we're about to close the file
                                693                 :                :      * anyway, so it doesn't matter.)  Although we don't ever test the flag
                                694                 :                :      * with feof(), some fread() implementations won't read more data if it's
                                695                 :                :      * set.  This also clears the error flag, but we already checked that.
                                696                 :                :      */
 2524 tgl@sss.pgh.pa.us         697                 :            431 :     clearerr(copystream);
                                698                 :                : 
                                699                 :                :     /*
                                700                 :                :      * Check command status and return to normal libpq state.
                                701                 :                :      *
                                702                 :                :      * We do not want to return with the status still PGRES_COPY_IN: our
                                703                 :                :      * caller would be unable to distinguish that situation from reaching the
                                704                 :                :      * next COPY in a command string that happened to contain two consecutive
                                705                 :                :      * COPY FROM STDIN commands.  We keep trying PQputCopyEnd() in the hope
                                706                 :                :      * it'll work eventually.  (What's actually likely to happen is that in
                                707                 :                :      * attempting to flush the data, libpq will eventually realize that the
                                708                 :                :      * connection is lost.  But that's fine; it will get us out of COPY_IN
                                709                 :                :      * state, which is what we need.)
                                710                 :                :      */
 3685                           711         [ -  + ]:            431 :     while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
                                712                 :                :     {
 4463 alvherre@alvh.no-ip.      713                 :UBC           0 :         OK = false;
 3685 tgl@sss.pgh.pa.us         714                 :              0 :         PQclear(*res);
                                715                 :                :         /* We can't send an error message if we're using protocol version 2 */
 3713                           716                 :              0 :         PQputCopyEnd(conn,
                                717         [ #  # ]:              0 :                      (PQprotocolVersion(conn) < 3) ? NULL :
                                718                 :              0 :                      _("trying to exit copy mode"));
                                719                 :                :     }
 3685 tgl@sss.pgh.pa.us         720         [ +  + ]:CBC         431 :     if (PQresultStatus(*res) != PGRES_COMMAND_OK)
                                721                 :                :     {
 1840 peter@eisentraut.org      722                 :             90 :         pg_log_info("%s", PQerrorMessage(conn));
 6617 tgl@sss.pgh.pa.us         723                 :             90 :         OK = false;
                                724                 :                :     }
                                725                 :                : 
                                726                 :            431 :     return OK;
                                727                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622