diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index e7f76d3e58112b144e14a1549b824cc89c9396dc..2ea68de912e3ffdcf053c3cfdfd6006749e26f18 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.85 2009/02/06 21:22:49 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.86 2009/07/25 00:07:10 adunstan Exp $ PostgreSQL documentation --> @@ -44,7 +44,7 @@ COPY { <replaceable class="parameter">tablename</replaceable> [ ( <replaceable c [ CSV [ HEADER ] [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ] - [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ] + [ FORCE QUOTE { <replaceable class="parameter">column</replaceable> [, ...] | * } ] </synopsis> </refsynopsisdiv> @@ -248,7 +248,9 @@ COPY { <replaceable class="parameter">tablename</replaceable> [ ( <replaceable c <para> In <literal>CSV</> <command>COPY TO</> mode, forces quoting to be used for all non-<literal>NULL</> values in each specified column. - <literal>NULL</> output is never quoted. + <literal>NULL</> output is never quoted. If <literal>*</> is specified, + non-<literal>NULL</> values for all columns of the table will be + quoted. </para> </listitem> </varlistentry> diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index c464ed7f6ce16dfed49104c236cfcfb3f1f59f3e..9048276c5461656ca20ef6e4252ecc617d354f04 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.312 2009/06/11 14:48:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.313 2009/07/25 00:07:11 adunstan Exp $ * *------------------------------------------------------------------------- */ @@ -730,6 +730,9 @@ DoCopy(const CopyStmt *stmt, const char *queryString) int num_phys_attrs; uint64 processed; + /* a dummy list that represents 'all-columns' */ + List all_columns = { T_List }; + /* Allocate workspace and zero all fields */ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); @@ -808,7 +811,11 @@ DoCopy(const CopyStmt *stmt, const char *queryString) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - force_quote = (List *) defel->arg; + + if (IsA(defel->arg, A_Star)) + force_quote = &all_columns; + else + force_quote = (List *) defel->arg; } else if (strcmp(defel->defname, "force_notnull") == 0) { @@ -1092,7 +1099,14 @@ DoCopy(const CopyStmt *stmt, const char *queryString) /* Convert FORCE QUOTE name list to per-column flags, check validity */ cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); - if (force_quote) + if (force_quote == &all_columns) + { + int i; + + for (i = 0; i < num_phys_attrs; i++) + cstate->force_quote_flags[i] = true; + } + else if (force_quote) { List *attnums; ListCell *cur; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7e6d55be71274da9c7636270d328e5e8388d783d..c88073d33ddb3d3e8519abc216732f5152460cc4 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.671 2009/07/20 02:42:28 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.672 2009/07/25 00:07:11 adunstan Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2028,6 +2028,10 @@ copy_opt_item: { $$ = makeDefElem("force_quote", (Node *)$3); } + | FORCE QUOTE '*' + { + $$ = makeDefElem("force_quote", (Node *)makeNode(A_Star)); + } | FORCE NOT NULL_P columnList { $$ = makeDefElem("force_notnull", (Node *)$4); diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index 7f374ac1a6f79e41dc530501ed85254d0db9864d..5f52d6ee6cf226b65d41e58d1630683107eef2e0 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -191,6 +191,10 @@ COPY y TO stdout WITH CSV FORCE QUOTE col2 ESCAPE E'\\'; "Jackson, Sam","\\h" "It is \"perfect\"."," " "", +COPY y TO stdout WITH CSV FORCE QUOTE *; +"Jackson, Sam","\h" +"It is ""perfect""."," " +"", --test that we read consecutive LFs properly CREATE TEMP TABLE testnl (a int, b text, c int); COPY testnl FROM stdin CSV; diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index 7c23ba253c4e28a47af195963ffd3d8094b829da..9dee93c14cd4c73a76644fa5beb386ce89005b10 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -128,6 +128,7 @@ INSERT INTO y VALUES ('', NULL); COPY y TO stdout WITH CSV; COPY y TO stdout WITH CSV QUOTE '''' DELIMITER '|'; COPY y TO stdout WITH CSV FORCE QUOTE col2 ESCAPE E'\\'; +COPY y TO stdout WITH CSV FORCE QUOTE *; --test that we read consecutive LFs properly