Commit b63990c6 authored by Bruce Momjian's avatar Bruce Momjian

Add COPY WITH CVS HEADER to allow a heading line as the first line in

COPY.

Andrew Dunstan
parent ce1ab398
<!-- <!--
$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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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; ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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 @@ ...@@ -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); ...@@ -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: ...@@ -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: ...@@ -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 @@ ...@@ -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[] = { ...@@ -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 @@ ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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);
} }
......
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