COPY patch - Mailing list pgsql-patches

From Bruce Momjian
Subject COPY patch
Date
Msg-id 200206190614.g5J6E2N15868@candle.pha.pa.us
Whole thread Raw
List pgsql-patches
Here is the COPY patch I mentioned on hackers.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
Index: doc/src/sgml/keywords.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/keywords.sgml,v
retrieving revision 2.5
diff -c -r2.5 keywords.sgml
*** doc/src/sgml/keywords.sgml    8 Jan 2002 15:38:42 -0000    2.5
--- doc/src/sgml/keywords.sgml    19 Jun 2002 06:08:55 -0000
***************
*** 891,896 ****
--- 891,902 ----
      <entry>reserved</entry>
     </row>
     <row>
+     <entry><token>DELIMITER</token></entry>
+     <entry>non-reserved</entry>
+     <entry></entry>
+     <entry></entry>
+    </row>
+    <row>
      <entry><token>DELIMITERS</token></entry>
      <entry>non-reserved</entry>
      <entry></entry>
Index: doc/src/sgml/ref/copy.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v
retrieving revision 1.31
diff -c -r1.31 copy.sgml
*** doc/src/sgml/ref/copy.sgml    14 May 2002 18:47:58 -0000    1.31
--- doc/src/sgml/ref/copy.sgml    19 Jun 2002 06:08:57 -0000
***************
*** 21,34 ****
     <date>1999-12-11</date>
    </refsynopsisdivinfo>
    <synopsis>
! COPY [ BINARY ] <replaceable class="parameter">table</replaceable> [ WITH OIDS ]
      FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
!     [ [USING] DELIMITERS '<replaceable class="parameter">delimiter</replaceable>' ]
!     [ WITH NULL AS '<replaceable class="parameter">null string</replaceable>' ]
! COPY [ BINARY ] <replaceable class="parameter">table</replaceable> [ WITH OIDS ]
      TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
!     [ [USING] DELIMITERS '<replaceable class="parameter">delimiter</replaceable>' ]
!     [ WITH NULL AS '<replaceable class="parameter">null string</replaceable>' ]
    </synopsis>
    
    <refsect2 id="R2-SQL-COPY-1">
--- 21,40 ----
     <date>1999-12-11</date>
    </refsynopsisdivinfo>
    <synopsis>
! COPY <replaceable class="parameter">table</replaceable>
      FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
!     [ [ WITH ] 
!           [ BINARY ] 
!           [ OIDS ]
!           [ DELIMITER '<replaceable class="parameter">delimiter</replaceable>' ]
!           [ NULL AS '<replaceable class="parameter">null string</replaceable>' ] ]
! COPY <replaceable class="parameter">table</replaceable>
      TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
!     [ [ WITH ] 
!           [ BINARY ]
!           [ OIDS ]
!           [ DELIMITER '<replaceable class="parameter">delimiter</replaceable>' ]
!           [ NULL AS '<replaceable class="parameter">null string</replaceable>' ] ]
    </synopsis>
    
    <refsect2 id="R2-SQL-COPY-1">
***************
*** 42,98 ****
  
      <variablelist>
       <varlistentry>
!       <term>BINARY</term>
        <listitem>
         <para>
!     Changes the behavior of field formatting, forcing all data to be
!     stored or read in binary format rather than as text.
!     The DELIMITERS and WITH NULL options are irrelevant for binary format.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><replaceable class="parameter">table</replaceable></term>
        <listitem>
         <para>
!     The name (possibly schema-qualified) of an existing table.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term>WITH OIDS</term>
        <listitem>
         <para>
!     Specifies copying the internal object id (OID) for each row.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><replaceable class="parameter">filename</replaceable></term>
        <listitem>
         <para>
!     The absolute Unix path name of the input or output file.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><filename>stdin</filename></term>
        <listitem>
         <para>
!     Specifies that input comes from the client application.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><filename>stdout</filename></term>
        <listitem>
         <para>
!     Specifies that output goes to the client application.
         </para>
        </listitem>
       </varlistentry>
--- 48,104 ----
  
      <variablelist>
       <varlistentry>
!       <term><replaceable class="parameter">table</replaceable></term>
        <listitem>
         <para>
!     The name (possibly schema-qualified) of an existing table.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><replaceable class="parameter">filename</replaceable></term>
        <listitem>
         <para>
!     The absolute Unix path name of the input or output file.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><filename>stdin</filename></term>
        <listitem>
         <para>
!     Specifies that input comes from the client application.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term><filename>stdout</filename></term>
        <listitem>
         <para>
!     Specifies that output goes to the client application.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term>BINARY</term>
        <listitem>
         <para>
!     Changes the behavior of field formatting, forcing all data to be
!     stored or read in binary format rather than as text. You can not
!     specify DELIMITER or NULL AS in binary mode.
         </para>
        </listitem>
       </varlistentry>
  
       <varlistentry>
!       <term>OIDS</term>
        <listitem>
         <para>
!     Specifies copying the internal object id (OID) for each row.
         </para>
        </listitem>
       </varlistentry>
***************
*** 111,118 ****
        <listitem>
         <para>
          The string that represents a NULL value. The default is
!         <quote><literal>\N</literal></quote> (backslash-N).
!     You might prefer an empty string, for example.
         </para>
         <note>
          <para>
--- 117,124 ----
        <listitem>
         <para>
          The string that represents a NULL value. The default is
!         <quote><literal>\N</literal></quote> (backslash-N). You might
!         prefer an empty string, for example.
         </para>
         <note>
          <para>
***************
*** 172,205 ****
    </title>
    <para>
     <command>COPY</command> moves data between
!    <productname>PostgreSQL</productname> tables and
!    standard file-system files.
  
     <command>COPY TO</command> copies the entire contents of a table
!    <emphasis>to</>
!    a file, while <command>COPY FROM</command> copies data <emphasis>from</> a
!    file to a
!    table (appending the data to whatever is in the table already).
    </para>
  
    <para>
!    <command>COPY</command> with a file name instructs
!    the <productname>PostgreSQL</productname> backend
!    to directly read from or write to a file.
!    The file must be accessible to the backend and the name must be specified
!    from the viewpoint of the backend.
!    When <filename>stdin</filename> or <filename>stdout</filename> is
!    specified, data flows through the client frontend to  the backend.
      
      <tip>
       <para>
        Do not confuse <command>COPY</command> with the
!       <application>psql</application> instruction <command>\copy</command>.
!       <command>\copy</command> invokes <command>COPY FROM stdin</command> 
!       or <command>COPY TO stdout</command>, and then fetches/stores the data
!       in a file accessible to the <application>psql</application> client.
!       Thus, file accessibility and access rights depend on the client
!       rather than the backend when <command>\copy</command> is used.
       </para>
      </tip>
     </para>
--- 178,210 ----
    </title>
    <para>
     <command>COPY</command> moves data between
!    <productname>PostgreSQL</productname> tables and standard file-system
!    files.
  
     <command>COPY TO</command> copies the entire contents of a table
!    <emphasis>to</> a file, while <command>COPY FROM</command> copies
!    data <emphasis>from</> a file to a table (appending the data to
!    whatever is in the table already).
    </para>
  
    <para>
!    <command>COPY</command> with a file name instructs the
!    <productname>PostgreSQL</productname> backend to directly read from
!    or write to a file. The file must be accessible to the backend and
!    the name must be specified from the viewpoint of the backend. When
!    <filename>stdin</filename> or <filename>stdout</filename> is
!    specified, data flows through the client frontend to the backend.
      
      <tip>
       <para>
        Do not confuse <command>COPY</command> with the
