diff -durpN pgsql.orig/doc/src/sgml/ref/copy.sgml pgsql/doc/src/sgml/ref/copy.sgml
--- pgsql.orig/doc/src/sgml/ref/copy.sgml	2006-04-22 05:03:11.000000000 +0200
+++ pgsql/doc/src/sgml/ref/copy.sgml	2006-08-26 18:52:00.000000000 +0200
@@ -33,7 +33,7 @@ COPY <replaceable class="parameter">tabl
                 [ 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> [, ...] ) ]
+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 ]
@@ -55,7 +55,9 @@ COPY <replaceable class="parameter">tabl
    <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
+   <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>
@@ -65,7 +67,10 @@ COPY <replaceable class="parameter">tabl
    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.
+   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>
@@ -148,7 +153,7 @@ COPY <replaceable class="parameter">tabl
      <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.)
+      have OIDs, or in the case of COPY (SELECT) TO.)
      </para>
     </listitem>
    </varlistentry>
diff -durpN pgsql.orig/src/backend/commands/copy.c pgsql/src/backend/commands/copy.c
--- pgsql.orig/src/backend/commands/copy.c	2006-07-29 23:44:01.000000000 +0200
+++ pgsql/src/backend/commands/copy.c	2006-08-26 18:52:00.000000000 +0200
@@ -33,6 +33,7 @@
 #include "miscadmin.h"
 #include "parser/parse_relation.h"
 #include "rewrite/rewriteHandler.h"
+#include "optimizer/planner.h"
 #include "storage/fd.h"
 #include "tcop/tcopprot.h"
 #include "utils/acl.h"
@@ -96,9 +97,24 @@ typedef struct CopyStateData
 	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	rel;			/* relation to copy to or from */
+	Plan		*plan;			/* plan for SELECT */
+	DestReceiver	*dest;			/*  */
+	QueryDesc	*desc;			/* descriptor for SELECT query */
 	List	   *attnumlist;		/* integer list of attnums to copy */
 	bool		binary;			/* binary format? */
 	bool		oids;			/* include OIDs? */
@@ -153,6 +169,11 @@ typedef struct CopyStateData
 
 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.
@@ -223,8 +244,11 @@ static const char BinarySignature[11] = 
 
 
 /* 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);
@@ -239,8 +263,9 @@ static Datum CopyReadBinaryAttribute(Cop
 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 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);
@@ -697,17 +722,7 @@ uint64
 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));
@@ -783,19 +798,19 @@ DoCopy(const CopyStmt *stmt)
 		}
 		else if (strcmp(defel->defname, "force_quote") == 0)
 		{
-			if (force_quote)
+			if (cstate->force_quote_orig)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("conflicting or redundant options")));
-			force_quote = (List *) defel->arg;
+			cstate->force_quote_orig = (List *) defel->arg;
 		}
 		else if (strcmp(defel->defname, "force_notnull") == 0)
 		{
-			if (force_notnull)
+			if (cstate->force_notnull)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("conflicting or redundant options")));
-			force_notnull = (List *) defel->arg;
+			cstate->force_notnull = (List *) defel->arg;
 		}
 		else
 			elog(ERROR, "option \"%s\" not recognized",
@@ -888,21 +903,21 @@ DoCopy(const CopyStmt *stmt)
 				 errmsg("COPY escape must be a single character")));
 
 	/* Check force_quote */
-	if (!cstate->csv_mode && force_quote != NIL)
+	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 (force_quote != NIL && is_from)
+	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 && force_notnull != NIL)
+	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 (force_notnull != NIL && !is_from)
+	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")));
@@ -920,12 +935,36 @@ DoCopy(const CopyStmt *stmt)
 				(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,
-							  (is_from ? RowExclusiveLock : AccessShareLock));
+							  (stmt->is_from ? RowExclusiveLock : AccessShareLock));
 
 	/* check read-only transaction */
