From 04cc4e18dd34f3c301237e60058cc7a00bad41f4 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 20 Jan 2004 23:48:56 +0000
Subject: [PATCH] Implement '\copy from -' to support reading copy data from
 the same 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 | 40 ++++++++++++++++++++---------
 src/bin/psql/common.c          |  9 ++-----
 src/bin/psql/copy.c            | 47 ++++++++++++++++++++++++++--------
 src/bin/psql/copy.h            |  4 +--
 4 files changed, 68 insertions(+), 32 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 50ea0c910ee..2e17c0310a0 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -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>
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 1e113ac49aa..ed3649dfe1d 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -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:
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 36937d2606e..e852dd5b7a1 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -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)
diff --git a/src/bin/psql/copy.h b/src/bin/psql/copy.h
index 0f6a6887969..8daf4a2609a 100644
--- a/src/bin/psql/copy.h
+++ b/src/bin/psql/copy.h
@@ -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
-- 
GitLab