diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3a8d8d3..c0eaee8 100644
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** BeginCopyFrom(Relation rel,
*** 2211,2240 ****
  }
  
  /*
!  * Read next tuple from file for COPY FROM. Return false if no more tuples.
   *
!  * valus and nulls arrays must be the same length as columns of the
!  * relation passed to BeginCopyFrom. Oid of the tuple is returned with
!  * tupleOid separately.
   */
  bool
! NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid)
  {
- 	TupleDesc	tupDesc;
- 	Form_pg_attribute *attr;
- 	AttrNumber	num_phys_attrs,
- 				attr_count,
- 				num_defaults = cstate->num_defaults;
- 	FmgrInfo   *in_functions = cstate->in_functions;
- 	Oid		   *typioparams = cstate->typioparams;
- 	int			i;
- 	int			nfields;
  	char	  **field_strings;
! 	bool		isnull;
! 	bool		file_has_oids = cstate->file_has_oids;
! 	int		   *defmap = cstate->defmap;
! 	ExprState **defexprs = cstate->defexprs;
! 	ExprContext	*econtext;		/* used for ExecEvalExpr for default atts */
  
  	/* on input just throw the header line away */
  	if (cstate->cur_lineno == 0 && cstate->header_line)
--- 2211,2236 ----
  }
  
  /*
!  * Read raw fields in the next line for COPY FROM in text or csv mode.
!  * Return false if no more lines.
   *
!  * An internal buffer is returned via fields, so the caller must not free it.
!  * Since the function returns all fields in the input file, nfields could be
!  * smaller or larger than the number of columns. Oid of the tuple is returned 
!  * with tupleOid separately.
   */
  bool
! NextLineCopyFrom(CopyState cstate, char ***fields, int *nfields, Oid *tupleOid)
  {
  	char	  **field_strings;
! 	ListCell   *cur;
! 	int			fldct;
! 	int			fieldno;
! 	char	   *string;
! 	bool		done;
! 
! 	/* only available for text or csv input */
! 	Assert(!cstate->binary);
  
  	/* on input just throw the header line away */
  	if (cstate->cur_lineno == 0 && cstate->header_line)
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2244,2270 ****
  			return false;	/* done */
  	}
  
- 	tupDesc = RelationGetDescr(cstate->rel);
- 	attr = tupDesc->attrs;
- 	num_phys_attrs = tupDesc->natts;
- 	attr_count = list_length(cstate->attnumlist);
- 	nfields = file_has_oids ? (attr_count + 1) : attr_count;
- 
  	/* XXX: Indentation is not adjusted to keep the patch small. */
  		cstate->cur_lineno++;
  
- 		/* Initialize all values for row to NULL */
- 		MemSet(values, 0, num_phys_attrs * sizeof(Datum));
- 		MemSet(nulls, true, num_phys_attrs * sizeof(bool));
- 
- 		if (!cstate->binary)
- 		{
- 			ListCell   *cur;
- 			int			fldct;
- 			int			fieldno;
- 			char	   *string;
- 			bool		done;
- 
  			/* Actually read the line into memory here */
  			done = CopyReadLine(cstate);
  
--- 2240,2248 ----
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2282,2298 ****
  			else
  				fldct = CopyReadAttributesText(cstate);
  
- 			/* check for overflowing fields */
- 			if (nfields > 0 && fldct > nfields)
- 				ereport(ERROR,
- 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- 						 errmsg("extra data after last expected column")));
- 
  			fieldno = 0;
  			field_strings = cstate->raw_fields;
  
  			/* Read the OID field if present */
