2000-01-18 23:30:24 +00:00
|
|
|
/*
|
|
|
|
* psql - the PostgreSQL interactive terminal
|
|
|
|
*
|
2003-08-04 23:59:41 +00:00
|
|
|
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
|
2000-01-18 23:30:24 +00:00
|
|
|
*
|
2004-01-25 03:07:22 +00:00
|
|
|
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.39 2004/01/25 03:07:22 neilc Exp $
|
2000-01-18 23:30:24 +00:00
|
|
|
*/
|
2001-02-10 02:31:31 +00:00
|
|
|
#include "postgres_fe.h"
|
1999-11-04 21:56:02 +00:00
|
|
|
#include "copy.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
2000-02-21 19:40:42 +00:00
|
|
|
#include <signal.h>
|
2002-02-23 21:46:03 +00:00
|
|
|
#include <sys/stat.h>
|
1999-11-04 21:56:02 +00:00
|
|
|
#ifndef WIN32
|
1999-11-04 23:14:30 +00:00
|
|
|
#include <unistd.h> /* for isatty */
|
1999-11-04 21:56:02 +00:00
|
|
|
#else
|
1999-11-04 23:14:30 +00:00
|
|
|
#include <io.h> /* I think */
|
1999-11-04 21:56:02 +00:00
|
|
|
#endif
|
|
|
|
|
2000-02-16 13:15:26 +00:00
|
|
|
#include "libpq-fe.h"
|
2002-04-24 05:24:00 +00:00
|
|
|
#include "pqexpbuffer.h"
|
2000-02-21 19:40:42 +00:00
|
|
|
#include "pqsignal.h"
|
1999-11-04 21:56:02 +00:00
|
|
|
|
|
|
|
#include "settings.h"
|
|
|
|
#include "common.h"
|
2004-01-20 23:48:56 +00:00
|
|
|
#include "prompt.h"
|
1999-11-04 21:56:02 +00:00
|
|
|
#include "stringutils.h"
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
#define strcasecmp(x,y) stricmp(x,y)
|
2003-08-04 00:43:34 +00:00
|
|
|
#define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
|
|
|
|
#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR)
|
1999-11-04 21:56:02 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parse_slash_copy
|
|
|
|
* -- parses \copy command line
|
|
|
|
*
|
2002-10-19 00:22:14 +00:00
|
|
|
* Accepted syntax: \copy table [(columnlist)] [with oids] from|to filename [with ] [ oids ] [ delimiter char] [ null as string ]
|
1999-11-04 21:56:02 +00:00
|
|
|
* (binary is not here yet)
|
|
|
|
*
|
2002-06-20 16:00:44 +00:00
|
|
|
* Old syntax for backward compatibility: (2002-06-19):
|
2002-10-19 00:22:14 +00:00
|
|
|
* \copy table [(columnlist)] [with oids] from|to filename [ using delimiters char] [ with null as string ]
|
|
|
|
*
|
|
|
|
* table name can be double-quoted and can have a schema part.
|
|
|
|
* column names can be double-quoted.
|
|
|
|
* filename, char, and string can be single-quoted like SQL literals.
|
2002-06-20 16:00:44 +00:00
|
|
|
*
|
1999-11-04 21:56:02 +00:00
|
|
|
* returns a malloc'ed structure with the options, or NULL on parsing error
|
|
|
|
*/
|
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
struct copy_options
|
|
|
|
{
|
|
|
|
char *table;
|
2002-10-19 00:22:14 +00:00
|
|
|
char *column_list;
|
2000-04-12 17:17:23 +00:00
|
|
|
char *file; /* NULL = stdin/stdout */
|
2004-01-20 23:48:56 +00:00
|
|
|
bool in_dash; /* true = use src stream not true stdin */
|
1999-11-04 23:14:30 +00:00
|
|
|
bool from;
|
|
|
|
bool binary;
|
|
|
|
bool oids;
|
|
|
|
char *delim;
|
2000-04-12 17:17:23 +00:00
|
|
|
char *null;
|
1999-11-04 21:56:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_copy_options(struct copy_options * ptr)
|
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!ptr)
|
|
|
|
return;
|
|
|
|
free(ptr->table);
|
2002-10-19 00:22:14 +00:00
|
|
|
free(ptr->column_list);
|
1999-11-04 23:14:30 +00:00
|
|
|
free(ptr->file);
|
|
|
|
free(ptr->delim);
|
2000-04-12 17:17:23 +00:00
|
|
|
free(ptr->null);
|
1999-11-04 23:14:30 +00:00
|
|
|
free(ptr);
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
/* catenate "more" onto "var", freeing the original value of *var */
|
|
|
|
static void
|
|
|
|
xstrcat(char **var, const char *more)
|
|
|
|
{
|
|
|
|
char *newvar;
|
|
|
|
|
2004-01-25 03:07:22 +00:00
|
|
|
newvar = pg_malloc(strlen(*var) + strlen(more) + 1);
|
2002-10-19 00:22:14 +00:00
|
|
|
strcpy(newvar, *var);
|
|
|
|
strcat(newvar, more);
|
|
|
|
free(*var);
|
|
|
|
*var = newvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-04 21:56:02 +00:00
|
|
|
static struct copy_options *
|
2000-01-18 23:30:24 +00:00
|
|
|
parse_slash_copy(const char *args)
|
1999-11-04 21:56:02 +00:00
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
struct copy_options *result;
|
|
|
|
char *line;
|
|
|
|
char *token;
|
2002-10-19 00:22:14 +00:00
|
|
|
const char *whitespace = " \t\n\r";
|
1999-11-04 23:14:30 +00:00
|
|
|
|
2000-04-16 15:46:40 +00:00
|
|
|
if (args)
|
2004-01-25 03:07:22 +00:00
|
|
|
line = pg_strdup(args);
|
2000-04-16 15:46:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
psql_error("\\copy: arguments required\n");
|
|
|
|
return NULL;
|
2001-03-22 04:01:46 +00:00
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
|
2004-01-25 03:07:22 +00:00
|
|
|
result = pg_calloc(1, sizeof(struct copy_options));
|
1999-11-04 23:14:30 +00:00
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(line, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!token)
|
2002-10-19 00:22:14 +00:00
|
|
|
goto error;
|
|
|
|
|
2000-01-12 19:36:36 +00:00
|
|
|
#ifdef NOT_USED
|
2002-10-19 00:22:14 +00:00
|
|
|
/* this is not implemented yet */
|
|
|
|
if (strcasecmp(token, "binary") == 0)
|
|
|
|
{
|
|
|
|
result->binary = true;
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-01-25 03:07:22 +00:00
|
|
|
result->table = pg_strdup(token);
|
2002-10-19 00:22:14 +00:00
|
|
|
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/*
|
2003-08-04 00:43:34 +00:00
|
|
|
* strtokx() will not have returned a multi-character token starting
|
|
|
|
* with '.', so we don't need strcmp() here. Likewise for '(', etc,
|
|
|
|
* below.
|
2002-10-19 00:22:14 +00:00
|
|
|
*/
|
|
|
|
if (token[0] == '.')
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
/* handle schema . table */
|
|
|
|
xstrcat(&result->table, token);
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!token)
|
2002-10-19 00:22:14 +00:00
|
|
|
goto error;
|
|
|
|
xstrcat(&result->table, token);
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
if (token[0] == '(')
|
|
|
|
{
|
|
|
|
/* handle parenthesized column list */
|
2004-01-25 03:07:22 +00:00
|
|
|
result->column_list = pg_strdup(token);
|
2002-10-19 00:22:14 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token || strchr(".,()", token[0]))
|
|
|
|
goto error;
|
|
|
|
xstrcat(&result->column_list, token);
|
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
|
|
|
xstrcat(&result->column_list, token);
|
|
|
|
if (token[0] == ')')
|
|
|
|
break;
|
|
|
|
if (token[0] != ',')
|
|
|
|
goto error;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(NULL, whitespace, ".,()", "\"",
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
/*
|
2003-08-04 00:43:34 +00:00
|
|
|
* Allows old COPY syntax for backward compatibility 2002-06-19
|
2002-10-19 00:22:14 +00:00
|
|
|
*/
|
|
|
|
if (strcasecmp(token, "with") == 0)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!token || strcasecmp(token, "oids") != 0)
|
|
|
|
goto error;
|
|
|
|
result->oids = true;
|
|
|
|
|
|
|
|
token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding);
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!token)
|
2002-10-19 00:22:14 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(token, "from") == 0)
|
|
|
|
result->from = true;
|
|
|
|
else if (strcasecmp(token, "to") == 0)
|
|
|
|
result->from = false;
|
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', true, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (strcasecmp(token, "stdin") == 0 ||
|
|
|
|
strcasecmp(token, "stdout") == 0)
|
2004-01-20 23:48:56 +00:00
|
|
|
{
|
|
|
|
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;
|
2002-10-19 00:22:14 +00:00
|
|
|
result->file = NULL;
|
2004-01-20 23:48:56 +00:00
|
|
|
}
|
2002-10-19 00:22:14 +00:00
|
|
|
else
|
2004-01-20 23:48:56 +00:00
|
|
|
{
|
|
|
|
result->in_dash = false;
|
2004-01-25 03:07:22 +00:00
|
|
|
result->file = pg_strdup(token);
|
2004-01-20 23:48:56 +00:00
|
|
|
expand_tilde(&result->file);
|
|
|
|
}
|
2002-10-19 00:22:14 +00:00
|
|
|
|
|
|
|
token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding);
|
|
|
|
|
|
|
|
/*
|
2003-08-04 00:43:34 +00:00
|
|
|
* Allows old COPY syntax for backward compatibility 2002-06-19
|
2002-10-19 00:22:14 +00:00
|
|
|
*/
|
|
|
|
if (token && strcasecmp(token, "using") == 0)
|
|
|
|
{
|
|
|
|
token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding);
|
|
|
|
if (!(token && strcasecmp(token, "delimiters") == 0))
|
|
|
|
goto error;
|
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', false, pset.encoding);
|
|
|
|
if (!token)
|
|
|
|
goto error;
|
2004-01-25 03:07:22 +00:00
|
|
|
result->delim = pg_strdup(token);
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding);
|
1999-11-04 23:14:30 +00:00
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
if (token)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
if (strcasecmp(token, "with") != 0)
|
|
|
|
goto error;
|
|
|
|
while ((token = strtokx(NULL, whitespace, NULL, NULL,
|
|
|
|
0, false, pset.encoding)) != NULL)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
if (strcasecmp(token, "delimiter") == 0)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', false, pset.encoding);
|
|
|
|
if (token && strcasecmp(token, "as") == 0)
|
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', false, pset.encoding);
|
|
|
|
if (token)
|
2004-01-25 03:07:22 +00:00
|
|
|
result->delim = pg_strdup(token);
|
2002-06-20 16:00:44 +00:00
|
|
|
else
|
2002-10-19 00:22:14 +00:00
|
|
|
goto error;
|
1999-11-04 23:14:30 +00:00
|
|
|
}
|
2002-10-19 00:22:14 +00:00
|
|
|
else if (strcasecmp(token, "null") == 0)
|
2000-04-12 17:17:23 +00:00
|
|
|
{
|
2002-10-19 00:22:14 +00:00
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', false, pset.encoding);
|
|
|
|
if (token && strcasecmp(token, "as") == 0)
|
|
|
|
token = strtokx(NULL, whitespace, NULL, "'",
|
|
|
|
'\\', false, pset.encoding);
|
|
|
|
if (token)
|
2004-01-25 03:07:22 +00:00
|
|
|
result->null = pg_strdup(token);
|
2002-09-04 20:31:48 +00:00
|
|
|
else
|
2002-10-19 00:22:14 +00:00
|
|
|
goto error;
|
2000-04-12 17:17:23 +00:00
|
|
|
}
|
2002-10-19 00:22:14 +00:00
|
|
|
else
|
|
|
|
goto error;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
free(line);
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
return result;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (token)
|
2003-07-23 08:47:41 +00:00
|
|
|
psql_error("\\copy: parse error at \"%s\"\n", token);
|
1999-11-04 21:56:02 +00:00
|
|
|
else
|
2002-10-19 00:22:14 +00:00
|
|
|
psql_error("\\copy: parse error at end of line\n");
|
|
|
|
free_copy_options(result);
|
|
|
|
free(line);
|
|
|
|
|
|
|
|
return NULL;
|
1999-11-04 23:14:30 +00:00
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute a \copy command (frontend copy). We have to open a file, then
|
|
|
|
* submit a COPY query to the backend and either feed it data from the
|
|
|
|
* file or route its response into the file.
|
1999-11-04 23:14:30 +00:00
|
|
|
*/
|
1999-11-04 21:56:02 +00:00
|
|
|
bool
|
2000-01-18 23:30:24 +00:00
|
|
|
do_copy(const char *args)
|
1999-11-04 21:56:02 +00:00
|
|
|
{
|
2002-04-24 05:24:00 +00:00
|
|
|
PQExpBufferData query;
|
1999-11-04 23:14:30 +00:00
|
|
|
FILE *copystream;
|
|
|
|
struct copy_options *options;
|
|
|
|
PGresult *result;
|
|
|
|
bool success;
|
2002-04-24 05:24:00 +00:00
|
|
|
struct stat st;
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
/* parse options */
|
2000-01-18 23:30:24 +00:00
|
|
|
options = parse_slash_copy(args);
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!options)
|
|
|
|
return false;
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2002-04-24 05:24:00 +00:00
|
|
|
initPQExpBuffer(&query);
|
|
|
|
|
|
|
|
printfPQExpBuffer(&query, "COPY ");
|
1999-11-04 23:14:30 +00:00
|
|
|
if (options->binary)
|
2002-04-24 05:24:00 +00:00
|
|
|
appendPQExpBuffer(&query, "BINARY ");
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2002-10-19 00:22:14 +00:00
|
|
|
appendPQExpBuffer(&query, "%s ", options->table);
|
|
|
|
|
|
|
|
if (options->column_list)
|
|
|
|
appendPQExpBuffer(&query, "%s ", options->column_list);
|
|
|
|
|
2002-06-20 16:00:44 +00:00
|
|
|
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
|
1999-11-04 23:14:30 +00:00
|
|
|
if (options->oids)
|
2002-04-24 05:24:00 +00:00
|
|
|
appendPQExpBuffer(&query, "WITH OIDS ");
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
if (options->from)
|
2002-04-24 05:24:00 +00:00
|
|
|
appendPQExpBuffer(&query, "FROM STDIN");
|
1999-11-04 23:14:30 +00:00
|
|
|
else
|
2002-04-24 05:24:00 +00:00
|
|
|
appendPQExpBuffer(&query, "TO STDOUT");
|
1999-11-04 21:56:02 +00:00
|
|
|
|
|
|
|
|
2002-06-20 16:00:44 +00:00
|
|
|
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
|
1999-11-04 23:14:30 +00:00
|
|
|
if (options->delim)
|
2002-10-19 00:22:14 +00:00
|
|
|
{
|
|
|
|
if (options->delim[0] == '\'')
|
|
|
|
appendPQExpBuffer(&query, " USING DELIMITERS %s",
|
|
|
|
options->delim);
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(&query, " USING DELIMITERS '%s'",
|
|
|
|
options->delim);
|
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2000-04-12 17:17:23 +00:00
|
|
|
if (options->null)
|
2002-10-19 00:22:14 +00:00
|
|
|
{
|
|
|
|
if (options->null[0] == '\'')
|
|
|
|
appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
|
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
if (options->from)
|
2000-04-12 17:17:23 +00:00
|
|
|
{
|
|
|
|
if (options->file)
|
|
|
|
copystream = fopen(options->file, "r");
|
2004-01-20 23:48:56 +00:00
|
|
|
else if (options->in_dash)
|
|
|
|
copystream = pset.cur_cmd_source;
|
2000-04-12 17:17:23 +00:00
|
|
|
else
|
2004-01-20 23:48:56 +00:00
|
|
|
copystream = stdin;
|
2000-04-12 17:17:23 +00:00
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
else
|
2000-04-12 17:17:23 +00:00
|
|
|
{
|
|
|
|
if (options->file)
|
|
|
|
copystream = fopen(options->file, "w");
|
|
|
|
else
|
|
|
|
copystream = stdout;
|
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
if (!copystream)
|
|
|
|
{
|
2000-04-12 17:17:23 +00:00
|
|
|
psql_error("%s: %s\n",
|
|
|
|
options->file, strerror(errno));
|
1999-11-04 23:14:30 +00:00
|
|
|
free_copy_options(options);
|
|
|
|
return false;
|
|
|
|
}
|
2002-04-24 05:24:00 +00:00
|
|
|
|
|
|
|
/* make sure the specified file is not a directory */
|
|
|
|
fstat(fileno(copystream), &st);
|
2002-09-04 20:31:48 +00:00
|
|
|
if (S_ISDIR(st.st_mode))
|
2002-04-24 05:24:00 +00:00
|
|
|
{
|
|
|
|
fclose(copystream);
|
2002-09-22 20:57:21 +00:00
|
|
|
psql_error("%s: cannot copy from/to a directory\n",
|
2002-02-23 21:46:03 +00:00
|
|
|
options->file);
|
|
|
|
free_copy_options(options);
|
|
|
|
return false;
|
2002-04-24 05:24:00 +00:00
|
|
|
}
|
2002-06-20 16:00:44 +00:00
|
|
|
|
2003-06-28 00:12:40 +00:00
|
|
|
result = PSQLexec(query.data, true);
|
2002-04-24 05:24:00 +00:00
|
|
|
termPQExpBuffer(&query);
|
1999-11-04 23:14:30 +00:00
|
|
|
|
|
|
|
switch (PQresultStatus(result))
|
|
|
|
{
|
|
|
|
case PGRES_COPY_OUT:
|
2000-01-14 22:18:03 +00:00
|
|
|
success = handleCopyOut(pset.db, copystream);
|
1999-11-04 23:14:30 +00:00
|
|
|
break;
|
|
|
|
case PGRES_COPY_IN:
|
2004-01-20 23:48:56 +00:00
|
|
|
success = handleCopyIn(pset.db, copystream);
|
1999-11-04 23:14:30 +00:00
|
|
|
break;
|
|
|
|
case PGRES_NONFATAL_ERROR:
|
|
|
|
case PGRES_FATAL_ERROR:
|
|
|
|
case PGRES_BAD_RESPONSE:
|
|
|
|
success = false;
|
2000-04-12 17:17:23 +00:00
|
|
|
psql_error("\\copy: %s", PQerrorMessage(pset.db));
|
1999-11-04 23:14:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
success = false;
|
2000-01-18 23:30:24 +00:00
|
|
|
psql_error("\\copy: unexpected response (%d)\n", PQresultStatus(result));
|
1999-11-04 23:14:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PQclear(result);
|
1999-11-04 21:56:02 +00:00
|
|
|
|
2004-01-20 23:48:56 +00:00
|
|
|
if (options->file != NULL)
|
2000-04-12 17:17:23 +00:00
|
|
|
fclose(copystream);
|
1999-11-04 23:14:30 +00:00
|
|
|
free_copy_options(options);
|
|
|
|
return success;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-04-24 21:00:10 +00:00
|
|
|
#define COPYBUFSIZ 8192 /* size doesn't matter */
|
1999-11-04 21:56:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2000-01-21 04:21:12 +00:00
|
|
|
* handleCopyOut
|
1999-11-04 21:56:02 +00:00
|
|
|
* receives data as a result of a COPY ... TO stdout command
|
|
|
|
*
|
|
|
|
* If you want to use COPY TO in your application, this is the code to steal :)
|
|
|
|
*
|
|
|
|
* conn should be a database connection that you just called COPY TO on
|
|
|
|
* (and which gave you PGRES_COPY_OUT back);
|
|
|
|
* copystream is the file stream you want the output to go to
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
handleCopyOut(PGconn *conn, FILE *copystream)
|
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
bool copydone = false; /* haven't started yet */
|
|
|
|
char copybuf[COPYBUFSIZ];
|
|
|
|
int ret;
|
1999-11-04 21:56:02 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
while (!copydone)
|
1999-11-04 21:56:02 +00:00
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
ret = PQgetline(conn, copybuf, COPYBUFSIZ);
|
|
|
|
|
|
|
|
if (copybuf[0] == '\\' &&
|
|
|
|
copybuf[1] == '.' &&
|
|
|
|
copybuf[2] == '\0')
|
|
|
|
{
|
|
|
|
copydone = true; /* we're at the end */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fputs(copybuf, copystream);
|
|
|
|
switch (ret)
|
|
|
|
{
|
|
|
|
case EOF:
|
|
|
|
copydone = true;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 0:
|
|
|
|
fputc('\n', copystream);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
fflush(copystream);
|
2000-02-21 19:40:42 +00:00
|
|
|
ret = !PQendcopy(conn);
|
2003-03-20 06:00:12 +00:00
|
|
|
ResetCancelConn();
|
2000-04-12 17:17:23 +00:00
|
|
|
return ret;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2000-01-21 04:21:12 +00:00
|
|
|
* handleCopyIn
|
1999-11-04 21:56:02 +00:00
|
|
|
* receives data as a result of a COPY ... FROM stdin command
|
|
|
|
*
|
|
|
|
* Again, if you want to use COPY FROM in your application, copy this.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool
|
2004-01-20 23:48:56 +00:00
|
|
|
handleCopyIn(PGconn *conn, FILE *copystream)
|
1999-11-04 21:56:02 +00:00
|
|
|
{
|
2004-01-20 23:48:56 +00:00
|
|
|
const char *prompt;
|
1999-11-04 23:14:30 +00:00
|
|
|
bool copydone = false;
|
|
|
|
bool firstload;
|
|
|
|
bool linedone;
|
|
|
|
char copybuf[COPYBUFSIZ];
|
|
|
|
char *s;
|
2000-01-21 04:21:12 +00:00
|
|
|
int bufleft;
|
1999-11-04 23:14:30 +00:00
|
|
|
int c = 0;
|
2000-04-12 17:17:23 +00:00
|
|
|
int ret;
|
2001-03-22 04:01:46 +00:00
|
|
|
unsigned int linecount = 0;
|
1999-11-04 23:14:30 +00:00
|
|
|
|
2004-01-20 23:48:56 +00:00
|
|
|
/* 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
|
2000-01-21 04:21:12 +00:00
|
|
|
{
|
2004-01-20 23:48:56 +00:00
|
|
|
prompt = NULL;
|
2000-01-21 04:21:12 +00:00
|
|
|
}
|
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
while (!copydone)
|
|
|
|
{ /* for each input line ... */
|
2000-01-21 04:21:12 +00:00
|
|
|
if (prompt)
|
1999-11-04 21:56:02 +00:00
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
fputs(prompt, stdout);
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
firstload = true;
|
|
|
|
linedone = false;
|
2000-03-01 21:10:05 +00:00
|
|
|
|
1999-11-04 23:14:30 +00:00
|
|
|
while (!linedone)
|
2000-01-21 04:21:12 +00:00
|
|
|
{ /* for each bufferload in line ... */
|
1999-11-04 23:14:30 +00:00
|
|
|
s = copybuf;
|
2000-04-12 17:17:23 +00:00
|
|
|
for (bufleft = COPYBUFSIZ - 1; bufleft > 0; bufleft--)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
|
|
|
c = getc(copystream);
|
|
|
|
if (c == '\n' || c == EOF)
|
|
|
|
{
|
|
|
|
linedone = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*s++ = c;
|
|
|
|
}
|
|
|
|
*s = '\0';
|
2000-01-21 04:21:12 +00:00
|
|
|
if (c == EOF && s == copybuf && firstload)
|
1999-11-04 23:14:30 +00:00
|
|
|
{
|
|
|
|
PQputline(conn, "\\.");
|
|
|
|
copydone = true;
|
2000-04-12 17:17:23 +00:00
|
|
|
if (pset.cur_cmd_interactive)
|
|
|
|
puts("\\.");
|
1999-11-04 23:14:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
PQputline(conn, copybuf);
|
|
|
|
if (firstload)
|
|
|
|
{
|
|
|
|
if (!strcmp(copybuf, "\\."))
|
2000-01-21 04:21:12 +00:00
|
|
|
{
|
1999-11-04 23:14:30 +00:00
|
|
|
copydone = true;
|
2000-01-21 04:21:12 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
firstload = false;
|
|
|
|
}
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
1999-11-04 23:14:30 +00:00
|
|
|
PQputline(conn, "\n");
|
2000-12-03 15:39:38 +00:00
|
|
|
linecount++;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|
2000-02-21 19:40:42 +00:00
|
|
|
ret = !PQendcopy(conn);
|
2000-12-03 15:39:38 +00:00
|
|
|
pset.lineno += linecount;
|
2000-04-12 17:17:23 +00:00
|
|
|
return ret;
|
1999-11-04 21:56:02 +00:00
|
|
|
}
|