I just changed the COPY CSV keywords:

        FORCE QUOTE to force quotes
        FORCE NOT NULL to quote null input values

Patch attached.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  [EMAIL PROTECTED]               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/ref/copy.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/ref/copy.sgml,v
retrieving revision 1.56
diff -c -c -r1.56 copy.sgml
*** doc/src/sgml/ref/copy.sgml  19 Apr 2004 17:22:30 -0000      1.56
--- doc/src/sgml/ref/copy.sgml  21 Apr 2004 00:17:51 -0000
***************
*** 29,35 ****
            [ 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> [, 
...] ]
  
  COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable 
class="parameter">column</replaceable> [, ...] ) ]
      TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
--- 29,35 ----
            [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
            [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' 
] 
                  [ ESCAPE [ AS ] '<replaceable 
class="parameter">escape</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,46 ****
            [ 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> [, ...] ]
  </synopsis>
   </refsynopsisdiv>
   
--- 40,46 ----
            [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
            [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' 
] 
                  [ ESCAPE [ AS ] '<replaceable 
class="parameter">escape</replaceable>' ]
!                 [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, 
...] ]
  </synopsis>
   </refsynopsisdiv>
   
***************
*** 185,194 ****
      <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.
       </para>
      </listitem>
     </varlistentry>
--- 185,194 ----
      <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.
       </para>
      </listitem>
     </varlistentry>
***************
*** 207,244 ****
      <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).
       </para>
      </listitem>
     </varlistentry>
  
     <varlistentry>
!     <term><literal>FORCE</></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.
       </para>
      </listitem>
     </varlistentry>
  
     <varlistentry>
!     <term><literal>LITERAL</></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.
       </para>
      </listitem>
     </varlistentry>
--- 207,239 ----
      <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).
       </para>
      </listitem>
     </varlistentry>
  
     <varlistentry>
!     <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.
       </para>
      </listitem>
     </varlistentry>
  
     <varlistentry>
!     <term><literal>FORCE NOT NULL</></term>
      <listitem>
       <para>
!       In <literal>CSV</> <command>COPY FROM</> mode, process each
!       specified column as though it was quoted and 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,489 ****
      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
      non-<literal>NULL</> values in specific columns.
     </para>
  
--- 478,484 ----
      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 QUOTE</> to force quotes when outputting
      non-<literal>NULL</> values in specific columns.
     </para>
  
***************
*** 496,502 ****
      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</>
      input comparisons for specific columns.
     </para>
  
--- 491,497 ----
      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>FORCE NOT NULL</> to prevent <literal>NULL</>
      input comparisons for specific columns.
     </para>
  
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.111
diff -c -c -r1.111 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml      19 Apr 2004 17:22:30 -0000      1.111
--- doc/src/sgml/ref/psql-ref.sgml      21 Apr 2004 00:17:53 -0000
***************
*** 713,720 ****
              [ <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> ] ]
          </term>
  
          <listitem>
--- 713,720 ----
              [ <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 quote</> <replaceable 
class="parameter">column_list</replaceable> ]
!                            [ <literal>force not null</> <replaceable 
class="parameter">column_list</replaceable> ] ]
          </term>
  
          <listitem>
