diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index e907da0b5d0c526221de864b2396e10fc7839232..0dd10d6754d1a5b49088e73ddf8fcb4876eb61c5 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.64 2005/05/06 03:38:05 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.65 2005/05/07 02:22:45 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -24,11 +24,12 @@ PostgreSQL documentation
 COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
     FROM { '<replaceable class="parameter">filename</replaceable>' | STDIN }
     [ [ WITH ] 
-          [ BINARY ] 
+          [ BINARY ]
           [ OIDS ]
           [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
           [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
-          [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
+          [ CSV [ HEADER ]
+                [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
                 [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
                 [ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
 
@@ -36,10 +37,12 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
     [ [ WITH ] 
           [ BINARY ]
+          [ HEADER ]
           [ OIDS ]
           [ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
           [ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ]
-          [ CSV [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
+          [ CSV [ HEADER ]
+                [ QUOTE [ AS ] '<replaceable class="parameter">quote</replaceable>' ] 
                 [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
                 [ FORCE QUOTE <replaceable class="parameter">column</replaceable> [, ...] ]
 </synopsis>
@@ -191,6 +194,17 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>HEADER</literal></term>
+    <listitem>
+     <para>
+      Specifies the file contains a header line with the names of each
+      column in the file.  On output, the first line contains the column 
+      names from the table, and on input, the first line is ignored.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">quote</replaceable></term>
     <listitem>
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index a40d6fc92223836f0c0bcbfa5c7106d5a0db99c1..35ae86814dcfc82574dafa1b061dd6ffdcdeede9 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.243 2005/05/06 17:24:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.244 2005/05/07 02:22:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -131,13 +131,13 @@ static bool line_buf_converted;
 /* non-export function prototypes */
 static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 		 char *delim, char *null_print, bool csv_mode, char *quote,
-		 char *escape, List *force_quote_atts, bool fe_copy);
+		 char *escape, List *force_quote_atts, bool header_line, bool fe_copy);
 static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
  char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
-	   List *force_quote_atts);
+	   List *force_quote_atts, bool header_line);
 static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
  char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
-		 List *force_notnull_atts);
+		 List *force_notnull_atts, bool header_line);
 static bool CopyReadLine(char * quote, char * escape);
 static char *CopyReadAttribute(const char *delim, const char *null_print,
 				  CopyReadResult *result, bool *isnull);
@@ -695,6 +695,7 @@ DoCopy(const CopyStmt *stmt)
 	bool		binary = false;
 	bool		oids = false;
 	bool		csv_mode = false;
+	bool        header_line = false;
 	char	   *delim = NULL;
 	char	   *quote = NULL;
 	char	   *escape = NULL;
@@ -752,6 +753,14 @@ DoCopy(const CopyStmt *stmt)
 						 errmsg("conflicting or redundant options")));
 			csv_mode = intVal(defel->arg);
 		}
+		else if (strcmp(defel->defname, "header") == 0)
+		{
+			if (header_line)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			header_line = intVal(defel->arg);
+		}
 		else if (strcmp(defel->defname, "quote") == 0)
 		{
 			if (quote)
@@ -825,6 +834,12 @@ DoCopy(const CopyStmt *stmt)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("COPY delimiter must be a single character")));
 
+  	/* Check header */
+	if (!csv_mode && header_line)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("COPY HEADER available only in CSV mode")));
+
 	/* Check quote */
 	if (!csv_mode && quote != NULL)
 		ereport(ERROR,
@@ -1015,7 +1030,7 @@ DoCopy(const CopyStmt *stmt)
 			}
 		}
 		CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
-				 quote, escape, force_notnull_atts);
+				 quote, escape, force_notnull_atts, header_line);
 	}
 	else
 	{							/* copy from database to file */
@@ -1079,7 +1094,7 @@ DoCopy(const CopyStmt *stmt)
 		}
 
 		DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
-				 quote, escape, force_quote_atts, fe_copy);
+				 quote, escape, force_quote_atts, header_line, fe_copy);
 	}
 
 	if (!pipe)
@@ -1111,7 +1126,7 @@ DoCopy(const CopyStmt *stmt)
 static void
 DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 		 char *delim, char *null_print, bool csv_mode, char *quote,
-		 char *escape, List *force_quote_atts, bool fe_copy)
+		 char *escape, List *force_quote_atts, bool header_line, bool fe_copy)
 {
 	PG_TRY();
 	{
@@ -1119,7 +1134,7 @@ DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 			SendCopyBegin(binary, list_length(attnumlist));
 
 		CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
-			   quote, escape, force_quote_atts);
+			   quote, escape, force_quote_atts, header_line);
 
 		if (fe_copy)
 			SendCopyEnd(binary);
@@ -1143,7 +1158,7 @@ DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 static void
 CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 	   char *delim, char *null_print, bool csv_mode, char *quote,
