Index: doc/src/sgml/ref/copy.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v retrieving revision 1.63 diff -c -c -r1.63 copy.sgml *** doc/src/sgml/ref/copy.sgml 4 Jan 2005 00:39:53 -0000 1.63 --- doc/src/sgml/ref/copy.sgml 6 May 2005 03:36:30 -0000 *************** *** 24,34 **** COPY tablename [ ( column [, ...] ) ] FROM { 'filename' | STDIN } [ [ WITH ] ! [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' ] [ NULL [ AS ] 'null string' ] ! [ CSV [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] [ FORCE NOT NULL column [, ...] ] --- 24,35 ---- COPY tablename [ ( column [, ...] ) ] FROM { 'filename' | STDIN } [ [ WITH ] ! [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' ] [ NULL [ AS ] 'null string' ] ! [ CSV [ HEADER ] ! [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] [ FORCE NOT NULL column [, ...] ] *************** *** 36,45 **** TO { 'filename' | STDOUT } [ [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' ] [ NULL [ AS ] 'null string' ] ! [ CSV [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] [ FORCE QUOTE column [, ...] ] --- 37,48 ---- TO { 'filename' | STDOUT } [ [ WITH ] [ BINARY ] + [ HEADER ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' ] [ NULL [ AS ] 'null string' ] ! [ CSV [ HEADER ] ! [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] [ FORCE QUOTE column [, ...] ] *************** *** 192,197 **** --- 195,211 ---- + HEADER + + + 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. + + + + + quote Index: src/backend/commands/copy.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/commands/copy.c,v retrieving revision 1.242 diff -c -c -r1.242 copy.c *** src/backend/commands/copy.c 6 May 2005 02:56:42 -0000 1.242 --- src/backend/commands/copy.c 6 May 2005 03:36:31 -0000 *************** *** 130,142 **** /* 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); 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); 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); static bool CopyReadLine(char * quote, char * escape); static char *CopyReadAttribute(const char *delim, const char *null_print, CopyReadResult *result, bool *isnull); --- 130,142 ---- /* 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 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, 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, bool header_line); static bool CopyReadLine(char * quote, char * escape); static char *CopyReadAttribute(const char *delim, const char *null_print, CopyReadResult *result, bool *isnull); *************** *** 694,699 **** --- 694,700 ---- bool binary = false; bool oids = false; bool csv_mode = false; + bool header_line = false; char *delim = NULL; char *quote = NULL; char *escape = NULL; *************** *** 751,756 **** --- 752,765 ---- 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) *************** *** 824,829 **** --- 833,844 ---- (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, *************** *** 1014,1020 **** } } CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_notnull_atts); } else { /* copy from database to file */ --- 1029,1035 ---- } } CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_notnull_atts, header_line); } else { /* copy from database to file */ *************** *** 1078,1084 **** } DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_quote_atts, fe_copy); } if (!pipe) --- 1093,1099 ---- } DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_quote_atts, header_line, fe_copy); } if (!pipe) *************** *** 1110,1116 **** 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) { PG_TRY(); { --- 1125,1131 ---- 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 header_line, bool fe_copy) { PG_TRY(); { *************** *** 1118,1124 **** SendCopyBegin(binary, list_length(attnumlist)); CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_quote_atts); if (fe_copy) SendCopyEnd(binary); --- 1133,1139 ---- SendCopyBegin(binary, list_length(attnumlist)); CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode, ! quote, escape, force_quote_atts, header_line); if (fe_copy) SendCopyEnd(binary); *************** *** 1142,1148 **** 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) { HeapTuple tuple; TupleDesc tupDesc; --- 1157,1163 ---- 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, bool header_line) { HeapTuple tuple; TupleDesc tupDesc; *************** *** 1225,1230 **** --- 1240,1269 ---- 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); *************** *** 1426,1432 **** 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) { HeapTuple tuple; TupleDesc tupDesc; --- 1465,1471 ---- 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, bool header_line) { HeapTuple tuple; TupleDesc tupDesc; *************** *** 1652,1657 **** --- 1691,1703 ---- 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; Index: src/backend/parser/gram.y =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.489 diff -c -c -r2.489 gram.y *** src/backend/parser/gram.y 28 Apr 2005 21:47:14 -0000 2.489 --- src/backend/parser/gram.y 6 May 2005 03:36:36 -0000 *************** *** 361,367 **** GLOBAL GRANT GROUP_P ! HANDLER HAVING HOLD HOUR_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P --- 361,367 ---- GLOBAL GRANT GROUP_P ! HANDLER HAVING HEADER HOLD HOUR_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P *************** *** 1443,1448 **** --- 1443,1452 ---- { $$ = makeDefElem("csv", (Node *)makeInteger(TRUE)); } + | HEADER + { + $$ = makeDefElem("header", (Node *)makeInteger(TRUE)); + } | QUOTE opt_as Sconst { $$ = makeDefElem("quote", (Node *)makeString($3)); *************** *** 7786,7791 **** --- 7790,7796 ---- | FUNCTION | GLOBAL | HANDLER + | HEADER | HOLD | HOUR_P | IMMEDIATE Index: src/backend/parser/keywords.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.154 diff -c -c -r1.154 keywords.c *** src/backend/parser/keywords.c 31 Dec 2004 22:00:27 -0000 1.154 --- src/backend/parser/keywords.c 6 May 2005 03:36:36 -0000 *************** *** 148,153 **** --- 148,154 ---- {"group", GROUP_P}, {"handler", HANDLER}, {"having", HAVING}, + {"header", HEADER}, {"hold", HOLD}, {"hour", HOUR_P}, {"ilike", ILIKE}, Index: src/bin/psql/copy.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v retrieving revision 1.56 diff -c -c -r1.56 copy.c *** src/bin/psql/copy.c 22 Feb 2005 04:40:54 -0000 1.56 --- src/bin/psql/copy.c 6 May 2005 03:36:39 -0000 *************** *** 66,71 **** --- 66,72 ---- bool binary; bool oids; bool csv_mode; + bool header; char *delim; char *null; char *quote; *************** *** 289,294 **** --- 290,297 ---- 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,486 **** --- 484,492 ---- if (options->csv_mode) appendPQExpBuffer(&query, " CSV"); + if (options->header) + appendPQExpBuffer(&query, " HEADER"); + if (options->quote) { if (options->quote[0] == '\'') Index: src/bin/psql/tab-complete.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v retrieving revision 1.126 diff -c -c -r1.126 tab-complete.c *** src/bin/psql/tab-complete.c 4 May 2005 14:25:24 -0000 1.126 --- src/bin/psql/tab-complete.c 6 May 2005 03:36:40 -0000 *************** *** 1040,1046 **** pg_strcasecmp(prev3_wd, "TO") == 0)) { static const char *const list_CSV[] = ! {"QUOTE", "ESCAPE", "FORCE QUOTE", NULL}; COMPLETE_WITH_LIST(list_CSV); } --- 1040,1046 ---- pg_strcasecmp(prev3_wd, "TO") == 0)) { static const char *const list_CSV[] = ! {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL}; COMPLETE_WITH_LIST(list_CSV); }