-	if (XactReadOnly && is_from &&
+	if (XactReadOnly && stmt->is_from &&
 		!isTempNamespace(RelationGetNamespace(cstate->rel)))
 		ereport(ERROR,
 				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
@@ -937,12 +976,6 @@ DoCopy(const CopyStmt *stmt)
 	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)
@@ -952,16 +985,16 @@ DoCopy(const CopyStmt *stmt)
 						RelationGetRelationName(cstate->rel))));
 
 	/* Generate or convert list of attributes to process */
-	cstate->attnumlist = CopyGetAttnums(cstate->rel, attnamelist);
+	cstate->attnumlist = CopyGetAttnums(RelationGetDescr(cstate->rel), attnamelist);
 
 	/* Convert FORCE QUOTE name list to column numbers, check validity */
-	if (force_quote)
+	if (cstate->force_quote_orig)
 	{
 		TupleDesc	tupDesc = RelationGetDescr(cstate->rel);
 		Form_pg_attribute *attr = tupDesc->attrs;
 		ListCell   *cur;
 
-		cstate->force_quote_atts = CopyGetAttnums(cstate->rel, force_quote);
+		cstate->force_quote_atts = CopyGetAttnums(tupDesc, cstate->force_quote_orig);
 
 		foreach(cur, cstate->force_quote_atts)
 		{
@@ -976,14 +1009,13 @@ DoCopy(const CopyStmt *stmt)
 	}
 
 	/* Convert FORCE NOT NULL name list to column numbers, check validity */
-	if (force_notnull)
+	if (cstate->force_notnull)
 	{
 		TupleDesc	tupDesc = RelationGetDescr(cstate->rel);
 		Form_pg_attribute *attr = tupDesc->attrs;
 		ListCell   *cur;
 
-		cstate->force_notnull_atts = CopyGetAttnums(cstate->rel,
-													force_notnull);
+		cstate->force_notnull_atts = CopyGetAttnums(tupDesc, cstate->force_notnull);
 
 		foreach(cur, cstate->force_notnull_atts)
 		{
@@ -1019,7 +1051,7 @@ DoCopy(const CopyStmt *stmt)
 
 	cstate->copy_dest = COPY_FILE;		/* default */
 
-	if (is_from)
+	if (stmt->is_from)
 	{							/* copy from file to database */
 		if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
 		{
@@ -1149,7 +1181,7 @@ DoCopy(const CopyStmt *stmt)
 	 * 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));
+	heap_close(cstate->rel, (stmt->is_from ? NoLock : AccessShareLock));
 
 	/* Clean up storage (probably not really necessary) */
 	processed = cstate->processed;
@@ -1163,6 +1195,167 @@ DoCopy(const CopyStmt *stmt)
 }
 
 
+static uint64
+DoCopySelect(const CopyStmt *stmt, CopyState cstate)
+{
+	DR_copy	   *copyDR;
+	char	   *filename = stmt->filename;
+	bool		pipe = (stmt->filename == NULL);
+	List	   *attnamelist = stmt->attlist;
+	uint64		processed = 0;
+
+	/* 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")));
+
+	/* Define executor destination */
+	cstate->dest = CreateDestReceiver(DestCopyDR, NULL);
+	copyDR = (DR_copy *)cstate->dest;
+	copyDR->cstate = cstate;
+	cstate->plan = planner(stmt->query, true, 0, NULL); 
+	cstate->desc = CreateQueryDesc(stmt->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);
+	pfree(cstate);
+
+	return processed;
+}
+
+
 /*
  * This intermediate routine just exists to localize the effects of setjmp
  * so we don't need to plaster a lot of variables with "volatile".
@@ -1193,58 +1386,74 @@ DoCopyTo(CopyState cstate)
 	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 relation TO file.
+ * Copy from select or 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;
+	if (cstate->rel)
+		tupDesc = cstate->rel->rd_att;
+	else
+		tupDesc = cstate->desc->tupDesc;
 	attr = tupDesc->attrs;
 	num_phys_attrs = tupDesc->natts;
-	attr_count = list_length(cstate->attnumlist);
-	null_print_client = cstate->null_print;		/* default */
+	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. */
-	out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
-	force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool));
+	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);
-		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;
+			cstate->force_quote[attnum - 1] = true;
 		else
