CSV format supports the HEADER option to output a header in the output, it is convenient when other programs need to consume the output. This patch adds the same option to the default text format.
Discussion: https://www.postgresql.org/message-id/flat/caf1-j-0ptcwmeltswwgv2m70u26n4g33gpe1rckqqe6wvqd...@mail.gmail.com --- contrib/file_fdw/input/file_fdw.source | 1 - contrib/file_fdw/output/file_fdw.source | 4 +--- doc/src/sgml/ref/copy.sgml | 3 ++- src/backend/commands/copy.c | 11 +++++++---- src/test/regress/input/copy.source | 12 ++++++++++++ src/test/regress/output/copy.source | 8 ++++++++ 6 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/contrib/file_fdw/input/file_fdw.source b/contrib/file_fdw/input/file_fdw.source index 45b728eeb3..83edb71077 100644 --- a/contrib/file_fdw/input/file_fdw.source +++ b/contrib/file_fdw/input/file_fdw.source @@ -37,7 +37,6 @@ CREATE USER MAPPING FOR regress_no_priv_user SERVER file_server; -- validator tests CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml'); -- ERROR -CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', header 'true'); -- ERROR CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', quote ':'); -- ERROR CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', escape ':'); -- ERROR CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'binary', header 'true'); -- ERROR diff --git a/contrib/file_fdw/output/file_fdw.source b/contrib/file_fdw/output/file_fdw.source index 52b4d5f1df..547b81fd16 100644 --- a/contrib/file_fdw/output/file_fdw.source +++ b/contrib/file_fdw/output/file_fdw.source @@ -33,14 +33,12 @@ CREATE USER MAPPING FOR regress_no_priv_user SERVER file_server; -- validator tests CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml'); -- ERROR ERROR: COPY format "xml" not recognized -CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', header 'true'); -- ERROR -ERROR: COPY HEADER available only in CSV mode CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', quote ':'); -- ERROR ERROR: COPY quote available only in CSV mode CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', escape ':'); -- ERROR ERROR: COPY escape available only in CSV mode CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'binary', header 'true'); -- ERROR -ERROR: COPY HEADER available only in CSV mode +ERROR: COPY HEADER available only in CSV and text mode CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'binary', quote ':'); -- ERROR ERROR: COPY quote available only in CSV mode CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'binary', escape ':'); -- ERROR diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index 18189abc6c..c628a69c57 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -269,7 +269,8 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable Specifies that 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. - This option is allowed only when using <literal>CSV</literal> format. + This option is allowed only when using <literal>CSV</literal> or + <literal>text</literal> format. </para> </listitem> </varlistentry> diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 44da71c4cb..a21508a974 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -136,7 +136,7 @@ typedef struct CopyStateData bool binary; /* binary format? */ bool freeze; /* freeze rows on loading? */ bool csv_mode; /* Comma Separated Value format? */ - bool header_line; /* CSV header line? */ + bool header_line; /* CSV or text header line? */ char *null_print; /* NULL marker string (server encoding!) */ int null_print_len; /* length of same */ char *null_print_client; /* same converted to file encoding */ @@ -1363,10 +1363,10 @@ ProcessCopyOptions(ParseState *pstate, errmsg("COPY delimiter cannot be \"%s\"", cstate->delim))); /* Check header */ - if (!cstate->csv_mode && cstate->header_line) + if (cstate->binary && cstate->header_line) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY HEADER available only in CSV mode"))); + errmsg("COPY HEADER available only in CSV and text mode"))); /* Check quote */ if (!cstate->csv_mode && cstate->quote != NULL) @@ -2099,8 +2099,11 @@ CopyTo(CopyState cstate) colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname); - CopyAttributeOutCSV(cstate, colname, false, + if (cstate->csv_mode) + CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, colname); } CopySendEndOfRow(cstate); diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source index a1d529ad36..2368649111 100644 --- a/src/test/regress/input/copy.source +++ b/src/test/regress/input/copy.source @@ -134,6 +134,18 @@ this is just a line full of junk that would error out if parsed copy copytest3 to stdout csv header; +create temp table copytest4 ( + c1 int, + "col with tabulation: " text); + +copy copytest4 from stdin (header); +this is just a line full of junk that would error out if parsed +1 a +2 b +\. + +copy copytest4 to stdout (header); + -- test copy from with a partitioned table create table parted_copytest ( a int, diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source index 938d3551da..c1f7f99747 100644 --- a/src/test/regress/output/copy.source +++ b/src/test/regress/output/copy.source @@ -95,6 +95,14 @@ copy copytest3 to stdout csv header; c1,"col with , comma","col with "" quote" 1,a,1 2,b,2 +create temp table copytest4 ( + c1 int, + "col with tabulation: " text); +copy copytest4 from stdin (header); +copy copytest4 to stdout (header); +c1 col with tabulation: \t +1 a +2 b -- test copy from with a partitioned table create table parted_copytest ( a int,