CSV keyword change - Mailing list pgsql-patches

From Bruce Momjian
Subject CSV keyword change
Date
Msg-id 200404210035.i3L0ZaC10890@candle.pha.pa.us
Whole thread Raw
List pgsql-patches
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
  pgman@candle.pha.pa.us               |  (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)

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Change of CSV keywords
Next
From: Bruce Momjian
Date:
Subject: Re: PSQLRC environment variable.