! 			if (file_has_oids)
  			{
  				if (fieldno >= fldct)
  					ereport(ERROR,
--- 2260,2270 ----
  			else
  				fldct = CopyReadAttributesText(cstate);
  
  			fieldno = 0;
  			field_strings = cstate->raw_fields;
  
  			/* Read the OID field if present */
! 			if (cstate->file_has_oids)
  			{
  				if (fieldno >= fldct)
  					ereport(ERROR,
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2319,2324 ****
--- 2291,2299 ----
  				}
  			}
  
+ 			*fields = &field_strings[fieldno];
+ 			*nfields = fldct - fieldno;
+ 
  			/* Loop to read the user attributes on the line. */
  			foreach(cur, cstate->attnumlist)
  			{
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2326,2344 ****
  				int			m = attnum - 1;
  
  				if (fieldno >= fldct)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg("missing data for column \"%s\"",
! 									NameStr(attr[m]->attname))));
! 				string = field_strings[fieldno++];
  
! 				if (cstate->csv_mode && string == NULL &&
  					cstate->force_notnull_flags[m])
  				{
  					/* Go ahead and read the NULL string */
! 					string = cstate->null_print;
  				}
  
  				cstate->cur_attname = NameStr(attr[m]->attname);
  				cstate->cur_attval = string;
  				values[m] = InputFunctionCall(&in_functions[m],
--- 2301,2393 ----
  				int			m = attnum - 1;
  
  				if (fieldno >= fldct)
! 					break;
  
! 				/*
! 				 * FIXME: force_notnull_flags won't work for extra columsn
! 				 * because we cannot specify it for unnamed columns...
! 				 */
! 				if (cstate->csv_mode && field_strings[fieldno] == NULL &&
  					cstate->force_notnull_flags[m])
  				{
  					/* Go ahead and read the NULL string */
! 					field_strings[fieldno] = cstate->null_print;
  				}
  
+ 				fieldno++;
+ 			}
+ 	/* XXX: End of only-indentation changes. */
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Read next tuple from file for COPY FROM. Return false if no more tuples.
+  *
+  * values and nulls arrays must be the same length as columns of the
+  * relation passed to BeginCopyFrom. Oid of the tuple is returned with
+  * tupleOid separately.
+  */
+ bool
+ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid)
+ {
+ 	TupleDesc	tupDesc;
+ 	Form_pg_attribute *attr;
+ 	AttrNumber	num_phys_attrs,
+ 				attr_count,
+ 				num_defaults = cstate->num_defaults;
+ 	FmgrInfo   *in_functions = cstate->in_functions;
+ 	Oid		   *typioparams = cstate->typioparams;
+ 	int			i;
+ 	bool		isnull;
+ 	bool		file_has_oids = cstate->file_has_oids;
+ 	int		   *defmap = cstate->defmap;
+ 	ExprState **defexprs = cstate->defexprs;
+ 	ExprContext	*econtext;		/* used for ExecEvalExpr for default atts */
+ 
+ 	tupDesc = RelationGetDescr(cstate->rel);
+ 	attr = tupDesc->attrs;
+ 	num_phys_attrs = tupDesc->natts;
+ 	attr_count = list_length(cstate->attnumlist);
+ 
+ 	/* XXX: Indentation is not adjusted to keep the patch small. */
+ 		/* Initialize all values for row to NULL */
+ 		MemSet(values, 0, num_phys_attrs * sizeof(Datum));
+ 		MemSet(nulls, true, num_phys_attrs * sizeof(bool));
+ 
+ 		if (!cstate->binary)
+ 		{
+ 			char	  **field_strings;
+ 			ListCell   *cur;
+ 			int			fldct;
+ 			int			fieldno;
+ 			char	   *string;
+ 
+ 			/* read raw fields in the next line */
+ 			if (!NextLineCopyFrom(cstate, &field_strings, &fldct, tupleOid))
+ 				return false;
+ 
+ 			/* check for overflowing fields */
+ 			if (attr_count > 0 && fldct > attr_count)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ 						 errmsg("extra data after last expected column")));
+ 
+ 			fieldno = 0;
+ 
+ 			/* Loop to read the user attributes on the line. */
+ 			foreach(cur, cstate->attnumlist)
+ 			{
+ 				int			attnum = lfirst_int(cur);
+ 				int			m = attnum - 1;
+ 
+ 				if (fieldno >= fldct)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ 							 errmsg("missing data for column \"%s\"",
+ 									NameStr(attr[m]->attname))));
+ 				string = field_strings[fieldno++];
+ 
  				cstate->cur_attname = NameStr(attr[m]->attname);
  				cstate->cur_attval = string;
  				values[m] = InputFunctionCall(&in_functions[m],
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2351,2357 ****
  				cstate->cur_attval = NULL;
  			}
  
! 			Assert(fieldno == nfields);
  		}
  		else
  		{
--- 2400,2406 ----
  				cstate->cur_attval = NULL;
  			}
  
! 			Assert(fieldno == attr_count);
  		}
  		else
  		{
*************** NextCopyFrom(CopyState cstate, Datum *va
*** 2359,2364 ****
--- 2408,2415 ----
  			int16		fld_count;
  			ListCell   *cur;
  
+ 			cstate->cur_lineno++;
+ 
  			if (!CopyGetInt16(cstate, &fld_count))
  			{
  				/* EOF detected (end of file, or protocol-level EOF) */
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index 8340e3d..ba5f69b 100644
*** a/src/include/commands/copy.h
--- b/src/include/commands/copy.h
*************** extern CopyState BeginCopyFrom(Relation 
*** 28,33 ****
--- 28,35 ----
  extern void EndCopyFrom(CopyState cstate);
  extern bool NextCopyFrom(CopyState cstate,
  						 Datum *values, bool *nulls, Oid *tupleOid);
+ extern bool NextLineCopyFrom(CopyState cstate,
+ 							 char ***fields, int *nfields, Oid *tupleOid);
  extern void CopyFromErrorCallback(void *arg);
  
  extern DestReceiver *CreateCopyDestReceiver(void);