Index: src/backend/commands/copy.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/commands/copy.c,v
retrieving revision 1.222
diff -c -c -r1.222 copy.c
*** src/backend/commands/copy.c 19 Apr 2004 21:58:02 -0000      1.222
--- src/backend/commands/copy.c 21 Apr 2004 00:17:55 -0000
***************
*** 132,141 ****
  /* 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);
  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);
  static bool CopyReadLine(void);
  static char *CopyReadAttribute(const char *delim, const char *null_print,
                                                           CopyReadResult *result, 
bool *isnull);
--- 132,141 ----
  /* 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_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 *force_notnull_atts);
  static bool CopyReadLine(void);
  static char *CopyReadAttribute(const char *delim, const char *null_print,
                                                           CopyReadResult *result, 
bool *isnull);
***************
*** 695,704 ****
        char       *quote = NULL;
        char       *escape = NULL;
        char       *null_print = NULL;
!       List       *force = NIL;
!       List       *literal = NIL;
!       List       *force_atts = NIL;
!       List       *literal_atts = NIL;
        Relation        rel;
        AclMode         required_access = (is_from ? ACL_INSERT : ACL_SELECT);
        AclResult       aclresult;
--- 695,704 ----
        char       *quote = NULL;
        char       *escape = NULL;
        char       *null_print = NULL;
!       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,784 ****
                                                 errmsg("conflicting or redundant 
options")));
                        escape = strVal(defel->arg);
                }
!               else if (strcmp(defel->defname, "force") == 0)
                {
!                       if (force)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or redundant 
options")));
!                       force = (List *)defel->arg;
                }
!               else if (strcmp(defel->defname, "literal") == 0)
                {
!                       if (literal)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or redundant 
options")));
!                       literal = (List *)defel->arg;
                }
                else
                        elog(ERROR, "option \"%s\" not recognized",
--- 764,784 ----
                                                 errmsg("conflicting or redundant 
options")));
                        escape = strVal(defel->arg);
                }
!               else if (strcmp(defel->defname, "force_quote") == 0)
                {
!                       if (force_quote)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or redundant 
options")));
!                       force_quote = (List *)defel->arg;
                }
!               else if (strcmp(defel->defname, "force_notnull") == 0)
                {
!                       if (force_notnull)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or redundant 
options")));
!                       force_notnull = (List *)defel->arg;
                }
                else
                        elog(ERROR, "option \"%s\" not recognized",
***************
*** 850,877 ****
                                 errmsg("COPY escape must be a single character")));
  
        /*
!        * Check force
         */
!       if (!csv_mode && force != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("COPY force available only in CSV mode")));
!       if (force != NIL && is_from)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("COPY force only available using COPY TO")));
  
        /*
!        * Check literal
         */
!       if (!csv_mode && literal != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("COPY literal available only in CSV mode")));
!       if (literal != NIL && !is_from)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("COPY literal only available using COPY 
FROM")));
  
        /*
         * Don't allow the delimiter to appear in the null string.
--- 850,877 ----
                                 errmsg("COPY escape must be a single character")));
  
        /*
!        * Check force_quote
         */
!       if (!csv_mode && force_quote != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                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 quote only available using COPY 
TO")));
  
        /*
!        * Check force_notnull
         */
!       if (!csv_mode && force_notnull != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                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 force not null only available using COPY 
FROM")));
  
        /*
         * Don't allow the delimiter to appear in the null string.
***************
*** 928,974 ****
        attnumlist = CopyGetAttnums(rel, attnamelist);
  
        /*
!        * Check that FORCE references valid COPY columns
         */
!       if (force)
        {
                TupleDesc       tupDesc = RelationGetDescr(rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                List       *cur;
  
!               force_atts = CopyGetAttnums(rel, force);
  
!               foreach(cur, force_atts)
                {
                        int                     attnum = lfirsti(cur);
  
                        if (!intMember(attnum, attnumlist))
                                ereport(ERROR,
                                                
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                                                errmsg("FORCE column \"%s\" not 
referenced by COPY",
                                                                NameStr(attr[attnum - 
1]->attname))));
                }
        }
        
        /*
!        * Check that LITERAL references valid COPY columns
         */
!       if (literal)
        {
                List       *cur;
                TupleDesc       tupDesc = RelationGetDescr(rel);
                Form_pg_attribute *attr = tupDesc->attrs;
  
!               literal_atts = CopyGetAttnums(rel, literal);
  
!               foreach(cur, literal_atts)
                {
                        int                     attnum = lfirsti(cur);
  
                        if (!intMember(attnum, attnumlist))
                                ereport(ERROR,
                                                
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                                                errmsg("LITERAL column \"%s\" not 
referenced by COPY",
                                                                NameStr(attr[attnum - 
1]->attname))));
                }
        }
--- 928,974 ----
        attnumlist = CopyGetAttnums(rel, attnamelist);
  
        /*
!        * Check that FORCE QUOTE references valid COPY columns
         */
!       if (force_quote)
        {
                TupleDesc       tupDesc = RelationGetDescr(rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                List       *cur;
  
!               force_quote_atts = CopyGetAttnums(rel, force_quote);
  
!               foreach(cur, force_quote_atts)
                {
                        int                     attnum = lfirsti(cur);
  
                        if (!intMember(attnum, attnumlist))
                                ereport(ERROR,
                                                
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                                                errmsg("FORCE QUOTE column \"%s\" not 
referenced by COPY",
                                                                NameStr(attr[attnum - 
1]->attname))));
                }
        }
        
        /*
!        * Check that FORCE NOT NULL references valid COPY columns
         */
!       if (force_notnull)
        {
                List       *cur;
                TupleDesc       tupDesc = RelationGetDescr(rel);
                Form_pg_attribute *attr = tupDesc->attrs;
  
!               force_notnull_atts = CopyGetAttnums(rel, force_notnull);
  
!               foreach(cur, force_notnull_atts)
                {
                        int                     attnum = lfirsti(cur);
  
                        if (!intMember(attnum, attnumlist))
                                ereport(ERROR,
                                                
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                                                errmsg("FORCE NOT NULL column \"%s\" 
not referenced by COPY",
                                                                NameStr(attr[attnum - 
1]->attname))));
                }
        }
***************
*** 1037,1043 ****
                        }
                }
                CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
