From 49d3d9cf40a6b0b9d891e6bfcaeffd154d87b293 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
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 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.56 2004/04/19 17:22:30 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.57 2004/04/21 00:34:18 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -29,7 +29,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
           [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
           [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
                 [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
-                [ LITERAL <replaceable class="parameter">column</replaceable> [, ...] ]
+                [ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
 
 COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
     TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
@@ -40,7 +40,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
           [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
           [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
                 [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
-                [ FORCE <replaceable class="parameter">column</replaceable> [, ...] ]
+                [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
 </synopsis>
  </refsynopsisdiv>
  
@@ -185,10 +185,10 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     <term><literal>CSV</literal></term>
     <listitem>
      <para>
-      Enables Comma Separated Variable (<literal>CSV</>) mode.  (Also called
-      Comma Separated Value).  It sets the default <literal>DELIMITER</> to 
-      comma, and <literal>QUOTE</> and <literal>ESCAPE</> values to 
-      double-quote.
+      Enables Comma Separated Variable (<literal>CSV</>) mode. (Also
+      called Comma Separated Value). It sets the default
+      <literal>DELIMITER</> to comma, and <literal>QUOTE</> and
+      <literal>ESCAPE</> values to double-quote.
      </para>
     </listitem>
    </varlistentry>
@@ -207,38 +207,33 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     <term><replaceable class="parameter">escape</replaceable></term>
     <listitem>
      <para>
-      Specifies the character that should appear before a <literal>QUOTE</>
-      data character value in <literal>CSV</> mode.  The default is the
-      <literal>QUOTE</> value (usually double-quote).
+      Specifies the character that should appear before a
+      <literal>QUOTE</> data character value in <literal>CSV</> mode.
+      The default is the <literal>QUOTE</> value (usually double-quote).
      </para>
     </listitem>
    </varlistentry>
 
    <varlistentry>
-    <term><literal>FORCE</></term>
+    <term><literal>FORCE QUOTE</></term>
     <listitem>
      <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.
+      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.
      </para>
     </listitem>
    </varlistentry>
 
    <varlistentry>
-    <term><literal>LITERAL</></term>
+    <term><literal>FORCE NOT NULL</></term>
     <listitem>
      <para>
-      In <literal>CSV</> <command>COPY FROM</> mode, for each column specified,
-      do not do a <literal>null string</> comparison;  instead load the value 
-      literally.  <literal>QUOTE</> and <literal>ESCAPE</> processing are still
-      performed.
-     </para>
-     <para>
-      If the <literal>null string</> is <literal>''</> (the default 
-      in <literal>CSV</> mode), a missing input value (<literal>delimiter, 
-      delimiter</>), will load as a zero-length string.  <literal>Delimiter, quote, 
-      quote, delimiter</> is always treated as a zero-length string on input.
+      In <literal>CSV</> <command>COPY FROM</> mode, process each
+      specified column as though it were quoted and hance not a
+      <literal>NULL</> value. For the default <literal>null string</> in
+      <literal>CSV</> mode (<literal>''</>), this causes a missing
+      values to be input as a zero-length strings.
      </para>
     </listitem>
    </varlistentry>
@@ -483,7 +478,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     suffixed by the <literal>QUOTE</> character, and any occurrence
     within the value of a <literal>QUOTE</> character or the
     <literal>ESCAPE</> character is preceded by the escape character.
-    You can also use <literal>FORCE</> to force quotes when outputting
+    You can also use <literal>FORCE QUOTE</> to force quotes when outputting
     non-<literal>NULL</> values in specific columns.
    </para>
 
@@ -496,7 +491,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     is quoted. Therefore, using the default settings, a <literal>NULL</> is
     written as an unquoted empty string, while an empty string is
     written with double quotes (<literal>""</>). Reading values follows 
-    similar rules. You can use <literal>LITERAL</> to prevent <literal>NULL</>
+    similar rules. You can use <literal>FORCE NOT NULL</> to prevent <literal>NULL</>
     input comparisons for specific columns.
    </para>
 
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 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.111 2004/04/19 17:22:30 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.112 2004/04/21 00:34:18 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -713,8 +713,8 @@ testdb=>
             [ <literal>null [as] </literal> '<replaceable class="parameter">string</replaceable>' ]</literal>
             [ <literal>csv [ quote [as] </literal> '<replaceable class="parameter">character</replaceable>' ]
                            [ <literal>escape [as] </literal> '<replaceable class="parameter">character</replaceable>' ]
-                           [ <literal>force</> <replaceable class="parameter">column_list</replaceable> ]
-                           [ <literal>literal</> <replaceable class="parameter">column_list</replaceable> ] ]
+                           [ <literal>force quote</> <replaceable class="parameter">column_list</replaceable> ]
+                           [ <literal>force not null</> <replaceable class="parameter">column_list</replaceable> ] ]
         </term>
 
         <listitem>
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