Unix Technical Forum

Re: COPY CSV header line feature

This is a discussion on Re: COPY CSV header line feature within the Pgsql Patches forums, part of the PostgreSQL category; --> Applied. --------------------------------------------------------------------------- pgman wrote: > > Here is an updated version of this patch, with documentation changes. > > ...


Go Back   Unix Technical Forum > Database Server Software > PostgreSQL > Pgsql Patches

FAQ Members List Calendar Search Today's Posts Mark Forums Read
  #1 (permalink)  
Old 04-18-2008, 12:17 AM
Bruce Momjian
 
Posts: n/a
Default Re: COPY CSV header line feature


Applied.

---------------------------------------------------------------------------

pgman wrote:
>
> Here is an updated version of this patch, with documentation changes.
>
> I have already updated the gram.y comment you suggested.
>
> ---------------------------------------------------------------------------
>
> Andrew Dunstan wrote:
> >
> > ammended patch attached. sorry for the oversight. I agree with Tom's
> > remark - it's far too easy to miss this.
> >
> > cheers
> >
> > andrew
> >
> > Alvaro Herrera wrote:
> >
> > >On Tue, Mar 15, 2005 at 08:55:36PM -0600, Andrew Dunstan wrote:
> > >
> > >
> > >>Alvaro Herrera said:
> > >>
> > >>
> > >>>On Sun, Mar 13, 2005 at 06:32:20PM -0500, Andrew Dunstan wrote:
> > >>>
> > >>>
> > >>>
> > >>>>The attached patch implements the previously discussed header line
> > >>>>feature for CSV mode COPY. It is triggered by the keyword HEADER
> > >>>>(blame Bruce - he chose it ;-) ).
> > >>>>
> > >>>>
> > >>>I think you should add the new reserved keyword to the
> > >>>unreserved_keywords list or some other.
> > >>>
> > >>>
> > >>Please be more specific. I'll be happy to add in anything I've missed.
> > >>
> > >>
> > >
> > >The Postgres grammar classifies keywords in one of several lists, in
> > >order to make them available as names to users (column names, function
> > >names, etc). So each time you create a new keyword and add it to the
> > >keywords.c list, you have to add it to one of the lists on gram.y too.
> > >See gram.y line 7669 ff.
> > >
> > >I'd add a comment on this on gram.y:
> > >
> > >Index: gram.y
> > >================================================= ==================
> > >RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
> > >retrieving revision 2.484
> > >diff -c -w -b -B -c -r2.484 gram.y
> > >*** gram.y 14 Mar 2005 00:19:36 -0000 2.484
> > >--- gram.y 16 Mar 2005 03:12:48 -0000
> > >***************
> > >*** 327,333 ****
> > > /*
> > > * If you make any token changes, update the keyword table in
> > > * parser/keywords.c and add new keywords to the appropriate one of
> > >! * the reserved-or-not-so-reserved keyword lists, below.
> > > */
> > >
> > > /* ordinary key words in alphabetical order */
> > >--- 327,334 ----
> > > /*
> > > * If you make any token changes, update the keyword table in
> > > * parser/keywords.c and add new keywords to the appropriate one of
> > >! * the reserved-or-not-so-reserved keyword lists, below; search this
> > >! * file for "Name classification hierarchy."
> > > */
> > >
> > > /* ordinary key words in alphabetical order */
> > >
> > >
> > >

>
>
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 4: Don't 'kill -9' the postmaster

>
> --
> Bruce Momjian | http://candle.pha.pa.us
> pgman@candle.pha.pa.us | (610) 359-1001
> + If your life is a hard drive, | 13 Roberts Road
> + Christ can be your backup. | Newtown Square, Pennsylvania 19073


> 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 <replaceable class="parameter">tablename</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
> FROM { '<replaceable class="parameter">filename</replaceable>' | STDIN }
> [ [ WITH ]
> ! [ BINARY ]
> [ OIDS ]
> [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
> [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
> ! [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
> [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
> [ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
>
> --- 24,35 ----
> COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
> FROM { '<replaceable class="parameter">filename</replaceable>' | STDIN }
> [ [ WITH ]
> ! [ BINARY ]
> [ OIDS ]
> [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
> [ NULL [ AS ] '<replaceable class="parameter">null string</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,45 ****
> TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
> [ [ WITH ]
> [ BINARY ]
> [ OIDS ]
> [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
> [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
> ! [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
> [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
> [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
> </synopsis>
> --- 37,48 ----
> 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 [ HEADER ]
> ! [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ]
> [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
> [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
> </synopsis>
> ***************
> *** 192,197 ****
> --- 195,211 ----
> </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>
> <para>
> 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);
> }


--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073

---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On
Forum Jump


All times are GMT. The time now is 05:27 PM.


Powered by vBulletin® Version 3.6.5
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
SEO by vBSEO 3.2.0
www.UnixAdminTalk.com