!       <application>psql</application> instruction
!       <command>\copy</command>. <command>\copy</command> invokes
!       <command>COPY FROM stdin</command> or <command>COPY TO
!       stdout</command>, and then fetches/stores the data in a file
!       accessible to the <application>psql</application> client. Thus,
!       file accessibility and access rights depend on the client rather
!       than the backend when <command>\copy</command> is used.
       </para>
      </tip>
     </para>
***************
*** 225,244 ****
  
      <para>
      By default, a text copy uses a tab ("\t") character as a delimiter
!     between fields.  The field delimiter may be changed to any other single
!     character with the keyword phrase USING DELIMITERS.  Characters
!     in data fields that happen to match the delimiter character will
!     be backslash quoted.
     </para>
     
     <para>
      You must have <firstterm>select privilege</firstterm> on any table
!     whose values are read by
!     <command>COPY TO</command>, and
!     <firstterm>insert privilege</firstterm> on a
!     table into which values are being inserted by <command>COPY FROM</command>.
!     The backend also needs appropriate Unix permissions for any file read
!     or written by <command>COPY</command>.
     </para>
  
     <para>
--- 230,248 ----
  
      <para>
      By default, a text copy uses a tab ("\t") character as a delimiter
!     between fields. The field delimiter may be changed to any other
!     single character with the keyword DELIMITER. Characters in data
!     fields that happen to match the delimiter character will be
!     backslash quoted.
     </para>
     
     <para>
      You must have <firstterm>select privilege</firstterm> on any table
!     whose values are read by <command>COPY TO</command>, and
!     <firstterm>insert privilege</firstterm> on a table into which values
!     are being inserted by <command>COPY FROM</command>. The backend also
!     needs appropriate Unix permissions for any file read or written by
!     <command>COPY</command>.
     </para>
  
     <para>
***************
*** 247,274 ****
     </para>
  
     <para>
!     <command>COPY</command> stops operation at the first error.  This
!     should not lead to problems in the event of
!     a <command>COPY TO</command>, but the
!     target relation will already have received earlier rows in a
!     <command>COPY FROM</command>.  These rows will not be visible or
!     accessible, but they still occupy disk space.  This may amount to a
!     considerable amount
!     of wasted disk space if the failure happened well into a large copy
!     operation.  You may wish to invoke <command>VACUUM</command> to recover
!     the wasted space.
     </para>
  
     <para>
      Files named in a <command>COPY</command> command are read or written
!     directly by the backend, not by the client application.  Therefore,
      they must reside on or be accessible to the database server machine,
!     not the client.  They must be accessible to and readable or writable
      by the <application>PostgreSQL</application> user (the user ID the
!     server runs as), not the client.
!     <command>COPY</command> naming a file is only allowed to database
!     superusers, since it allows reading or writing any file that the backend
!     has privileges to access.
      
      <tip>
       <para>
--- 251,275 ----
     </para>
  
     <para>
!     <command>COPY</command> stops operation at the first error. This
!     should not lead to problems in the event of a <command>COPY
!     TO</command>, but the target relation will already have received
!     earlier rows in a <command>COPY FROM</command>. These rows will not
!     be visible or accessible, but they still occupy disk space. This may
!     amount to a considerable amount of wasted disk space if the failure
!     happened well into a large copy operation. You may wish to invoke
!     <command>VACUUM</command> to recover the wasted space.
     </para>
  
     <para>
      Files named in a <command>COPY</command> command are read or written
!     directly by the backend, not by the client application. Therefore,
      they must reside on or be accessible to the database server machine,
!     not the client. They must be accessible to and readable or writable
      by the <application>PostgreSQL</application> user (the user ID the
!     server runs as), not the client. <command>COPY</command> naming a
!     file is only allowed to database superusers, since it allows reading
!     or writing any file that the backend has privileges to access.
      
      <tip>
       <para>
***************
*** 282,292 ****
  
     <para>
      It is recommended that the file name used in <command>COPY</command>
!     always be specified as an absolute path.  This is enforced by the backend
!     in the case of <command>COPY TO</command>, but for <command>COPY
!     FROM</command> you do have the option of reading from a file specified
!     by a relative path.  The path will be interpreted relative to the
!     backend's working directory (somewhere below
      <filename>$PGDATA</filename>), not the client's working directory.
     </para>
    </refsect2>
--- 283,293 ----
  
     <para>
      It is recommended that the file name used in <command>COPY</command>
!     always be specified as an absolute path. This is enforced by the
!     backend in the case of <command>COPY TO</command>, but for
!     <command>COPY FROM</command> you do have the option of reading from
!     a file specified by a relative path. The path will be interpreted
!     relative to the backend's working directory (somewhere below
      <filename>$PGDATA</filename>), not the client's working directory.
     </para>
    </refsect2>
***************
*** 312,319 ****
      place of attributes that are NULL.
     </para>
     <para>
!     If WITH OIDS is specified, the OID is read or written as the first column,
!     preceding the user data columns.  (An error is raised if WITH OIDS is
      specified for a table that does not have OIDs.)
     </para>
     <para>
--- 313,320 ----
      place of attributes that are NULL.
     </para>
     <para>
!     If OIDS is specified, the OID is read or written as the first column,
!     preceding the user data columns.  (An error is raised if OIDS is
      specified for a table that does not have OIDs.)
     </para>
     <para>
***************
*** 325,335 ****
     </para>
     <para>
      Backslash characters (<literal>\</>) may be used in the
!     <command>COPY</command> data to quote data characters that might otherwise
!     be taken as row or column delimiters.  In particular, the following
!     characters <emphasis>must</> be preceded by a backslash if they appear
!     as part of an attribute value: backslash itself, newline, and the current
!     delimiter character.
     </para>
     <para>
      The following special backslash sequences are recognized by
--- 326,336 ----
     </para>
     <para>
      Backslash characters (<literal>\</>) may be used in the
!     <command>COPY</command> data to quote data characters that might
!     otherwise be taken as row or column delimiters. In particular, the
!     following characters <emphasis>must</> be preceded by a backslash if
!     they appear as part of an attribute value: backslash itself,
!     newline, and the current delimiter character.
     </para>
     <para>
      The following special backslash sequences are recognized by
***************
*** 412,420 ****
     <title>Binary Format</title>
     <para>
      The file format used for <command>COPY BINARY</command> changed in
!     <application>PostgreSQL</application> v7.1.
!     The new format consists of a file header, zero or more
!     tuples, and a file trailer.
     </para>
  
     <refsect3>
--- 413,420 ----
     <title>Binary Format</title>
     <para>
      The file format used for <command>COPY BINARY</command> changed in
!     <application>PostgreSQL</application> v7.1. The new format consists
!     of a file header, zero or more tuples, and a file trailer.
     </para>
  
     <refsect3>
***************
*** 446,454 ****
        <term>Integer layout field</term>
        <listitem>
         <para>
! int32 constant 0x01020304 in source's byte order.
! Potentially, a reader could engage in byte-flipping of subsequent fields
! if the wrong byte order is detected here.
         </para>
        </listitem>
       </varlistentry>
--- 446,454 ----
        <term>Integer layout field</term>
        <listitem>
         <para>
! int32 constant 0x01020304 in source's byte order. Potentially, a reader
! could engage in byte-flipping of subsequent fields if the wrong byte
! order is detected here.
         </para>
        </listitem>
       </varlistentry>
***************
*** 457,470 ****
        <term>Flags field</term>
        <listitem>
         <para>
! int32 bit mask to denote important aspects of the file
! format.  Bits are numbered from 0 (LSB) to 31 (MSB) --- note that this
! field is stored with source's endianness, as are all subsequent integer
! fields.  Bits 16-31 are reserved to denote critical file format issues;
! a reader should abort if it finds an unexpected bit set in this range.
! Bits 0-15 are reserved to signal backwards-compatible format issues;
! a reader should simply ignore any unexpected bits set in this range.
! Currently only one flag bit is defined, and the rest must be zero:
          <variablelist>
           <varlistentry>
            <term>Bit 16</term>