-			force_quote[attnum - 1] = false;
+			cstate->force_quote[attnum - 1] = false;
 	}
 
 	/*
@@ -1253,7 +1462,7 @@ CopyTo(CopyState cstate)
 	 * 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,
+	cstate->rowcxt = AllocSetContextCreate(CurrentMemoryContext,
 									  "COPY TO",
 									  ALLOCSET_DEFAULT_MINSIZE,
 									  ALLOCSET_DEFAULT_INITSIZE,
@@ -1282,7 +1491,7 @@ CopyTo(CopyState cstate)
 		 * 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_client = pg_server_to_client(cstate->null_print,
 													cstate->null_print_len);
 
 		/* if a header has been requested send the line */
@@ -1309,100 +1518,30 @@ CopyTo(CopyState cstate)
 		}
 	}
 
-	scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
-
-	while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+	if (cstate->rel)
 	{
-		bool		need_delim = false;
-
-		CHECK_FOR_INTERRUPTS();
-
-		MemoryContextReset(mycontext);
-		oldcontext = MemoryContextSwitchTo(mycontext);
+		HeapScanDesc	scandesc;
+		HeapTuple	tuple;
+	
+		scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
 
-		if (cstate->binary)
+		while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
 		{
-			/* 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);
+			Oid oid = InvalidOid;
 
-				/* 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 */
+			CHECK_FOR_INTERRUPTS();
+		
 			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;
+				oid = HeapTupleGetOid(tuple);
 
-					outputbytes = SendFunctionCall(&out_functions[attnum - 1],
-												   value);
-					CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
-					CopySendData(cstate, VARDATA(outputbytes),
-								 VARSIZE(outputbytes) - VARHDRSZ);
-				}
-			}
+			heap_deform_tuple(tuple, tupDesc, cstate->values, cstate->isnull);
+			CopyValuesTo(cstate, oid, cstate->values, cstate->isnull);
 		}
 
-		CopySendEndOfRow(cstate);
-
-		MemoryContextSwitchTo(oldcontext);
-		
-		cstate->processed++;
+		heap_endscan(scandesc);
 	}
-
-	heap_endscan(scandesc);
+	else
+		ExecutorRun(cstate->desc, ForwardScanDirection, 0);
 
 	if (cstate->binary)
 	{
@@ -1412,10 +1551,12 @@ CopyTo(CopyState cstate)
 		CopySendEndOfRow(cstate);
 	}
 
-	MemoryContextDelete(mycontext);
+	MemoryContextDelete(cstate->rowcxt);
 
-	pfree(out_functions);
-	pfree(force_quote);
+	pfree(cstate->out_functions);
+	pfree(cstate->force_quote);
+	pfree(cstate->values);
+	pfree(cstate->isnull);
 }
 
 
@@ -3057,14 +3198,13 @@ CopyAttributeOutCSV(CopyState cstate, ch
  * columns).
  */
 static List *
