diff -cpr HEAD/doc/src/sgml/ref/copy.sgml force_quote_all/doc/src/sgml/ref/copy.sgml *** HEAD/doc/src/sgml/ref/copy.sgml Mon Feb 9 09:25:51 2009 --- force_quote_all/doc/src/sgml/ref/copy.sgml Tue Jul 14 16:23:12 2009 *************** COPY tabl *** 32,38 **** [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] ! [ FORCE NOT NULL column [, ...] ] COPY { tablename [ ( column [, ...] ) ] | ( query ) } TO { 'filename' | STDOUT } --- 32,38 ---- [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] ! [ FORCE NOT NULL { column [, ...] | * } ] COPY { tablename [ ( column [, ...] ) ] | ( query ) } TO { 'filename' | STDOUT } *************** COPY { ta *** 44,50 **** [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] ! [ FORCE QUOTE column [, ...] ] --- 44,50 ---- [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] ! [ FORCE QUOTE { column [, ...] | * } ] *************** COPY { ta *** 248,254 **** In CSV COPY TO mode, forces quoting to be used for all non-NULL values in each specified column. ! NULL output is never quoted. --- 248,255 ---- In CSV COPY TO mode, forces quoting to be used for all non-NULL values in each specified column. ! NULL output is never quoted. If * is specified, all ! columns of the table will be quoted. *************** COPY { ta *** 261,267 **** specified column as though it were quoted and hence not a NULL value. For the default null string in CSV mode (''), this causes missing ! values to be input as zero-length strings. --- 262,269 ---- specified column as though it were quoted and hence not a NULL value. For the default null string in CSV mode (''), this causes missing ! values to be input as zero-length strings. If * is specified, ! all columns of the table will be not nulls. diff -cpr HEAD/src/backend/commands/copy.c force_quote_all/src/backend/commands/copy.c *** HEAD/src/backend/commands/copy.c Fri Jun 12 09:52:43 2009 --- force_quote_all/src/backend/commands/copy.c Tue Jul 14 16:23:12 2009 *************** DoCopy(const CopyStmt *stmt, const char *** 730,735 **** --- 730,738 ---- 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)); *************** DoCopy(const CopyStmt *stmt, const char *** 809,814 **** --- 812,821 ---- (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); force_quote = (List *) defel->arg; + + /* NIL means all-columns */ + if (force_quote == NIL) + force_quote = &all_columns; } else if (strcmp(defel->defname, "force_notnull") == 0) { *************** DoCopy(const CopyStmt *stmt, const char *** 817,822 **** --- 824,833 ---- (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); force_notnull = (List *) defel->arg; + + /* NIL means all-columns */ + if (force_notnull == NIL) + force_notnull = &all_columns; } else elog(ERROR, "option \"%s\" not recognized", *************** DoCopy(const CopyStmt *stmt, const char *** 1092,1098 **** /* 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) { List *attnums; ListCell *cur; --- 1103,1116 ---- /* 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 == &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; *************** DoCopy(const CopyStmt *stmt, const char *** 1114,1120 **** /* Convert FORCE NOT NULL name list to per-column flags, check validity */ cstate->force_notnull_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); ! if (force_notnull) { List *attnums; ListCell *cur; --- 1132,1145 ---- /* Convert FORCE NOT NULL name list to per-column flags, check validity */ cstate->force_notnull_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); ! if (force_notnull == &all_columns) ! { ! int i; ! ! for (i = 0; i < num_phys_attrs; i++) ! cstate->force_notnull_flags[i] = true; ! } ! else if (force_notnull) { List *attnums; ListCell *cur; diff -cpr HEAD/src/backend/parser/gram.y force_quote_all/src/backend/parser/gram.y *** HEAD/src/backend/parser/gram.y Tue Jul 14 09:36:59 2009 --- force_quote_all/src/backend/parser/gram.y Tue Jul 14 16:23:12 2009 *************** copy_opt_item: *** 1995,2003 **** --- 1995,2011 ---- { $$ = makeDefElem("force_quote", (Node *)$3); } + | FORCE QUOTE '*' + { + $$ = makeDefElem("force_quote", NULL); + } | FORCE NOT NULL_P columnList { $$ = makeDefElem("force_notnull", (Node *)$4); + } + | FORCE NOT NULL_P '*' + { + $$ = makeDefElem("force_notnull", NULL); } ; diff -cpr HEAD/src/test/regress/expected/copy2.out force_quote_all/src/test/regress/expected/copy2.out *** HEAD/src/test/regress/expected/copy2.out Mon Jun 15 09:50:34 2009 --- force_quote_all/src/test/regress/expected/copy2.out Tue Jul 14 16:23:12 2009 *************** COPY y TO stdout WITH CSV FORCE QUOTE co *** 191,196 **** --- 191,200 ---- "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 -cpr HEAD/src/test/regress/sql/copy2.sql force_quote_all/src/test/regress/sql/copy2.sql *** HEAD/src/test/regress/sql/copy2.sql Mon Jun 15 09:50:34 2009 --- force_quote_all/src/test/regress/sql/copy2.sql Tue Jul 14 16:23:12 2009 *************** INSERT INTO y VALUES ('', NULL); *** 128,133 **** --- 128,134 ---- 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