Implement '\copy from -' to support reading copy data from the same
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Jan 2004 23:48:56 +0000 (23:48 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Jan 2004 23:48:56 +0000 (23:48 +0000)
source the \copy came from.  Also, fix prompting logic so that initial
and per-line prompts appear for all cases of reading from an interactive
terminal.  Patch by Mark Feit, with some kibitzing by Tom Lane.

doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/common.c
src/bin/psql/copy.c
src/bin/psql/copy.h

index 50ea0c910ee9b7be16017c86b2ea7b626f7e32e4..2e17c0310a08efb666e1267cd81f522d99b6ee03 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.103 2004/01/20 19:49:34 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.104 2004/01/20 23:48:56 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -705,7 +705,7 @@ testdb=>
         <term><literal>\copy <replaceable class="parameter">table</replaceable>
    [ ( <replaceable class="parameter">column_list</replaceable> ) ]
         { <literal>from</literal> | <literal>to</literal> }
-   <replaceable class="parameter">filename</replaceable> | stdin | stdout
+   { <replaceable class="parameter">filename</replaceable> | stdin | stdout | - }
         [ <literal>with</literal> ] 
             [ <literal>oids</literal> ] 
             [ <literal>delimiter [as] </literal> '<replaceable class="parameter">character</replaceable>' ]
@@ -720,26 +720,41 @@ testdb=>
         reading or writing the specified file,
         <application>psql</application> reads or writes the file and
         routes the data between the server and the local file system.
-   This means that file accessibility and privileges are those
-   of the local user, not the server, and no SQL superuser
-   privileges are required.
+        This means that file accessibility and privileges are those of
+        the local user, not the server, and no SQL superuser
+        privileges are required.
    </para>
 
    <para>
    The syntax of the command is similar to that of the
-   <acronym>SQL</acronym> <command>COPY</command> command.  (See its
-   description for the details.)  Note that, because of this,
+   <acronym>SQL</acronym> <xref linkend="sql-copy"
+   endterm="sql-copy-title"> command.  Note that, because of this,
    special parsing rules apply to the <command>\copy</command>
    command. In particular, the variable substitution rules and
    backslash escapes do not apply.
    </para>
 
+   <para>
+   For <literal>\copy <replaceable
+   class="parameter">table</replaceable> from <replaceable
+   class="parameter">filename</replaceable></literal> operations,
+   <application>psql</application> adds the option of using a
+   hyphen instead of <replaceable
+   class="parameter">filename</replaceable>.  This causes
+   <literal>\copy</literal> to read rows from the same source that
+   issued the command, continuing until <literal>\.</literal> is
+   read or the stream reaches <acronym>EOF</>.  This option is
+   useful for populating tables in-line within a SQL script file.
+   In contrast, <literal>\copy from stdin</> always reads from
+   <application>psql</application>'s standard input.
+   </para>
+
         <tip>
         <para>
    This operation is not as efficient as the <acronym>SQL</acronym>
    <command>COPY</command> command because all data must pass
    through the client/server connection. For large
-   amounts of data the other technique may be preferable.
+   amounts of data the <acronym>SQL</acronym> command may be preferable.
         </para>
         </tip>
 
@@ -747,11 +762,12 @@ testdb=>
         <para>
         Note the difference in interpretation of
         <literal>stdin</literal> and <literal>stdout</literal> between
-        client and server copies: in a client copy these always
+   <literal>\copy</literal> and <command>COPY</command>.
+   In <literal>\copy</literal> these always
         refer to <application>psql</application>'s input and output
-        stream. On a server copy <literal>stdin</literal> comes from
-        wherever the <command>COPY</command> itself came from (for
-        example, a script run with the <option>-f</option> option), and
+        streams. In <command>COPY</command>, <literal>stdin</literal> comes
+   from wherever the <command>COPY</command> itself came from (for
+        example, a script run with the <option>-f</option> option), while
         <literal>stdout</literal> refers to the query output stream (see
         <command>\o</command> meta-command below).
         </para>
index 1e113ac49aa21ad73c3c0c0e9fc2c992b4fbcb73..ed3649dfe1dc52c8b662d58985d84b160fae4e8a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.79 2004/01/09 21:12:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.80 2004/01/20 23:48:56 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
@@ -513,12 +513,7 @@ ProcessCopyResult(PGresult *results)
            break;
 
        case PGRES_COPY_IN:
-           if (pset.cur_cmd_interactive && !QUIET())
-               puts(gettext("Enter data to be copied followed by a newline.\n"
-                            "End with a backslash and a period on a line by itself."));
-
-           success = handleCopyIn(pset.db, pset.cur_cmd_source,
-             pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
+           success = handleCopyIn(pset.db, pset.cur_cmd_source);
            break;
 
        default:
index 36937d2606e47db4237a54498c534adabd3fb467..e852dd5b7a1948964565eca45335803c689000af 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.36 2004/01/09 21:12:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.37 2004/01/20 23:48:56 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "copy.h"
@@ -23,6 +23,7 @@
 
 #include "settings.h"
 #include "common.h"
+#include "prompt.h"
 #include "stringutils.h"
 
 #ifdef WIN32
@@ -53,6 +54,7 @@ struct copy_options
    char       *table;
    char       *column_list;
    char       *file;           /* NULL = stdin/stdout */
+   bool        in_dash;        /* true = use src stream not true stdin */
    bool        from;
    bool        binary;
    bool        oids;
@@ -218,10 +220,25 @@ parse_slash_copy(const char *args)
 
    if (strcasecmp(token, "stdin") == 0 ||
        strcasecmp(token, "stdout") == 0)
+   {
+       result->in_dash = false;
+       result->file = NULL;
+   }
+   else if (strcmp(token, "-") == 0)
+   {
+       /* Can't do this on output */
+       if (!result->from)
+           goto error;
+
+       result->in_dash = true;
        result->file = NULL;
+   }
    else
+   {
+       result->in_dash = false;
        result->file = xstrdup(token);
-   expand_tilde(&result->file);
+       expand_tilde(&result->file);
+   }
 
    token = strtokx(NULL, whitespace, NULL, NULL,
                    0, false, pset.encoding);
@@ -362,8 +379,10 @@ do_copy(const char *args)
    {
        if (options->file)
            copystream = fopen(options->file, "r");
+       else if (options->in_dash)
+           copystream = pset.cur_cmd_source;
        else
-           copystream = stdin;
+           copystream = stdin;
    }
    else
    {
@@ -401,7 +420,7 @@ do_copy(const char *args)
            success = handleCopyOut(pset.db, copystream);
            break;
        case PGRES_COPY_IN:
-           success = handleCopyIn(pset.db, copystream, NULL);
+           success = handleCopyIn(pset.db, copystream);
            break;
        case PGRES_NONFATAL_ERROR:
        case PGRES_FATAL_ERROR:
@@ -416,7 +435,7 @@ do_copy(const char *args)
 
    PQclear(result);
 
-   if (copystream != stdout && copystream != stdin)
+   if (options->file != NULL)
        fclose(copystream);
    free_copy_options(options);
    return success;
@@ -486,13 +505,12 @@ handleCopyOut(PGconn *conn, FILE *copystream)
  * conn should be a database connection that you just called COPY FROM on
  * (and which gave you PGRES_COPY_IN back);
  * copystream is the file stream you want the input to come from
- * prompt is something to display to request user input (only makes sense
- *  if stdin is an interactive tty)
  */
 
 bool
-handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt)
+handleCopyIn(PGconn *conn, FILE *copystream)
 {
+   const char *prompt;
    bool        copydone = false;
    bool        firstload;
    bool        linedone;
@@ -503,10 +521,17 @@ handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt)
    int         ret;
    unsigned int linecount = 0;
 
-   if (prompt)                 /* disable prompt if not interactive */
+   /* Prompt if interactive input */
+   if (isatty(fileno(copystream)))
+   {
+       if (!QUIET())
+           puts(gettext("Enter data to be copied followed by a newline.\n"
+               "End with a backslash and a period on a line by itself."));
+       prompt = get_prompt(PROMPT_COPY);
+   }
+   else
    {
-       if (!isatty(fileno(copystream)))
-           prompt = NULL;
+       prompt = NULL;
    }
 
    while (!copydone)
index 0f6a688796993cf23be90fc5ea6ce09f50f06190..8daf4a2609afd9327db19c19d14aaf4a9990558c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.14 2003/11/29 19:52:06 pgsql Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.15 2004/01/20 23:48:56 tgl Exp $
  */
 #ifndef COPY_H
 #define COPY_H
@@ -17,6 +17,6 @@ bool      do_copy(const char *args);
 /* lower level processors for copy in/out streams */
 
 bool       handleCopyOut(PGconn *conn, FILE *copystream);
-bool       handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt);
+bool       handleCopyIn(PGconn *conn, FILE *copystream);
 
 #endif