From a90db34b542d9f31582627a0bfa43901811b84b2 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Thu, 18 Jul 2002 04:43:51 +0000
Subject: [PATCH] The attached patch (against HEAD) implements

  COPY x (a,d,c,b) from stdin;
  COPY x (a,c) to stdout;

as well as the corresponding changes to pg_dump to use the new
functionality.  This functionality is not available when using
the BINARY option.  If a column is not specified in the COPY FROM
statement, its default values will be used.

In addition to this functionality, I tweaked a couple of the
error messages emitted by the new COPY <options> checks.

Brent Verner
---
 doc/src/sgml/ref/copy.sgml           |  24 ++-
 src/backend/commands/copy.c          | 218 +++++++++++++++++++++------
 src/backend/parser/gram.y            |  25 +--
 src/backend/rewrite/rewriteHandler.c |   5 +-
 src/bin/pg_dump/pg_dump.c            |  54 ++++++-
 src/include/nodes/parsenodes.h       |   3 +-
 src/include/rewrite/rewriteHandler.h |   4 +-
 src/test/regress/parallel_schedule   |   2 +-
 src/test/regress/serial_schedule     |   3 +-
 9 files changed, 267 insertions(+), 71 deletions(-)

diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 28ca264c65c..a1a5e9baa76 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.32 2002/06/20 16:00:43 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.33 2002/07/18 04:43:50 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -21,7 +21,8 @@ PostgreSQL documentation
    <date>1999-12-11</date>
   </refsynopsisdivinfo>
   <synopsis>