!                                quote, escape, literal_atts);
        }
        else
        {                                                       /* copy from database 
to file */
--- 1037,1043 ----
                        }
                }
                CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
!                                quote, escape, force_notnull_atts);
        }
        else
        {                                                       /* copy from database 
to file */
***************
*** 1100,1106 ****
                        }
                }
                CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
!                               quote, escape, force_atts);
        }
  
        if (!pipe)
--- 1100,1106 ----
                        }
                }
                CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
!                               quote, escape, force_quote_atts);
        }
  
        if (!pipe)
***************
*** 1133,1139 ****
  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)
  {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
--- 1133,1139 ----
  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_quote_atts)
  {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
***************
*** 1180,1186 ****
                                                          &isvarlena[attnum - 1]);
                fmgr_info(out_func_oid, &out_functions[attnum - 1]);
  
!               if (intMember(attnum, force_atts))
                        force_quote[attnum - 1] = true;
                else
                        force_quote[attnum - 1] = false;
--- 1180,1186 ----
                                                          &isvarlena[attnum - 1]);
                fmgr_info(out_func_oid, &out_functions[attnum - 1]);
  
!               if (intMember(attnum, force_quote_atts))
                        force_quote[attnum - 1] = true;
                else
                        force_quote[attnum - 1] = false;
***************
*** 1434,1440 ****
  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)
  {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
--- 1434,1440 ----
  static void
  CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                 char *delim, char *null_print, bool csv_mode, char *quote,
!                char *escape, List *force_notnull_atts)
  {
        HeapTuple       tuple;
        TupleDesc       tupDesc;
***************
*** 1447,1453 ****
        Oid                *elements;
        Oid                     oid_in_element;
        ExprState **constraintexprs;
!       bool       *literal_nullstr;
        bool            hasConstraints = false;
        int                     attnum;
        int                     i;
--- 1447,1453 ----
        Oid                *elements;
        Oid                     oid_in_element;
        ExprState **constraintexprs;
!       bool       *force_notnull;
        bool            hasConstraints = false;
        int                     attnum;
        int                     i;
***************
*** 1509,1515 ****
        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));
  
        for (attnum = 1; attnum <= num_phys_attrs; attnum++)
        {
--- 1509,1515 ----
        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 *));
!       force_notnull = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool));
  
        for (attnum = 1; attnum <= num_phys_attrs; attnum++)
        {
***************
*** 1526,1535 ****
                                                         &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;
                else
!                       literal_nullstr[attnum - 1] = false;
                
                /* Get default info if needed */
                if (!intMember(attnum, attnumlist))
--- 1526,1535 ----
                                                         &in_func_oid, 
&elements[attnum - 1]);
                fmgr_info(in_func_oid, &in_functions[attnum - 1]);
  
!               if (intMember(attnum, force_notnull_atts))
!                       force_notnull[attnum - 1] = true;
                else
!                       force_notnull[attnum - 1] = false;
                
                /* Get default info if needed */
                if (!intMember(attnum, attnumlist))