--- 457,470 ----
        <term>Flags field</term>
        <listitem>
         <para>
! int32 bit mask to denote important aspects of the file format. Bits are
! numbered from 0 (LSB) to 31 (MSB) --- note that this field is stored
! with source's endianness, as are all subsequent integer fields. Bits
! 16-31 are reserved to denote critical file format issues; a reader
! should abort if it finds an unexpected bit set in this range. Bits 0-15
! are reserved to signal backwards-compatible format issues; a reader
! should simply ignore any unexpected bits set in this range. Currently
! only one flag bit is defined, and the rest must be zero:
          <variablelist>
           <varlistentry>
            <term>Bit 16</term>
***************
*** 620,626 ****
   delimiter:
    </para>
    <programlisting>
! COPY country TO <filename>stdout</filename> USING DELIMITERS '|';
    </programlisting>
    <para>
     To copy data from a Unix file into a table country:
--- 620,626 ----
   delimiter:
    </para>
    <programlisting>
! COPY country TO <filename>stdout</filename> WITH DELIMITER '|';
    </programlisting>
    <para>
     To copy data from a Unix file into a table country:
***************
*** 629,637 ****
  COPY country FROM '/usr1/proj/bray/sql/country_data';
    </programlisting>
    <para>
!    Here is a sample of data suitable for copying into a table
!  from <filename>stdin</filename> (so it
! has the termination sequence on the last line):
    </para>
    <programlisting>
  AF      AFGHANISTAN
--- 629,637 ----
  COPY country FROM '/usr1/proj/bray/sql/country_data';
    </programlisting>
    <para>
!    Here is a sample of data suitable for copying into a table from
!    <filename>stdin</filename> (so it has the termination sequence on the
!    last line):
    </para>
    <programlisting>
  AF      AFGHANISTAN
***************
*** 645,657 ****
     Note that the white space on each line is actually a TAB.
    </para>
    <para>
!    The following is the same data, output in binary format on a Linux/i586
!    machine. The data is shown after filtering through
!    the Unix utility <command>od -c</command>. The table has
!    three fields; the first is <type>char(2)</type>,
!    the second is <type>text</type>, and the third is
!    <type>integer</type>. All the
!    rows have a null value in the third field.
    </para>
    <programlisting>
  0000000   P   G   B   C   O   P   Y  \n 377  \r  \n  \0 004 003 002 001
--- 645,656 ----
     Note that the white space on each line is actually a TAB.
    </para>
    <para>
!    The following is the same data, output in binary format on a
!    Linux/i586 machine. The data is shown after filtering through the
!    Unix utility <command>od -c</command>. The table has three fields;
!    the first is <type>char(2)</type>, the second is <type>text</type>,
!    and the third is <type>integer</type>. All the rows have a null value
!    in the third field.
    </para>
    <programlisting>
  0000000   P   G   B   C   O   P   Y  \n 377  \r  \n  \0 004 003 002 001
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.66
diff -c -r1.66 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    22 Mar 2002 19:20:44 -0000    1.66
--- doc/src/sgml/ref/psql-ref.sgml    19 Jun 2002 06:09:07 -0000
***************
*** 252,329 ****
          <term><literal>\connect</literal> (or <literal>\c</literal>) [ <replaceable
class="parameter">dbname</replaceable>[ <replaceable class="parameter">username</replaceable> ] ]</term>
 
          <listitem>
          <para>
!     Establishes a connection to a new database and/or under a user name. The
!     previous connection is closed.
!     If <replaceable class="parameter">dbname</replaceable> is <literal>-</literal>
      the current database name is assumed.
      </para>
  
      <para>
!     If <replaceable class="parameter">username</replaceable> is omitted
!         the current user name is assumed.
!     </para>
  
      <para>
!     As a special rule, <command>\connect</command> without any arguments will connect
!     to the default database as the default user (as you would have gotten
!     by starting <application>psql</application> without any arguments).
      </para>
  
      <para>
!     If the connection attempt failed (wrong user name, access denied, etc.), the
!     previous connection will be kept if and only if <application>psql</application> is
!     in interactive mode. When executing a non-interactive script, processing
!     will immediately stop with an error. This distinction was chosen as a user
!     convenience against typos on the one hand, and a safety mechanism that
!     scripts are not accidentally acting on the wrong database on the other hand.
      </para>
          </listitem>
        </varlistentry>
  
        <varlistentry>
          <term><literal>\copy</literal> <replaceable class="parameter">table</replaceable>
!         [ <literal>with oids</literal> ] { <literal>from</literal> | <literal>to</literal> }
      <replaceable class="parameter">filename</replaceable> | stdin | stdout
!         [ <literal>using delimiters</literal> '<replaceable class="parameter">characters</replaceable>' ]
          [ <literal>with null as</literal> '<replaceable class="parameter">string</replaceable>' ]
          </term>
  
          <listitem>
          <para>
!         Performs a frontend (client) copy. This is an operation that runs an
!     <acronym>SQL</acronym> <xref linkend="SQL-COPY" endterm="SQL-COPY-title"> command,
!         but instead of the backend's reading or writing the specified file, and 
!         consequently requiring backend access and special user privilege, 
!     as well as being bound to the file system accessible by the backend,
!         <application>psql</application> reads or writes the 
!         file and routes the data between the backend and the local file system.
      </para>
  
      <para>
!     The syntax of the command is similar to that of the <acronym>SQL</acronym>
!     <command>COPY</command> command (see its description for the details).
!     Note that, because of this, special parsing rules apply to the
!     <command>\copy</command> command. In particular, the variable
!     substitution rules and backslash escapes do not apply.
      </para>
  
          <tip>
          <para>
!     This operation is not as efficient as the <acronym>SQL</acronym> 
!     <command>COPY</command> command because all data must pass through the
!     client/server IP or socket connection. For large amounts of data the other
!     technique may be preferable.
          </para>
          </tip>
  
          <note>
          <para>
!         Note the difference in interpretation of <literal>stdin</literal> and <literal>stdout</literal>
!         between frontend and backend copies: in a frontend copy these always refer
!         to <application>psql</application>'s input and output stream. On a backend
!         copy <literal>stdin</literal> comes from wherever the <command>COPY</command>
!         itself came from (for example, a script run with the <option>-f</option> option),
!         and <literal>stdout</literal> refers to the query output stream (see
          <command>\o</command> meta-command below).
          </para>
          </note>
--- 261,344 ----
          <term><literal>\connect</literal> (or <literal>\c</literal>) [ <replaceable
class="parameter">dbname</replaceable>[ <replaceable class="parameter">username</replaceable> ] ]</term>
 
          <listitem>
          <para>
!     Establishes a connection to a new database and/or under a user
!     name. The previous connection is closed. If <replaceable
!     class="parameter">dbname</replaceable> is <literal>-</literal>
      the current database name is assumed.
      </para>
  
      <para>
!     If <replaceable class="parameter">username</replaceable> is
!     omitted the current user name is assumed. </para>
  
      <para>
!     As a special rule, <command>\connect</command> without any
!     arguments will connect to the default database as the default
!     user (as you would have gotten by starting
!     <application>psql</application> without any arguments).
      </para>
  
      <para>
!     If the connection attempt failed (wrong user name, access
!     denied, etc.), the previous connection will be kept if and only
!     if <application>psql</application> is in interactive mode. When
!     executing a non-interactive script, processing will immediately
!     stop with an error. This distinction was chosen as a user
!     convenience against typos on the one hand, and a safety
!     mechanism that scripts are not accidentally acting on the wrong
!     database on the other hand.
      </para>
          </listitem>
        </varlistentry>
  
        <varlistentry>
          <term><literal>\copy</literal> <replaceable class="parameter">table</replaceable>
