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
-->
......@@ -28,7 +28,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
[ OIDS ]
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</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>' ]
[ 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 }
[ [ WITH ]
[ BINARY ]
[ HEADER ]
[ OIDS ]
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</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>' ]
[ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
</synopsis>
......@@ -191,6 +194,17 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
</listitem>
</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>
<term><replaceable class="parameter">quote</replaceable></term>
<listitem>
......
......@@ -8,7 +8,7 @@
*
*
* 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 */
static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
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,
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,
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 char *CopyReadAttribute(const char *delim, const char *null_print,
CopyReadResult *result, bool *isnull);
......@@ -695,6 +695,7 @@ DoCopy(const CopyStmt *stmt)
bool binary = false;
bool oids = false;
bool csv_mode = false;
bool header_line = false;
char *delim = NULL;
char *quote = NULL;
char *escape = NULL;
......@@ -752,6 +753,14 @@ DoCopy(const CopyStmt *stmt)
errmsg("conflicting or redundant options")));
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)
{
if (quote)
......@@ -825,6 +834,12 @@ DoCopy(const CopyStmt *stmt)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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 */
if (!csv_mode && quote != NULL)
ereport(ERROR,
......@@ -1015,7 +1030,7 @@ DoCopy(const CopyStmt *stmt)
}
}
CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
quote, escape, force_notnull_atts);
quote, escape, force_notnull_atts, header_line);
}
else
{ /* copy from database to file */
......@@ -1079,7 +1094,7 @@ DoCopy(const CopyStmt *stmt)
}
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)
......@@ -1111,7 +1126,7 @@ DoCopy(const CopyStmt *stmt)
static void
DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
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();
{
......@@ -1119,7 +1134,7 @@ DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
SendCopyBegin(binary, list_length(attnumlist));
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)
SendCopyEnd(binary);
......@@ -1143,7 +1158,7 @@ DoCopyTo(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, List *force_quote_atts)
char *escape, List *force_quote_atts, bool header_line)
{
HeapTuple tuple;
TupleDesc tupDesc;
......@@ -1226,6 +1241,30 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
null_print = (char *)
pg_server_to_client((unsigned char *) 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);
......@@ -1427,7 +1466,7 @@ limit_printout_length(StringInfo buf)
static void
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
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;
TupleDesc tupDesc;
......@@ -1653,6 +1692,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
errcontext.previous = error_context_stack;
error_context_stack = &errcontext;
/* on input just throw the header line away */
if (header_line)
{
copy_lineno++;
done = CopyReadLine(quote, escape) ;
}
while (!done)
{
bool skip_tuple;
......
......@@ -11,7 +11,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
......@@ -362,7 +362,7 @@ static void doNegateFloat(Value *v);
GLOBAL GRANT GROUP_P
HANDLER HAVING HOLD HOUR_P
HANDLER HAVING HEADER HOLD HOUR_P
ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
......@@ -1444,6 +1444,10 @@ copy_opt_item:
{
$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
}
| HEADER
{
$$ = makeDefElem("header", (Node *)makeInteger(TRUE));
}
| QUOTE opt_as Sconst
{
$$ = makeDefElem("quote", (Node *)makeString($3));
......@@ -7787,6 +7791,7 @@ unreserved_keyword:
| FUNCTION
| GLOBAL
| HANDLER
| HEADER
| HOLD
| HOUR_P
| IMMEDIATE
......
......@@ -8,7 +8,7 @@
*
*
* 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},
{"handler", HANDLER},
{"having", HAVING},
{"header", HEADER},
{"hold", HOLD},
{"hour", HOUR_P},
{"ilike", ILIKE},
......
......@@ -3,7 +3,7 @@
*
* 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 "copy.h"
......@@ -66,6 +66,7 @@ struct copy_options
bool binary;
bool oids;
bool csv_mode;
bool header;
char *delim;
char *null;
char *quote;
......@@ -289,6 +290,8 @@ parse_slash_copy(const char *args)
result->oids = true;
else if (pg_strcasecmp(token, "csv") == 0)
result->csv_mode = true;
else if (pg_strcasecmp(token, "header") == 0)
result->header = true;
else if (pg_strcasecmp(token, "delimiter") == 0)
{
token = strtokx(NULL, whitespace, NULL, "'",
......@@ -481,6 +484,9 @@ do_copy(const char *args)
if (options->csv_mode)
appendPQExpBuffer(&query, " CSV");
if (options->header)
appendPQExpBuffer(&query, " HEADER");
if (options->quote)
{
if (options->quote[0] == '\'')
......
......@@ -3,7 +3,7 @@
*
* 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))
{
static const char *const list_CSV[] =
{"QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
{"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
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