Commit 04cc4e18 authored by Tom Lane's avatar Tom Lane

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.
parent 0f8a3135
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -705,7 +705,7 @@ testdb=> ...@@ -705,7 +705,7 @@ testdb=>
<term><literal>\copy <replaceable class="parameter">table</replaceable> <term><literal>\copy <replaceable class="parameter">table</replaceable>
[ ( <replaceable class="parameter">column_list</replaceable> ) ] [ ( <replaceable class="parameter">column_list</replaceable> ) ]
{ <literal>from</literal> | <literal>to</literal> } { <literal>from</literal> | <literal>to</literal> }
<replaceable class="parameter">filename</replaceable> | stdin | stdout { <replaceable class="parameter">filename</replaceable> | stdin | stdout | - }
[ <literal>with</literal> ] [ <literal>with</literal> ]
[ <literal>oids</literal> ] [ <literal>oids</literal> ]
[ <literal>delimiter [as] </literal> '<replaceable class="parameter">character</replaceable>' ] [ <literal>delimiter [as] </literal> '<replaceable class="parameter">character</replaceable>' ]
...@@ -720,26 +720,41 @@ testdb=> ...@@ -720,26 +720,41 @@ testdb=>
reading or writing the specified file, reading or writing the specified file,
<application>psql</application> reads or writes the file and <application>psql</application> reads or writes the file and
routes the data between the server and the local file system. routes the data between the server and the local file system.
This means that file accessibility and privileges are those This means that file accessibility and privileges are those of
of the local user, not the server, and no SQL superuser the local user, not the server, and no SQL superuser
privileges are required. privileges are required.
</para> </para>
<para> <para>
The syntax of the command is similar to that of the The syntax of the command is similar to that of the
<acronym>SQL</acronym> <command>COPY</command> command. (See its <acronym>SQL</acronym> <xref linkend="sql-copy"
description for the details.) Note that, because of this, endterm="sql-copy-title"> command. Note that, because of this,
special parsing rules apply to the <command>\copy</command> special parsing rules apply to the <command>\copy</command>
command. In particular, the variable substitution rules and command. In particular, the variable substitution rules and
backslash escapes do not apply. backslash escapes do not apply.
</para> </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> <tip>
<para> <para>
This operation is not as efficient as the <acronym>SQL</acronym> This operation is not as efficient as the <acronym>SQL</acronym>
<command>COPY</command> command because all data must pass <command>COPY</command> command because all data must pass
through the client/server connection. For large 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> </para>
</tip> </tip>
...@@ -747,11 +762,12 @@ testdb=> ...@@ -747,11 +762,12 @@ testdb=>
<para> <para>
Note the difference in interpretation of Note the difference in interpretation of
<literal>stdin</literal> and <literal>stdout</literal> between <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 refer to <application>psql</application>'s input and output
stream. On a server copy <literal>stdin</literal> comes from streams. In <command>COPY</command>, <literal>stdin</literal> comes
wherever the <command>COPY</command> itself came from (for from wherever the <command>COPY</command> itself came from (for
example, a script run with the <option>-f</option> option), and example, a script run with the <option>-f</option> option), while
<literal>stdout</literal> refers to the query output stream (see <literal>stdout</literal> refers to the query output stream (see
<command>\o</command> meta-command below). <command>\o</command> meta-command below).
</para> </para>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2003, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "common.h" #include "common.h"
...@@ -513,12 +513,7 @@ ProcessCopyResult(PGresult *results) ...@@ -513,12 +513,7 @@ ProcessCopyResult(PGresult *results)
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
if (pset.cur_cmd_interactive && !QUIET()) success = handleCopyIn(pset.db, pset.cur_cmd_source);
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);
break; break;
default: default:
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2003, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "copy.h" #include "copy.h"
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "settings.h" #include "settings.h"
#include "common.h" #include "common.h"
#include "prompt.h"
#include "stringutils.h" #include "stringutils.h"
#ifdef WIN32 #ifdef WIN32
...@@ -53,6 +54,7 @@ struct copy_options ...@@ -53,6 +54,7 @@ struct copy_options
char *table; char *table;
char *column_list; char *column_list;
char *file; /* NULL = stdin/stdout */ char *file; /* NULL = stdin/stdout */
bool in_dash; /* true = use src stream not true stdin */
bool from; bool from;
bool binary; bool binary;
bool oids; bool oids;
...@@ -218,10 +220,25 @@ parse_slash_copy(const char *args) ...@@ -218,10 +220,25 @@ parse_slash_copy(const char *args)
if (strcasecmp(token, "stdin") == 0 || if (strcasecmp(token, "stdin") == 0 ||
strcasecmp(token, "stdout") == 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; result->file = NULL;
}
else else
{
result->in_dash = false;
result->file = xstrdup(token); result->file = xstrdup(token);
expand_tilde(&result->file); expand_tilde(&result->file);
}
token = strtokx(NULL, whitespace, NULL, NULL, token = strtokx(NULL, whitespace, NULL, NULL,
0, false, pset.encoding); 0, false, pset.encoding);
...@@ -362,8 +379,10 @@ do_copy(const char *args) ...@@ -362,8 +379,10 @@ do_copy(const char *args)
{ {
if (options->file) if (options->file)
copystream = fopen(options->file, "r"); copystream = fopen(options->file, "r");
else if (options->in_dash)
copystream = pset.cur_cmd_source;
else else
copystream = stdin; copystream = stdin;
} }
else else
{ {
...@@ -401,7 +420,7 @@ do_copy(const char *args) ...@@ -401,7 +420,7 @@ do_copy(const char *args)
success = handleCopyOut(pset.db, copystream); success = handleCopyOut(pset.db, copystream);
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
success = handleCopyIn(pset.db, copystream, NULL); success = handleCopyIn(pset.db, copystream);
break; break;
case PGRES_NONFATAL_ERROR: case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
...@@ -416,7 +435,7 @@ do_copy(const char *args) ...@@ -416,7 +435,7 @@ do_copy(const char *args)
PQclear(result); PQclear(result);
if (copystream != stdout && copystream != stdin) if (options->file != NULL)
fclose(copystream); fclose(copystream);
free_copy_options(options); free_copy_options(options);
return success; return success;
...@@ -486,13 +505,12 @@ handleCopyOut(PGconn *conn, FILE *copystream) ...@@ -486,13 +505,12 @@ handleCopyOut(PGconn *conn, FILE *copystream)
* conn should be a database connection that you just called COPY FROM on * conn should be a database connection that you just called COPY FROM on
* (and which gave you PGRES_COPY_IN back); * (and which gave you PGRES_COPY_IN back);
* copystream is the file stream you want the input to come from * 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 bool
handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt) handleCopyIn(PGconn *conn, FILE *copystream)
{ {
const char *prompt;
bool copydone = false; bool copydone = false;
bool firstload; bool firstload;
bool linedone; bool linedone;
...@@ -503,10 +521,17 @@ handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt) ...@@ -503,10 +521,17 @@ handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt)
int ret; int ret;
unsigned int linecount = 0; 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) while (!copydone)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2003, PostgreSQL Global Development Group * 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 #ifndef COPY_H
#define COPY_H #define COPY_H
...@@ -17,6 +17,6 @@ bool do_copy(const char *args); ...@@ -17,6 +17,6 @@ bool do_copy(const char *args);
/* lower level processors for copy in/out streams */ /* lower level processors for copy in/out streams */
bool handleCopyOut(PGconn *conn, FILE *copystream); bool handleCopyOut(PGconn *conn, FILE *copystream);
bool handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt); bool handleCopyIn(PGconn *conn, FILE *copystream);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment