Removed Cc: to pgsql-hackers.

Zoltán,

Zoltan Boszormenyi wrote:

> >Your patch has been added to the PostgreSQL unapplied patches list at:
> >
> >     http://momjian.postgresql.org/cgi-bin/pgpatches
> >
> >It will be applied as soon as one of the PostgreSQL committers reviews
> >and approves it.
> 
> Thanks. Would you please add this instead?
> psql built-in \copy (select ...) now also work.


Please check this one out.  I took the version you posted here and
changed the stuff in the parser that I didn't like, and removed the ugly
"SELECT * FROM" stuff that was bothering me.  I also removed the
transformCopyStmt stuff as it seems unnecessary to me.  I did all that
stuff in a cleaner way (IMO).

I also cleaned up the grammar -- basically added a separate case from
the regular COPY.  I took the opportunity to remove the
backwards-compatible options from there.  I didn't check that stuff very
much but it should continue to work ...

I noticed that this works:

alvherre=# copy (values (1, 'uno'), (2, 'dos'), (3, 'tr;es'), (4, NULL)) to 
stdout with delimiter ';' null 'NUL' csv quote as '"';
1;uno
2;dos
3;"tr;es"
4;NUL

which is nice.


With this patch, the COPY view FROM stdout path now throws an error --
in your version it worked (because of that "COPY * FROM" stuff), and
from previous discussion it seems reasonable to behave differently for
views than for plain tables (i.e. it's reasonable that we fail for
views).
 
I also broke the check for a FOR UPDATE clause.  Not sure where but it
must be easy to fix :-)  I'd do it myself but I'm heading to bed right
now.

I also wanted to check these hunks in your patch, which I didn't like
very much:

-ERROR:  column "a" of relation "test" does not exist
+ERROR:  column "a" does not exist

but didn't got around to it.

I also noticed that the new copyselect regression test is not added to
the serial schedule.

I'll repost a reworked version at some point, if no one beats me to it.

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Index: doc/src/sgml/ref/copy.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/copy.sgml,v
retrieving revision 1.74
diff -c -p -r1.74 copy.sgml
*** doc/src/sgml/ref/copy.sgml  22 Apr 2006 03:03:11 -0000      1.74
--- doc/src/sgml/ref/copy.sgml  27 Aug 2006 04:53:19 -0000
***************
*** 1,5 ****
  <!--
! $PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.73 2006/03/03 19:54:10 tgl 
Exp $
  PostgreSQL documentation
  -->
  
--- 1,5 ----
  <!--
! $PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.74 2006-04-22 03:03:11 
momjian Exp $
  PostgreSQL documentation
  -->
  
*************** COPY <replaceable class="parameter">tabl
*** 33,39 ****
                  [ 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 }
      [ [ WITH ] 
            [ BINARY ]
--- 33,39 ----
                  [ ESCAPE [ AS ] '<replaceable 
class="parameter">escape</replaceable>' ]
                  [ FORCE NOT NULL <replaceable 
class="parameter">column</replaceable> [, ...] ]
  
! COPY { <replaceable class="parameter">tablename</replaceable> | <replaceable 
class="parameter">viewname</replaceable> | ( select_statement ) } [ ( 
<replaceable class="parameter">column</replaceable> [, ...] ) ]
      TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
      [ [ WITH ] 
            [ BINARY ]
*************** COPY <replaceable class="parameter">tabl
*** 55,61 ****
     <command>COPY</command> moves data between
     <productname>PostgreSQL</productname> tables and standard file-system
     files. <command>COPY TO</command> copies the 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>
--- 55,63 ----
     <command>COPY</command> moves data between
     <productname>PostgreSQL</productname> tables and standard file-system
     files. <command>COPY TO</command> copies the contents of a table
!    <emphasis>to</> a file, which also work on views and arbitrary
!    SELECT statements. (Internally, the view case is rewitten as
!    <command>SELECT * FROM viewname</command>.) <command>COPY FROM</command> 
copies
     data <emphasis>from</> a file to a table (appending the data to
     whatever is in the table already).
    </para>
*************** COPY <replaceable class="parameter">tabl
*** 65,71 ****
     only copy the data in the specified columns to or from the file.
     If there are any columns in the table that are not in the column list,
     <command>COPY FROM</command> will insert the default values for
!    those columns.
    </para>
  
    <para>
--- 67,76 ----
     only copy the data in the specified columns to or from the file.
     If there are any columns in the table that are not in the column list,
     <command>COPY FROM</command> will insert the default values for
!    those columns. <command>COPY TO</command> also accepts the list of
!    columns, which can be useful for exporting only a subset of the
!    columns that are in the table, view or select statement, or
!    reordering them in the export.
    </para>
  
    <para>
*************** COPY <replaceable class="parameter">tabl
*** 148,154 ****
       <para>
        Specifies copying the OID for each row.  (An error is raised if
        <literal>OIDS</literal> is specified for a table that does not
!       have OIDs.)
       </para>
      </listitem>
     </varlistentry>
--- 153,159 ----
       <para>
        Specifies copying the OID for each row.  (An error is raised if
        <literal>OIDS</literal> is specified for a table that does not
!       have OIDs, or in the case of COPY (SELECT) TO.)
       </para>
      </listitem>
     </varlistentry>
Index: src/backend/commands/copy.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.268
diff -c -p -r1.268 copy.c
*** src/backend/commands/copy.c 14 Jul 2006 14:52:18 -0000      1.268
--- src/backend/commands/copy.c 28 Aug 2006 03:12:07 -0000
***************
*** 31,38 ****
  #include "libpq/pqformat.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
! #include "parser/parse_relation.h"
  #include "rewrite/rewriteHandler.h"
  #include "storage/fd.h"
  #include "tcop/tcopprot.h"
  #include "utils/acl.h"
--- 31,39 ----
  #include "libpq/pqformat.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
! #include "parser/analyze.h"
  #include "rewrite/rewriteHandler.h"
+ #include "optimizer/planner.h"
  #include "storage/fd.h"
  #include "tcop/tcopprot.h"
  #include "utils/acl.h"
*************** typedef struct CopyStateData
*** 96,104 ****
        bool            need_transcoding;               /* client encoding diff 
from server? */
        bool            encoding_embeds_ascii;  /* ASCII can be non-first byte? 
*/
        uint64          processed;              /* # of tuples processed */
  
        /* parameters from the COPY command */
!       Relation        rel;                    /* relation to copy to or from 
*/
        List       *attnumlist;         /* integer list of attnums to copy */
        bool            binary;                 /* binary format? */
        bool            oids;                   /* include OIDs? */
--- 97,125 ----
        bool            need_transcoding;               /* client encoding diff 
from server? */
        bool            encoding_embeds_ascii;  /* ASCII can be non-first byte? 
*/
        uint64          processed;              /* # of tuples processed */
+       FmgrInfo   *out_functions;              /* array of output functions 
(COPY TO) */
+       MemoryContext rowcxt;                   /* memory for output formatting 
*/
+       List       *force_quote_orig;           /* quote attribute names */
+       List       *force_notnull;
+       bool       *force_quote;                /* quote attribute? */
+       char       *null_print_client;          /* final version of null_print 
*/
+       Datum      *values;                     /* row data (COPY TO) */
+       bool       *isnull;                     /* row 'isnull' flags (COPY TO) 
*/
+       Oid                     out_func_oid;
+       bool            isvarlena;              /* tuple should be de-toasted */
+       TupleDesc       attrinfo;
+       int                     natts;
  
        /* parameters from the COPY command */
!       /* relation to copy to or from, in the "COPY table" case */
!       Relation        rel;
! 
!       /* The following are valid in the "COPY (SELECT)" case: */
!       DestReceiver    *dest;          /* DestReceiver for the COPY (SELECT) 
case */
!       QueryDesc       *desc;                  /* descriptor for SELECT query 
*/
!       Plan            *plan;                  /* plan for SELECT */
! 
!       /* These are valid in both cases */
        List       *attnumlist;         /* integer list of attnums to copy */
        bool            binary;                 /* binary format? */
        bool            oids;                   /* include OIDs? */
*************** typedef struct CopyStateData
*** 153,158 ****
--- 174,184 ----
  
  typedef CopyStateData *CopyState;
  
+ typedef struct
+ {
+       DestReceiver pub;       /* publicly-known function pointers */
+       void    *cstate;        /* CopyStateData we are working with */
+ } DR_copy;
  
  /*
   * These macros centralize code used to process line_buf and raw_buf buffers.
*************** static const char BinarySignature[11] = 
*** 223,230 ****
--- 249,259 ----
  
  
  /* non-export function prototypes */
+ static uint64 DoCopyRelation(const CopyStmt *stmt, CopyState cstate);
+ static uint64 DoCopySelect(const CopyStmt *stmt, CopyState cstate);
  static void DoCopyTo(CopyState cstate);
  static void CopyTo(CopyState cstate);
+ static void CopyValuesTo(CopyState cstate, Oid oid, Datum *values, bool 
*isnull);
  static void CopyFrom(CopyState cstate);
  static bool CopyReadLine(CopyState cstate);
  static bool CopyReadLineText(CopyState cstate);
*************** static Datum CopyReadBinaryAttribute(Cop
*** 239,246 ****
  static void CopyAttributeOutText(CopyState cstate, char *string);
  static void CopyAttributeOutCSV(CopyState cstate, char *string,
                                        bool use_quote, bool single_attr);
! static List *CopyGetAttnums(Relation rel, List *attnamelist);
  static char *limit_printout_length(const char *str);
  
  /* Low-level communications functions */
  static void SendCopyBegin(CopyState cstate);
--- 268,276 ----
  static void CopyAttributeOutText(CopyState cstate, char *string);
  static void CopyAttributeOutCSV(CopyState cstate, char *string,
                                        bool use_quote, bool single_attr);
! static List *CopyGetAttnums(TupleDesc tupDesc, List *attnamelist);
  static char *limit_printout_length(const char *str);
+ static void prepare_cstate_info(CopyState cstate, TupleDesc typeinfo, int 
numAttrs);
  
  /* Low-level communications functions */
  static void SendCopyBegin(CopyState cstate);
*************** uint64
*** 697,713 ****
  DoCopy(const CopyStmt *stmt)
  {
        CopyState       cstate;
-       RangeVar   *relation = stmt->relation;
-       char       *filename = stmt->filename;
-       bool            is_from = stmt->is_from;
-       bool            pipe = (stmt->filename == NULL);
-       List       *attnamelist = stmt->attlist;
-       List       *force_quote = NIL;
-       List       *force_notnull = NIL;
-       AclMode         required_access = (is_from ? ACL_INSERT : ACL_SELECT);
-       AclResult       aclresult;
        ListCell   *option;
-       uint64          processed;
  
        /* Allocate workspace and zero all fields */
        cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
--- 727,733 ----
*************** DoCopy(const CopyStmt *stmt)
*** 783,801 ****
                }
                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",
--- 803,821 ----
                }
                else if (strcmp(defel->defname, "force_quote") == 0)
                {
!                       if (cstate->force_quote_orig)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or 
redundant options")));
!                       cstate->force_quote_orig = (List *) defel->arg;
                }
                else if (strcmp(defel->defname, "force_notnull") == 0)
                {
!                       if (cstate->force_notnull)
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or 
redundant options")));
!                       cstate->force_notnull = (List *) defel->arg;
                }
                else
                        elog(ERROR, "option \"%s\" not recognized",
*************** DoCopy(const CopyStmt *stmt)
*** 888,908 ****
                                 errmsg("COPY escape must be a single 
character")));
  
        /* Check force_quote */
!       if (!cstate->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 (!cstate->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")));
--- 908,928 ----
                                 errmsg("COPY escape must be a single 
character")));
  
        /* Check force_quote */
!       if (!cstate->csv_mode && cstate->force_quote != NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("COPY force quote available only in CSV 
mode")));
!       if (cstate->force_quote != NULL && stmt->is_from)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("COPY force quote only available using 
COPY TO")));
  
        /* Check force_notnull */
!       if (!cstate->csv_mode && cstate->force_notnull != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("COPY force not null available only in 
CSV mode")));
!       if (cstate->force_notnull != NIL && !stmt->is_from)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("COPY force not null only available using COPY 
FROM")));
*************** DoCopy(const CopyStmt *stmt)
*** 920,931 ****
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("CSV quote character must not appear in 
the NULL specification")));
  
        /* Open and lock the relation, using the appropriate lock type. */
        cstate->rel = heap_openrv(relation,
!                                                         (is_from ? 
RowExclusiveLock : AccessShareLock));
  
        /* check read-only transaction */
!       if (XactReadOnly && is_from &&
                !isTempNamespace(RelationGetNamespace(cstate->rel)))
                ereport(ERROR,
                                (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
--- 940,975 ----
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("CSV quote character must not appear in 
the NULL specification")));
  
+       if (stmt->filename != NULL && !superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to COPY to or from a 
file"),
+                                errhint("Anyone can COPY to stdout or from 
stdin. "
+                                                "psql's \\copy command also 
works for anyone.")));
+ 
+       if (stmt->relation)
+               return DoCopyRelation(stmt, cstate);
+       else
+               return DoCopySelect(stmt, cstate);
+ }
+ 
+ static uint64
+ DoCopyRelation(const CopyStmt *stmt, CopyState cstate)
+ {
+       RangeVar   *relation = stmt->relation;
+       char       *filename = stmt->filename;
+       bool            pipe = (stmt->filename == NULL);
+       List       *attnamelist = stmt->attlist;
+       AclMode         required_access = (stmt->is_from ? ACL_INSERT : 
ACL_SELECT);
+       AclResult       aclresult;
+       uint64          processed;
+ 
        /* Open and lock the relation, using the appropriate lock type. */
        cstate->rel = heap_openrv(relation,
!                                                         (stmt->is_from ? 
RowExclusiveLock : AccessShareLock));
  
        /* check read-only transaction */
!       if (XactReadOnly && stmt->is_from &&
                !isTempNamespace(RelationGetNamespace(cstate->rel)))
                ereport(ERROR,
                                (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
*************** DoCopy(const CopyStmt *stmt)
*** 937,948 ****
        if (aclresult != ACLCHECK_OK)
                aclcheck_error(aclresult, ACL_KIND_CLASS,
                                           
RelationGetRelationName(cstate->rel));
-       if (!pipe && !superuser())
-               ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("must be superuser to COPY to or from a 
file"),
-                                errhint("Anyone can COPY to stdout or from 
stdin. "
-                                                "psql's \\copy command also 
works for anyone.")));
  
        /* Don't allow COPY w/ OIDs to or from a table without them */
        if (cstate->oids && !cstate->rel->rd_rel->relhasoids)
--- 981,986 ----
*************** DoCopy(const CopyStmt *stmt)
*** 952,967 ****
                                                
RelationGetRelationName(cstate->rel))));
  
        /* Generate or convert list of attributes to process */
!       cstate->attnumlist = CopyGetAttnums(cstate->rel, attnamelist);
  
        /* Convert FORCE QUOTE name list to column numbers, check validity */
!       if (force_quote)
        {
                TupleDesc       tupDesc = RelationGetDescr(cstate->rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                ListCell   *cur;
  
!               cstate->force_quote_atts = CopyGetAttnums(cstate->rel, 
force_quote);
  
                foreach(cur, cstate->force_quote_atts)
                {
--- 990,1005 ----
                                                
RelationGetRelationName(cstate->rel))));
  
        /* Generate or convert list of attributes to process */
!       cstate->attnumlist = CopyGetAttnums(RelationGetDescr(cstate->rel), 
attnamelist);
  
        /* Convert FORCE QUOTE name list to column numbers, check validity */
!       if (cstate->force_quote_orig)
        {
                TupleDesc       tupDesc = RelationGetDescr(cstate->rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                ListCell   *cur;
  
!               cstate->force_quote_atts = CopyGetAttnums(tupDesc, 
cstate->force_quote_orig);
  
                foreach(cur, cstate->force_quote_atts)
                {
*************** DoCopy(const CopyStmt *stmt)
*** 976,989 ****
        }
  
        /* Convert FORCE NOT NULL name list to column numbers, check validity */
!       if (force_notnull)
        {
                TupleDesc       tupDesc = RelationGetDescr(cstate->rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                ListCell   *cur;
  
!               cstate->force_notnull_atts = CopyGetAttnums(cstate->rel,
!                                                                               
                        force_notnull);
  
                foreach(cur, cstate->force_notnull_atts)
                {
--- 1014,1026 ----
        }
  
        /* Convert FORCE NOT NULL name list to column numbers, check validity */
!       if (cstate->force_notnull)
        {
                TupleDesc       tupDesc = RelationGetDescr(cstate->rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                ListCell   *cur;
  
!               cstate->force_notnull_atts = CopyGetAttnums(tupDesc, 
cstate->force_notnull);
  
                foreach(cur, cstate->force_notnull_atts)
                {
*************** DoCopy(const CopyStmt *stmt)
*** 1019,1025 ****
  
        cstate->copy_dest = COPY_FILE;          /* default */
  
!       if (is_from)
        {                                                       /* copy from 
file to database */
                if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
                {
--- 1056,1062 ----
  
        cstate->copy_dest = COPY_FILE;          /* default */
  
!       if (stmt->is_from)
        {                                                       /* copy from 
file to database */
                if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
                {
*************** DoCopy(const CopyStmt *stmt)
*** 1149,1159 ****
         * got; if writing, we should hold the lock until end of transaction to
         * ensure that updates will be committed before lock is released.
         */
!       heap_close(cstate->rel, (is_from ? NoLock : AccessShareLock));
  
        /* Clean up storage (probably not really necessary) */
        processed = cstate->processed;
  
        pfree(cstate->attribute_buf.data);
        pfree(cstate->line_buf.data);
        pfree(cstate->raw_buf);
--- 1186,1372 ----
         * got; if writing, we should hold the lock until end of transaction to
         * ensure that updates will be committed before lock is released.
         */
!       heap_close(cstate->rel, (stmt->is_from ? NoLock : AccessShareLock));
! 
!       /* Clean up storage (probably not really necessary) */
!       processed = cstate->processed;
! 
!       pfree(cstate->attribute_buf.data);
!       pfree(cstate->line_buf.data);
!       pfree(cstate->raw_buf);
!       pfree(cstate);
! 
!       return processed;
! }
! 
! static uint64
! DoCopySelect(const CopyStmt *stmt, CopyState cstate)
! {
!       char       *filename = stmt->filename;
!       bool            pipe = (stmt->filename == NULL);
!       List       *attnamelist = stmt->attlist;
!       uint64          processed = 0;
!       List       *queries;
!       Query      *query;
! 
!       /* Don't allow COPY w/ OIDs from a select */
!       if (cstate->oids)
!               ereport(ERROR,
!                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("COPY (SELECT) WITH OIDS is not 
supported")));
! 
!       queries = parse_analyze((Node *) stmt->selectstmt, NULL, NULL, 0);
! 
!       /* shouldn't happen */
!       if (list_length(queries) != 1)
!               elog(ERROR, "COPY (SELECT) returns more than one Query");
!       query = linitial(queries);
! 
!       /* pass the query through the rewriter */
!       queries = QueryRewrite(query);
! 
!       /* shouldn't happen, or could it? */
!       if (list_length(queries) != 1)
!               elog(ERROR, "COPY (SELECT) returns more than one Query after 
rewrite");
!       query = linitial(queries);
! 
!       cstate->plan = planner(query, true, 0, NULL);
! 
!       cstate->dest = CreateDestReceiver(DestCopyDR, NULL);
!       ((DR_copy *) cstate->dest)->cstate = cstate;
!       cstate->desc = CreateQueryDesc(query, cstate->plan,
!                                                                  
ActiveSnapshot, InvalidSnapshot,
!                                                                  
cstate->dest, NULL, false);
!       /* Execute query */
!       ExecutorStart(cstate->desc, false);
! 
!       /* Generate or convert list of attributes to process */
!       cstate->attnumlist = CopyGetAttnums(cstate->desc->tupDesc, attnamelist);
! 
!       /* Convert FORCE QUOTE name list to column numbers, check validity */
!       if (cstate->force_quote_orig)
!       {
!               TupleDesc       tupDesc = cstate->desc->tupDesc;
!               Form_pg_attribute *attr = tupDesc->attrs;
!               ListCell   *cur;
! 
!               cstate->force_quote_atts = CopyGetAttnums(tupDesc, 
cstate->force_quote_orig);
! 
!               foreach(cur, cstate->force_quote_atts)
!               {
!                       int                     attnum = lfirst_int(cur);
! 
!                       if (!list_member_int(cstate->attnumlist, attnum))
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                                  errmsg("FORCE QUOTE column \"%s\" not 
referenced by COPY",
!                                                 NameStr(attr[attnum - 
1]->attname))));
!               }
!       }
! 
!       /* Convert FORCE NOT NULL name list to column numbers, check validity */
!       if (cstate->force_notnull)
!       {
!               TupleDesc       tupDesc = cstate->desc->tupDesc;
!               Form_pg_attribute *attr = tupDesc->attrs;
!               ListCell   *cur;
! 
!               cstate->force_notnull_atts = CopyGetAttnums(tupDesc, 
cstate->force_notnull);
! 
!               foreach(cur, cstate->force_notnull_atts)
!               {
!                       int                     attnum = lfirst_int(cur);
! 
!                       if (!list_member_int(cstate->attnumlist, attnum))
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
!                               errmsg("FORCE NOT NULL column \"%s\" not 
referenced by COPY",
!                                          NameStr(attr[attnum - 
1]->attname))));
!               }
!       }
! 
!       /* Set up variables to avoid per-attribute overhead. */
!       initStringInfo(&cstate->attribute_buf);
!       initStringInfo(&cstate->line_buf);
!       cstate->line_buf_converted = false;
!       cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
!       cstate->raw_buf_index = cstate->raw_buf_len = 0;
!       cstate->processed = 0;
! 
!       /*
!        * Set up encoding conversion info.  Even if the client and server
!        * encodings are the same, we must apply pg_client_to_server() to
!        * validate data in multibyte encodings.
!        */
!       cstate->client_encoding = pg_get_client_encoding();
!       cstate->need_transcoding =
!               (cstate->client_encoding != GetDatabaseEncoding() ||
!                pg_database_encoding_max_length() > 1);
!       /* See Multibyte encoding comment above */
!       cstate->encoding_embeds_ascii = 
PG_ENCODING_IS_CLIENT_ONLY(cstate->client_encoding);
! 
!       cstate->copy_dest = COPY_FILE;          /* default */
! 
!       /* copy from database to file */
!       if (pipe)
!       {
!               if (whereToSendOutput == DestRemote)
!                       cstate->fe_copy = true;
!               else
!                       cstate->copy_file = stdout;
!       }
!       else
!       {
!               mode_t          oumask; /* Pre-existing umask value */
!               struct stat st;
! 
!               /*
!                * Prevent write to relative path ... too easy to shoot oneself 
in
!                * the foot by overwriting a database file ...
!                */
!               if (!is_absolute_path(filename))
!                       ereport(ERROR,
!                                       (errcode(ERRCODE_INVALID_NAME),
!                                         errmsg("relative path not allowed for 
COPY to file")));
! 
!               oumask = umask((mode_t) 022);
!               cstate->copy_file = AllocateFile(filename, PG_BINARY_W);
!               umask(oumask);
! 
!               if (cstate->copy_file == NULL)
!                       ereport(ERROR,
!                                       (errcode_for_file_access(),
!                                        errmsg("could not open file \"%s\" for 
writing: %m",
!                                                       filename)));
! 
!               fstat(fileno(cstate->copy_file), &st);
!               if (S_ISDIR(st.st_mode))
!               {
!                       FreeFile(cstate->copy_file);
!                       ereport(ERROR,
!                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                                        errmsg("\"%s\" is a directory", 
filename)));
!               }
!       }
! 
!       DoCopyTo(cstate);
! 
!       if (!pipe)
!       {
!               /* we assume only the write case could fail here */
!               if (FreeFile(cstate->copy_file))
!                       ereport(ERROR,
!                                       (errcode_for_file_access(),
!                                        errmsg("could not write to file 
\"%s\": %m",
!                                                       filename)));
!       }
  
        /* Clean up storage (probably not really necessary) */
        processed = cstate->processed;
  
+       ExecutorEnd(cstate->desc);
+       FreeQueryDesc(cstate->desc);
+ 
        pfree(cstate->attribute_buf.data);
        pfree(cstate->line_buf.data);
        pfree(cstate->raw_buf);
*************** DoCopyTo(CopyState cstate)
*** 1193,1250 ****
        PG_END_TRY();
  }
  
  /*
!  * Copy from relation TO file.
   */
  static void
  CopyTo(CopyState cstate)
  {
-       HeapTuple       tuple;
        TupleDesc       tupDesc;
-       HeapScanDesc scandesc;
        int                     num_phys_attrs;
-       int                     attr_count;
        Form_pg_attribute *attr;
-       FmgrInfo   *out_functions;
-       bool       *force_quote;
-       char       *string;
-       char       *null_print_client;
        ListCell   *cur;
-       MemoryContext oldcontext;
-       MemoryContext mycontext;
  
!       tupDesc = cstate->rel->rd_att;
        attr = tupDesc->attrs;
        num_phys_attrs = tupDesc->natts;
!       attr_count = list_length(cstate->attnumlist);
!       null_print_client = cstate->null_print;         /* default */
  
        /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
        cstate->fe_msgbuf = makeStringInfo();
  
        /* Get info about the columns we need to process. */
!       out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
!       force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool));
        foreach(cur, cstate->attnumlist)
        {
                int                     attnum = lfirst_int(cur);
-               Oid                     out_func_oid;
-               bool            isvarlena;
- 
-               if (cstate->binary)
-                       getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
-                                                                       
&out_func_oid,
-                                                                       
&isvarlena);
-               else
-                       getTypeOutputInfo(attr[attnum - 1]->atttypid,
-                                                         &out_func_oid,
-                                                         &isvarlena);
-               fmgr_info(out_func_oid, &out_functions[attnum - 1]);
  
                if (list_member_int(cstate->force_quote_atts, attnum))
!                       force_quote[attnum - 1] = true;
                else
!                       force_quote[attnum - 1] = false;
        }
  
        /*
--- 1406,1479 ----
        PG_END_TRY();
  }
  
+ static void
+ prepare_cstate_info(CopyState cstate, TupleDesc typeinfo, int numAttrs)
+ {
+       Form_pg_attribute *attr;
+       ListCell   *cur;
+ 
+       /* get rid of any old data */
+       attr = typeinfo->attrs;
+       cstate->attrinfo = typeinfo;
+       cstate->natts = numAttrs;
+       if (numAttrs <= 0)
+               return;
+ 
+       foreach(cur, cstate->attnumlist)
+       {
+               int                     attnum = lfirst_int(cur);
+ 
+               if (cstate->binary)
+                       getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
+                                                                       
&cstate->out_func_oid,
+                                                                       
&cstate->isvarlena);
+               else
+                       getTypeOutputInfo(attr[attnum - 1]->atttypid,
+                                                         &cstate->out_func_oid,
+                                                         &cstate->isvarlena);
+               fmgr_info(cstate->out_func_oid, &cstate->out_functions[attnum - 
1]);
+       }
+ }
+ 
  /*
!  * Copy from select or relation TO file.
   */
  static void
  CopyTo(CopyState cstate)
  {
        TupleDesc       tupDesc;
        int                     num_phys_attrs;
        Form_pg_attribute *attr;
        ListCell   *cur;
  
!       if (cstate->rel)
!               tupDesc = cstate->rel->rd_att;
!       else
!               tupDesc = cstate->desc->tupDesc;
        attr = tupDesc->attrs;
        num_phys_attrs = tupDesc->natts;
!       cstate->null_print_client = cstate->null_print;         /* default */
  
        /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
        cstate->fe_msgbuf = makeStringInfo();
  
        /* Get info about the columns we need to process. */
!       cstate->out_functions = (FmgrInfo *) palloc(num_phys_attrs * 
sizeof(FmgrInfo));
!       cstate->force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool));
! 
!       cstate->isnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
!       cstate->values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
! 
!       prepare_cstate_info(cstate, tupDesc, tupDesc->natts);
! 
        foreach(cur, cstate->attnumlist)
        {
                int                     attnum = lfirst_int(cur);
  
                if (list_member_int(cstate->force_quote_atts, attnum))
!                       cstate->force_quote[attnum - 1] = true;
                else
!                       cstate->force_quote[attnum - 1] = false;
        }
  
        /*
*************** CopyTo(CopyState cstate)
*** 1253,1259 ****
         * datatype output routines, and should be faster than retail pfree's
         * anyway.      (We don't need a whole econtext as CopyFrom does.)
         */
!       mycontext = AllocSetContextCreate(CurrentMemoryContext,
                                                                          "COPY 
TO",
                                                                          
ALLOCSET_DEFAULT_MINSIZE,
                                                                          
ALLOCSET_DEFAULT_INITSIZE,
--- 1482,1488 ----
         * datatype output routines, and should be faster than retail pfree's
         * anyway.      (We don't need a whole econtext as CopyFrom does.)
         */
!       cstate->rowcxt = AllocSetContextCreate(CurrentMemoryContext,
                                                                          "COPY 
TO",
                                                                          
ALLOCSET_DEFAULT_MINSIZE,
                                                                          
ALLOCSET_DEFAULT_INITSIZE,
*************** CopyTo(CopyState cstate)
*** 1282,1288 ****
                 * encoding, because it will be sent directly with 
CopySendString.
                 */
                if (cstate->need_transcoding)
!                       null_print_client = 
pg_server_to_client(cstate->null_print,
                                                                                
                        cstate->null_print_len);
  
                /* if a header has been requested send the line */
--- 1511,1517 ----
                 * encoding, because it will be sent directly with 
CopySendString.
                 */
                if (cstate->need_transcoding)
!                       cstate->null_print_client = 
pg_server_to_client(cstate->null_print,
                                                                                
                        cstate->null_print_len);
  
                /* if a header has been requested send the line */
*************** CopyTo(CopyState cstate)
*** 1309,1408 ****
                }
        }
  
!       scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
! 
!       while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
        {
!               bool            need_delim = false;
! 
!               CHECK_FOR_INTERRUPTS();
  
!               MemoryContextReset(mycontext);
!               oldcontext = MemoryContextSwitchTo(mycontext);
! 
!               if (cstate->binary)
                {
!                       /* Binary per-tuple header */
!                       CopySendInt16(cstate, attr_count);
!                       /* Send OID if wanted --- note attr_count doesn't 
include it */
!                       if (cstate->oids)
!                       {
!                               Oid                     oid = 
HeapTupleGetOid(tuple);
  
!                               /* Hack --- assume Oid is same size as int32 */
!                               CopySendInt32(cstate, sizeof(int32));
!                               CopySendInt32(cstate, oid);
!                       }
!               }
!               else
!               {
!                       /* Text format has no per-tuple header, but send OID if 
wanted */
!                       /* Assume digits don't need any quoting or encoding 
conversion */
                        if (cstate->oids)
!                       {
!                               string = 
DatumGetCString(DirectFunctionCall1(oidout,
!                                                                 
ObjectIdGetDatum(HeapTupleGetOid(tuple))));
!                               CopySendString(cstate, string);
!                               need_delim = true;
!                       }
!               }
! 
!               foreach(cur, cstate->attnumlist)
!               {
!                       int                     attnum = lfirst_int(cur);
!                       Datum           value;
!                       bool            isnull;
! 
!                       value = heap_getattr(tuple, attnum, tupDesc, &isnull);
! 
!                       if (!cstate->binary)
!                       {
!                               if (need_delim)
!                                       CopySendChar(cstate, cstate->delim[0]);
!                               need_delim = true;
!                       }
  
!                       if (isnull)
!                       {
!                               if (!cstate->binary)
!                                       CopySendString(cstate, 
null_print_client);
!                               else
!                                       CopySendInt32(cstate, -1);
!                       }
!                       else
!                       {
!                               if (!cstate->binary)
!                               {
!                                       string = 
OutputFunctionCall(&out_functions[attnum - 1],
!                                                                               
                value);
!                                       if (cstate->csv_mode)
!                                               CopyAttributeOutCSV(cstate, 
string,
!                                                                               
        force_quote[attnum - 1],
!                                                                               
        list_length(cstate->attnumlist) == 1);
!                                       else
!                                               CopyAttributeOutText(cstate, 
string);
!                               }
!                               else
!                               {
!                                       bytea      *outputbytes;
! 
!                                       outputbytes = 
SendFunctionCall(&out_functions[attnum - 1],
!                                                                               
                   value);
!                                       CopySendInt32(cstate, 
VARSIZE(outputbytes) - VARHDRSZ);
!                                       CopySendData(cstate, 
VARDATA(outputbytes),
!                                                                
VARSIZE(outputbytes) - VARHDRSZ);
!                               }
!                       }
                }
  
!               CopySendEndOfRow(cstate);
! 
!               MemoryContextSwitchTo(oldcontext);
!               
!               cstate->processed++;
        }
! 
!       heap_endscan(scandesc);
  
        if (cstate->binary)
        {
--- 1538,1567 ----
                }
        }
  
!       if (cstate->rel)
        {
!               HeapScanDesc    scandesc;
!               HeapTuple       tuple;
!       
!               scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
  
!               while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) 
!= NULL)
                {
!                       Oid oid = InvalidOid;
  
!                       CHECK_FOR_INTERRUPTS();
!               
                        if (cstate->oids)
!                               oid = HeapTupleGetOid(tuple);
  
!                       heap_deform_tuple(tuple, tupDesc, cstate->values, 
cstate->isnull);
!                       CopyValuesTo(cstate, oid, cstate->values, 
cstate->isnull);
                }
  
!               heap_endscan(scandesc);
        }
!       else
!               ExecutorRun(cstate->desc, ForwardScanDirection, 0);
  
        if (cstate->binary)
        {
*************** CopyTo(CopyState cstate)
*** 1412,1421 ****
                CopySendEndOfRow(cstate);
        }
  
!       MemoryContextDelete(mycontext);
  
!       pfree(out_functions);
!       pfree(force_quote);
  }
  
  
--- 1571,1582 ----
                CopySendEndOfRow(cstate);
        }
  
!       MemoryContextDelete(cstate->rowcxt);
  
!       pfree(cstate->out_functions);
!       pfree(cstate->force_quote);
!       pfree(cstate->values);
!       pfree(cstate->isnull);
  }
  
  
*************** CopyAttributeOutCSV(CopyState cstate, ch
*** 3057,3070 ****
   * columns).
   */
  static List *
! CopyGetAttnums(Relation rel, List *attnamelist)
  {
        List       *attnums = NIL;
  
        if (attnamelist == NIL)
        {
                /* Generate default column list */
-               TupleDesc       tupDesc = RelationGetDescr(rel);
                Form_pg_attribute *attr = tupDesc->attrs;
                int                     attr_count = tupDesc->natts;
                int                     i;
--- 3218,3230 ----
   * columns).
   */
  static List *
! CopyGetAttnums(TupleDesc tupDesc, List *attnamelist)
  {
        List       *attnums = NIL;
  
        if (attnamelist == NIL)
        {
                /* Generate default column list */
                Form_pg_attribute *attr = tupDesc->attrs;
                int                     attr_count = tupDesc->natts;
                int                     i;
*************** CopyGetAttnums(Relation rel, List *attna
*** 3085,3099 ****
                {
                        char       *name = strVal(lfirst(l));
                        int                     attnum;
  
                        /* Lookup column name */
!                       /* Note we disallow system columns here */
!                       attnum = attnameAttNum(rel, name, false);
                        if (attnum == InvalidAttrNumber)
                                ereport(ERROR,
                                                
(errcode(ERRCODE_UNDEFINED_COLUMN),
!                                                errmsg("column \"%s\" of 
relation \"%s\" does not exist",
!                                                               name, 
RelationGetRelationName(rel))));
                        /* Check for duplicates */
                        if (list_member_int(attnums, attnum))
                                ereport(ERROR,
--- 3245,3269 ----
                {
                        char       *name = strVal(lfirst(l));
                        int                     attnum;
+                       int                     i;
  
                        /* Lookup column name */
!                       attnum = InvalidAttrNumber;
!                       for (i = 0; i < tupDesc->natts; i++)
!                       {
!                               if (tupDesc->attrs[i]->attisdropped)
!                                       continue;
!                               if (namestrcmp(&(tupDesc->attrs[i]->attname), 
name) == 0)
!                               {
!                                       attnum = tupDesc->attrs[i]->attnum;
!                                       break;
!                               }
!                       }
                        if (attnum == InvalidAttrNumber)
                                ereport(ERROR,
                                                
(errcode(ERRCODE_UNDEFINED_COLUMN),
!                                                errmsg("column \"%s\" does not 
exist",
!                                                               name)));
                        /* Check for duplicates */
                        if (list_member_int(attnums, attnum))
                                ereport(ERROR,
*************** CopyGetAttnums(Relation rel, List *attna
*** 3106,3108 ****
--- 3276,3438 ----
  
        return attnums;
  }
+ 
+ /*
+  * Copies data from 'values' array
+  */
+ static void 
+ CopyValuesTo(CopyState cstate, Oid oid, Datum *values, bool *isnull)
+ {
+       int attr_count;
+       char *string; 
+       ListCell *cur;
+       MemoryContext oldcontext;
+       bool need_delim = false;
+ 
+       Assert(cstate);
+       Assert(values);
+       Assert(isnull);
+ 
+       attr_count = list_length(cstate->attnumlist);
+ 
+       MemoryContextReset(cstate->rowcxt);
+       oldcontext = MemoryContextSwitchTo(cstate->rowcxt);
+ 
+       if (cstate->binary)
+       {
+               /* Binary per-tuple header */
+               CopySendInt16(cstate, attr_count);
+               /* Send OID if wanted --- note attr_count doesn't include it */
+               if (cstate->oids)
+                 {
+                       Assert(oid);
+                       /* Hack --- assume Oid is same size as int32 */
+                       CopySendInt32(cstate, sizeof(int32));
+                       CopySendInt32(cstate, oid);
+               }
+       }
+       else
+       {   
+               /* Text format has no per-tuple header, but send OID if wanted 
*/
+               /* Assume digits don't need any quoting or encoding conversion 
*/
+               if (cstate->oids)
+               {
+                       Assert(oid);
+                       string = DatumGetCString(DirectFunctionCall1(oidout,
+                                                ObjectIdGetDatum(oid)));
+                       CopySendString(cstate, string);
+                       need_delim = true;
+               }
+       }
+ 
+       foreach(cur, cstate->attnumlist)
+       {
+               int attnum = lfirst_int(cur);
+ 
+               if (!cstate->binary)
+               {
+                       if (need_delim)
+                               CopySendChar(cstate, cstate->delim[0]);
+                       need_delim = true;
+               }
+               if (isnull[attnum-1])
+               {
+                       if (!cstate->binary)
+                               CopySendString(cstate, 
cstate->null_print_client);
+                       else
+                               CopySendInt32(cstate, -1);
+               }
+               else
+               {
+                       if (!cstate->binary)
+                       {
+                               string = DatumGetCString(FunctionCall1(
+                                                       
&cstate->out_functions[attnum - 1],
+                                                       values[attnum - 1]));
+                               if (cstate->csv_mode)
+                                       CopyAttributeOutCSV(cstate, string, 
+                                                       
cstate->force_quote[attnum - 1],
+                                                       
list_length(cstate->attnumlist) == 1);
+                               else
+                                       CopyAttributeOutText(cstate, string);
+                       }
+                       else
+                       {   
+                               bytea      *outputbytes;
+ 
+                               outputbytes = 
SendFunctionCall(&cstate->out_functions[attnum - 1],
+                                                                       
values[attnum - 1]);
+ 
+                               /* We assume the result will not have been 
toasted */
+                               CopySendInt32(cstate, VARSIZE(outputbytes) - 
VARHDRSZ);
+                               CopySendData(cstate, VARDATA(outputbytes),
+                                                       VARSIZE(outputbytes) - 
VARHDRSZ);
+                       }
+               }
+       }
+       CopySendEndOfRow(cstate);
+       MemoryContextSwitchTo(oldcontext);
+ 
+       cstate->processed++;
+ }
+ 
+ 
+ /*
+  * Callback for executor destination receiver (COPY view TO)
+  */
+ static void
+ copy_dest_printtup(TupleTableSlot *slot, DestReceiver *self)
+ {
+       TupleDesc       typeinfo = slot->tts_tupleDescriptor;
+       DR_copy    *copyDR = (DR_copy *)self;
+       CopyState       cstate = copyDR->cstate;
+       int             natts = typeinfo->natts;
+ 
+       if (cstate->attrinfo != typeinfo ||cstate->natts != natts)
+               prepare_cstate_info(cstate, typeinfo, natts);
+ 
+       /* Make sure the tuple is fully deconstructed */
+       slot_getallattrs(slot);
+ 
+       if (slot->tts_tuple)
+       {
+               /* Executor returns standard tuple */
+               heap_deform_tuple(slot->tts_tuple, typeinfo, cstate->values, 
cstate->isnull);
+               CopyValuesTo(cstate, InvalidOid, cstate->values, 
cstate->isnull);
+       }
+       else
+               /* Executor returns "virtual" tuple */
+               CopyValuesTo(cstate, InvalidOid, slot->tts_values, 
slot->tts_isnull);
+ 
+ }
+ 
+ static void
+ copy_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
+ {
+       /* no-op */
+ }
+ 
+ static void
+ copy_shutdown(DestReceiver *self)
+ {
+       /* no-op */
+ }
+ 
+ static void
+ copy_destroy(DestReceiver *self)
+ {
+       /* no-op */
+ }
+ 
+ DestReceiver *
+ CreateCopyDestReceiver(void)
+ {
+       DR_copy *self = (DR_copy *) palloc(sizeof(DR_copy));
+ 
+       self->pub.receiveSlot = copy_dest_printtup;
+       self->pub.rStartup = copy_startup;
+       self->pub.rShutdown = copy_shutdown;
+       self->pub.rDestroy = copy_destroy;
+       self->pub.mydest = DestCopyDR;
+       return (DestReceiver *)self;
+ }
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.558
diff -c -p -r2.558 gram.y
*** src/backend/parser/gram.y   25 Aug 2006 04:06:51 -0000      2.558
--- src/backend/parser/gram.y   27 Aug 2006 05:33:07 -0000
*************** ClosePortalStmt:
*** 1614,1624 ****
  /*****************************************************************************
   *
   *            QUERY :
!  *                            COPY <relname> ['(' columnList ')'] 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_column_list opt_oids
--- 1614,1627 ----
  /*****************************************************************************
   *
   *            QUERY :
!  *                            COPY <relname> ['(' columnList ')'] FROM/TO 
<file> [WITH options]
   *
   *                            BINARY, OIDS, and DELIMITERS kept in old 
locations
   *                            for backward compatibility.  2002-06-18
   *
+  *                            COPY ( SELECT ... ) TO <file> [WITH options]
+  *                            This form doesn't have the backwards-compatible 
locations for options.
+  *
   
*****************************************************************************/
  
  CopyStmt:     COPY opt_binary qualified_name opt_column_list opt_oids
*************** CopyStmt:       COPY opt_binary qualified_name
*** 1626,1631 ****
--- 1629,1635 ----
                                {
                                        CopyStmt *n = makeNode(CopyStmt);
                                        n->relation = $3;
+                                       n->selectstmt = NULL;
                                        n->attlist = $4;
                                        n->is_from = $6;
                                        n->filename = $7;
*************** CopyStmt:       COPY opt_binary qualified_name
*** 1642,1647 ****
--- 1646,1664 ----
                                                n->options = 
list_concat(n->options, $10);
                                        $$ = (Node *)n;
                                }
+                       | COPY select_with_parens TO copy_file_name opt_with
+                         copy_opt_list
+                               {
+                                       CopyStmt *n = makeNode(CopyStmt);
+                                       n->relation = NULL;
+                                       n->selectstmt = (SelectStmt *) $2;
+                                       n->attlist = NIL;
+                                       n->is_from = false;
+                                       n->filename = $4;
+ 
+                                       n->options = $6;
+                                       $$ = (Node *)n;
+                               }
                ;
  
  copy_from:
*************** copy_from:
*** 1652,1658 ****
  /*
   * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
   * used depends on the direction. (It really doesn't make sense to copy from
!  * stdout. We silently correct the "typo".             - AY 9/94
   */
  copy_file_name:
                        Sconst                                                  
                { $$ = $1; }
--- 1669,1675 ----
  /*
   * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
   * used depends on the direction. (It really doesn't make sense to copy from
!  * stdout. We silently correct the "typo".)            - AY 9/94
   */
  copy_file_name:
                        Sconst                                                  
                { $$ = $1; }
Index: src/backend/tcop/dest.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/dest.c,v
retrieving revision 1.69
diff -c -p -r1.69 dest.c
*** src/backend/tcop/dest.c     12 Aug 2006 02:52:05 -0000      1.69
--- src/backend/tcop/dest.c     27 Aug 2006 04:53:19 -0000
***************
*** 30,35 ****
--- 30,36 ----
  
  #include "access/printtup.h"
  #include "access/xact.h"
+ #include "commands/copy.h"
  #include "executor/executor.h"
  #include "executor/tstoreReceiver.h"
  #include "libpq/libpq.h"
*************** CreateDestReceiver(CommandDest dest, Por
*** 117,122 ****
--- 118,126 ----
                case DestSPI:
                        return &spi_printtupDR;
  
+               case DestCopyDR:
+                       return CreateCopyDestReceiver();
+ 
                case DestTuplestore:
                        if (portal == NULL)
                                elog(ERROR, "no portal specified for 
DestTuplestore receiver");
*************** EndCommand(const char *commandTag, Comma
*** 153,158 ****
--- 157,163 ----
                case DestSPI:
                case DestTuplestore:
                case DestIntoRel:
+               case DestCopyDR:
                        break;
        }
  }
*************** NullCommand(CommandDest dest)
*** 192,197 ****
--- 197,203 ----
                case DestSPI:
                case DestTuplestore:
                case DestIntoRel:
+               case DestCopyDR:
                        break;
        }
  }
*************** ReadyForQuery(CommandDest dest)
*** 233,238 ****
--- 239,245 ----
                case DestSPI:
                case DestTuplestore:
                case DestIntoRel:
+               case DestCopyDR:
                        break;
        }
  }
Index: src/bin/psql/copy.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/psql/copy.c,v
retrieving revision 1.66
diff -c -p -r1.66 copy.c
*** src/bin/psql/copy.c 14 Jun 2006 16:49:02 -0000      1.66
--- src/bin/psql/copy.c 27 Aug 2006 04:53:19 -0000
***************
*** 36,42 ****
   * -- parses \copy command line
   *
   * The documented preferred syntax is:
!  *    \copy tablename [(columnlist)] from|to filename
   *      [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] 
string ]
   *
   * The pre-7.3 syntax was:
--- 36,42 ----
   * -- parses \copy command line
   *
   * The documented preferred syntax is:
!  *    \copy { tablename | viewname | ( select stmt ) } [(columnlist)] from|to 
filename
   *      [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] 
string ]
   *
   * The pre-7.3 syntax was:
*************** parse_slash_copy(const char *args)
*** 142,147 ****
--- 142,168 ----
  
        result->table = pg_strdup(token);
  
+       /* Handle COPY (SELECT) case */
+       if (token[0] == '(')
+       {
+               char    *selectstmt;
+               int     brackets = 1;
+ 
+               while (brackets > 0)
+               {
+                       token = strtokx(NULL, whitespace, ".,()", "\"'",
+                                               0, false, false, pset.encoding);
+                       if (!token)
+                               goto error;
+                       if (token[0] == '(')
+                               brackets++;
+                       else if (token[0] == ')')
+                               brackets--;
+                       xstrcat(&result->table, " ");
+                       xstrcat(&result->table, token);
+               }
+       }
+ 
        token = strtokx(NULL, whitespace, ".,()", "\"",
                                        0, false, false, pset.encoding);
        if (!token)
Index: src/include/commands/copy.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/commands/copy.h,v
retrieving revision 1.27
diff -c -p -r1.27 copy.h
*** src/include/commands/copy.h 5 Mar 2006 15:58:55 -0000       1.27
--- src/include/commands/copy.h 27 Aug 2006 04:53:19 -0000
***************
*** 7,13 ****
   * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
!  * $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.26 2006/03/03 19:54:10 
tgl Exp $
   *
   *-------------------------------------------------------------------------
   */
--- 7,13 ----
   * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
!  * $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.27 2006-03-05 15:58:55 
momjian Exp $
   *
   *-------------------------------------------------------------------------
   */
***************
*** 15,22 ****
--- 15,24 ----
  #define COPY_H
  
  #include "nodes/parsenodes.h"
+ #include "tcop/dest.h"
  
  
+ extern DestReceiver *CreateCopyDestReceiver(void);
  extern uint64 DoCopy(const CopyStmt *stmt);
  
  #endif   /* COPY_H */
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.325
diff -c -p -r1.325 parsenodes.h
*** src/include/nodes/parsenodes.h      25 Aug 2006 04:06:56 -0000      1.325
--- src/include/nodes/parsenodes.h      27 Aug 2006 05:26:28 -0000
*************** typedef struct GrantRoleStmt
*** 1017,1023 ****
--- 1017,1025 ----
  typedef struct CopyStmt
  {
        NodeTag         type;
+       /* one of the two following must be NULL and the other must not be: */
        RangeVar   *relation;           /* the relation to copy */
+       SelectStmt *selectstmt;         /* the SELECT to copy */
        List       *attlist;            /* List of column names (as Strings), 
or NIL
                                                                 * for all 
columns */
        bool            is_from;                /* TO or FROM */
Index: src/include/tcop/dest.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/tcop/dest.h,v
retrieving revision 1.51
diff -c -p -r1.51 dest.h
*** src/include/tcop/dest.h     12 Aug 2006 02:52:06 -0000      1.51
--- src/include/tcop/dest.h     27 Aug 2006 04:53:19 -0000
*************** typedef enum
*** 85,91 ****
        DestRemoteExecute,                      /* sent to frontend, in Execute 
command */
        DestSPI,                                        /* results sent to SPI 
manager */
        DestTuplestore,                         /* results sent to Tuplestore */
!       DestIntoRel                                     /* results sent to 
relation (SELECT INTO) */
  } CommandDest;
  
  /* ----------------
--- 85,92 ----
        DestRemoteExecute,                      /* sent to frontend, in Execute 
command */
        DestSPI,                                        /* results sent to SPI 
manager */
        DestTuplestore,                         /* results sent to Tuplestore */
!       DestIntoRel,                                    /* results sent to 
relation (SELECT INTO) */
!       DestCopyDR                                      /* results sent to file 
*/
  } CommandDest;
  
  /* ----------------
Index: src/interfaces/ecpg/test/Makefile.regress
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/interfaces/ecpg/test/Makefile.regress,v
retrieving revision 1.3
diff -c -p -r1.3 Makefile.regress
*** src/interfaces/ecpg/test/Makefile.regress   9 Aug 2006 22:48:17 -0000       
1.3
--- src/interfaces/ecpg/test/Makefile.regress   28 Aug 2006 02:50:19 -0000
***************
*** 1,4 ****
! override CPPFLAGS := -I$(srcdir)/../../include -I$(libpq_srcdir) $(CPPFLAGS) 
  override CFLAGS += $(PTHREAD_CFLAGS) 
  
  override LDFLAGS := -L../../ecpglib -L../../pgtypeslib -L../../../libpq 
$(LDFLAGS)
--- 1,4 ----
! override CPPFLAGS := -I$(srcdir)/../../include 
-I$(top_builddir)/$(subdir)/../../include -I$(libpq_srcdir) $(CPPFLAGS) 
  override CFLAGS += $(PTHREAD_CFLAGS) 
  
  override LDFLAGS := -L../../ecpglib -L../../pgtypeslib -L../../../libpq 
$(LDFLAGS)
Index: src/test/regress/parallel_schedule
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/test/regress/parallel_schedule,v
retrieving revision 1.34
diff -c -p -r1.34 parallel_schedule
*** src/test/regress/parallel_schedule  12 Aug 2006 02:52:06 -0000      1.34
--- src/test/regress/parallel_schedule  27 Aug 2006 04:53:19 -0000
*************** test: create_function_2
*** 34,40 ****
  # execute two copy tests parallel, to check that copy itself
  # is concurrent safe.
  # ----------
! test: copy
  
  # ----------
  # The third group of parallel test
--- 34,40 ----
  # execute two copy tests parallel, to check that copy itself
  # is concurrent safe.
  # ----------
! test: copy copyselect
  
  # ----------
  # The third group of parallel test
Index: src/test/regress/expected/alter_table.out
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/alter_table.out,v
retrieving revision 1.97
diff -c -p -r1.97 alter_table.out
*** src/test/regress/expected/alter_table.out   21 Aug 2006 00:57:26 -0000      
1.97
--- src/test/regress/expected/alter_table.out   27 Aug 2006 04:53:19 -0000
*************** alter table test drop a;
*** 947,955 ****
  copy test to stdout;
  2     3
  copy test(a) to stdout;
! ERROR:  column "a" of relation "test" does not exist
  copy test("........pg.dropped.1........") to stdout;
! ERROR:  column "........pg.dropped.1........" of relation "test" does not 
exist
  copy test from stdin;
  ERROR:  extra data after last expected column
  CONTEXT:  COPY test, line 1: "10      11      12"
--- 947,955 ----
  copy test to stdout;
  2     3
  copy test(a) to stdout;
! ERROR:  column "a" does not exist
  copy test("........pg.dropped.1........") to stdout;
! ERROR:  column "........pg.dropped.1........" does not exist
  copy test from stdin;
  ERROR:  extra data after last expected column
  CONTEXT:  COPY test, line 1: "10      11      12"
*************** select * from test;
*** 968,976 ****
  (2 rows)
  
  copy test(a) from stdin;
! ERROR:  column "a" of relation "test" does not exist
  copy test("........pg.dropped.1........") from stdin;
! ERROR:  column "........pg.dropped.1........" of relation "test" does not 
exist
  copy test(b,c) from stdin;
  select * from test;
   b  | c  
--- 968,976 ----
  (2 rows)
  
  copy test(a) from stdin;
! ERROR:  column "a" does not exist
  copy test("........pg.dropped.1........") from stdin;
! ERROR:  column "........pg.dropped.1........" does not exist
  copy test(b,c) from stdin;
  select * from test;
   b  | c  
Index: src/test/regress/expected/copy2.out
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/copy2.out,v
retrieving revision 1.25
diff -c -p -r1.25 copy2.out
*** src/test/regress/expected/copy2.out 27 Feb 2006 16:09:50 -0000      1.25
--- src/test/regress/expected/copy2.out 27 Aug 2006 04:53:19 -0000
*************** COPY x (b, d) from stdin;
*** 28,34 ****
  COPY x (a, b, c, d, e) from stdin;
  -- non-existent column in column list: should fail
  COPY x (xyz) from stdin;
! ERROR:  column "xyz" of relation "x" does not exist
  -- too many columns in column list: should fail
  COPY x (a, b, c, d, e, d, c) from stdin;
  ERROR:  column "d" specified more than once
--- 28,34 ----
  COPY x (a, b, c, d, e) from stdin;
  -- non-existent column in column list: should fail
  COPY x (xyz) from stdin;
! ERROR:  column "xyz" does not exist
  -- too many columns in column list: should fail
  COPY x (a, b, c, d, e, d, c) from stdin;
  ERROR:  column "d" specified more than once
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to