-COPY <replaceable class="parameter">table</replaceable>
+COPY <replaceable class="parameter">table</replaceable> 
+    [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
     FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
     [ [ WITH ] 
           [ BINARY ] 
@@ -29,6 +30,7 @@ COPY <replaceable class="parameter">table</replaceable>
           [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
           [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ] ]
 COPY <replaceable class="parameter">table</replaceable>
+    [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
     TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
     [ [ WITH ] 
           [ BINARY ]
@@ -55,6 +57,16 @@ COPY <replaceable class="parameter">table</replaceable>
        </para>
       </listitem>
      </varlistentry>
+     
+		 <varlistentry>
+      <term><replaceable class="parameter">column list</replaceable></term>
+      <listitem>
+       <para>
+	An optional list of columns to be copied.  If no column list is
+	specified, all columns will be used.
+       </para>
+      </listitem>
+     </varlistentry>
 
      <varlistentry>
       <term><replaceable class="parameter">filename</replaceable></term>
@@ -187,6 +199,14 @@ ERROR: <replaceable>reason</replaceable>
    whatever is in the table already).
   </para>
 
+	<para>
+   When using the optional column list syntax, <command>COPY TO</command> 
+	 and <command>COPY FROM</command> will only copy the specified
+	 columns' values to/from the table. If a column in the table
+	 is not in the column list, <command>COPY FROM</command> will insert 
+	 default values for that column if a default value is defined.
+  </para>
+
   <para>
    <command>COPY</command> with a file name instructs the
    <productname>PostgreSQL</productname> backend to directly read from
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 7410bff04b1..438126a3e18 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.158 2002/06/20 20:29:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.159 2002/07/18 04:43:50 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "commands/copy.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
+#include "rewrite/rewriteHandler.h"
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "tcop/pquery.h"
@@ -46,13 +47,14 @@
 
 
 /* non-export function prototypes */
-static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
-static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
+static void CopyTo(Relation rel, List *attlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
+static void CopyFrom(Relation rel, List *attlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
 static Oid	GetInputFunction(Oid type);
 static Oid	GetTypeElement(Oid type);
 static void CopyReadNewline(FILE *fp, int *newline);
 static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print);
 static void CopyAttributeOut(FILE *fp, char *string, char *delim);
+static void CopyAssertAttlist(Relation rel, List* attlist, bool from);
 
 static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
 
@@ -267,7 +269,8 @@ DoCopy(const CopyStmt *stmt)
 	char *filename = stmt->filename;
 	bool is_from = stmt->is_from;
 	bool pipe = (stmt->filename == NULL);
-	List	   *option;
+	List *option;
+	List *attlist = stmt->attlist;
 	DefElem    *dbinary = NULL;
 	DefElem    *doids = NULL;
 	DefElem    *ddelim = NULL;
@@ -289,25 +292,27 @@ DoCopy(const CopyStmt *stmt)
 		if (strcmp(defel->defname, "binary") == 0)
 		{
 			if (dbinary)
-				elog(ERROR, "COPY: conflicting options");
+				/* should this really be an error? */
+				elog(ERROR, "COPY: BINARY option appears more than once");
 			dbinary = defel;
 		}
 		else if (strcmp(defel->defname, "oids") == 0)
 		{
 			if (doids)
-				elog(ERROR, "COPY: conflicting options");
+				/* should this really be an error? */
+				elog(ERROR, "COPY: OIDS option appears more than once");
 			doids = defel;
 		}
 		else if (strcmp(defel->defname, "delimiter") == 0)
 		{
 			if (ddelim)
-				elog(ERROR, "COPY: conflicting options");
+				elog(ERROR, "COPY: DELIMITER string may only be defined once in query");
 			ddelim = defel;
 		}
 		else if (strcmp(defel->defname, "null") == 0)
 		{
 			if (dnull)
-				elog(ERROR, "COPY: conflicting options");
+				elog(ERROR, "COPY: NULL representation may only be defined once in query");
 			dnull = defel;
 		}
 		else
@@ -367,6 +372,24 @@ DoCopy(const CopyStmt *stmt)
 	server_encoding = GetDatabaseEncoding();
 #endif
 
+	if( attlist == NIL ){
+		/* get list of attributes in the relation */
+ 		TupleDesc desc = RelationGetDescr(rel);
+ 		int i;
+ 		for(i = 0; i < desc->natts; ++i){
+ 			Ident* id = makeNode(Ident);
+ 			id->name = NameStr(desc->attrs[i]->attname);
+ 			attlist = lappend(attlist,id);
+ 		}
+	}
+	else{
+		if( binary ){
+			elog(ERROR,"COPY: BINARY format cannot be used with specific column list");
+		}
+		/* verify that any user-specified attributes exist in the relation */
+		CopyAssertAttlist(rel,attlist,is_from);
+	}
+	
 	if (is_from)
 	{							/* copy from file to database */
 		if (rel->rd_rel->relkind != RELKIND_RELATION)
@@ -410,7 +433,7 @@ DoCopy(const CopyStmt *stmt)
 				elog(ERROR, "COPY: %s is a directory.", filename);
 			}
 		}
-		CopyFrom(rel, binary, oids, fp, delim, null_print);
+		CopyFrom(rel, attlist, binary, oids, fp, delim, null_print);
 	}
 	else
 	{							/* copy from database to file */
@@ -466,7 +489,7 @@ DoCopy(const CopyStmt *stmt)
 				elog(ERROR, "COPY: %s is a directory.", filename);
 			}
 		}
-		CopyTo(rel, binary, oids, fp, delim, null_print);
+		CopyTo(rel, attlist, binary, oids, fp, delim, null_print);
 	}
 
 	if (!pipe)
@@ -494,8 +517,8 @@ DoCopy(const CopyStmt *stmt)
  * Copy from relation TO file.
  */
 static void
-CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
-	   char *delim, char *null_print)
+CopyTo(Relation rel, List *attlist, bool binary, bool oids, 
+		 FILE *fp, char *delim, char *null_print)
 {
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -509,6 +532,10 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
 	int16		fld_size;
 	char	   *string;
 	Snapshot	mySnapshot;
+	int copy_attr_count;
+	int* attmap;
+	int p = 0;
+	List* cur;
 
 	if (oids && !rel->rd_rel->relhasoids)
 		elog(ERROR, "COPY: table %s does not have OIDs",
@@ -517,6 +544,18 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
 	tupDesc = rel->rd_att;
 	attr_count = rel->rd_att->natts;
 	attr = rel->rd_att->attrs;
+	copy_attr_count = length(attlist);
+	{
+		attmap = (int*)palloc(copy_attr_count * sizeof(int));
+		foreach(cur,attlist){
+			for (i = 0; i < attr_count; i++){
+				if( strcmp(strVal(lfirst(cur)),NameStr(attr[i]->attname)) == 0){
+					attmap[p++] = i;
+					continue;
+				}
+			}
+		}
+	}
 
 	/*
 	 * For binary copy we really only need isvarlena, but compute it
@@ -593,13 +632,14 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
 			}
 		}
 
-		for (i = 0; i < attr_count; i++)
+		for (i = 0; i < copy_attr_count; i++)
 		{
 			Datum		origvalue,
 						value;
 			bool		isnull;
+			int mi = attmap[i];
 
-			origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull);
+			origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull);
 
 			if (!binary)
 			{
@@ -628,25 +668,25 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
 				 * (or for binary case, becase we must output untoasted
 				 * value).
 				 */
-				if (isvarlena[i])
+				if (isvarlena[mi])
 					value = PointerGetDatum(PG_DETOAST_DATUM(origvalue));
 				else
 					value = origvalue;
 
 				if (!binary)
 				{
-					string = DatumGetCString(FunctionCall3(&out_functions[i],
+					string = DatumGetCString(FunctionCall3(&out_functions[mi],
 														   value,
-										   ObjectIdGetDatum(elements[i]),
-									 Int32GetDatum(attr[i]->atttypmod)));
+										   ObjectIdGetDatum(elements[mi]),
+									 Int32GetDatum(attr[mi]->atttypmod)));
 					CopyAttributeOut(fp, string, delim);
 					pfree(string);
 				}
 				else
 				{
-					fld_size = attr[i]->attlen;
+					fld_size = attr[mi]->attlen;
 					CopySendData(&fld_size, sizeof(int16), fp);
-					if (isvarlena[i])
+					if (isvarlena[mi])
 					{
 						/* varlena */
 						Assert(fld_size == -1);
@@ -654,7 +694,7 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
 									 VARSIZE(value),
 									 fp);
 					}
-					else if (!attr[i]->attbyval)
+					else if (!attr[mi]->attbyval)
 					{
 						/* fixed-length pass-by-reference */
 						Assert(fld_size > 0);
@@ -709,13 +749,13 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
  * Copy FROM file to relation.
  */
 static void
-CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
-		 char *delim, char *null_print)
+CopyFrom(Relation rel, List *attlist, bool binary, bool oids, 
+		 FILE *fp, char *delim, char *null_print)
 {
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
 	Form_pg_attribute *attr;
-	AttrNumber	attr_count;
+	AttrNumber	attr_count, copy_attr_count, def_attr_count;
 	FmgrInfo   *in_functions;
 	Oid		   *elements;
 	int			i;
@@ -732,10 +772,17 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	Oid			loaded_oid = InvalidOid;
 	bool		skip_tuple = false;
 	bool		file_has_oids;
+  int*    attmap = NULL;
+  int*    defmap = NULL;
+  Node**  defexprs = NULL; /* array of default att expressions */
+	ExprContext *econtext; /* used for ExecEvalExpr for default atts */
+	ExprDoneCond isdone;
 
 	tupDesc = RelationGetDescr(rel);
 	attr = tupDesc->attrs;
 	attr_count = tupDesc->natts;
+	copy_attr_count = length(attlist);
+	def_attr_count = 0;
 
 	/*
 	 * We need a ResultRelInfo so we can use the regular executor's
@@ -758,15 +805,42 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	slot = ExecAllocTableSlot(tupleTable);
 	ExecSetSlotDescriptor(slot, tupDesc, false);
 
+
 	if (!binary)
 	{
+		/*
+		 * pick up the input function and default expression (if any) for 
+		 * each attribute in the relation.
+		 */
+		List* cur;
+		attmap = (int*)palloc(sizeof(int) * attr_count);
+		defmap = (int*)palloc(sizeof(int) * attr_count);
+		defexprs = (Node**)palloc(sizeof(Node*) * attr_count);
 		in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
 		elements = (Oid *) palloc(attr_count * sizeof(Oid));
 		for (i = 0; i < attr_count; i++)
 		{
+			int p = 0;
+			bool specified = false;
 			in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
 			fmgr_info(in_func_oid, &in_functions[i]);
 			elements[i] = GetTypeElement(attr[i]->atttypid);
+			foreach(cur,attlist){
+				if( strcmp(strVal(lfirst(cur)),NameStr(attr[i]->attname)) == 0){
+					attmap[p] = i;
+					specified = true;
+					continue;
+				}
+				++p;
+			}
+			if( ! specified ){
+				/* column not specified, try to get a default */
+				defexprs[def_attr_count] = build_column_default(rel,i+1);
+				if( defexprs[def_attr_count] != NULL ){
+					defmap[def_attr_count] = i;
+					++def_attr_count;
+				}
+			}
 		}
 		file_has_oids = oids;	/* must rely on user to tell us this... */
 	}
@@ -821,12 +895,14 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	copy_lineno = 0;
 	fe_eof = false;
 
+	econtext = GetPerTupleExprContext(estate);
+
 	while (!done)
 	{
 		CHECK_FOR_INTERRUPTS();
 
 		copy_lineno++;
-
+		
 		/* Reset the per-output-tuple exprcontext */
 		ResetPerTupleExprContext(estate);
 
@@ -854,26 +930,42 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 						elog(ERROR, "COPY TEXT: Invalid Oid");
 				}
 			}
-
-			for (i = 0; i < attr_count && !done; i++)
+      
+			/* 
+			 * here, we only try to read as many attributes as 
+			 * were specified.
+			 */
+			for (i = 0; i < copy_attr_count && !done; i++)
 			{
+				int m = attmap[i];
 				string = CopyReadAttribute(fp, &isnull, delim,
 										   &newline, null_print);
-				if (isnull)
-				{
-					/* already set values[i] and nulls[i] */
+
+				if( isnull ){
+					/* nothing */
 				}
 				else if (string == NULL)
 					done = 1;	/* end of file */
 				else
 				{
-					values[i] = FunctionCall3(&in_functions[i],
-											  CStringGetDatum(string),
-										   ObjectIdGetDatum(elements[i]),
-									  Int32GetDatum(attr[i]->atttypmod));
-					nulls[i] = ' ';
+					values[m] = FunctionCall3(&in_functions[m],
+												CStringGetDatum(string),
+										   	ObjectIdGetDatum(elements[m]),
+									  		Int32GetDatum(attr[m]->atttypmod));
+					nulls[m] = ' ';
 				}
 			}
+	
+			/*
+			 * as above, we only try a default lookup if one is
+			 * known to be available
+			 */
+			for (i = 0; i < def_attr_count && !done; i++){
+				bool isnull;
+				values[defmap[i]] = ExecEvalExpr(defexprs[i],econtext,&isnull,&isdone);
+				if( ! isnull )
+    			nulls[defmap[i]] = ' ';
+			}
 			if (!done)
 				CopyReadNewline(fp, &newline);
 		}
@@ -975,7 +1067,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 			break;
 
 		tuple = heap_formtuple(tupDesc, values, nulls);
-
+  	
 		if (oids && file_has_oids)
 			tuple->t_data->t_oid = loaded_oid;
 
@@ -1021,12 +1113,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 				ExecARInsertTriggers(estate, resultRelInfo, tuple);
 		}
 