-CopyGetAttnums(Relation rel, List *attnamelist)
+CopyGetAttnums(TupleDesc tupDesc, 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;
@@ -3085,15 +3225,25 @@ CopyGetAttnums(Relation rel, List *attna
 		{
 			char	   *name = strVal(lfirst(l));
 			int			attnum;
+			int			i;
 
 			/* Lookup column name */
-			/* Note we disallow system columns here */
-			attnum = attnameAttNum(rel, name, false);
+			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\" of relation \"%s\" does not exist",
-								name, RelationGetRelationName(rel))));
+						 errmsg("column \"%s\" does not exist",
+								name)));
 			/* Check for duplicates */
 			if (list_member_int(attnums, attnum))
 				ereport(ERROR,
@@ -3106,3 +3256,163 @@ CopyGetAttnums(Relation rel, List *attna
 
 	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;
+}
diff -durpN pgsql.orig/src/backend/parser/analyze.c pgsql/src/backend/parser/analyze.c
--- pgsql.orig/src/backend/parser/analyze.c	2006-08-25 11:17:25.000000000 +0200
+++ pgsql/src/backend/parser/analyze.c	2006-08-26 18:52:00.000000000 +0200
@@ -35,6 +35,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parse_type.h"
+#include "parser/parser.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/acl.h"
@@ -117,6 +118,7 @@ static Query *transformCreateStmt(ParseS
 					List **extras_before, List **extras_after);
 static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
 						List **extras_before, List **extras_after);
+static Query *transformCopyStmt(ParseState *pstate, CopyStmt *stmt);
 static void transformColumnDefinition(ParseState *pstate,
 						  CreateStmtContext *cxt,
 						  ColumnDef *column);
@@ -389,6 +391,9 @@ transformStmt(ParseState *pstate, Node *
 											(DeclareCursorStmt *) parseTree);
 			break;
 
+		case T_CopyStmt:
+			result = transformCopyStmt(pstate, (CopyStmt *) parseTree);
+			break;
 		default:
 
 			/*
@@ -3800,3 +3805,78 @@ check_parameter_resolution_walker(Node *
 	return expression_tree_walker(node, check_parameter_resolution_walker,
 								  (void *) context);
 }
+
+static Query *
+transformCopyStmt(ParseState *pstate, CopyStmt *stmt)
+{
+	Query	   *result;
+	Oid		relOid;
+	char	   *query_tpl = "SELECT * FROM %s";
+	char	   *query_str;
+	char	   *relname;
+	size_t		len;
+	List	   *querylist_raw;
+
+	if (stmt->relation)
+	{
+		relOid = RangeVarGetRelid(stmt->relation, false);
+		if (OidIsValid(relOid))
+		{
+			relname = get_rel_name(relOid);
+			if (get_rel_relkind(relOid) == RELKIND_VIEW)
+			{
+				if (stmt->is_from)
+					ereport(ERROR,
+							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+							 errmsg("cannot copy to view \"%s\"",
+								 		relname)));
+				len = strlen(query_tpl) + strlen(relname);
+				query_str = palloc(len);
+				snprintf(query_str, len, query_tpl, relname);
+				querylist_raw = raw_parser(query_str);
+				if (list_length(querylist_raw) != 1)
+					elog(ERROR, "parsing \"SELECT * FROM %s\" results more than one parse tree",
+						relname);
+
+				stmt->selectstmt = linitial(querylist_raw);
+
+				pfree(relname);
+				pfree(query_str);
+				pfree(stmt->relation);
+				stmt->relation = NULL;
+			}
+		}
+	}
+
+	if (stmt->selectstmt)
+	{
+		ListCell   *locking;
+
+		if (stmt->is_from)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("COPY (SELECT) FROM is not supported")));
+
+		if (stmt->selectstmt->into)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("COPY (SELECT INTO) TO is not supported")));
+
+		foreach(locking, stmt->selectstmt->lockingClause)
+		{
+			LockingClause	*n = lfirst(locking);
+			if (n->forUpdate)
+				ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("COPY (SELECT FOR UPDATE) TO is not supported")));
+				
+		}
+	}
+
+	result = makeNode(Query);
+	result->commandType = CMD_UTILITY;
+	result->utilityStmt = (Node *) stmt;
+
+	return result;
+}
+
diff -durpN pgsql.orig/src/backend/parser/gram.y pgsql/src/backend/parser/gram.y
--- pgsql.orig/src/backend/parser/gram.y	2006-08-25 11:17:26.000000000 +0200
+++ pgsql/src/backend/parser/gram.y	2006-08-26 18:52:00.000000000 +0200
@@ -174,6 +174,8 @@ static void doNegateFloat(Value *v);
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select values_clause
 
+%type <node>	copy_source_target
+
 %type <node>	alter_column_default opclass_item alter_using
 %type <ival>	add_drop
 
@@ -1621,11 +1623,10 @@ ClosePortalStmt:
  *
  *****************************************************************************/
 