!         { <literal>from</literal> | <literal>to</literal> }
      <replaceable class="parameter">filename</replaceable> | stdin | stdout
!         [ <literal>with oids</literal> ] [ <literal>with delimiter</literal> '<replaceable
class="parameter">character</replaceable>']
 
          [ <literal>with null as</literal> '<replaceable class="parameter">string</replaceable>' ]
          </term>
  
          <listitem>
          <para>
!         Performs a frontend (client) copy. This is an operation that
!         runs an <acronym>SQL</acronym> <xref linkend="SQL-COPY"
!         endterm="SQL-COPY-title"> command, but instead of the backend's
!         reading or writing the specified file, and consequently
!         requiring backend access and special user privilege, as well as
!         being bound to the file system accessible by the backend,
!         <application>psql</application> reads or writes the file and
!         routes the data between the backend and the local file system.
      </para>
  
      <para>
!     The syntax of the command is similar to that of the
!     <acronym>SQL</acronym> <command>COPY</command> command (see its
!     description for the details). Note that, because of this,
!     special parsing rules apply to the <command>\copy</command>
!     command. In particular, the variable substitution rules and
!     backslash escapes do not apply.
      </para>
  
          <tip>
          <para>
!     This operation is not as efficient as the <acronym>SQL</acronym>
!     <command>COPY</command> command because all data must pass
!     through the client/server IP or socket connection. For large
!     amounts of data the other technique may be preferable.
          </para>
          </tip>
  
          <note>
          <para>
!         Note the difference in interpretation of
!         <literal>stdin</literal> and <literal>stdout</literal> between
!         frontend and backend copies: in a frontend copy these always
!         refer to <application>psql</application>'s input and output
!         stream. On a backend copy <literal>stdin</literal> comes from
!         wherever the <command>COPY</command> itself came from (for
!         example, a script run with the <option>-f</option> option), and
!         <literal>stdout</literal> refers to the query output stream (see
          <command>\o</command> meta-command below).
          </para>
          </note>
Index: src/backend/commands/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.156
diff -c -r1.156 copy.c
*** src/backend/commands/copy.c    21 May 2002 22:59:00 -0000    1.156
--- src/backend/commands/copy.c    19 Jun 2002 06:09:09 -0000
***************
*** 261,278 ****
   * the table.
   */
  void
! DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe,
!        char *filename, char *delim, char *null_print)
  {
      FILE       *fp;
      Relation    rel;
!     AclMode        required_access = (from ? ACL_INSERT : ACL_SELECT);
      AclResult    aclresult;
  
      /*
       * Open and lock the relation, using the appropriate lock type.
       */
!     rel = heap_openrv(relation, (from ? RowExclusiveLock : AccessShareLock));
  
      /* Check permissions. */
      aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
--- 261,339 ----
   * the table.
   */
  void
! DoCopy(const CopyStmt *stmt)
  {
+     RangeVar *relation = stmt->relation;
+     char *filename = stmt->filename;
+     bool is_from = stmt->is_from;
+     bool pipe = (stmt->filename == NULL);
+     List       *option;
+     DefElem    *dbinary = NULL;
+     DefElem    *doids = NULL;
+     DefElem    *ddelim = NULL;
+     DefElem    *dnull = NULL;
+     bool binary = false;
+     bool oids = false;
+     char *delim = "\t";
+     char *null_print = "\\N";
      FILE       *fp;
      Relation    rel;
!     AclMode        required_access = (is_from ? ACL_INSERT : ACL_SELECT);
      AclResult    aclresult;
  
+     /* Extract options from the statement node tree */
+     foreach(option, stmt->options)
+     {
+         DefElem    *defel = (DefElem *) lfirst(option);
+ 
+         if (strcmp(defel->defname, "binary") == 0)
+         {
+             if (dbinary)
+                 elog(ERROR, "COPY: conflicting options");
+             dbinary = defel;
+         }
+         else if (strcmp(defel->defname, "oids") == 0)
+         {
+             if (doids)
+                 elog(ERROR, "COPY: conflicting options");
+             doids = defel;
+         }
+         else if (strcmp(defel->defname, "delimiter") == 0)
+         {
+             if (ddelim)
+                 elog(ERROR, "COPY: conflicting options");
+             ddelim = defel;
+         }
+         else if (strcmp(defel->defname, "null") == 0)
+         {
+             if (dnull)
+                 elog(ERROR, "COPY: conflicting options");
+             dnull = defel;
+         }
+         else
+             elog(ERROR, "COPY: option \"%s\" not recognized",
+                  defel->defname);
+     }
+ 
+     if (dbinary)
+         binary = intVal(dbinary->arg);
+     if (doids)
+         oids = intVal(doids->arg);
+     if (ddelim)
+         delim = strVal(ddelim->arg);
+     if (dnull)
+         null_print = strVal(dnull->arg);
+ 
+     if (binary && ddelim)
+         elog(ERROR, "You can not specify the DELIMITER in BINARY mode.");
+ 
+     if (binary && dnull)
+         elog(ERROR, "You can not specify NULL AS in BINARY mode.");
+     
      /*
       * Open and lock the relation, using the appropriate lock type.
       */
!     rel = heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock));
  
      /* Check permissions. */
      aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
***************
*** 306,312 ****
      server_encoding = GetDatabaseEncoding();
  #endif
  
!     if (from)
      {                            /* copy from file to database */
          if (rel->rd_rel->relkind != RELKIND_RELATION)
          {
--- 367,373 ----
      server_encoding = GetDatabaseEncoding();
  #endif
  
!     if (is_from)
      {                            /* copy from file to database */
          if (rel->rd_rel->relkind != RELKIND_RELATION)
          {
***************
*** 410,416 ****
  
      if (!pipe)
          FreeFile(fp);
!     else if (!from)
      {
          if (!binary)
              CopySendData("\\.\n", 3, fp);
--- 471,477 ----
  
      if (!pipe)
          FreeFile(fp);
!     else if (!is_from)
      {
          if (!binary)
              CopySendData("\\.\n", 3, fp);
***************
*** 425,431 ****
       * transaction to ensure that updates will be committed before lock is
       * released.
       */
!     heap_close(rel, (from ? NoLock : AccessShareLock));
  }
  
  
--- 486,492 ----
       * transaction to ensure that updates will be committed before lock is
       * released.
       */
!     heap_close(rel, (is_from ? NoLock : AccessShareLock));
  }
  
  
Index: src/backend/commands/dbcommands.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v
retrieving revision 1.93
diff -c -r1.93 dbcommands.c
*** src/backend/commands/dbcommands.c    18 Jun 2002 17:27:57 -0000    1.93
--- src/backend/commands/dbcommands.c    19 Jun 2002 06:09:13 -0000
***************
*** 58,64 ****
   */
  
  void
! createdb(CreatedbStmt *stmt)
  {
      char       *nominal_loc;
      char       *alt_loc;
--- 58,64 ----
   */
  
  void
! createdb(const CreatedbStmt *stmt)
  {
      char       *nominal_loc;
      char       *alt_loc;
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.189
diff -c -r1.189 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    18 Jun 2002 17:27:57 -0000    1.189
--- src/backend/nodes/copyfuncs.c    19 Jun 2002 06:09:16 -0000
***************
*** 1992,2007 ****
  {
      CopyStmt   *newnode = makeNode(CopyStmt);
  
-     newnode->binary = from->binary;
      Node_Copy(from, newnode, relation);
!     newnode->oids = from->oids;
!     newnode->direction = from->direction;
      if (from->filename)
          newnode->filename = pstrdup(from->filename);
!     if (from->delimiter)
!         newnode->delimiter = pstrdup(from->delimiter);
!     if (from->null_print)
!         newnode->null_print = pstrdup(from->null_print);
  
      return newnode;
  }
--- 1992,2002 ----
  {
      CopyStmt   *newnode = makeNode(CopyStmt);
  
      Node_Copy(from, newnode, relation);
!     newnode->is_from = from->is_from;
      if (from->filename)
          newnode->filename = pstrdup(from->filename);
!     Node_Copy(from, newnode, options);
  
      return newnode;
  }
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.136
diff -c -r1.136 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    18 Jun 2002 17:27:57 -0000    1.136
--- src/backend/nodes/equalfuncs.c    19 Jun 2002 06:09:17 -0000
***************
*** 799,817 ****
  static bool
  _equalCopyStmt(CopyStmt *a, CopyStmt *b)
  {
-     if (a->binary != b->binary)
-         return false;
      if (!equal(a->relation, b->relation))
          return false;
!     if (a->oids != b->oids)
!         return false;
!     if (a->direction != b->direction)
          return false;
      if (!equalstr(a->filename, b->filename))
          return false;
!     if (!equalstr(a->delimiter, b->delimiter))
!         return false;
!     if (!equalstr(a->null_print, b->null_print))
          return false;
  
      return true;
--- 799,811 ----
  static bool
  _equalCopyStmt(CopyStmt *a, CopyStmt *b)
  {
      if (!equal(a->relation, b->relation))
          return false;
!     if (a->is_from != b->is_from)
          return false;
      if (!equalstr(a->filename, b->filename))
          return false;
!     if (!equal(a->options, b->options))
          return false;
  
      return true;
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.330
diff -c -r2.330 gram.y
*** src/backend/parser/gram.y    18 Jun 2002 17:56:41 -0000    2.330
--- src/backend/parser/gram.y    19 Jun 2002 06:09:33 -0000
***************
*** 159,166 ****
  %type <node>    alter_column_default
  %type <ival>    add_drop, drop_behavior, opt_drop_behavior
  
! %type <list>    createdb_opt_list
! %type <defelt>    createdb_opt_item
  %type <boolean> opt_equal
  
  %type <ival>    opt_lock, lock_type
--- 159,166 ----
  %type <node>    alter_column_default
  %type <ival>    add_drop, drop_behavior, opt_drop_behavior
  
! %type <list>    createdb_opt_list, copy_opt_list
! %type <defelt>    createdb_opt_item, copy_opt_item
  %type <boolean> opt_equal
  
  %type <ival>    opt_lock, lock_type
***************
*** 183,189 ****
  %type <str>        TriggerEvents
  %type <value>    TriggerFuncArg
  
! %type <str>        relation_name, copy_file_name, copy_delimiter, copy_null,
                  database_name, access_method_clause, access_method, attr_name,
                  index_name, name, function_name, file_name
  
--- 183,189 ----
  %type <str>        TriggerEvents
  %type <value>    TriggerFuncArg
  
! %type <str>        relation_name, copy_file_name,
                  database_name, access_method_clause, access_method, attr_name,
                  index_name, name, function_name, file_name
  
***************
*** 239,249 ****
  %type <ival>    opt_interval
  %type <node>    overlay_placing, substr_from, substr_for
  
! %type <boolean> opt_binary, opt_instead, opt_cursor
! %type <boolean> opt_with_copy, index_opt_unique, opt_verbose, opt_full
  %type <boolean> opt_freeze
  
! %type <ival>    copy_dirn, direction, reindex_type, drop_type,
                  opt_column, event, comment_type
  
  %type <ival>    fetch_how_many
--- 239,252 ----
  %type <ival>    opt_interval
  %type <node>    overlay_placing, substr_from, substr_for
  
! %type <boolean> opt_instead, opt_cursor
! %type <boolean> index_opt_unique, opt_verbose, opt_full
  %type <boolean> opt_freeze
+ %type <defelt>    opt_binary, opt_oids, copy_delimiter
  
! %type <boolean> copy_from
! 
! %type <ival>    direction, reindex_type, drop_type,
                  opt_column, event, comment_type
  
  %type <ival>    fetch_how_many
***************
*** 335,342 ****
      CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, CYCLE,
  
      DATABASE, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT,
!     DEFERRABLE, DEFERRED, DEFINER, DELETE_P, DELIMITERS, DESC,
!     DISTINCT, DO, DOMAIN_P, DOUBLE, DROP,
  
      EACH, ELSE, ENCODING, ENCRYPTED, END_TRANS, ESCAPE, EXCEPT,
      EXCLUSIVE, EXECUTE, EXISTS, EXPLAIN, EXTERNAL, EXTRACT,
--- 338,345 ----
      CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, CYCLE,
  
      DATABASE, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT,
!     DEFERRABLE, DEFERRED, DEFINER, DELETE_P, DELIMITER, DELIMITERS,
!     DESC, DISTINCT, DO, DOMAIN_P, DOUBLE, DROP,
  
      EACH, ELSE, ENCODING, ENCRYPTED, END_TRANS, ESCAPE, EXCEPT,
      EXCLUSIVE, EXECUTE, EXISTS, EXPLAIN, EXTERNAL, EXTRACT,
***************
*** 1298,1324 ****
  /*****************************************************************************
   *
   *        QUERY :
!  *                COPY [BINARY] <relname> FROM/TO
!  *                [USING DELIMITERS <delimiter>]
   *
   *****************************************************************************/
  
! CopyStmt:    COPY opt_binary qualified_name opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null
                  {
                      CopyStmt *n = makeNode(CopyStmt);
-                     n->binary = $2;
                      n->relation = $3;
!                     n->oids = $4;
!                     n->direction = $5;
                      n->filename = $6;
!                     n->delimiter = $7;
!                     n->null_print = $8;
                      $$ = (Node *)n;
                  }
          ;
  
! copy_dirn:    TO                                    { $$ = TO; }
!             | FROM                                { $$ = FROM; }
          ;
  
  /*
--- 1301,1338 ----
  /*****************************************************************************
   *
   *        QUERY :
!  *                COPY <relname> FROM/TO [WITH options]
!  *
!  *                BINARY, OIDS, and DELIMITERS kept in old locations
!  *                for backward compatibility.  2002-06-18
   *
   *****************************************************************************/
  
! CopyStmt:    COPY opt_binary qualified_name opt_oids copy_from
!             copy_file_name copy_delimiter opt_with copy_opt_list
                  {
                      CopyStmt *n = makeNode(CopyStmt);
                      n->relation = $3;
!                     n->is_from = $5;
                      n->filename = $6;
! 
!                     n->options = NIL;
!                     /* Concatenate user-supplied flags */
!                     if ($2)
!                         n->options = lappend(n->options, $2);
!                     if ($4)
!                         n->options = lappend(n->options, $4);
!                     if ($7)
!                         n->options = lappend(n->options, $7);
!                     if ($9)
!                         n->options = nconc(n->options, $9);
                      $$ = (Node *)n;
                  }
          ;
  
! copy_from:
!             FROM                                    { $$ = TRUE; }
!             | TO                                    { $$ = FALSE; }
          ;
  
  /*
***************
*** 1332,1361 ****
              | STDOUT                                { $$ = NULL; }
          ;
  
! opt_binary: BINARY                                    { $$ = TRUE; }
!             | /*EMPTY*/                                { $$ = FALSE; }
          ;
  
! opt_with_copy:
!             WITH OIDS                                { $$ = TRUE; }
!             | /*EMPTY*/                                { $$ = FALSE; }
          ;
  
- /*
-  * the default copy delimiter is tab but the user can configure it
-  */
  copy_delimiter:
!             opt_using DELIMITERS Sconst                { $$ = $3; }
!             | /*EMPTY*/                                { $$ = "\t"; }
          ;
  
! opt_using:    USING                                    {}
              | /*EMPTY*/                                {}
          ;
  
- copy_null:    WITH NULL_P AS Sconst                    { $$ = $4; }
-             | /*EMPTY*/                                { $$ = "\\N"; }
-         ;
  
  /*****************************************************************************
   *
--- 1346,1424 ----
              | STDOUT                                { $$ = NULL; }
          ;
  
! 
! 
! copy_opt_list:
!             copy_opt_list copy_opt_item                { $$ = lappend($1, $2); }
!             | /* EMPTY */                            { $$ = NIL; }
          ;
  
! 
! copy_opt_item:
!             BINARY
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "binary";
!                     $$->arg = (Node *)makeInteger(TRUE);
!                 }
!             | OIDS
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "oids";
!                     $$->arg = (Node *)makeInteger(TRUE);
!                 }
!             | DELIMITER Sconst
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "delimiter";
!                     $$->arg = (Node *)makeString($2);
!                 }
!             | NULL_P AS Sconst
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "null";
!                     $$->arg = (Node *)makeString($3);
!                 }
!         ;
! 
! /* The following exist for backward compatibility */
! 
! opt_binary:
!             BINARY
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "binary";
!                     $$->arg = (Node *)makeInteger(TRUE);
!                 }
!             | /*EMPTY*/                                { $$ = NULL; }
!         ;
! 
! opt_oids:
!             WITH OIDS
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "oids";
!                     $$->arg = (Node *)makeInteger(TRUE);
!                 }
!             | /*EMPTY*/                                { $$ = NULL; }
          ;
  
  copy_delimiter:
!             /* USING DELIMITERS kept for backward compatibility. 2002-06-15 */
!             opt_using DELIMITERS Sconst
!                 {
!                     $$ = makeNode(DefElem);
!                     $$->defname = "delimiter";
!                     $$->arg = (Node *)makeString($3);
!                 }
!             | /*EMPTY*/                                { $$ = NULL; }
          ;
  
! opt_using:
!             USING                                    {}
              | /*EMPTY*/                                {}
          ;
  
  
  /*****************************************************************************
   *
***************
*** 3428,3437 ****
              | /* EMPTY */                            { $$ = NIL; }
          ;
  
- /*
-  * createdb_opt_item returns 2-element lists, with the first element
-  * being an integer code to indicate which item was specified.
-  */
  createdb_opt_item:
              LOCATION opt_equal Sconst
                  {
--- 3491,3496 ----
***************
*** 6535,6540 ****
--- 6594,6600 ----
              | DEFERRED
              | DEFINER
              | DELETE_P
+             | DELIMITER
              | DELIMITERS
              | DOMAIN_P
              | DOUBLE
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.114
diff -c -r1.114 keywords.c
*** src/backend/parser/keywords.c    15 Jun 2002 03:00:03 -0000    1.114
--- src/backend/parser/keywords.c    19 Jun 2002 06:09:33 -0000
***************
*** 98,103 ****
--- 98,104 ----
      {"deferred", DEFERRED},
      {"definer", DEFINER},
      {"delete", DELETE_P},
+     {"delimiter", DELIMITER},
      {"delimiters", DELIMITERS},
      {"desc", DESC},
      {"distinct", DISTINCT},
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.157
diff -c -r1.157 utility.c
*** src/backend/tcop/utility.c    18 Jun 2002 17:27:58 -0000    1.157
--- src/backend/tcop/utility.c    19 Jun 2002 06:09:35 -0000
***************
*** 347,368 ****
              {
                  CopyStmt   *stmt = (CopyStmt *) parsetree;
  
!                 if (stmt->direction != FROM)
                      SetQuerySnapshot();
  
!                 DoCopy(stmt->relation,
!                        stmt->binary,
!                        stmt->oids,
!                        (bool) (stmt->direction == FROM),
!                        (bool) (stmt->filename == NULL),
! 
!                 /*
!                  * null filename means copy to/from stdout/stdin, rather
!                  * than to/from a file.
!                  */
!                        stmt->filename,
!                        stmt->delimiter,
!                        stmt->null_print);
              }
              break;
  
--- 347,356 ----
              {
                  CopyStmt   *stmt = (CopyStmt *) parsetree;
  
!                 if (!stmt->is_from)
                      SetQuerySnapshot();
  
!                 DoCopy(stmt);
              }
              break;
  
Index: src/bin/pgaccess/doc/html/tutorial/tut_edit.html
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pgaccess/doc/html/tutorial/tut_edit.html,v
retrieving revision 1.4
diff -c -r1.4 tut_edit.html
*** src/bin/pgaccess/doc/html/tutorial/tut_edit.html    30 Oct 1999 16:18:44 -0000    1.4
--- src/bin/pgaccess/doc/html/tutorial/tut_edit.html    19 Jun 2002 06:09:35 -0000
***************
*** 29,35 ****
  Notice that there are two consecutive tildes to allow for the fact that this
  particular entry doesn't have anything in the <b>Editor</b> field.
  You can then perform a <em>Query</em> as follows:<p>
! <samp>COPY psyref FROM '/home/jim/newref.txt' USING DELIMITERS
  '~';</samp><p>
  This will read the records from <samp>newref.txt</samp> and insert them into the
  table <samp>psyref</samp>. See the PostgreSQL documentation under the headings 
--- 29,35 ----
  Notice that there are two consecutive tildes to allow for the fact that this
  particular entry doesn't have anything in the <b>Editor</b> field.
  You can then perform a <em>Query</em> as follows:<p>
! <samp>COPY psyref FROM '/home/jim/newref.txt' WITH DELIMITER
  '~';</samp><p>
  This will read the records from <samp>newref.txt</samp> and insert them into the
  table <samp>psyref</samp>. See the PostgreSQL documentation under the headings 
Index: src/bin/pgaccess/lib/mainlib.tcl
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pgaccess/lib/mainlib.tcl,v
retrieving revision 1.8
diff -c -r1.8 mainlib.tcl
*** src/bin/pgaccess/lib/mainlib.tcl    30 May 2001 18:08:24 -0000    1.8
--- src/bin/pgaccess/lib/mainlib.tcl    19 Jun 2002 06:09:36 -0000
***************
*** 636,641 ****
--- 636,643 ----
      if {$PgAcVar(impexp,delimiter)==""} {
          set sup ""
      } else {
+         # now we use WITH DELIMITER, but keep old syntax for
+         # backward compatibility.  2002-06-15
          set sup " USING DELIMITERS '$PgAcVar(impexp,delimiter)'"
      }
      if {[.pgaw:ImportExport.expbtn cget -text]=="Import"} {
Index: src/bin/pgaccess/lib/help/copy.hlp
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pgaccess/lib/help/copy.hlp,v
retrieving revision 1.4
diff -c -r1.4 copy.hlp
*** src/bin/pgaccess/lib/help/copy.hlp    30 Oct 1999 16:18:50 -0000    1.4
--- src/bin/pgaccess/lib/help/copy.hlp    19 Jun 2002 06:09:42 -0000
***************
*** 5,14 ****
  " {} "
  COPY \[ BINARY \] table \[ WITH OIDS \]
      FROM { 'filename' | stdin }
!     \[ USING DELIMITERS 'delimiter' \]
  COPY \[ BINARY \] table \[ WITH OIDS \]
      TO { 'filename' | stdout }
!     \[ USING DELIMITERS 'delimiter' \]    
  
  " {code} "Inputs" {bold} "
  
--- 5,14 ----
  " {} "
  COPY \[ BINARY \] table \[ WITH OIDS \]
      FROM { 'filename' | stdin }
!     \[ WITH DELIMITER 'delimiter' \]
  COPY \[ BINARY \] table \[ WITH OIDS \]
      TO { 'filename' | stdout }
!     \[ WITH DELIMITER 'delimiter' \]    
  
  " {code} "Inputs" {bold} "
  
***************
*** 44,50 ****
  " {} "Usage" {bold} "
  
  The following example copies a table to standard output, using a vertical bar \(\"|\"\) as the field delimiter:
! COPY country TO stdout USING DELIMITERS '|';
  
  To copy data from a Unix file into a table \"country\": 
  COPY country FROM '/usr1/proj/bray/sql/country_data';
--- 44,50 ----
  " {} "Usage" {bold} "
  
  The following example copies a table to standard output, using a vertical bar \(\"|\"\) as the field delimiter:
! COPY country TO stdout WITH DELIMITER '|';
  
  To copy data from a Unix file into a table \"country\": 
  COPY country FROM '/usr1/proj/bray/sql/country_data';
***************
*** 93,105 ****
  
  The " {} "BINARY" {bold} " keyword will force all data to be stored/read as binary objects rather than as text. It is
somewhatfaster than the normal copy command, but is not generally portable, and the files \
 
  generated are somewhat larger, although this factor is highly dependent on the data itself. By default, a text copy
usesa tab \
 
! \(\"\\t\"\) character as a delimiter. The delimiter may also be changed to any other single character with the
keywordphrase USING DELIMITERS. Characters in data fields which happen to match the delimiter character will be
quoted.
  
  You must have select access on any table whose values are read by " {} "COPY" {bold} ", and either insert or update
accessto a table into which values are being inserted by \
 
  " {} "COPY" {bold} ". The backend also needs appropriate Unix permissions for any file read or written by \
  " {} "COPY" {bold} ". 
  
! The keyword phrase " {} "USING DELIMITERS" {bold} " specifies a single character to be used for all delimiters
betweencolumns. If multiple characters are specified in the delimiter string, only the first \
 
! character is used. 
  
         Tip: Do not confuse " {} "COPY" {bold} " with the psql instruction \\copy. "
--- 93,104 ----
  
  The " {} "BINARY" {bold} " keyword will force all data to be stored/read as binary objects rather than as text. It is
somewhatfaster than the normal copy command, but is not generally portable, and the files \
 
  generated are somewhat larger, although this factor is highly dependent on the data itself. By default, a text copy
usesa tab \
 
! \(\"\\t\"\) character as a delimiter. The delimiter may also be changed to any other single character with the
keywordphrase WITH DELIMITER. Characters in data fields which happen to match the delimiter character will be quoted.
 
  
  You must have select access on any table whose values are read by " {} "COPY" {bold} ", and either insert or update
accessto a table into which values are being inserted by \
 
  " {} "COPY" {bold} ". The backend also needs appropriate Unix permissions for any file read or written by \
  " {} "COPY" {bold} ". 
  
! The keyword phrase " {} "WITH DELIMITER" {bold} " specifies a single character to be used for all delimiters between
columns.
  
         Tip: Do not confuse " {} "COPY" {bold} " with the psql instruction \\copy. "
Index: src/bin/psql/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v
retrieving revision 1.22
diff -c -r1.22 copy.c
*** src/bin/psql/copy.c    24 Apr 2002 21:00:10 -0000    1.22
--- src/bin/psql/copy.c    19 Jun 2002 06:09:43 -0000
***************
*** 36,42 ****
   * parse_slash_copy
   * -- parses \copy command line
   *
!  * Accepted syntax: \copy [binary] table|"table" [with oids] from|to filename|'filename' [ using delimiters '<char>']
[with null as 'string' ]
 
   * (binary is not here yet)
   *
   * returns a malloc'ed structure with the options, or NULL on parsing error
--- 36,42 ----
   * parse_slash_copy
   * -- parses \copy command line
   *
!  * Accepted syntax: \copy [binary] table|"table" [with oids] from|to filename|'filename' [with ] [ oids ] [ delimiter
'<char>'][ null as 'string' ]
 
   * (binary is not here yet)
   *
   * returns a malloc'ed structure with the options, or NULL on parsing error
***************
*** 161,192 ****
          token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
          if (token)
          {
!             if (strcasecmp(token, "using") == 0)
              {
!                 token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
!                 if (!token || strcasecmp(token, "delimiters") != 0)
!                     error = true;
!                 else
                  {
!                     token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
!                     if (token)
                      {
!                         result->delim = xstrdup(token);
!                         token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
                      }
!                     else
!                         error = true;
!                 }
!             }
! 
!             if (!error && token)
!             {
!                 if (strcasecmp(token, "with") == 0)
!                 {
!                     token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
!                     if (!token || strcasecmp(token, "null") != 0)
!                         error = true;
!                     else
                      {
                          token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
                          if (!token || strcasecmp(token, "as") != 0)
--- 161,179 ----
          token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
          if (token)
          {
!             if (strcasecmp(token, "with") == 0)
              {
!                 while (!error && (token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding)))
                  {
!                     if (strcasecmp(token, "delimiter") == 0)
                      {
!                         token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
!                         if (token)
!                             result->delim = xstrdup(token);
!                         else
!                             error = true;
                      }
!                     else if (strcasecmp(token, "null") == 0)
                      {
                          token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
                          if (!token || strcasecmp(token, "as") != 0)
***************
*** 196,207 ****
                              token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
                              if (token)
                                  result->null = xstrdup(token);
                          }
                      }
                  }
-                 else
-                     error = true;
              }
          }
      }
  
--- 183,196 ----
                              token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
                              if (token)
                                  result->null = xstrdup(token);
+                             else
+                                 error = true;
                          }
                      }
+                     else error = true;
                  }
              }
+             else error = true;
          }
      }
  
***************
*** 250,257 ****
          appendPQExpBuffer(&query, "BINARY ");
  
      appendPQExpBuffer(&query, "\"%s\" ", options->table);
-     if (options->oids)
-         appendPQExpBuffer(&query, "WITH OIDS ");
  
      if (options->from)
          appendPQExpBuffer(&query, "FROM STDIN");
--- 239,244 ----
***************
*** 259,269 ****
          appendPQExpBuffer(&query, "TO STDOUT");
  
  
!     if (options->delim)
!         appendPQExpBuffer(&query, " USING DELIMITERS '%s'", options->delim);
  
      if (options->null)
!         appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
  
      if (options->from)
      {
--- 246,260 ----
          appendPQExpBuffer(&query, "TO STDOUT");
  
  
!     if (options->oids || options->delim || options->null)
!         appendPQExpBuffer(&query, " WITH");
  
+     if (options->oids)
+         appendPQExpBuffer(&query, " OIDS ");
+     if (options->delim)
+         appendPQExpBuffer(&query, " DELIMITER '%s'", options->delim);
      if (options->null)
!         appendPQExpBuffer(&query, " NULL AS '%s'", options->null);
  
      if (options->from)
      {
Index: src/include/commands/copy.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/commands/copy.h,v
retrieving revision 1.17
diff -c -r1.17 copy.h
*** src/include/commands/copy.h    29 Mar 2002 19:06:21 -0000    1.17
--- src/include/commands/copy.h    19 Jun 2002 06:09:44 -0000
***************
*** 14,25 ****
  #ifndef COPY_H
  #define COPY_H
  
  #include "nodes/primnodes.h"
  
  extern int    copy_lineno;
  
! void DoCopy(const RangeVar *relation, bool binary, bool oids,
!             bool from, bool pipe,
!             char *filename, char *delim, char *null_print);
  
  #endif   /* COPY_H */
--- 14,24 ----
  #ifndef COPY_H
  #define COPY_H
  
+ #include "nodes/parsenodes.h"
  #include "nodes/primnodes.h"
  
  extern int    copy_lineno;
  
! void DoCopy(const CopyStmt *stmt);
  
  #endif   /* COPY_H */
Index: src/include/commands/dbcommands.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/commands/dbcommands.h,v
retrieving revision 1.22
diff -c -r1.22 dbcommands.h
*** src/include/commands/dbcommands.h    18 Jun 2002 17:27:58 -0000    1.22
--- src/include/commands/dbcommands.h    19 Jun 2002 06:09:44 -0000
***************
*** 16,22 ****
  
  #include <nodes/parsenodes.h>
  
! extern void createdb(CreatedbStmt *stmt);
  extern void dropdb(const char *dbname);
  extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
  
--- 16,22 ----
  
  #include <nodes/parsenodes.h>
  
! extern void createdb(const CreatedbStmt *stmt);
  extern void dropdb(const char *dbname);
  extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
  
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.180
diff -c -r1.180 parsenodes.h
*** src/include/nodes/parsenodes.h    18 Jun 2002 17:27:58 -0000    1.180
--- src/include/nodes/parsenodes.h    19 Jun 2002 06:09:51 -0000
***************
*** 851,863 ****
  typedef struct CopyStmt
  {
      NodeTag        type;
-     bool        binary;            /* is a binary copy? */
      RangeVar   *relation;        /* the relation to copy */
!     bool        oids;            /* copy oid's? */
!     int            direction;        /* TO or FROM */
      char       *filename;        /* if NULL, use stdin/stdout */
!     char       *delimiter;        /* delimiter character, \t by default */
!     char       *null_print;        /* how to print NULLs, `\N' by default */
  } CopyStmt;
  
  /* ----------------------
--- 851,860 ----
  typedef struct CopyStmt
  {
      NodeTag        type;
      RangeVar   *relation;        /* the relation to copy */
!     bool        is_from;        /* TO or FROM */
      char       *filename;        /* if NULL, use stdin/stdout */
!     List       *options;        /* List of DefElem nodes */
  } CopyStmt;
  
  /* ----------------------
Index: src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java,v
retrieving revision 1.46
diff -c -r1.46 DatabaseMetaData.java
*** src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java    11 Jun 2002 02:55:16 -0000    1.46
--- src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java    19 Jun 2002 06:09:56 -0000
***************
*** 370,376 ****
       */
      public String getSQLKeywords() throws SQLException
      {
!         return
"abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version";
      }
  
      public String getNumericFunctions() throws SQLException
--- 370,376 ----
       */
      public String getSQLKeywords() throws SQLException
      {
!         return
"abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiter,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version";
      }
  
      public String getNumericFunctions() throws SQLException
Index: src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java,v
retrieving revision 1.55
diff -c -r1.55 DatabaseMetaData.java
*** src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java    11 Jun 2002 02:55:16 -0000    1.55
--- src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java    19 Jun 2002 06:10:06 -0000
***************
*** 409,415 ****
      public String getSQLKeywords() throws SQLException
      {
          if (Driver.logDebug) Driver.debug("getSQLKeyWords");
!         return
"abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version";
      }
  
      public String getNumericFunctions() throws SQLException
--- 409,415 ----
      public String getSQLKeywords() throws SQLException
      {
          if (Driver.logDebug) Driver.debug("getSQLKeyWords");
!         return
"abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiter,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version";
      }
  
      public String getNumericFunctions() throws SQLException
Index: src/test/locale/de_DE.ISO8859-1/test-de-upper.sql.in
===================================================================
RCS file: /cvsroot/pgsql/src/test/locale/de_DE.ISO8859-1/test-de-upper.sql.in,v
retrieving revision 1.1
diff -c -r1.1 test-de-upper.sql.in
*** src/test/locale/de_DE.ISO8859-1/test-de-upper.sql.in    19 Mar 2000 19:52:43 -0000    1.1
--- src/test/locale/de_DE.ISO8859-1/test-de-upper.sql.in    19 Jun 2002 06:10:07 -0000
***************
*** 1,4 ****
! COPY wordlist FROM stdin USING DELIMITERS '|';
  AAA           |���
  AAAAa         |�����
  BBBB          |BBBB
--- 1,4 ----
! COPY wordlist FROM stdin WITH DELIMITER '|';
  AAA           |���
  AAAAa         |�����
  BBBB          |BBBB
Index: src/test/locale/de_DE.ISO8859-1/test-de.sql.in
===================================================================
RCS file: /cvsroot/pgsql/src/test/locale/de_DE.ISO8859-1/test-de.sql.in,v
retrieving revision 1.1
diff -c -r1.1 test-de.sql.in
*** src/test/locale/de_DE.ISO8859-1/test-de.sql.in    19 Mar 2000 19:52:43 -0000    1.1
--- src/test/locale/de_DE.ISO8859-1/test-de.sql.in    19 Jun 2002 06:10:07 -0000
***************
*** 1,4 ****
! COPY wordlist FROM stdin USING DELIMITERS '|';
  AAA           |���
  AAAAa         |�����
  BBBB          |BBBB
--- 1,4 ----
! COPY wordlist FROM stdin WITH DELIMITER '|';
  AAA           |���
  AAAAa         |�����
  BBBB          |BBBB
Index: src/test/locale/gr_GR.ISO8859-7/test-gr.sql.in
===================================================================
RCS file: /cvsroot/pgsql/src/test/locale/gr_GR.ISO8859-7/test-gr.sql.in,v
retrieving revision 1.1
diff -c -r1.1 test-gr.sql.in
*** src/test/locale/gr_GR.ISO8859-7/test-gr.sql.in    29 Mar 1999 08:59:54 -0000    1.1
--- src/test/locale/gr_GR.ISO8859-7/test-gr.sql.in    19 Jun 2002 06:10:07 -0000
***************
*** 1,4 ****
! COPY usastates FROM stdin USING DELIMITERS '|';
  AK|Alaska        |������
  WA|Washington    |������������
  OR|Oregon        |�������
--- 1,4 ----
! COPY usastates FROM stdin WITH DELIMITER '|';
  AK|Alaska        |������
  WA|Washington    |������������
  OR|Oregon        |�������
Index: src/test/locale/koi8-r/test-koi8.sql.in
===================================================================
RCS file: /cvsroot/pgsql/src/test/locale/koi8-r/test-koi8.sql.in,v
retrieving revision 1.1
diff -c -r1.1 test-koi8.sql.in
*** src/test/locale/koi8-r/test-koi8.sql.in    16 Jun 1998 06:41:58 -0000    1.1
--- src/test/locale/koi8-r/test-koi8.sql.in    19 Jun 2002 06:10:07 -0000
***************
*** 1,4 ****
! COPY usastates FROM stdin USING DELIMITERS '|';
  AK|Alaska        |������
  WA|Washington    |���������
  OR|Oregon        |������
--- 1,4 ----
! COPY usastates FROM stdin WITH DELIMITER '|';
  AK|Alaska        |������
  WA|Washington    |���������
  OR|Oregon        |������
Index: src/test/locale/koi8-to-win1251/test-koi8.sql.in
===================================================================
RCS file: /cvsroot/pgsql/src/test/locale/koi8-to-win1251/test-koi8.sql.in,v
retrieving revision 1.1
diff -c -r1.1 test-koi8.sql.in
*** src/test/locale/koi8-to-win1251/test-koi8.sql.in    29 Mar 1999 09:00:08 -0000    1.1
--- src/test/locale/koi8-to-win1251/test-koi8.sql.in    19 Jun 2002 06:10:07 -0000
***************
*** 1,4 ****
! COPY usastates FROM stdin USING DELIMITERS '|';
  AK|Alaska        |������
  WA|Washington    |���������
  OR|Oregon        |������
--- 1,4 ----
! COPY usastates FROM stdin WITH DELIMITER '|';
  AK|Alaska        |������
  WA|Washington    |���������
  OR|Oregon        |������

pgsql-patches by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: [Fwd: contrib/showguc (was Re: [HACKERS] revised sample
Next
From: "Christopher Kings-Lynne"
Date:
Subject: Re: Dependency / Constraint patch