mirror of
https://github.com/postgres/postgres.git
synced 2025-06-26 12:21:12 +03:00
Add COPY WITH CVS HEADER to allow a heading line as the first line in
COPY. Andrew Dunstan
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.64 2005/05/06 03:38:05 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.65 2005/05/07 02:22:45 momjian Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -28,7 +28,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
|
|||||||
[ OIDS ]
|
[ OIDS ]
|
||||||
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
||||||
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
|
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
|
||||||
[ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
|
[ CSV [ HEADER ]
|
||||||
|
[ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
|
||||||
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
|
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
|
||||||
[ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
|
[ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
|
||||||
|
|
||||||
@ -36,10 +37,12 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
|
|||||||
TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
|
TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
|
||||||
[ [ WITH ]
|
[ [ WITH ]
|
||||||
[ BINARY ]
|
[ BINARY ]
|
||||||
|
[ HEADER ]
|
||||||
[ OIDS ]
|
[ OIDS ]
|
||||||
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
||||||
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
|
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
|
||||||
[ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
|
[ CSV [ HEADER ]
|
||||||
|
[ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
|
||||||
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
|
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
|
||||||
[ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
|
[ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
|
||||||
</synopsis>
|
</synopsis>
|
||||||
@ -191,6 +194,17 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>HEADER</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Specifies the file contains a header line with the names of each
|
||||||
|
column in the file. On output, the first line contains the column
|
||||||
|
names from the table, and on input, the first line is ignored.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><replaceable class="parameter">quote</replaceable></term>
|
<term><replaceable class="parameter">quote</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.243 2005/05/06 17:24:53 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.244 2005/05/07 02:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -131,13 +131,13 @@ static bool line_buf_converted;
|
|||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote,
|
char *delim, char *null_print, bool csv_mode, char *quote,
|
||||||
char *escape, List *force_quote_atts, bool fe_copy);
|
char *escape, List *force_quote_atts, bool header_line, bool fe_copy);
|
||||||
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
|
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
|
||||||
List *force_quote_atts);
|
List *force_quote_atts, bool header_line);
|
||||||
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
|
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
|
||||||
List *force_notnull_atts);
|
List *force_notnull_atts, bool header_line);
|
||||||
static bool CopyReadLine(char * quote, char * escape);
|
static bool CopyReadLine(char * quote, char * escape);
|
||||||
static char *CopyReadAttribute(const char *delim, const char *null_print,
|
static char *CopyReadAttribute(const char *delim, const char *null_print,
|
||||||
CopyReadResult *result, bool *isnull);
|
CopyReadResult *result, bool *isnull);
|
||||||
@ -695,6 +695,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
bool binary = false;
|
bool binary = false;
|
||||||
bool oids = false;
|
bool oids = false;
|
||||||
bool csv_mode = false;
|
bool csv_mode = false;
|
||||||
|
bool header_line = false;
|
||||||
char *delim = NULL;
|
char *delim = NULL;
|
||||||
char *quote = NULL;
|
char *quote = NULL;
|
||||||
char *escape = NULL;
|
char *escape = NULL;
|
||||||
@ -752,6 +753,14 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
errmsg("conflicting or redundant options")));
|
errmsg("conflicting or redundant options")));
|
||||||
csv_mode = intVal(defel->arg);
|
csv_mode = intVal(defel->arg);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(defel->defname, "header") == 0)
|
||||||
|
{
|
||||||
|
if (header_line)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
header_line = intVal(defel->arg);
|
||||||
|
}
|
||||||
else if (strcmp(defel->defname, "quote") == 0)
|
else if (strcmp(defel->defname, "quote") == 0)
|
||||||
{
|
{
|
||||||
if (quote)
|
if (quote)
|
||||||
@ -825,6 +834,12 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("COPY delimiter must be a single character")));
|
errmsg("COPY delimiter must be a single character")));
|
||||||
|
|
||||||
|
/* Check header */
|
||||||
|
if (!csv_mode && header_line)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("COPY HEADER available only in CSV mode")));
|
||||||
|
|
||||||
/* Check quote */
|
/* Check quote */
|
||||||
if (!csv_mode && quote != NULL)
|
if (!csv_mode && quote != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -1015,7 +1030,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
||||||
quote, escape, force_notnull_atts);
|
quote, escape, force_notnull_atts, header_line);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* copy from database to file */
|
{ /* copy from database to file */
|
||||||
@ -1079,7 +1094,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
||||||
quote, escape, force_quote_atts, fe_copy);
|
quote, escape, force_quote_atts, header_line, fe_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
@ -1111,7 +1126,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
static void
|
static void
|
||||||
DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote,
|
char *delim, char *null_print, bool csv_mode, char *quote,
|
||||||
char *escape, List *force_quote_atts, bool fe_copy)
|
char *escape, List *force_quote_atts, bool header_line, bool fe_copy)
|
||||||
{
|
{
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
@ -1119,7 +1134,7 @@ DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
SendCopyBegin(binary, list_length(attnumlist));
|
SendCopyBegin(binary, list_length(attnumlist));
|
||||||
|
|
||||||
CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
|
||||||
quote, escape, force_quote_atts);
|
quote, escape, force_quote_atts, header_line);
|
||||||
|
|
||||||
if (fe_copy)
|
if (fe_copy)
|
||||||
SendCopyEnd(binary);
|
SendCopyEnd(binary);
|
||||||
@ -1143,7 +1158,7 @@ DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
static void
|
static void
|
||||||
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote,
|
char *delim, char *null_print, bool csv_mode, char *quote,
|
||||||
char *escape, List *force_quote_atts)
|
char *escape, List *force_quote_atts, bool header_line)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -1226,6 +1241,30 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
null_print = (char *)
|
null_print = (char *)
|
||||||
pg_server_to_client((unsigned char *) null_print,
|
pg_server_to_client((unsigned char *) null_print,
|
||||||
strlen(null_print));
|
strlen(null_print));
|
||||||
|
|
||||||
|
/* if a header has been requested send the line */
|
||||||
|
if (header_line)
|
||||||
|
{
|
||||||
|
bool hdr_delim = false;
|
||||||
|
char *colname;
|
||||||
|
|
||||||
|
foreach(cur, attnumlist)
|
||||||
|
{
|
||||||
|
int attnum = lfirst_int(cur);
|
||||||
|
|
||||||
|
if (hdr_delim)
|
||||||
|
CopySendChar(delim[0]);
|
||||||
|
hdr_delim = true;
|
||||||
|
|
||||||
|
colname = NameStr(attr[attnum - 1]->attname);
|
||||||
|
|
||||||
|
CopyAttributeOutCSV(colname, delim, quote, escape,
|
||||||
|
strcmp(colname, null_print) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CopySendEndOfRow(binary);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scandesc = heap_beginscan(rel, ActiveSnapshot, 0, NULL);
|
scandesc = heap_beginscan(rel, ActiveSnapshot, 0, NULL);
|
||||||
@ -1427,7 +1466,7 @@ limit_printout_length(StringInfo buf)
|
|||||||
static void
|
static void
|
||||||
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print, bool csv_mode, char *quote,
|
char *delim, char *null_print, bool csv_mode, char *quote,
|
||||||
char *escape, List *force_notnull_atts)
|
char *escape, List *force_notnull_atts, bool header_line)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -1653,6 +1692,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
errcontext.previous = error_context_stack;
|
errcontext.previous = error_context_stack;
|
||||||
error_context_stack = &errcontext;
|
error_context_stack = &errcontext;
|
||||||
|
|
||||||
|
/* on input just throw the header line away */
|
||||||
|
if (header_line)
|
||||||
|
{
|
||||||
|
copy_lineno++;
|
||||||
|
done = CopyReadLine(quote, escape) ;
|
||||||
|
}
|
||||||
|
|
||||||
while (!done)
|
while (!done)
|
||||||
{
|
{
|
||||||
bool skip_tuple;
|
bool skip_tuple;
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.490 2005/05/06 03:42:17 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.491 2005/05/07 02:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -362,7 +362,7 @@ static void doNegateFloat(Value *v);
|
|||||||
|
|
||||||
GLOBAL GRANT GROUP_P
|
GLOBAL GRANT GROUP_P
|
||||||
|
|
||||||
HANDLER HAVING HOLD HOUR_P
|
HANDLER HAVING HEADER HOLD HOUR_P
|
||||||
|
|
||||||
ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
|
ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
|
||||||
INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
|
INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
|
||||||
@ -1444,6 +1444,10 @@ copy_opt_item:
|
|||||||
{
|
{
|
||||||
$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
|
$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
|
||||||
}
|
}
|
||||||
|
| HEADER
|
||||||
|
{
|
||||||
|
$$ = makeDefElem("header", (Node *)makeInteger(TRUE));
|
||||||
|
}
|
||||||
| QUOTE opt_as Sconst
|
| QUOTE opt_as Sconst
|
||||||
{
|
{
|
||||||
$$ = makeDefElem("quote", (Node *)makeString($3));
|
$$ = makeDefElem("quote", (Node *)makeString($3));
|
||||||
@ -7787,6 +7791,7 @@ unreserved_keyword:
|
|||||||
| FUNCTION
|
| FUNCTION
|
||||||
| GLOBAL
|
| GLOBAL
|
||||||
| HANDLER
|
| HANDLER
|
||||||
|
| HEADER
|
||||||
| HOLD
|
| HOLD
|
||||||
| HOUR_P
|
| HOUR_P
|
||||||
| IMMEDIATE
|
| IMMEDIATE
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.154 2004/12/31 22:00:27 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.155 2005/05/07 02:22:47 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -148,6 +148,7 @@ static const ScanKeyword ScanKeywords[] = {
|
|||||||
{"group", GROUP_P},
|
{"group", GROUP_P},
|
||||||
{"handler", HANDLER},
|
{"handler", HANDLER},
|
||||||
{"having", HAVING},
|
{"having", HAVING},
|
||||||
|
{"header", HEADER},
|
||||||
{"hold", HOLD},
|
{"hold", HOLD},
|
||||||
{"hour", HOUR_P},
|
{"hour", HOUR_P},
|
||||||
{"ilike", ILIKE},
|
{"ilike", ILIKE},
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.56 2005/02/22 04:40:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.57 2005/05/07 02:22:49 momjian Exp $
|
||||||
*/
|
*/
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
@ -66,6 +66,7 @@ struct copy_options
|
|||||||
bool binary;
|
bool binary;
|
||||||
bool oids;
|
bool oids;
|
||||||
bool csv_mode;
|
bool csv_mode;
|
||||||
|
bool header;
|
||||||
char *delim;
|
char *delim;
|
||||||
char *null;
|
char *null;
|
||||||
char *quote;
|
char *quote;
|
||||||
@ -289,6 +290,8 @@ parse_slash_copy(const char *args)
|
|||||||
result->oids = true;
|
result->oids = true;
|
||||||
else if (pg_strcasecmp(token, "csv") == 0)
|
else if (pg_strcasecmp(token, "csv") == 0)
|
||||||
result->csv_mode = true;
|
result->csv_mode = true;
|
||||||
|
else if (pg_strcasecmp(token, "header") == 0)
|
||||||
|
result->header = true;
|
||||||
else if (pg_strcasecmp(token, "delimiter") == 0)
|
else if (pg_strcasecmp(token, "delimiter") == 0)
|
||||||
{
|
{
|
||||||
token = strtokx(NULL, whitespace, NULL, "'",
|
token = strtokx(NULL, whitespace, NULL, "'",
|
||||||
@ -481,6 +484,9 @@ do_copy(const char *args)
|
|||||||
if (options->csv_mode)
|
if (options->csv_mode)
|
||||||
appendPQExpBuffer(&query, " CSV");
|
appendPQExpBuffer(&query, " CSV");
|
||||||
|
|
||||||
|
if (options->header)
|
||||||
|
appendPQExpBuffer(&query, " HEADER");
|
||||||
|
|
||||||
if (options->quote)
|
if (options->quote)
|
||||||
{
|
{
|
||||||
if (options->quote[0] == '\'')
|
if (options->quote[0] == '\'')
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.126 2005/05/04 14:25:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.127 2005/05/07 02:22:49 momjian Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*----------------------------------------------------------------------
|
/*----------------------------------------------------------------------
|
||||||
@ -1040,7 +1040,7 @@ psql_completion(char *text, int start, int end)
|
|||||||
pg_strcasecmp(prev3_wd, "TO") == 0))
|
pg_strcasecmp(prev3_wd, "TO") == 0))
|
||||||
{
|
{
|
||||||
static const char *const list_CSV[] =
|
static const char *const list_CSV[] =
|
||||||
{"QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
|
{"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
|
||||||
|
|
||||||
COMPLETE_WITH_LIST(list_CSV);
|
COMPLETE_WITH_LIST(list_CSV);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user