-CopyStmt:	COPY opt_binary qualified_name opt_column_list opt_oids
+CopyStmt:	COPY opt_binary copy_source_target opt_column_list opt_oids
 			copy_from copy_file_name copy_delimiter opt_with copy_opt_list
 				{
-					CopyStmt *n = makeNode(CopyStmt);
-					n->relation = $3;
+					CopyStmt *n = (CopyStmt *)$3;
 					n->attlist = $4;
 					n->is_from = $6;
 					n->filename = $7;
@@ -1644,6 +1645,22 @@ CopyStmt:	COPY opt_binary qualified_name
 				}
 		;
 
+
+copy_source_target:
+			qualified_name
+				{
+					CopyStmt *n = makeNode(CopyStmt);
+					n->relation = $1;
+					$$ = (Node *)n;
+				}
+			| '(' select_no_parens ')'
+				{
+					CopyStmt *n = makeNode(CopyStmt);
+					n->selectstmt = $2;
+					$$ = (Node *)n;
+				}
+		;
+
 copy_from:
 			FROM									{ $$ = TRUE; }
 			| TO									{ $$ = FALSE; }
diff -durpN pgsql.orig/src/backend/tcop/dest.c pgsql/src/backend/tcop/dest.c
--- pgsql.orig/src/backend/tcop/dest.c	2006-08-17 23:55:34.000000000 +0200
+++ pgsql/src/backend/tcop/dest.c	2006-08-26 18:52:00.000000000 +0200
@@ -30,6 +30,7 @@
 
 #include "access/printtup.h"
 #include "access/xact.h"
+#include "commands/copy.h"
 #include "executor/executor.h"
 #include "executor/tstoreReceiver.h"
 #include "libpq/libpq.h"
@@ -117,6 +118,9 @@ CreateDestReceiver(CommandDest dest, Por
 		case DestSPI:
 			return &spi_printtupDR;
 
+		case DestCopyDR:
+			return CreateCopyDestReceiver();
+
 		case DestTuplestore:
 			if (portal == NULL)
 				elog(ERROR, "no portal specified for DestTuplestore receiver");
@@ -153,6 +157,7 @@ EndCommand(const char *commandTag, Comma
 		case DestSPI:
 		case DestTuplestore:
 		case DestIntoRel:
+		case DestCopyDR:
 			break;
 	}
 }
@@ -192,6 +197,7 @@ NullCommand(CommandDest dest)
 		case DestSPI:
 		case DestTuplestore:
 		case DestIntoRel:
+		case DestCopyDR:
 			break;
 	}
 }
@@ -233,6 +239,7 @@ ReadyForQuery(CommandDest dest)
 		case DestSPI:
 		case DestTuplestore:
 		case DestIntoRel:
+		case DestCopyDR:
 			break;
 	}
 }
diff -durpN pgsql.orig/src/backend/tcop/utility.c pgsql/src/backend/tcop/utility.c
--- pgsql.orig/src/backend/tcop/utility.c	2006-08-25 11:17:27.000000000 +0200
+++ pgsql/src/backend/tcop/utility.c	2006-08-26 18:52:00.000000000 +0200
@@ -49,6 +49,7 @@
 #include "rewrite/rewriteRemove.h"
 #include "storage/fd.h"
 #include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/guc.h"