***************
*** 1748,1754 ****
                                        string = CopyReadAttribute(delim, null_print, 
                                                                                       
    &result, &isnull);
  
!                               if (csv_mode && isnull && literal_nullstr[m])
                                {
                                        string = null_print;    /* set to NULL string 
*/
                                        isnull = false;
--- 1748,1754 ----
                                        string = CopyReadAttribute(delim, null_print, 
                                                                                       
    &result, &isnull);
  
!                               if (csv_mode && isnull && force_notnull[m])
                                {
                                        string = null_print;    /* set to NULL string 
*/
                                        isnull = false;
***************
*** 1947,1953 ****
        pfree(defmap);
        pfree(defexprs);
        pfree(constraintexprs);
!       pfree(literal_nullstr);
  
        ExecDropTupleTable(tupleTable, true);
  
--- 1947,1953 ----
        pfree(defmap);
        pfree(defexprs);
        pfree(constraintexprs);
!       pfree(force_notnull);
  
        ExecDropTupleTable(tupleTable, true);
  
***************
*** 2558,2571 ****
   */
  static void
  CopyAttributeOutCSV(char *server_string, char *delim, char *quote,
!                                       char *escape, bool force_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;
--- 2558,2570 ----
   */
  static void
  CopyAttributeOutCSV(char *server_string, char *delim, char *quote,
!                                       char *escape, bool use_quote)
  {
        char       *string;
        char            c;
        char            delimc = delim[0];
        char        quotec = quote[0];
        char        escapec = escape[0];
        char        *test_string;
        bool            same_encoding;
        int                     mblen;
***************
*** 2583,2605 ****
         */
  
        for(test_string = string; 
!               !need_quote && (c = *test_string) != '\0'; 
                test_string += mblen)
        {
                if (c == delimc || c == quotec || c == '\n' || c == '\r')
!                       need_quote = true;
                if (!same_encoding)
                        mblen = pg_encoding_mblen(client_encoding, test_string);
                else
                        mblen = 1;
        }
  
!       if (need_quote)
                CopySendChar(quotec);
  
        for (; (c = *string) != '\0'; string += mblen)
        {
!               if (need_quote && (c == quotec || c == escapec))
                        CopySendChar(escapec);
  
                CopySendChar(c);
--- 2582,2604 ----
         */
  
        for(test_string = string; 
!               !use_quote && (c = *test_string) != '\0'; 
                test_string += mblen)
        {
                if (c == delimc || c == quotec || c == '\n' || c == '\r')
!                       use_quote = true;
                if (!same_encoding)
                        mblen = pg_encoding_mblen(client_encoding, test_string);
                else
                        mblen = 1;
        }
  
!       if (use_quote)
                CopySendChar(quotec);
  
        for (; (c = *string) != '\0'; string += mblen)
        {
!               if (use_quote && (c == quotec || c == escapec))
                        CopySendChar(escapec);
  
                CopySendChar(c);
***************
*** 2615,2621 ****
                        mblen = 1;
        }
  
!       if (need_quote)
                CopySendChar(quotec);
  }
  
--- 2614,2620 ----
                        mblen = 1;
        }
  
!       if (use_quote)
                CopySendChar(quotec);
  }
  
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.451
diff -c -c -r2.451 gram.y
*** src/backend/parser/gram.y   19 Apr 2004 17:22:30 -0000      2.451
--- src/backend/parser/gram.y   21 Apr 2004 00:18:00 -0000
***************
*** 370,376 ****
        KEY
  
        LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT
!       LISTEN LITERAL LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
        LOCK_P
  
        MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
--- 370,376 ----
        KEY
  
        LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT
!       LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
        LOCK_P
  
        MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
***************
*** 1374,1386 ****
                                {
                                        $$ = makeDefElem("escape", (Node 
*)makeString($3));
                                }
!                       | FORCE columnList
                                {
!                                       $$ = makeDefElem("force", (Node *)$2);
                                }
!                       | LITERAL columnList
                                {
!                                       $$ = makeDefElem("literal", (Node *)$2);
                                }
                ;
  
--- 1374,1386 ----
                                {
                                        $$ = makeDefElem("escape", (Node 
*)makeString($3));
                                }
!                       | FORCE QUOTE columnList
                                {
!                                       $$ = makeDefElem("force_quote", (Node *)$3);
                                }
!                       | FORCE NOT NULL_P columnList
                                {
!                                       $$ = makeDefElem("force_notnull", (Node *)$4);
                                }
                ;
  
***************
*** 7496,7502 ****
                        | LAST_P
                        | LEVEL
                        | LISTEN
-                       | LITERAL
                        | LOAD
                        | LOCAL
                        | LOCATION
--- 7496,7501 ----
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/parser/keywords.c,v
retrieving revision 1.148
diff -c -c -r1.148 keywords.c
*** src/backend/parser/keywords.c       19 Apr 2004 17:22:31 -0000      1.148
--- src/backend/parser/keywords.c       21 Apr 2004 00:18:01 -0000
***************
*** 187,193 ****
        {"like", LIKE},
        {"limit", LIMIT},
        {"listen", LISTEN},
-       {"literal", LITERAL},
        {"load", LOAD},
        {"local", LOCAL},
        {"localtime", LOCALTIME},
--- 187,192 ----
Index: src/bin/psql/copy.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/psql/copy.c,v
retrieving revision 1.45
diff -c -c -r1.45 copy.c
*** src/bin/psql/copy.c 19 Apr 2004 17:42:58 -0000      1.45
--- src/bin/psql/copy.c 21 Apr 2004 00:18:03 -0000
***************
*** 71,78 ****
        char       *null;
        char       *quote;
        char       *escape;
!       char       *force_list;
!       char       *literal_list;
  };
  
  
--- 71,78 ----
        char       *null;
        char       *quote;
        char       *escape;
!       char       *force_quote_list;
!       char       *force_notnull_list;
  };
  
  