-		for (i = 0; i < attr_count; i++)
-		{
-			if (!attr[i]->attbyval && nulls[i] != 'n')
-				pfree(DatumGetPointer(values[i]));
-		}
-
 		heap_freetuple(tuple);
 	}
 
@@ -1361,3 +1447,51 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
 		pfree(string_start);	/* pfree pg_server_to_client result */
 #endif
 }
+
+/*
+ * CopyAssertAttlist: elog(ERROR,...) if the specified attlist
+ *                    is not valid for the Relation
+ */
+static void
+CopyAssertAttlist(Relation rel, List* attlist, bool from)
+{
+	TupleDesc tupDesc;
+  List* cur;
+  char* illegalattname = NULL;
+  int attr_count;
+  const char* to_or_from;
+	
+	if( attlist == NIL )
+		return;
+
+	to_or_from = (from == true ? "FROM" : "TO");
+
+	tupDesc = RelationGetDescr(rel);
+  Assert(tupDesc != NULL);
+  
+  /*
+   * make sure there aren't more columns specified than are in the table 
+   */
+  attr_count = tupDesc->natts;
+  if( attr_count < length(attlist) )
+    elog(ERROR,"More columns specified in COPY %s command than in target relation",to_or_from);
+
+  /* 
+   * make sure no columns are specified that don't exist in the table 
+   */
+  foreach(cur,attlist)
+  {
+    int found = 0;
+    int i = 0;
+    for(;i<attr_count;++i)
+    {
+      if( strcmp(strVal(lfirst(cur)),NameStr(tupDesc->attrs[i]->attname)) == 0)
+        ++found;
+    }
+    if( ! found )
+      illegalattname = strVal(lfirst(cur));
+  }
+  if( illegalattname )
+    elog(ERROR,"Attribute referenced in COPY %s command does not exist: \"%s.%s\"",to_or_from,RelationGetRelationName(rel),illegalattname); 
+}
+
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f2220aa00b9..17a3b61e762 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.343 2002/07/18 04:41:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.344 2002/07/18 04:43:50 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1262,31 +1262,32 @@ opt_id: 	ColId									{ $$ = $1; }
 /*****************************************************************************
  *
  *		QUERY :
- *				COPY <relname> FROM/TO [WITH options]
+ *				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_oids copy_from
-			copy_file_name copy_delimiter opt_with copy_opt_list
+CopyStmt:	COPY opt_binary qualified_name opt_column_list opt_oids 
+			copy_from copy_file_name copy_delimiter opt_with copy_opt_list
 				{
 					CopyStmt *n = makeNode(CopyStmt);
 					n->relation = $3;
-					n->is_from = $5;
-					n->filename = $6;
+					n->attlist = $4;
+					n->is_from = $6;
+					n->filename = $7;
 
 					n->options = NIL;
 					/* Concatenate user-supplied flags */
 					if ($2)
 						n->options = lappend(n->options, $2);
