From 43f33dc018a4b77ced78a0a6df8ed5d450cfe5f4 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 28 Jan 2022 09:22:53 +0100 Subject: [PATCH] Add HEADER support to COPY text format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The COPY CSV format supports the HEADER option to output a header line. This patch adds the same option to the default text format. On input, the HEADER option causes the first line to be skipped, same as with CSV. Author: RĂ©mi Lapeyre Discussion: https://www.postgresql.org/message-id/flat/CAF1-J-0PtCWMeLtswwGV2M70U26n4g33gpe1rcKQqe6wVQDrFA@mail.gmail.com --- contrib/file_fdw/expected/file_fdw.out | 4 +--- contrib/file_fdw/sql/file_fdw.sql | 1 - doc/src/sgml/ref/copy.sgml | 2 +- src/backend/commands/copy.c | 4 ++-- src/backend/commands/copyto.c | 5 ++++- src/include/commands/copy.h | 2 +- src/test/regress/expected/copy.out | 8 ++++++++ src/test/regress/sql/copy.sql | 12 ++++++++++++ 8 files changed, 29 insertions(+), 9 deletions(-) diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out index 891146fef3..0ac6e4e0d7 100644 --- a/contrib/file_fdw/expected/file_fdw.out +++ b/contrib/file_fdw/expected/file_fdw.out @@ -50,14 +50,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: cannot specify HEADER in BINARY 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/contrib/file_fdw/sql/file_fdw.sql b/contrib/file_fdw/sql/file_fdw.sql index 0ea8b14508..86f876d565 100644 --- a/contrib/file_fdw/sql/file_fdw.sql +++ b/contrib/file_fdw/sql/file_fdw.sql @@ -56,7 +56,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/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index 4624c8f4c9..1b7d001963 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -277,7 +277,7 @@ COPY { table_name [ ( CSV format. + This option is not allowed when using binary format. diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index bb9c21bc6b..7da7105d44 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -555,10 +555,10 @@ ProcessCopyOptions(ParseState *pstate, errmsg("COPY delimiter cannot be \"%s\"", opts_out->delim))); /* Check header */ - if (!opts_out->csv_mode && opts_out->header_line) + if (opts_out->binary && opts_out->header_line) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY HEADER available only in CSV mode"))); + errmsg("cannot specify HEADER in BINARY mode"))); /* Check quote */ if (!opts_out->csv_mode && opts_out->quote != NULL) diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index 20bfd49112..e793b64bda 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -863,8 +863,11 @@ DoCopyTo(CopyToState cstate) colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname); - CopyAttributeOutCSV(cstate, colname, false, + if (cstate->opts.csv_mode) + CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, colname); } CopySendEndOfRow(cstate); diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index c01cf42bcd..8694da5004 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -32,7 +32,7 @@ typedef struct CopyFormatOptions 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; /* 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 */ diff --git a/src/test/regress/expected/copy.out b/src/test/regress/expected/copy.out index 931e7b2e69..851b9a4a2d 100644 --- a/src/test/regress/expected/copy.out +++ b/src/test/regress/expected/copy.out @@ -120,6 +120,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, + "colname with tab: " text); +copy copytest4 from stdin (header); +copy copytest4 to stdout (header); +c1 colname with tab: \t +1 a +2 b -- test copy from with a partitioned table create table parted_copytest ( a int, diff --git a/src/test/regress/sql/copy.sql b/src/test/regress/sql/copy.sql index 15e26517ec..016fedf675 100644 --- a/src/test/regress/sql/copy.sql +++ b/src/test/regress/sql/copy.sql @@ -160,6 +160,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, + "colname with tab: " 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,