@@ -632,7 +633,19 @@ ProcessUtility(Node *parsetree,
 
 		case T_CopyStmt:
 			{
-				uint64	processed = DoCopy((CopyStmt *) parsetree);
+				CopyStmt *stmt = (CopyStmt *) parsetree;
+				uint64	processed;
+
+				if (stmt->selectstmt)
+				{
+					List	*querylist;
+					querylist = pg_analyze_and_rewrite((Node *)stmt->selectstmt, NULL, NULL, 0);
+					if (list_length(querylist) != 1)
+						elog(ERROR, "pg_analyze_and_rewrite produced more than one queries");
+					stmt->query = (Query *) linitial(querylist);
+				}
+
+				processed = DoCopy(stmt);
 
 				if (completionTag)
 					snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
diff -durpN pgsql.orig/src/bin/psql/copy.c pgsql/src/bin/psql/copy.c
--- pgsql.orig/src/bin/psql/copy.c	2006-06-14 20:19:17.000000000 +0200
+++ pgsql/src/bin/psql/copy.c	2006-08-26 21:21:20.000000000 +0200
@@ -36,7 +36,7 @@
  * -- parses \copy command line
  *
  * The documented preferred syntax is:
- *	\copy tablename [(columnlist)] from|to filename
+ *	\copy { tablename | viewname | ( select stmt ) } [(columnlist)] from|to filename
  *	  [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
  *
  * The pre-7.3 syntax was:
@@ -142,6 +142,27 @@ parse_slash_copy(const char *args)
 
 	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)
diff -durpN pgsql.orig/src/include/commands/copy.h pgsql/src/include/commands/copy.h
--- pgsql.orig/src/include/commands/copy.h	2006-03-05 16:58:55.000000000 +0100
+++ pgsql/src/include/commands/copy.h	2006-08-26 18:52:00.000000000 +0200
@@ -15,8 +15,10 @@
 #define COPY_H
 
 #include "nodes/parsenodes.h"
+#include "tcop/dest.h"
 
 
+extern DestReceiver *CreateCopyDestReceiver(void);
 extern uint64 DoCopy(const CopyStmt *stmt);
 
 #endif   /* COPY_H */
diff -durpN pgsql.orig/src/include/nodes/parsenodes.h pgsql/src/include/nodes/parsenodes.h
--- pgsql.orig/src/include/nodes/parsenodes.h	2006-08-25 11:17:28.000000000 +0200
+++ pgsql/src/include/nodes/parsenodes.h	2006-08-26 18:52:00.000000000 +0200
@@ -1018,6 +1018,8 @@ typedef struct CopyStmt
 {
 	NodeTag		type;
 	RangeVar   *relation;		/* the relation to copy */
+	SelectStmt *selectstmt;		/* the SELECT to copy */
+	Query	   *query;		/* transformed SELECT */
 	List	   *attlist;		/* List of column names (as Strings), or NIL
 								 * for all columns */
 	bool		is_from;		/* TO or FROM */
diff -durpN pgsql.orig/src/include/tcop/dest.h pgsql/src/include/tcop/dest.h
--- pgsql.orig/src/include/tcop/dest.h	2006-08-17 23:55:40.000000000 +0200
+++ pgsql/src/include/tcop/dest.h	2006-08-26 18:52:00.000000000 +0200
@@ -85,7 +85,8 @@ typedef enum
 	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) */
+	DestIntoRel,					/* results sent to relation (SELECT INTO) */
+	DestCopyDR					/* results sent to file */
 } CommandDest;
 
 /* ----------------
diff -durpN pgsql.orig/src/test/regress/expected/alter_table.out pgsql/src/test/regress/expected/alter_table.out
--- pgsql.orig/src/test/regress/expected/alter_table.out	2006-08-22 07:39:24.000000000 +0200
+++ pgsql/src/test/regress/expected/alter_table.out	2006-08-26 18:52:00.000000000 +0200
@@ -947,9 +947,9 @@ alter table test drop a;
 copy test to stdout;
 2	3
 copy test(a) to stdout;
-ERROR:  column "a" of relation "test" does not exist
+ERROR:  column "a" does not exist
 copy test("........pg.dropped.1........") to stdout;
-ERROR:  column "........pg.dropped.1........" of relation "test" does not exist
+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"
@@ -968,9 +968,9 @@ select * from test;
 (2 rows)
 
 copy test(a) from stdin;
-ERROR:  column "a" of relation "test" does not exist
+ERROR:  column "a" does not exist
 copy test("........pg.dropped.1........") from stdin;
-ERROR:  column "........pg.dropped.1........" of relation "test" does not exist
+ERROR:  column "........pg.dropped.1........" does not exist
 copy test(b,c) from stdin;
 select * from test;
  b  | c  
diff -durpN pgsql.orig/src/test/regress/expected/copy2.out pgsql/src/test/regress/expected/copy2.out
--- pgsql.orig/src/test/regress/expected/copy2.out	2006-02-27 17:09:50.000000000 +0100
+++ pgsql/src/test/regress/expected/copy2.out	2006-08-26 18:52:00.000000000 +0200
@@ -28,7 +28,7 @@ COPY x (b, d) from stdin;
 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
+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
diff -durpN pgsql.orig/src/test/regress/expected/copyselect.out pgsql/src/test/regress/expected/copyselect.out
--- pgsql.orig/src/test/regress/expected/copyselect.out	1970-01-01 01:00:00.000000000 +0100
+++ pgsql/src/test/regress/expected/copyselect.out	2006-08-26 21:24:52.000000000 +0200
@@ -0,0 +1,122 @@
+--
+-- Test cases for COPY (select) TO
+--
+create table test1 (id serial, t text);
+NOTICE:  CREATE TABLE will create implicit sequence "test1_id_seq" for serial column "test1.id"
+create view v_test1
+as select 'v_'||t from test1;
+insert into test1 (t) values ('a');
+insert into test1 (t) values ('b');
+insert into test1 (t) values ('c');
+insert into test1 (t) values ('d');
+insert into test1 (t) values ('e');
+create table test2 (id serial, t text);
+NOTICE:  CREATE TABLE will create implicit sequence "test2_id_seq" for serial column "test2.id"
+insert into test2 (t) values ('A');
+insert into test2 (t) values ('B');
+insert into test2 (t) values ('C');
+insert into test2 (t) values ('D');
+insert into test2 (t) values ('E');
+--
+-- Test COPY table TO
+--
+copy test1 to stdout;
+1	a
+2	b
+3	c
+4	d
+5	e
+--
+-- Test COPY view TO
+--
+copy v_test1 to stdout;
+v_a
+v_b
+v_c
+v_d
+v_e
+--
+-- Test COPY (select) TO
+--
+copy (select t from test1 where id=1) to stdout;
+a
+--
+-- Test COPY (select FOR SHARE) TO
+--
+copy (select t from test1 where id=2 for share) to stdout;
+b
+--
+-- This should fail
+--
+copy (select t from test1 where id=3 for update) to stdout;
+ERROR:  COPY (SELECT FOR UPDATE) TO is not supported
+--
+-- This should fail
+--
+copy v_test1 from stdin;
+ERROR:  cannot copy to view "v_test1"
+--
+-- This should fail
+--
+copy (select * from test1) from stdin;
+ERROR:  COPY (SELECT) FROM is not supported
+--
+-- Test JOIN
+--
+copy (select * from test1 join test2 using (id)) to stdout;
+1	a	A
+2	b	B
+3	c	C
+4	d	D
+5	e	E
+--
+-- Test UNION SELECT
+--
+copy (select t from test1 where id = 1 UNION select * from v_test1) to stdout;
+a
+v_a
+v_b
+v_c
+v_d
+v_e
+--
+-- Test subselect
+--
+copy (select * from
+	(select t from test1 where id = 1 UNION select * from v_test1) t1)
+to stdout;
+a
+v_a
+v_b
+v_c
+v_d
+v_e
+--
+-- Test headers, CSV and quotes
+--
+copy (select t from test1 where id = 1) to stdout csv header force quote t;
+t
+"a"
+--
+-- Test psql builtins
+--
+\copy test1 to stdout
+1	a
+2	b
+3	c
+4	d
+5	e
+\copy v_test1 to stdout
+v_a
+v_b
+v_c
+v_d
+v_e
+\copy (select "id",'id','id""'||t,(id + 1)*id,t,"test1"."t" from test1 where id=3) to stdout
+3	id	id""c	12	c	c
+--
+-- Drop everything
+--
+drop table test2;
+drop view v_test1;
+drop table test1;
diff -durpN pgsql.orig/src/test/regress/parallel_schedule pgsql/src/test/regress/parallel_schedule
--- pgsql.orig/src/test/regress/parallel_schedule	2006-08-17 23:55:47.000000000 +0200
+++ pgsql/src/test/regress/parallel_schedule	2006-08-26 18:52:00.000000000 +0200
@@ -34,7 +34,7 @@ test: create_function_2
 # execute two copy tests parallel, to check that copy itself
 # is concurrent safe.
 # ----------
-test: copy
+test: copy copyselect
 
 # ----------
 # The third group of parallel test
diff -durpN pgsql.orig/src/test/regress/sql/copyselect.sql pgsql/src/test/regress/sql/copyselect.sql
--- pgsql.orig/src/test/regress/sql/copyselect.sql	1970-01-01 01:00:00.000000000 +0100
+++ pgsql/src/test/regress/sql/copyselect.sql	2006-08-26 21:16:55.000000000 +0200
@@ -0,0 +1,75 @@
+--
+-- Test cases for COPY (select) TO
+--
+create table test1 (id serial, t text);
+create view v_test1
+as select 'v_'||t from test1;
+insert into test1 (t) values ('a');
+insert into test1 (t) values ('b');
+insert into test1 (t) values ('c');
+insert into test1 (t) values ('d');
+insert into test1 (t) values ('e');
+create table test2 (id serial, t text);
+insert into test2 (t) values ('A');
+insert into test2 (t) values ('B');
+insert into test2 (t) values ('C');
+insert into test2 (t) values ('D');
+insert into test2 (t) values ('E');
+--
+-- Test COPY table TO
+--
+copy test1 to stdout;
+--
+-- Test COPY view TO
+--
+copy v_test1 to stdout;
+--
+-- Test COPY (select) TO
+--
+copy (select t from test1 where id=1) to stdout;
+--
+-- Test COPY (select FOR SHARE) TO
+--
+copy (select t from test1 where id=2 for share) to stdout;
+--
+-- This should fail
+--
+copy (select t from test1 where id=3 for update) to stdout;
+--
+-- This should fail
+--
+copy v_test1 from stdin;
+--
+-- This should fail
+--
+copy (select * from test1) from stdin;
+--
+-- Test JOIN
+--
+copy (select * from test1 join test2 using (id)) to stdout;
+--
+-- Test UNION SELECT
+--
+copy (select t from test1 where id = 1 UNION select * from v_test1) to stdout;
+--
+-- Test subselect
+--
+copy (select * from
+	(select t from test1 where id = 1 UNION select * from v_test1) t1)
+to stdout;
+--
+-- Test headers, CSV and quotes
+--
+copy (select t from test1 where id = 1) to stdout csv header force quote t;
+--
+-- Test psql builtins
+--
+\copy test1 to stdout
+\copy v_test1 to stdout
+\copy (select "id",'id','id""'||t,(id + 1)*id,t,"test1"."t" from test1 where id=3) to stdout
+--
+-- Drop everything
+--
+drop table test2;
+drop view v_test1;
+drop table test1;