-					if ($4)
-						n->options = lappend(n->options, $4);
-					if ($7)
-						n->options = lappend(n->options, $7);
-					if ($9)
-						n->options = nconc(n->options, $9);
+					if ($5)
+						n->options = lappend(n->options, $5);
+					if ($8)
+						n->options = lappend(n->options, $8);
+					if ($10)
+						n->options = nconc(n->options, $10);
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index ae199d51ce8..0ae1e223baa 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.103 2002/06/20 20:29:34 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.104 2002/07/18 04:43:50 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,7 +43,6 @@ static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
 static void rewriteTargetList(Query *parsetree, Relation target_relation);
 static TargetEntry *process_matched_tle(TargetEntry *src_tle,
 										TargetEntry *prior_tle);
-static Node *build_column_default(Relation rel, int attrno);
 static void markQueryForUpdate(Query *qry, bool skipOldNew);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
 		   int varno, Query *parsetree);
@@ -411,7 +410,7 @@ process_matched_tle(TargetEntry *src_tle,
  *
  * If there is no default, return a NULL instead.
  */
-static Node *
+Node *
 build_column_default(Relation rel, int attrno)
 {
 	TupleDesc	rd_att = rel->rd_att;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index dd6aad5503d..7f263c3eb47 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.271 2002/07/12 18:43:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.272 2002/07/18 04:43:50 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -133,6 +133,7 @@ static char *GetPrivileges(Archive *AH, const char *s, const char *type);
 static int	dumpBlobs(Archive *AH, char *, void *);
 static int	dumpDatabase(Archive *AH);
 static const char *getAttrName(int attrnum, TableInfo *tblInfo);
+static const char* fmtCopyColumnList(const TableInfo* ti);
 
 extern char *optarg;
 extern int	optind,
@@ -842,6 +843,7 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
 	int			ret;
 	bool		copydone;
 	char		copybuf[COPYBUFSIZ];
+  const char*   column_list;
 
 	if (g_verbose)
 		write_msg(NULL, "dumping out the contents of table %s\n", classname);
@@ -854,17 +856,19 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
 	 */
 	selectSourceSchema(tbinfo->relnamespace->nspname);
 
+	column_list = fmtCopyColumnList(tbinfo);
+
 	if (oids && hasoids)
 	{
-		appendPQExpBuffer(q, "COPY %s WITH OIDS TO stdout;",
+		appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
 						  fmtQualifiedId(tbinfo->relnamespace->nspname,
-										 classname));
+										 classname),column_list);
 	}
 	else
 	{
-		appendPQExpBuffer(q, "COPY %s TO stdout;",
+		appendPQExpBuffer(q, "COPY %s %s TO stdout;",
 						  fmtQualifiedId(tbinfo->relnamespace->nspname,
-										 classname));
+										 classname), column_list);
 	}
 	res = PQexec(g_conn, q->data);
 	if (!res ||
@@ -1189,8 +1193,9 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
 			{
 				/* Dump/restore using COPY */
 				dumpFn = dumpClasses_nodumpData;
-				sprintf(copyBuf, "COPY %s %sFROM stdin;\n",
-						fmtId(tblinfo[i].relname, force_quotes),
+				sprintf(copyBuf, "COPY %s %s %sFROM stdin;\n",
+						fmtQualifiedId(tblinfo[i].relnamespace->nspname,tblinfo[i].relname),
+						fmtCopyColumnList(&(tblinfo[i])),
 						(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
 				copyStmt = copyBuf;
 			}
@@ -5860,3 +5865,38 @@ fmtQualifiedId(const char *schema, const char *id)
 
 	return id_return->data;
 }
+
+/*
+ * return a column list clause for the qualified relname.
+ * returns an empty string if the remote server is older than
+ * 7.3.
+ */
+static const char*
+fmtCopyColumnList(const TableInfo* ti)
+{
+	static PQExpBuffer q = NULL;
+	int			numatts = ti->numatts;
+	char**  attnames = ti->attnames;
+	int i;
+
+	if (g_fout->remoteVersion < 70300 )
+		return "";
+
+	if (q)				/* first time through? */
+		resetPQExpBuffer(q);
+	else
+		q = createPQExpBuffer();
+
+	resetPQExpBuffer(q);
+	
+	appendPQExpBuffer(q,"(");
+	for (i = 0; i < numatts; i++)
+	{
+		if( i > 0 )
+			appendPQExpBuffer(q,",");
+		appendPQExpBuffer(q, fmtId(attnames[i], force_quotes));
+	}
+	appendPQExpBuffer(q, ")");
+	return q->data;
+}
+
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f64f1b3d746..e6b27d03af4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.189 2002/07/18 04:42:29 momjian Exp $
+ * $Id: parsenodes.h,v 1.190 2002/07/18 04:43:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -884,6 +884,7 @@ typedef struct CopyStmt
 {
 	NodeTag		type;
 	RangeVar   *relation;		/* the relation to copy */
+	List *attlist;
 	bool		is_from;		/* TO or FROM */
 	char	   *filename;		/* if NULL, use stdin/stdout */
 	List	   *options;		/* List of DefElem nodes */
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 900ffca658f..69fecc8142d 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rewriteHandler.h,v 1.19 2002/06/20 20:29:52 momjian Exp $
+ * $Id: rewriteHandler.h,v 1.20 2002/07/18 04:43:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 
 #include "nodes/parsenodes.h"
 
-
 extern List *QueryRewrite(Query *parsetree);
+extern Node *build_column_default(Relation rel, int attrno);
 
 #endif   /* REWRITEHANDLER_H */
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1a72d1bc6e0..919c37e13f6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -74,4 +74,4 @@ test: select_views alter_table portals_p2 rules foreign_key
 # The sixth group of parallel test
 # ----------
 # "plpgsql" cannot run concurrently with "rules"
-test: limit plpgsql temp domain rangefuncs
+test: limit plpgsql temp domain rangefuncs copy2
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 87afc7c4512..552d63f9e30 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -1,4 +1,4 @@
-# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.10 2002/06/20 17:09:42 momjian Exp $
+# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.11 2002/07/18 04:43:51 momjian Exp $
 # This should probably be in an order similar to parallel_schedule.
 test: boolean
 test: char
@@ -80,6 +80,7 @@ test: rules
 test: foreign_key
 test: limit
 test: plpgsql
+test: copy2
 test: temp
 test: domain
 test: rangefuncs
-- 
GitLab