From 49d3d9cf40a6b0b9d891e6bfcaeffd154d87b293 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 21 Apr 2004 00:34:18 +0000 Subject: [PATCH] Change COPY CSV keyword to be: FORCE QUOTE to force quotes FORCE NOT NULL to quote null input values --- doc/src/sgml/ref/copy.sgml | 49 +++++++--------- doc/src/sgml/ref/psql-ref.sgml | 6 +- src/backend/commands/copy.c | 103 ++++++++++++++++----------------- src/backend/parser/gram.y | 13 ++--- src/backend/parser/keywords.c | 3 +- src/bin/psql/copy.c | 93 ++++++++++++++++------------- 6 files changed, 135 insertions(+), 132 deletions(-) diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index 7d53d5d3aa..cc86af1ad1 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -1,5 +1,5 @@ @@ -29,7 +29,7 @@ COPY tablename [ ( null string' ] [ CSV [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] - [ LITERAL column [, ...] ] + [ FORCE NOT NULL column [, ...] ] COPY tablename [ ( column [, ...] ) ] TO { 'filename' | STDOUT } @@ -40,7 +40,7 @@ COPY tablename [ ( null string' ] [ CSV [ QUOTE [ AS ] 'quote' ] [ ESCAPE [ AS ] 'escape' ] - [ FORCE column [, ...] ] + [ FORCE QUOTE column [, ...] ] @@ -185,10 +185,10 @@ COPY tablename [ ( CSV - Enables Comma Separated Variable (CSV) mode. (Also called - Comma Separated Value). It sets the default DELIMITER to - comma, and QUOTE and ESCAPE values to - double-quote. + Enables Comma Separated Variable (CSV) mode. (Also + called Comma Separated Value). It sets the default + DELIMITER to comma, and QUOTE and + ESCAPE values to double-quote. @@ -207,38 +207,33 @@ COPY tablename [ ( escape - Specifies the character that should appear before a QUOTE - data character value in CSV mode. The default is the - QUOTE value (usually double-quote). + Specifies the character that should appear before a + QUOTE data character value in CSV mode. + The default is the QUOTE value (usually double-quote). - FORCE + FORCE QUOTE - In CSV COPY TO mode, forces quoting - to be used for all non-NULL values in each specified - column. NULL output is never quoted. + In CSV COPY TO mode, forces quoting to be + used for all non-NULL values in each specified column. + NULL output is never quoted. - LITERAL + FORCE NOT NULL - In CSV COPY FROM mode, for each column specified, - do not do a null string comparison; instead load the value - literally. QUOTE and ESCAPE processing are still - performed. - - - If the null string is '' (the default - in CSV mode), a missing input value (delimiter, - delimiter), will load as a zero-length string. Delimiter, quote, - quote, delimiter is always treated as a zero-length string on input. + In CSV COPY FROM mode, process each + specified column as though it were quoted and hance not a + NULL value. For the default null string in + CSV mode (''), this causes a missing + values to be input as a zero-length strings. @@ -483,7 +478,7 @@ COPY tablename [ ( QUOTE character, and any occurrence within the value of a QUOTE character or the ESCAPE character is preceded by the escape character. - You can also use FORCE to force quotes when outputting + You can also use FORCE QUOTE to force quotes when outputting non-NULL values in specific columns. @@ -496,7 +491,7 @@ COPY tablename [ ( NULL is written as an unquoted empty string, while an empty string is written with double quotes (""). Reading values follows - similar rules. You can use LITERAL to prevent NULL + similar rules. You can use FORCE NOT NULL to prevent NULL input comparisons for specific columns. diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index c41080e1ba..99c0dbd95e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -713,8 +713,8 @@ testdb=> [ null [as] 'string' ] [ csv [ quote [as] 'character' ] [ escape [as] 'character' ] - [ force column_list ] - [ literal column_list ] ] + [ force quote column_list ] + [ force not null column_list ] ] diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index b532c158bc..74025ad041 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.222 2004/04/19 21:58:02 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.223 2004/04/21 00:34:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -132,10 +132,10 @@ static bool line_buf_converted; /* non-export function prototypes */ 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_atts); + 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 *literal_atts); + List *force_notnull_atts); static bool CopyReadLine(void); static char *CopyReadAttribute(const char *delim, const char *null_print, CopyReadResult *result, bool *isnull); @@ -695,10 +695,10 @@ DoCopy(const CopyStmt *stmt) char *quote = NULL; char *escape = NULL; char *null_print = NULL; - List *force = NIL; - List *literal = NIL; - List *force_atts = NIL; - List *literal_atts = NIL; + List *force_quote = NIL; + List *force_notnull = NIL; + List *force_quote_atts = NIL; + List *force_notnull_atts = NIL; Relation rel; AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); AclResult aclresult; @@ -764,21 +764,21 @@ DoCopy(const CopyStmt *stmt) errmsg("conflicting or redundant options"))); escape = strVal(defel->arg); } - else if (strcmp(defel->defname, "force") == 0) + else if (strcmp(defel->defname, "force_quote") == 0) { - if (force) + if (force_quote) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - force = (List *)defel->arg; + force_quote = (List *)defel->arg; } - else if (strcmp(defel->defname, "literal") == 0) + else if (strcmp(defel->defname, "force_notnull") == 0) { - if (literal) + if (force_notnull) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - literal = (List *)defel->arg; + force_notnull = (List *)defel->arg; } else elog(ERROR, "option \"%s\" not recognized", @@ -850,28 +850,28 @@ DoCopy(const CopyStmt *stmt) errmsg("COPY escape must be a single character"))); /* - * Check force + * Check force_quote */ - if (!csv_mode && force != NIL) + if (!csv_mode && force_quote != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY force available only in CSV mode"))); - if (force != NIL && is_from) + errmsg("COPY force quote available only in CSV mode"))); + if (force_quote != NIL && is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY force only available using COPY TO"))); + errmsg("COPY force quote only available using COPY TO"))); /* - * Check literal + * Check force_notnull */ - if (!csv_mode && literal != NIL) + if (!csv_mode && force_notnull != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY literal available only in CSV mode"))); - if (literal != NIL && !is_from) + errmsg("COPY force not null available only in CSV mode"))); + if (force_notnull != NIL && !is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY literal only available using COPY FROM"))); + errmsg("COPY force not null only available using COPY FROM"))); /* * Don't allow the delimiter to appear in the null string. @@ -928,47 +928,47 @@ DoCopy(const CopyStmt *stmt) attnumlist = CopyGetAttnums(rel, attnamelist); /* - * Check that FORCE references valid COPY columns + * Check that FORCE QUOTE references valid COPY columns */ - if (force) + if (force_quote) { TupleDesc tupDesc = RelationGetDescr(rel); Form_pg_attribute *attr = tupDesc->attrs; List *cur; - force_atts = CopyGetAttnums(rel, force); + force_quote_atts = CopyGetAttnums(rel, force_quote); - foreach(cur, force_atts) + foreach(cur, force_quote_atts) { int attnum = lfirsti(cur); if (!intMember(attnum, attnumlist)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE column \"%s\" not referenced by COPY", + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", NameStr(attr[attnum - 1]->attname)))); } } /* - * Check that LITERAL references valid COPY columns + * Check that FORCE NOT NULL references valid COPY columns */ - if (literal) + if (force_notnull) { List *cur; TupleDesc tupDesc = RelationGetDescr(rel); Form_pg_attribute *attr = tupDesc->attrs; - literal_atts = CopyGetAttnums(rel, literal); + force_notnull_atts = CopyGetAttnums(rel, force_notnull); - foreach(cur, literal_atts) + foreach(cur, force_notnull_atts) { int attnum = lfirsti(cur); if (!intMember(attnum, attnumlist)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("LITERAL column \"%s\" not referenced by COPY", + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", NameStr(attr[attnum - 1]->attname)))); } } @@ -1037,7 +1037,7 @@ DoCopy(const CopyStmt *stmt) } } CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode, - quote, escape, literal_atts); + quote, escape, force_notnull_atts); } else { /* copy from database to file */ @@ -1100,7 +1100,7 @@ DoCopy(const CopyStmt *stmt) } } CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode, - quote, escape, force_atts); + quote, escape, force_quote_atts); } if (!pipe) @@ -1133,7 +1133,7 @@ DoCopy(const CopyStmt *stmt) 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_atts) + char *escape, List *force_quote_atts) { HeapTuple tuple; TupleDesc tupDesc; @@ -1180,7 +1180,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, &isvarlena[attnum - 1]); fmgr_info(out_func_oid, &out_functions[attnum - 1]); - if (intMember(attnum, force_atts)) + if (intMember(attnum, force_quote_atts)) force_quote[attnum - 1] = true; else force_quote[attnum - 1] = false; @@ -1434,7 +1434,7 @@ limit_printout_length(StringInfo buf) static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, char *delim, char *null_print, bool csv_mode, char *quote, - char *escape, List *literal_atts) + char *escape, List *force_notnull_atts) { HeapTuple tuple; TupleDesc tupDesc; @@ -1447,7 +1447,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, Oid *elements; Oid oid_in_element; ExprState **constraintexprs; - bool *literal_nullstr; + bool *force_notnull; bool hasConstraints = false; int attnum; int i; @@ -1509,7 +1509,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, defmap = (int *) palloc((num_phys_attrs + 1) * sizeof(int)); defexprs = (ExprState **) palloc((num_phys_attrs + 1) * sizeof(ExprState *)); constraintexprs = (ExprState **) palloc0((num_phys_attrs + 1) * sizeof(ExprState *)); - literal_nullstr = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool)); + force_notnull = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool)); for (attnum = 1; attnum <= num_phys_attrs; attnum++) { @@ -1526,10 +1526,10 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, &in_func_oid, &elements[attnum - 1]); fmgr_info(in_func_oid, &in_functions[attnum - 1]); - if (intMember(attnum, literal_atts)) - literal_nullstr[attnum - 1] = true; + if (intMember(attnum, force_notnull_atts)) + force_notnull[attnum - 1] = true; else - literal_nullstr[attnum - 1] = false; + force_notnull[attnum - 1] = false; /* Get default info if needed */ if (!intMember(attnum, attnumlist)) @@ -1748,7 +1748,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, string = CopyReadAttribute(delim, null_print, &result, &isnull); - if (csv_mode && isnull && literal_nullstr[m]) + if (csv_mode && isnull && force_notnull[m]) { string = null_print; /* set to NULL string */ isnull = false; @@ -1947,7 +1947,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, pfree(defmap); pfree(defexprs); pfree(constraintexprs); - pfree(literal_nullstr); + pfree(force_notnull); ExecDropTupleTable(tupleTable, true); @@ -2558,14 +2558,13 @@ CopyAttributeOut(char *server_string, char *delim) */ static void CopyAttributeOutCSV(char *server_string, char *delim, char *quote, - char *escape, bool force_quote) + char *escape, bool use_quote) { char *string; char c; char delimc = delim[0]; char quotec = quote[0]; char escapec = escape[0]; - bool need_quote = force_quote; char *test_string; bool same_encoding; int mblen; @@ -2583,23 +2582,23 @@ CopyAttributeOutCSV(char *server_string, char *delim, char *quote, */ for(test_string = string; - !need_quote && (c = *test_string) != '\0'; + !use_quote && (c = *test_string) != '\0'; test_string += mblen) { if (c == delimc || c == quotec || c == '\n' || c == '\r') - need_quote = true; + use_quote = true; if (!same_encoding) mblen = pg_encoding_mblen(client_encoding, test_string); else mblen = 1; } - if (need_quote) + if (use_quote) CopySendChar(quotec); for (; (c = *string) != '\0'; string += mblen) { - if (need_quote && (c == quotec || c == escapec)) + if (use_quote && (c == quotec || c == escapec)) CopySendChar(escapec); CopySendChar(c); @@ -2615,7 +2614,7 @@ CopyAttributeOutCSV(char *server_string, char *delim, char *quote, mblen = 1; } - if (need_quote) + if (use_quote) CopySendChar(quotec); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 461a39dbb7..de8597ed9e 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.451 2004/04/19 17:22:30 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.452 2004/04/21 00:34:18 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -370,7 +370,7 @@ static void doNegateFloat(Value *v); KEY LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT - LISTEN LITERAL LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION + LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE @@ -1374,13 +1374,13 @@ copy_opt_item: { $$ = makeDefElem("escape", (Node *)makeString($3)); } - | FORCE columnList + | FORCE QUOTE columnList { - $$ = makeDefElem("force", (Node *)$2); + $$ = makeDefElem("force_quote", (Node *)$3); } - | LITERAL columnList + | FORCE NOT NULL_P columnList { - $$ = makeDefElem("literal", (Node *)$2); + $$ = makeDefElem("force_notnull", (Node *)$4); } ; @@ -7496,7 +7496,6 @@ unreserved_keyword: | LAST_P | LEVEL | LISTEN - | LITERAL | LOAD | LOCAL | LOCATION diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 613d22ac30..a3c765a195 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.148 2004/04/19 17:22:31 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.149 2004/04/21 00:34:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -187,7 +187,6 @@ static const ScanKeyword ScanKeywords[] = { {"like", LIKE}, {"limit", LIMIT}, {"listen", LISTEN}, - {"literal", LITERAL}, {"load", LOAD}, {"local", LOCAL}, {"localtime", LOCALTIME}, diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index 0241b8eda2..da91fe8c71 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.45 2004/04/19 17:42:58 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.46 2004/04/21 00:34:18 momjian Exp $ */ #include "postgres_fe.h" #include "copy.h" @@ -71,8 +71,8 @@ struct copy_options char *null; char *quote; char *escape; - char *force_list; - char *literal_list; + char *force_quote_list; + char *force_notnull_list; }; @@ -88,8 +88,8 @@ free_copy_options(struct copy_options * ptr) free(ptr->null); free(ptr->quote); free(ptr->escape); - free(ptr->force_list); - free(ptr->literal_list); + free(ptr->force_quote_list); + free(ptr->force_notnull_list); free(ptr); } @@ -344,45 +344,56 @@ parse_slash_copy(const char *args) } else if (strcasecmp(token, "force") == 0) { - /* handle column list */ - fetch_next = false; - for (;;) + token = strtokx(NULL, whitespace, ",", "\"", + 0, false, pset.encoding); + if (strcasecmp(token, "quote") == 0) { - token = strtokx(NULL, whitespace, ",", "\"", - 0, false, pset.encoding); - if (!token || strchr(",", token[0])) - goto error; - if (!result->force_list) - result->force_list = pg_strdup(token); - else - xstrcat(&result->force_list, token); - token = strtokx(NULL, whitespace, ",", "\"", - 0, false, pset.encoding); - if (!token || token[0] != ',') - break; - xstrcat(&result->force_list, token); + /* handle column list */ + fetch_next = false; + for (;;) + { + token = strtokx(NULL, whitespace, ",", "\"", + 0, false, pset.encoding); + if (!token || strchr(",", token[0])) + goto error; + if (!result->force_quote_list) + result->force_quote_list = pg_strdup(token); + else + xstrcat(&result->force_quote_list, token); + token = strtokx(NULL, whitespace, ",", "\"", + 0, false, pset.encoding); + if (!token || token[0] != ',') + break; + xstrcat(&result->force_quote_list, token); + } } - } - else if (strcasecmp(token, "literal") == 0) - { - /* handle column list */ - fetch_next = false; - for (;;) + else if (strcasecmp(token, "not") == 0) { token = strtokx(NULL, whitespace, ",", "\"", 0, false, pset.encoding); - if (!token || strchr(",", token[0])) + if (strcasecmp(token, "null") != 0) goto error; - if (!result->literal_list) - result->literal_list = pg_strdup(token); - else - xstrcat(&result->literal_list, token); - token = strtokx(NULL, whitespace, ",", "\"", - 0, false, pset.encoding); - if (!token || token[0] != ',') - break; - xstrcat(&result->literal_list, token); + /* handle column list */ + fetch_next = false; + for (;;) + { + token = strtokx(NULL, whitespace, ",", "\"", + 0, false, pset.encoding); + if (!token || strchr(",", token[0])) + goto error; + if (!result->force_notnull_list) + result->force_notnull_list = pg_strdup(token); + else + xstrcat(&result->force_notnull_list, token); + token = strtokx(NULL, whitespace, ",", "\"", + 0, false, pset.encoding); + if (!token || token[0] != ',') + break; + xstrcat(&result->force_notnull_list, token); + } } + else + goto error; } else goto error; @@ -493,14 +504,14 @@ do_copy(const char *args) appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape); } - if (options->force_list) + if (options->force_quote_list) { - appendPQExpBuffer(&query, " FORCE %s", options->force_list); + appendPQExpBuffer(&query, " FORCE QUOTE %s", options->force_quote_list); } - if (options->literal_list) + if (options->force_notnull_list) { - appendPQExpBuffer(&query, " LITERAL %s", options->literal_list); + appendPQExpBuffer(&query, " FORCE NOT NULL %s", options->force_notnull_list); } if (options->from) -- 2.40.0