***************
*** 88,95 ****
        free(ptr->null);
        free(ptr->quote);
        free(ptr->escape);
!       free(ptr->force_list);
!       free(ptr->literal_list);
        free(ptr);
  }
  
--- 88,95 ----
        free(ptr->null);
        free(ptr->quote);
        free(ptr->escape);
!       free(ptr->force_quote_list);
!       free(ptr->force_notnull_list);
        free(ptr);
  }
  
***************
*** 344,388 ****
                        }
                        else if (strcasecmp(token, "force") == 0)
                        {
!                               /* 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_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);
                                }
!                       }
!                       else if (strcasecmp(token, "literal") == 0)
!                       {
!                               /* handle column list */
!                               fetch_next = false;
!                               for (;;)
                                {
                                        token = strtokx(NULL, whitespace, ",", "\"",
                                                                        0, false, 
pset.encoding);
!                                       if (!token || strchr(",", token[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);
                                }
                        }
                        else
                                goto error;
--- 344,399 ----
                        }
                        else if (strcasecmp(token, "force") == 0)
                        {
!                               token = strtokx(NULL, whitespace, ",", "\"",
!                                                               0, false, 
pset.encoding);
!                               if (strcasecmp(token, "quote") == 0)
                                {
!                                       /* 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, "not") == 0)
                                {
                                        token = strtokx(NULL, whitespace, ",", "\"",
                                                                        0, false, 
pset.encoding);
!                                       if (strcasecmp(token, "null") != 0)
                                                goto error;
!                                       /* 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,506 ****
                        appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
        }
  
!       if (options->force_list)
        {
!               appendPQExpBuffer(&query, " FORCE %s", options->force_list);
        }
  
!       if (options->literal_list)
        {
!               appendPQExpBuffer(&query, " LITERAL %s", options->literal_list);
        }
  
        if (options->from)
--- 504,517 ----
                        appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
        }
  
!       if (options->force_quote_list)
        {
!               appendPQExpBuffer(&query, " FORCE QUOTE %s", 
options->force_quote_list);
        }
  
!       if (options->force_notnull_list)
        {
!               appendPQExpBuffer(&query, " FORCE NOT NULL %s", 
options->force_notnull_list);
        }
  
        if (options->from)
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
      subscribe-nomail command to [EMAIL PROTECTED] so that your
      message can get through to the mailing list cleanly

Reply via email to