-	   char *escape, List *force_quote_atts)
+	   char *escape, List *force_quote_atts, bool header_line)
 {
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -1226,6 +1241,30 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 			null_print = (char *)
 				pg_server_to_client((unsigned char *) null_print,
 									strlen(null_print));
+
+		/* if a header has been requested send the line */
+		if (header_line)
+		{
+			bool hdr_delim = false;
+			char *colname;
+			
+			foreach(cur, attnumlist)
+			{
+				int			attnum = lfirst_int(cur);
+
+				if (hdr_delim)
+					CopySendChar(delim[0]);
+				hdr_delim = true;
+
+				colname = NameStr(attr[attnum - 1]->attname);
+
+				CopyAttributeOutCSV(colname, delim, quote, escape,
+									strcmp(colname, null_print) == 0);
+			}
+
+			CopySendEndOfRow(binary);
+
+		}
 	}
 
 	scandesc = heap_beginscan(rel, ActiveSnapshot, 0, NULL);
@@ -1427,7 +1466,7 @@ limit_printout_length(StringInfo buf)
 static void
 CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 		 char *delim, char *null_print, bool csv_mode, char *quote,
-		 char *escape, List *force_notnull_atts)
+		 char *escape, List *force_notnull_atts, bool header_line)
 {
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -1653,6 +1692,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	errcontext.previous = error_context_stack;
 	error_context_stack = &errcontext;
 
+	/* on input just throw the header line away */
+	if (header_line)
+	{
+		copy_lineno++;
+		done = CopyReadLine(quote, escape) ;
+	}
+
 	while (!done)
 	{
 		bool		skip_tuple;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 90c2f5528e561f2e747c318fa12fc611eb9c845c..c8ab0d3f4dff351289bf174727a2cdd8b32beab2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.490 2005/05/06 03:42:17 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.491 2005/05/07 02:22:46 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -362,7 +362,7 @@ static void doNegateFloat(Value *v);
 
 	GLOBAL GRANT GROUP_P
 
-	HANDLER HAVING HOLD HOUR_P
+	HANDLER HAVING HEADER HOLD HOUR_P
 
 	ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
 	INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
@@ -1444,6 +1444,10 @@ copy_opt_item:
 				{
 					$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
 				}
+			| HEADER
+				{
+					$$ = makeDefElem("header", (Node *)makeInteger(TRUE));
+				}
 			| QUOTE opt_as Sconst
 				{
 					$$ = makeDefElem("quote", (Node *)makeString($3));
@@ -7787,6 +7791,7 @@ unreserved_keyword:
 			| FUNCTION
 			| GLOBAL
 			| HANDLER
+			| HEADER
 			| HOLD
 			| HOUR_P
 			| IMMEDIATE
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 00d0c6bd51e4097d28a0819d84902ae4d40c4959..359fb845659d47e9f61db8c99befcde2a3b0830d 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.154 2004/12/31 22:00:27 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.155 2005/05/07 02:22:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -148,6 +148,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"group", GROUP_P},
 	{"handler", HANDLER},
 	{"having", HAVING},
+	{"header", HEADER},
 	{"hold", HOLD},
 	{"hour", HOUR_P},
 	{"ilike", ILIKE},
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 096f81aeb8810c71e4a68572ec2d33f9204a38c8..0ebe248b0c1864daa7f3b29a1bb6d5a0ed97f6ab 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.56 2005/02/22 04:40:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.57 2005/05/07 02:22:49 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "copy.h"
@@ -66,6 +66,7 @@ struct copy_options
 	bool		binary;
 	bool		oids;
 	bool		csv_mode;
+	bool        header;
 	char	   *delim;
 	char	   *null;
 	char	   *quote;
@@ -289,6 +290,8 @@ parse_slash_copy(const char *args)
 				result->oids = true;
 			else if (pg_strcasecmp(token, "csv") == 0)
 				result->csv_mode = true;
+			else if (pg_strcasecmp(token, "header") == 0)
+				result->header = true;
 			else if (pg_strcasecmp(token, "delimiter") == 0)
 			{
 				token = strtokx(NULL, whitespace, NULL, "'",
@@ -481,6 +484,9 @@ do_copy(const char *args)
 	if (options->csv_mode)
 		appendPQExpBuffer(&query, " CSV");
 
+	if (options->header)
+		appendPQExpBuffer(&query, " HEADER");
+
 	if (options->quote)
 	{
 		if (options->quote[0] == '\'')
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b45ceeeeed6217023863fb9174eafe7a99458135..4aed36c66a310d73d5d8cbd09019664e5c7e9829 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.126 2005/05/04 14:25:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.127 2005/05/07 02:22:49 momjian Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -1040,7 +1040,7 @@ psql_completion(char *text, int start, int end)
 			  pg_strcasecmp(prev3_wd, "TO") == 0))
 		{
 			static const char *const list_CSV[] =
-			{"QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
+			{"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
 
 			COMPLETE_WITH_LIST(list_CSV);
 		}