fast_copy_V12_8.1.patch

application/octet-stream

Filename: fast_copy_V12_8.1.patch
Type: application/octet-stream
Part: 1
Message: Re: COPY FROM performance improvements

Patch

Format: context
File+
src/backend/commands/copy.c 2059 334
Index: src/backend/commands/copy.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.245
diff -c -r1.245 copy.c
*** src/backend/commands/copy.c	2 Jun 2005 01:21:22 -0000	1.245
--- src/backend/commands/copy.c	27 Jun 2005 18:45:39 -0000
***************
*** 1,3 ****
--- 1,4 ----
+ 
  /*-------------------------------------------------------------------------
   *
   * copy.c
***************
*** 51,56 ****
--- 52,58 ----
  
  #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
  #define OCTVALUE(c) ((c) - '0')
+ #define COPY_BUF_SIZE 65536
  
  /*
   * Represents the different source/dest cases we need to worry about at
***************
*** 84,90 ****
  	EOL_CRNL
  } EolType;
  
- 
  static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
  
  /*
--- 86,91 ----
***************
*** 92,97 ****
--- 93,101 ----
   * never been reentrant...
   */
  static CopyDest copy_dest;
+ static int	eol_ch[2];			/* The byte values of the 1 or 2 eol bytes */
+ static char escapec;			/* escape char for delimited data format. */
+ static bool client_encoding_only;		/* true if client encoding is a non supported server encoding */
  static FILE *copy_file;			/* used if copy_dest == COPY_FILE */
  static StringInfo copy_msgbuf;	/* used if copy_dest == COPY_NEW_FE */
  static bool fe_eof;				/* true if detected end of copy data */
***************
*** 101,156 ****
  
  /* these are just for error messages, see copy_in_error_callback */
  static bool copy_binary;		/* is it a binary copy? */
! static const char *copy_relname;	/* table name for error messages */
  static int	copy_lineno;		/* line number for error messages */
! static const char *copy_attname;	/* current att for error messages */
  
  
  /*
   * These static variables are used to avoid incurring overhead for each
!  * attribute processed.  attribute_buf is reused on each CopyReadAttribute
   * call to hold the string being read in.  Under normal use it will soon
   * grow to a suitable size, and then we will avoid palloc/pfree overhead
   * for subsequent attributes.  Note that CopyReadAttribute returns a pointer
!  * to attribute_buf's data buffer!
   */
! static StringInfoData attribute_buf;
  
  /*
   * Similarly, line_buf holds the whole input line being processed (its
   * cursor field points to the next character to be read by CopyReadAttribute).
   * The input cycle is first to read the whole line into line_buf, convert it
   * to server encoding, and then extract individual attribute fields into
!  * attribute_buf.  (We used to have CopyReadAttribute read the input source
   * directly, but that caused a lot of encoding issues and unnecessary logic
   * complexity.)
   */
! static StringInfoData line_buf;
  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 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, 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, bool header_line);
! static bool CopyReadLine(char * quote, char * escape);
  static char *CopyReadAttribute(const char *delim, const char *null_print,
! 				  CopyReadResult *result, bool *isnull);
  static char *CopyReadAttributeCSV(const char *delim, const char *null_print,
! 					 char *quote, char *escape,
! 					 CopyReadResult *result, bool *isnull);
  static Datum CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo,
! 						Oid typioparam, bool *isnull);
  static void CopyAttributeOut(char *string, char *delim);
  static void CopyAttributeOutCSV(char *string, char *delim, char *quote,
! 					char *escape, bool force_quote);
  static List *CopyGetAttnums(Relation rel, List *attnamelist);
  static void limit_printout_length(StringInfo buf);
  
  /* Internal communications functions */
  static void SendCopyBegin(bool binary, int natts);
--- 105,189 ----
  
  /* these are just for error messages, see copy_in_error_callback */
  static bool copy_binary;		/* is it a binary copy? */
! static const char *copy_relname;		/* table name for error messages */
  static int	copy_lineno;		/* line number for error messages */
! static const char *copy_attname;		/* current att for error messages */
! 
! /* Static variables for buffered input parsing */
! 
! /* leave room for extra null (to enable use of string functions) */
! static char input_buf[COPY_BUF_SIZE + 1];
! static char *begloc;
! static char *endloc;
! static bool esc_in_prevbuf;		/* backslash was last character of the data input buffer */
! static bool cr_in_prevbuf;		/* CR was last character of the data input buffer */
! static bool line_done;			/* finished processing the whole line or stopped in the middle */
! static bool buf_done;			/* finished processing the current buffer */
! static int	buffer_index;		/* input buffer index */
  
  
  /*
   * These static variables are used to avoid incurring overhead for each
!  * attribute processed.  attr_buf is reused on each CopyReadAttribute
   * call to hold the string being read in.  Under normal use it will soon
   * grow to a suitable size, and then we will avoid palloc/pfree overhead
   * for subsequent attributes.  Note that CopyReadAttribute returns a pointer
!  * to attr_buf's data buffer!
   */
! static StringInfoData attr_buf; /* still used in CopyFrom()		*/
  
  /*
   * Similarly, line_buf holds the whole input line being processed (its
   * cursor field points to the next character to be read by CopyReadAttribute).
   * The input cycle is first to read the whole line into line_buf, convert it
   * to server encoding, and then extract individual attribute fields into
!  * attr_buf.  (We used to have CopyReadAttribute read the input source
   * directly, but that caused a lot of encoding issues and unnecessary logic
   * complexity.)
   */
! static StringInfoData line_buf; /* still used in CopyFromBinary/CSV() */
  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 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, 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, bool header_line);
! static bool CopyReadLine(char *quote, char *escape);
  static char *CopyReadAttribute(const char *delim, const char *null_print,
! 							   CopyReadResult *result, bool *isnull);
  static char *CopyReadAttributeCSV(const char *delim, const char *null_print,
! 								  char *quote, char *escape,
! 								  CopyReadResult *result, bool *isnull);
  static Datum CopyReadBinaryAttribute(int column_no, FmgrInfo *flinfo,
! 									 Oid typioparam, bool *isnull);
  static void CopyAttributeOut(char *string, char *delim);
  static void CopyAttributeOutCSV(char *string, char *delim, char *quote,
! 								char *escape, bool force_quote);
  static List *CopyGetAttnums(Relation rel, List *attnamelist);
  static void limit_printout_length(StringInfo buf);
+ static bool CopyReadLineBuffered(size_t bytesread);
+ static void CopyFromDelimited(Relation rel, List *attnumlist, bool binary,
+ 							  bool oids, char *delim, char *null_print,
+ 							  bool csv_mode, char *quote, char *escape,
+ 							  List *force_notnull_atts);
+ static char *CopyReadAttribute(const char *delim, const char *null_print,
+ 							   CopyReadResult *result, bool *isnull);
+ static void CopyReadAllAttrs(const char *delim, const char *null_print,
+ 							 int null_print_len, char *nulls, List *attnumlist,
+ 							 int *attr_offsets, int num_phys_attrs,
+ 							 Form_pg_attribute *attr);
+ static char *CopyReadOidAttr(const char *delim, const char *null_print,
+ 							 int null_print_len, CopyReadResult *result,
+ 							 bool *isnull);
+ static bool DetectLineEnd(size_t bytesread);
+ 
  
  /* Internal communications functions */
  static void SendCopyBegin(bool binary, int natts);
***************
*** 160,166 ****
  static void CopySendString(const char *str);
  static void CopySendChar(char c);
  static void CopySendEndOfRow(bool binary);
! static void CopyGetData(void *databuf, int datasize);
  static int	CopyGetChar(void);
  
  #define CopyGetEof()  (fe_eof)
--- 193,199 ----
  static void CopySendString(const char *str);
  static void CopySendChar(char c);
  static void CopySendEndOfRow(bool binary);
! static int	CopyGetData(void *databuf, int datasize);
  static int	CopyGetChar(void);
  
  #define CopyGetEof()  (fe_eof)
***************
*** 171,176 ****
--- 204,213 ----
  static void CopySendInt16(int16 val);
  static int16 CopyGetInt16(void);
  
+ /* new parsing utils */
+ static char *strchrlen(const char *s, int c, size_t len);
+ static char *str2chrlen(const char *s, int c1, int c2, size_t len,
+ 						char *c_found);
  
  /*
   * Send copy start/stop messages for frontend copies.  These have changed
***************
*** 181,187 ****
  {
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
  	{
! 		/* new way */
  		StringInfoData buf;
  		int16		format = (binary ? 1 : 0);
  		int			i;
--- 218,226 ----
  {
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
  	{
! 		/*
! 		 * new way
! 		 */
  		StringInfoData buf;
  		int16		format = (binary ? 1 : 0);
  		int			i;
***************
*** 197,221 ****
  	}
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
! 		/* old way */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('H');
! 		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
  		copy_dest = COPY_OLD_FE;
  	}
  	else
  	{
! 		/* very old way */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('B');
! 		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
  		copy_dest = COPY_OLD_FE;
  	}
--- 236,270 ----
  	}
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
! 		/*
! 		 * old way
! 		 */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg
! 					 ("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('H');
! 		/*
! 		 * grottiness needed for old COPY OUT protocol
! 		 */
  		pq_startcopyout();
  		copy_dest = COPY_OLD_FE;
  	}
  	else
  	{
! 		/*
! 		 * very old way
! 		 */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg
! 					 ("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('B');
! 		/*
! 		 * grottiness needed for old COPY OUT protocol
! 		 */
  		pq_startcopyout();
  		copy_dest = COPY_OLD_FE;
  	}
***************
*** 226,232 ****
  {
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
  	{
! 		/* new way */
  		StringInfoData buf;
  		int16		format = (binary ? 1 : 0);
  		int			i;
--- 275,283 ----
  {
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
  	{
! 		/*
! 		 * new way
! 		 */
  		StringInfoData buf;
  		int16		format = (binary ? 1 : 0);
  		int			i;
***************
*** 242,266 ****
  	}
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
! 		/* old way */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('G');
  		copy_dest = COPY_OLD_FE;
  	}
  	else
  	{
! 		/* very old way */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('D');
  		copy_dest = COPY_OLD_FE;
  	}
! 	/* We *must* flush here to ensure FE knows it can send. */
  	pq_flush();
  }
  
--- 293,325 ----
  	}
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
! 		/*
! 		 * old way
! 		 */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg
! 					 ("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('G');
  		copy_dest = COPY_OLD_FE;
  	}
  	else
  	{
! 		/*
! 		 * very old way
! 		 */
  		if (binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg
! 					 ("COPY BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('D');
  		copy_dest = COPY_OLD_FE;
  	}
! 	/*
! 	 * We *must* flush here to ensure FE knows it can send.
! 	 */
  	pq_flush();
  }
  
***************
*** 271,290 ****
  	{
  		if (binary)
  		{
! 			/* Need to flush out file trailer word */
  			CopySendEndOfRow(true);
  		}
  		else
  		{
! 			/* Shouldn't have any unsent data */
  			Assert(copy_msgbuf->len == 0);
  		}
! 		/* Send Copy Done message */
  		pq_putemptymessage('c');
  	}
  	else
  	{
! 		/* The FE/BE protocol uses \n as newline for all platforms */
  		CopySendData("\\.\n", 3);
  		pq_endcopyout(false);
  	}
--- 330,357 ----
  	{
  		if (binary)
  		{
! 			/*
! 			 * Need to flush out file trailer word
! 			 */
  			CopySendEndOfRow(true);
  		}
  		else
  		{
! 			/*
! 			 * Shouldn't have any unsent data 
! 			 */
  			Assert(copy_msgbuf->len == 0);
  		}
! 		/*
! 		 * Send Copy Done message
! 		 */
  		pq_putemptymessage('c');
  	}
  	else
  	{
! 		/*
! 		 * The FE/BE protocol uses \n as newline for all platforms
! 		 */
  		CopySendData("\\.\n", 3);
  		pq_endcopyout(false);
  	}
***************
*** 314,323 ****
  		case COPY_OLD_FE:
  			if (pq_putbytes((char *) databuf, datasize))
  			{
! 				/* no hope of recovering connection sync, so FATAL */
  				ereport(FATAL,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
! 					   errmsg("connection lost during COPY to stdout")));
  			}
  			break;
  		case COPY_NEW_FE:
--- 381,392 ----
  		case COPY_OLD_FE:
  			if (pq_putbytes((char *) databuf, datasize))
  			{
! 				/*
! 				 * no hope of recovering connection sync, so FATAL
! 				 */
  				ereport(FATAL,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
! 						 errmsg("connection lost during COPY to stdout")));
  			}
  			break;
  		case COPY_NEW_FE:
***************
*** 346,352 ****
  		case COPY_FILE:
  			if (!binary)
  			{
! 				/* Default line termination depends on platform */
  #ifndef WIN32
  				CopySendChar('\n');
  #else
--- 415,423 ----
  		case COPY_FILE:
  			if (!binary)
  			{
! 				/*
! 				 * Default line termination depends on platform
! 				 */
  #ifndef WIN32
  				CopySendChar('\n');
  #else
***************
*** 355,371 ****
  			}
  			break;
  		case COPY_OLD_FE:
! 			/* The FE/BE protocol uses \n as newline for all platforms */
  			if (!binary)
  				CopySendChar('\n');
  			break;
  		case COPY_NEW_FE:
! 			/* The FE/BE protocol uses \n as newline for all platforms */
  			if (!binary)
  				CopySendChar('\n');
! 			/* Dump the accumulated row as one CopyData message */
  			(void) pq_putmessage('d', copy_msgbuf->data, copy_msgbuf->len);
! 			/* Reset copy_msgbuf to empty */
  			copy_msgbuf->len = 0;
  			copy_msgbuf->data[0] = '\0';
  			break;
--- 426,450 ----
  			}
  			break;
  		case COPY_OLD_FE:
! 			/*
! 			 * The FE/BE protocol uses \n as newline for all platforms
! 			 */
  			if (!binary)
  				CopySendChar('\n');
  			break;
  		case COPY_NEW_FE:
! 			/*
! 			 * The FE/BE protocol uses \n as newline for all platforms
! 			 */
  			if (!binary)
  				CopySendChar('\n');
! 			/*
! 			 * Dump the accumulated row as one CopyData message
! 			 */
  			(void) pq_putmessage('d', copy_msgbuf->data, copy_msgbuf->len);
! 			/*
! 			 * Reset copy_msgbuf to empty
! 			 */
  			copy_msgbuf->len = 0;
  			copy_msgbuf->data[0] = '\0';
  			break;
***************
*** 383,407 ****
   * It seems unwise to allow the COPY IN to complete normally in that case.
   *
   * NB: no data conversion is applied by these functions
   */
! static void
  CopyGetData(void *databuf, int datasize)
  {
  	switch (copy_dest)
  	{
  		case COPY_FILE:
! 			fread(databuf, datasize, 1, copy_file);
  			if (feof(copy_file))
  				fe_eof = true;
  			break;
  		case COPY_OLD_FE:
  			if (pq_getbytes((char *) databuf, datasize))
  			{
! 				/* Only a \. terminator is legal EOF in old protocol */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
  			}
  			break;
  		case COPY_NEW_FE:
  			while (datasize > 0 && !fe_eof)
--- 462,494 ----
   * It seems unwise to allow the COPY IN to complete normally in that case.
   *
   * NB: no data conversion is applied by these functions
+  *
+  * Returns: the number of bytes that were successfully read
+  * into the data buffer.
   */
! static int
  CopyGetData(void *databuf, int datasize)
  {
+ 	size_t		bytesread = 0;
+ 
  	switch (copy_dest)
  	{
  		case COPY_FILE:
! 			bytesread = fread(databuf, 1, datasize, copy_file);
  			if (feof(copy_file))
  				fe_eof = true;
  			break;
  		case COPY_OLD_FE:
  			if (pq_getbytes((char *) databuf, datasize))
  			{
! 				/*
! 				 * Only a \. terminator is legal EOF in old protocol
! 				 */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
  			}
+ 			bytesread += datasize;		/* update the count of bytes that were read so far */
  			break;
  		case COPY_NEW_FE:
  			while (datasize > 0 && !fe_eof)
***************
*** 410,441 ****
  
  				while (copy_msgbuf->cursor >= copy_msgbuf->len)
  				{
! 					/* Try to receive another message */
  					int			mtype;
  
! 			readmessage:
  					mtype = pq_getbyte();
  					if (mtype == EOF)
  						ereport(ERROR,
  								(errcode(ERRCODE_CONNECTION_FAILURE),
! 						 errmsg("unexpected EOF on client connection")));
  					if (pq_getmessage(copy_msgbuf, 0))
  						ereport(ERROR,
  								(errcode(ERRCODE_CONNECTION_FAILURE),
! 						 errmsg("unexpected EOF on client connection")));
  					switch (mtype)
  					{
  						case 'd':		/* CopyData */
  							break;
  						case 'c':		/* CopyDone */
! 							/* COPY IN correctly terminated by frontend */
  							fe_eof = true;
! 							return;
  						case 'f':		/* CopyFail */
  							ereport(ERROR,
  									(errcode(ERRCODE_QUERY_CANCELED),
  									 errmsg("COPY from stdin failed: %s",
! 										 pq_getmsgstring(copy_msgbuf))));
  							break;
  						case 'H':		/* Flush */
  						case 'S':		/* Sync */
--- 497,534 ----
  
  				while (copy_msgbuf->cursor >= copy_msgbuf->len)
  				{
! 					/*
! 					 * Try to receive another message
! 					 */
  					int			mtype;
  
! 				  readmessage:
  					mtype = pq_getbyte();
  					if (mtype == EOF)
  						ereport(ERROR,
  								(errcode(ERRCODE_CONNECTION_FAILURE),
! 								 errmsg
! 								 ("unexpected EOF on client connection")));
  					if (pq_getmessage(copy_msgbuf, 0))
  						ereport(ERROR,
  								(errcode(ERRCODE_CONNECTION_FAILURE),
! 								 errmsg
! 								 ("unexpected EOF on client connection")));
  					switch (mtype)
  					{
  						case 'd':		/* CopyData */
  							break;
  						case 'c':		/* CopyDone */
! 							/*
! 							 * COPY IN correctly terminated by frontend
! 							 */
  							fe_eof = true;
! 							return bytesread;
  						case 'f':		/* CopyFail */
  							ereport(ERROR,
  									(errcode(ERRCODE_QUERY_CANCELED),
  									 errmsg("COPY from stdin failed: %s",
! 											pq_getmsgstring(copy_msgbuf))));
  							break;
  						case 'H':		/* Flush */
  						case 'S':		/* Sync */
***************
*** 450,457 ****
  						default:
  							ereport(ERROR,
  									(errcode(ERRCODE_PROTOCOL_VIOLATION),
! 									 errmsg("unexpected message type 0x%02X during COPY from stdin",
! 											mtype)));
  							break;
  					}
  				}
--- 543,551 ----
  						default:
  							ereport(ERROR,
  									(errcode(ERRCODE_PROTOCOL_VIOLATION),
! 									 errmsg
! 									 ("unexpected message type 0x%02X during COPY from stdin",
! 									  mtype)));
  							break;
  					}
  				}
***************
*** 460,469 ****
--- 554,566 ----
  					avail = datasize;
  				pq_copymsgbytes(copy_msgbuf, databuf, avail);
  				databuf = (void *) ((char *) databuf + avail);
+ 				bytesread += avail;		/* update the count of bytes that were read so far */
  				datasize -= avail;
  			}
  			break;
  	}
+ 
+ 	return bytesread;
  }
  
  static int
***************
*** 480,486 ****
  			ch = pq_getbyte();
  			if (ch == EOF)
  			{
! 				/* Only a \. terminator is legal EOF in old protocol */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
--- 577,585 ----
  			ch = pq_getbyte();
  			if (ch == EOF)
  			{
! 				/*
! 				 * Only a \. terminator is legal EOF in old protocol
! 				 */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
***************
*** 529,535 ****
  			ch = pq_peekbyte();
  			if (ch == EOF)
  			{
! 				/* Only a \. terminator is legal EOF in old protocol */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
--- 628,636 ----
  			ch = pq_peekbyte();
  			if (ch == EOF)
  			{
! 				/*
! 				 * Only a \. terminator is legal EOF in old protocol
! 				 */
  				ereport(ERROR,
  						(errcode(ERRCODE_CONNECTION_FAILURE),
  						 errmsg("unexpected EOF on client connection")));
***************
*** 565,579 ****
  		case COPY_FILE:
  			if (!pickup)
  			{
! 				/* We don't want to pick it up - so put it back in there */
  				ungetc(c, copy_file);
  			}
! 			/* If we wanted to pick it up, it's already done */
  			break;
  		case COPY_OLD_FE:
  			if (pickup)
  			{
! 				/* We want to pick it up */
  				(void) pq_getbyte();
  			}
  
--- 666,686 ----
  		case COPY_FILE:
  			if (!pickup)
  			{
! 				/*
! 				 * We don't want to pick it up - so put it back in there 
! 				 */
  				ungetc(c, copy_file);
  			}
! 			/*
! 			 * If we wanted to pick it up, it's already done 
! 			 */
  			break;
  		case COPY_OLD_FE:
  			if (pickup)
  			{
! 				/*
! 				 * We want to pick it up
! 				 */
  				(void) pq_getbyte();
  			}
  
***************
*** 585,594 ****
  		case COPY_NEW_FE:
  			if (!pickup)
  			{
! 				/* We don't want to pick it up - so put it back in there */
  				copy_msgbuf->cursor--;
  			}
! 			/* If we wanted to pick it up, it's already done */
  			break;
  	}
  }
--- 692,705 ----
  		case COPY_NEW_FE:
  			if (!pickup)
  			{
! 				/*
! 				 * We don't want to pick it up - so put it back in there 
! 				 */
  				copy_msgbuf->cursor--;
  			}
! 			/*
! 			 * If we wanted to pick it up, it's already done 
! 			 */
  			break;
  	}
  }
***************
*** 695,701 ****
  	bool		binary = false;
  	bool		oids = false;
  	bool		csv_mode = false;
! 	bool        header_line = false;
  	char	   *delim = NULL;
  	char	   *quote = NULL;
  	char	   *escape = NULL;
--- 806,812 ----
  	bool		binary = false;
  	bool		oids = false;
  	bool		csv_mode = false;
! 	bool		header_line = false;
  	char	   *delim = NULL;
  	char	   *quote = NULL;
  	char	   *escape = NULL;
***************
*** 708,714 ****
  	AclMode		required_access = (is_from ? ACL_INSERT : ACL_SELECT);
  	AclResult	aclresult;
  
! 	/* Extract options from the statement node tree */
  	foreach(option, stmt->options)
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
--- 819,827 ----
  	AclMode		required_access = (is_from ? ACL_INSERT : ACL_SELECT);
  	AclResult	aclresult;
  
! 	/*
! 	 * Extract options from the statement node tree
! 	 */
  	foreach(option, stmt->options)
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
***************
*** 794,801 ****
  			force_notnull = (List *) defel->arg;
  		}
  		else
! 			elog(ERROR, "option \"%s\" not recognized",
! 				 defel->defname);
  	}
  
  	if (binary && delim)
--- 907,913 ----
  			force_notnull = (List *) defel->arg;
  		}
  		else
! 			elog(ERROR, "option \"%s\" not recognized", defel->defname);
  	}
  
  	if (binary && delim)
***************
*** 813,819 ****
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("cannot specify NULL in BINARY mode")));
  
! 	/* Set defaults */
  	if (!delim)
  		delim = csv_mode ? "," : "\t";
  
--- 925,933 ----
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("cannot specify NULL in BINARY mode")));
  
! 	/*
! 	 * Set defaults
! 	 */
  	if (!delim)
  		delim = csv_mode ? "," : "\t";
  
***************
*** 828,846 ****
  			escape = quote;
  	}
  
! 	/* Only single-character delimiter strings are supported. */
  	if (strlen(delim) != 1)
  		ereport(ERROR,
  				(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,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 942,968 ----
  			escape = quote;
  	}
  
! 	escapec = '\\';				/* will be configurable in the future */
! 
! 	/*
! 	 * Only single-character delimiter strings are supported.
! 	 */
  	if (strlen(delim) != 1)
  		ereport(ERROR,
  				(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,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 851,857 ****
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("COPY quote must be a single character")));
  
! 	/* Check escape */
  	if (!csv_mode && escape != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 973,981 ----
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("COPY quote must be a single character")));
  
! 	/*
! 	 * Check escape
! 	 */
  	if (!csv_mode && escape != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 862,868 ****
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("COPY escape must be a single character")));
  
! 	/* Check force_quote */
  	if (!csv_mode && force_quote != NIL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 986,994 ----
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("COPY escape must be a single character")));
  
! 	/*
! 	 * Check force_quote
! 	 */
  	if (!csv_mode && force_quote != NIL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 870,909 ****
  	if (force_quote != NIL && is_from)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			   errmsg("COPY force quote only available using COPY TO")));
  
! 	/* Check force_notnull */
  	if (!csv_mode && force_notnull != NIL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			  errmsg("COPY force not null available only in CSV mode")));
  	if (force_notnull != NIL && !is_from)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 		  errmsg("COPY force not null only available using COPY FROM")));
  
! 	/* Don't allow the delimiter to appear in the null string. */
  	if (strchr(null_print, delim[0]) != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("COPY delimiter must not appear in the NULL specification")));
  
! 	/* Don't allow the csv quote char to appear in the null string. */
  	if (csv_mode && strchr(null_print, quote[0]) != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("CSV quote character must not appear in the NULL specification")));
  
! 	/* Open and lock the relation, using the appropriate lock type. */
! 	rel = heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock));
  
! 	/* check read-only transaction */
! 	if (XactReadOnly && !is_from && !isTempNamespace(RelationGetNamespace(rel)))
  		ereport(ERROR,
  				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
  				 errmsg("transaction is read-only")));
  
! 	/* Check permissions. */
  	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
  								  required_access);
  	if (aclresult != ACLCHECK_OK)
--- 996,1052 ----
  	if (force_quote != NIL && is_from)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("COPY force quote only available using COPY TO")));
  
! 	/*
! 	 * Check force_notnull
! 	 */
  	if (!csv_mode && force_notnull != NIL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("COPY force not null available only in CSV mode")));
  	if (force_notnull != NIL && !is_from)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg
! 				 ("COPY force not null only available using COPY FROM")));
  
! 	/*
! 	 * Don't allow the delimiter to appear in the null string. 
! 	 */
  	if (strchr(null_print, delim[0]) != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg
! 				 ("COPY delimiter must not appear in the NULL specification")));
  
! 	/*
! 	 * Don't allow the csv quote char to appear in the null string. 
! 	 */
  	if (csv_mode && strchr(null_print, quote[0]) != NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg
! 				 ("CSV quote character must not appear in the NULL specification")));
  
! 	/*
! 	 * Open and lock the relation, using the appropriate lock type.
! 	 */
! 	rel =
! 		heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock));
  
! 	/*
! 	 * check read-only transaction
! 	 */
! 	if (XactReadOnly && !is_from
! 		&& !isTempNamespace(RelationGetNamespace(rel)))
  		ereport(ERROR,
  				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
  				 errmsg("transaction is read-only")));
  
! 	/*
! 	 * Check permissions.
! 	 */
  	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
  								  required_access);
  	if (aclresult != ACLCHECK_OK)
***************
*** 914,932 ****
  				(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 (oids && !rel->rd_rel->relhasoids)
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_COLUMN),
  				 errmsg("table \"%s\" does not have OIDs",
  						RelationGetRelationName(rel))));
  
! 	/* Generate or convert list of attributes to process */
  	attnumlist = CopyGetAttnums(rel, attnamelist);
  
! 	/* Check that FORCE QUOTE references valid COPY columns */
  	if (force_quote)
  	{
  		TupleDesc	tupDesc = RelationGetDescr(rel);
--- 1057,1081 ----
  				(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 (oids && !rel->rd_rel->relhasoids)
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_COLUMN),
  				 errmsg("table \"%s\" does not have OIDs",
  						RelationGetRelationName(rel))));
  
! 	/*
! 	 * Generate or convert list of attributes to process
! 	 */
  	attnumlist = CopyGetAttnums(rel, attnamelist);
  
! 	/*
! 	 * Check that FORCE QUOTE references valid COPY columns
! 	 */
  	if (force_quote)
  	{
  		TupleDesc	tupDesc = RelationGetDescr(rel);
***************
*** 942,953 ****
  			if (!list_member_int(attnumlist, attnum))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
! 				errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
! 					   NameStr(attr[attnum - 1]->attname))));
  		}
  	}
  
! 	/* Check that FORCE NOT NULL references valid COPY columns */
  	if (force_notnull)
  	{
  		ListCell   *cur;
--- 1091,1105 ----
  			if (!list_member_int(attnumlist, attnum))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
! 						 errmsg
! 						 ("FORCE QUOTE column \"%s\" not referenced by COPY",
! 						  NameStr(attr[attnum - 1]->attname))));
  		}
  	}
  
! 	/*
! 	 * Check that FORCE NOT NULL references valid COPY columns
! 	 */
  	if (force_notnull)
  	{
  		ListCell   *cur;
***************
*** 963,981 ****
  			if (!list_member_int(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(&attribute_buf);
  	initStringInfo(&line_buf);
  	line_buf_converted = false;
  
  	client_encoding = pg_get_client_encoding();
  	server_encoding = GetDatabaseEncoding();
  
  	copy_dest = COPY_FILE;		/* default */
  	copy_file = NULL;
  	copy_msgbuf = NULL;
--- 1115,1154 ----
  			if (!list_member_int(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(&attr_buf);
  	initStringInfo(&line_buf);
  	line_buf_converted = false;
  
  	client_encoding = pg_get_client_encoding();
  	server_encoding = GetDatabaseEncoding();
  
+ 	/*
+ 	 * check if the client encoding is one of the 5 encodings
+ 	 * that are not supported as a server encodings.
+ 	 */
+ 	switch (client_encoding)
+ 	{
+ 		case PG_SJIS:
+ 		case PG_BIG5:
+ 		case PG_GBK:
+ 		case PG_UHC:
+ 		case PG_GB18030:
+ 			client_encoding_only = true;
+ 			break;
+ 		default:
+ 			client_encoding_only = false;
+ 	}
+ 
+ 
  	copy_dest = COPY_FILE;		/* default */
  	copy_file = NULL;
  	copy_msgbuf = NULL;
***************
*** 998,1005 ****
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 					   errmsg("cannot copy to non-table relation \"%s\"",
! 							  RelationGetRelationName(rel))));
  		}
  		if (pipe)
  		{
--- 1171,1178 ----
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 						 errmsg("cannot copy to non-table relation \"%s\"",
! 								RelationGetRelationName(rel))));
  		}
  		if (pipe)
  		{
***************
*** 1017,1024 ****
  			if (copy_file == NULL)
  				ereport(ERROR,
  						(errcode_for_file_access(),
! 					 errmsg("could not open file \"%s\" for reading: %m",
! 							filename)));
  
  			fstat(fileno(copy_file), &st);
  			if (S_ISDIR(st.st_mode))
--- 1190,1197 ----
  			if (copy_file == NULL)
  				ereport(ERROR,
  						(errcode_for_file_access(),
! 						 errmsg("could not open file \"%s\" for reading: %m",
! 								filename)));
  
  			fstat(fileno(copy_file), &st);
  			if (S_ISDIR(st.st_mode))
***************
*** 1029,1036 ****
  						 errmsg("\"%s\" is a directory", filename)));
  			}
  		}
! 		CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
! 				 quote, escape, force_notnull_atts, header_line);
  	}
  	else
  	{							/* copy from database to file */
--- 1202,1218 ----
  						 errmsg("\"%s\" is a directory", filename)));
  			}
  		}
! 
! 		if (csv_mode || binary) /* old path */
! 			CopyFrom(rel, attnumlist, binary, oids, delim, null_print,
! 					 csv_mode, quote, escape, force_notnull_atts, header_line);
! 		/*
! 		 * new path for improved performance (only for delimited format for now)
! 		 */
! 		else
! 			CopyFromDelimited(rel, attnumlist, binary, oids, delim, null_print,
! 							  csv_mode, quote, escape, force_notnull_atts);
! 
  	}
  	else
  	{							/* copy from database to file */
***************
*** 1049,1056 ****
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 					 errmsg("cannot copy from non-table relation \"%s\"",
! 							RelationGetRelationName(rel))));
  		}
  		if (pipe)
  		{
--- 1231,1238 ----
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 						 errmsg("cannot copy from non-table relation \"%s\"",
! 								RelationGetRelationName(rel))));
  		}
  		if (pipe)
  		{
***************
*** 1071,1077 ****
  			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);
  			copy_file = AllocateFile(filename, PG_BINARY_W);
--- 1253,1260 ----
  			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);
  			copy_file = AllocateFile(filename, PG_BINARY_W);
***************
*** 1080,1087 ****
  			if (copy_file == NULL)
  				ereport(ERROR,
  						(errcode_for_file_access(),
! 					 errmsg("could not open file \"%s\" for writing: %m",
! 							filename)));
  
  			fstat(fileno(copy_file), &st);
  			if (S_ISDIR(st.st_mode))
--- 1263,1270 ----
  			if (copy_file == NULL)
  				ereport(ERROR,
  						(errcode_for_file_access(),
! 						 errmsg("could not open file \"%s\" for writing: %m",
! 								filename)));
  
  			fstat(fileno(copy_file), &st);
  			if (S_ISDIR(st.st_mode))
***************
*** 1099,1114 ****
  
  	if (!pipe)
  	{
! 		/* we assume only the write case could fail here */
  		if (FreeFile(copy_file))
  			ereport(ERROR,
  					(errcode_for_file_access(),
! 					 errmsg("could not write to file \"%s\": %m",
! 							filename)));
  	}
! 	pfree(attribute_buf.data);
  	pfree(line_buf.data);
  
  	/*
  	 * Close the relation.	If reading, we can release the AccessShareLock
  	 * we got; if writing, we should hold the lock until end of
--- 1282,1299 ----
  
  	if (!pipe)
  	{
! 		/*
! 		 * we assume only the write case could fail here
! 		 */
  		if (FreeFile(copy_file))
  			ereport(ERROR,
  					(errcode_for_file_access(),
! 					 errmsg("could not write to file \"%s\": %m", filename)));
  	}
! 	pfree(attr_buf.data);
  	pfree(line_buf.data);
  
+ 
  	/*
  	 * Close the relation.	If reading, we can release the AccessShareLock
  	 * we got; if writing, we should hold the lock until end of
***************
*** 1178,1184 ****
  	num_phys_attrs = tupDesc->natts;
  	attr_count = list_length(attnumlist);
  
! 	/* Get info about the columns we need to process. */
  	out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
  	force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool));
  	foreach(cur, attnumlist)
--- 1363,1371 ----
  	num_phys_attrs = tupDesc->natts;
  	attr_count = list_length(attnumlist);
  
! 	/*
! 	 * Get info about the columns we need to process.
! 	 */
  	out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
  	force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool));
  	foreach(cur, attnumlist)
***************
*** 1189,1200 ****
  
  		if (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(force_quote_atts, attnum))
--- 1376,1385 ----
  
  		if (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(force_quote_atts, attnum))
***************
*** 1217,1233 ****
  
  	if (binary)
  	{
! 		/* Generate header for a binary copy */
  		int32		tmp;
  
! 		/* Signature */
  		CopySendData((char *) BinarySignature, 11);
! 		/* Flags field */
  		tmp = 0;
  		if (oids)
  			tmp |= (1 << 16);
  		CopySendInt32(tmp);
! 		/* No header extension */
  		tmp = 0;
  		CopySendInt32(tmp);
  	}
--- 1402,1426 ----
  
  	if (binary)
  	{
! 		/*
! 		 * Generate header for a binary copy
! 		 */
  		int32		tmp;
  
! 		/*
! 		 * Signature
! 		 */
  		CopySendData((char *) BinarySignature, 11);
! 		/*
! 		 * Flags field
! 		 */
  		tmp = 0;
  		if (oids)
  			tmp |= (1 << 16);
  		CopySendInt32(tmp);
! 		/*
! 		 * No header extension
! 		 */
  		tmp = 0;
  		CopySendInt32(tmp);
  	}
***************
*** 1242,1253 ****
  				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);
--- 1435,1448 ----
  				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);
***************
*** 1280,1304 ****
  
  		if (binary)
  		{
! 			/* Binary per-tuple header */
  			CopySendInt16(attr_count);
! 			/* Send OID if wanted --- note attr_count doesn't include it */
  			if (oids)
  			{
  				Oid			oid = HeapTupleGetOid(tuple);
  
! 				/* Hack --- assume Oid is same size as int32 */
  				CopySendInt32(sizeof(int32));
  				CopySendInt32(oid);
  			}
  		}
  		else
  		{
! 			/* Text format has no per-tuple header, but send OID if wanted */
  			if (oids)
  			{
  				string = DatumGetCString(DirectFunctionCall1(oidout,
! 							  ObjectIdGetDatum(HeapTupleGetOid(tuple))));
  				CopySendString(string);
  				need_delim = true;
  			}
--- 1475,1509 ----
  
  		if (binary)
  		{
! 			/*
! 			 * Binary per-tuple header
! 			 */
  			CopySendInt16(attr_count);
! 			/*
! 			 * Send OID if wanted --- note attr_count doesn't include it 
! 			 */
  			if (oids)
  			{
  				Oid			oid = HeapTupleGetOid(tuple);
  
! 				/*
! 				 * Hack --- assume Oid is same size as int32
! 				 */
  				CopySendInt32(sizeof(int32));
  				CopySendInt32(oid);
  			}
  		}
  		else
  		{
! 			/*
! 			 * Text format has no per-tuple header, but send OID if wanted
! 			 */
  			if (oids)
  			{
  				string = DatumGetCString(DirectFunctionCall1(oidout,
! 															 ObjectIdGetDatum
! 															 (HeapTupleGetOid
! 															  (tuple))));
  				CopySendString(string);
  				need_delim = true;
  			}
***************
*** 1330,1342 ****
  			{
  				if (!binary)
  				{
! 					string = DatumGetCString(FunctionCall1(&out_functions[attnum - 1],
! 														   value));
  					if (csv_mode)
  					{
  						CopyAttributeOutCSV(string, delim, quote, escape,
! 									  (strcmp(string, null_print) == 0 ||
! 									   force_quote[attnum - 1]));
  					}
  					else
  						CopyAttributeOut(string, delim);
--- 1535,1548 ----
  			{
  				if (!binary)
  				{
! 					string =
! 						DatumGetCString(FunctionCall1
! 										(&out_functions[attnum - 1], value));
  					if (csv_mode)
  					{
  						CopyAttributeOutCSV(string, delim, quote, escape,
! 											(strcmp(string, null_print) == 0 ||
! 											 force_quote[attnum - 1]));
  					}
  					else
  						CopyAttributeOut(string, delim);
***************
*** 1346,1354 ****
  				{
  					bytea	   *outputbytes;
  
! 					outputbytes = DatumGetByteaP(FunctionCall1(&out_functions[attnum - 1],
! 															   value));
! 					/* We assume the result will not have been toasted */
  					CopySendInt32(VARSIZE(outputbytes) - VARHDRSZ);
  					CopySendData(VARDATA(outputbytes),
  								 VARSIZE(outputbytes) - VARHDRSZ);
--- 1552,1563 ----
  				{
  					bytea	   *outputbytes;
  
! 					outputbytes =
! 						DatumGetByteaP(FunctionCall1
! 									   (&out_functions[attnum - 1], value));
! 					/*
! 					 * We assume the result will not have been toasted
! 					 */
  					CopySendInt32(VARSIZE(outputbytes) - VARHDRSZ);
  					CopySendData(VARDATA(outputbytes),
  								 VARSIZE(outputbytes) - VARHDRSZ);
***************
*** 1365,1371 ****
  
  	if (binary)
  	{
! 		/* Generate trailer for a binary copy */
  		CopySendInt16(-1);
  	}
  
--- 1574,1582 ----
  
  	if (binary)
  	{
! 		/*
! 		 * Generate trailer for a binary copy
! 		 */
  		CopySendInt16(-1);
  	}
  
***************
*** 1384,1390 ****
  {
  	if (copy_binary)
  	{
! 		/* can't usefully display the data */
  		if (copy_attname)
  			errcontext("COPY %s, line %d, column %s",
  					   copy_relname, copy_lineno, copy_attname);
--- 1595,1603 ----
  {
  	if (copy_binary)
  	{
! 		/*
! 		 * can't usefully display the data 
! 		 */
  		if (copy_attname)
  			errcontext("COPY %s, line %d, column %s",
  					   copy_relname, copy_lineno, copy_attname);
***************
*** 1395,1423 ****
  	{
  		if (copy_attname)
  		{
! 			/* error is relevant to a particular column */
! 			limit_printout_length(&attribute_buf);
  			errcontext("COPY %s, line %d, column %s: \"%s\"",
! 					   copy_relname, copy_lineno, copy_attname,
! 					   attribute_buf.data);
  		}
  		else
  		{
! 			/* error is relevant to a particular line */
! 			if (line_buf_converted ||
! 				client_encoding == server_encoding)
  			{
  				limit_printout_length(&line_buf);
  				errcontext("COPY %s, line %d: \"%s\"",
! 						   copy_relname, copy_lineno,
! 						   line_buf.data);
  			}
  			else
  			{
  				/*
  				 * Here, the line buffer is still in a foreign encoding,
  				 * and indeed it's quite likely that the error is precisely
! 				 * a failure to do encoding conversion (ie, bad data).  We
  				 * dare not try to convert it, and at present there's no way
  				 * to regurgitate it without conversion.  So we have to punt
  				 * and just report the line number.
--- 1608,1641 ----
  	{
  		if (copy_attname)
  		{
! 			/*
! 			 * error is relevant to a particular column
! 			 */
! 			limit_printout_length(&attr_buf);
  			errcontext("COPY %s, line %d, column %s: \"%s\"",
! 					   copy_relname, copy_lineno, copy_attname, attr_buf.data);
  		}
  		else
  		{
! 			/*
! 			 * error is relevant to a particular line
! 			 */
! 			if (line_buf_converted || client_encoding == server_encoding)
  			{
+ 				/*
+ 				 * Strip off the newline
+ 				 */
+ 				*(line_buf.data + line_buf.len - 1) = '\0';
  				limit_printout_length(&line_buf);
  				errcontext("COPY %s, line %d: \"%s\"",
! 						   copy_relname, copy_lineno, line_buf.data);
  			}
  			else
  			{
  				/*
  				 * Here, the line buffer is still in a foreign encoding,
  				 * and indeed it's quite likely that the error is precisely
! 				 * a failure to do encoding conversion (ie, bad data).	We
  				 * dare not try to convert it, and at present there's no way
  				 * to regurgitate it without conversion.  So we have to punt
  				 * and just report the line number.
***************
*** 1445,1465 ****
  
  	int			len;
  
! 	/* Fast path if definitely okay */
  	if (buf->len <= MAX_COPY_DATA_DISPLAY)
  		return;
  
! 	/* Apply encoding-dependent truncation */
  	len = pg_mbcliplen(buf->data, buf->len, MAX_COPY_DATA_DISPLAY);
  	if (buf->len <= len)
  		return;					/* no need to truncate */
  	buf->len = len;
  	buf->data[len] = '\0';
  
! 	/* Add "..." to show we truncated the input */
  	appendStringInfoString(buf, "...");
  }
  
  /*
   * Copy FROM file to relation.
   */
--- 1663,1690 ----
  
  	int			len;
  
! 	/*
! 	 * Fast path if definitely okay
! 	 */
  	if (buf->len <= MAX_COPY_DATA_DISPLAY)
  		return;
  
! 	/*
! 	 * Apply encoding-dependent truncation
! 	 */
  	len = pg_mbcliplen(buf->data, buf->len, MAX_COPY_DATA_DISPLAY);
  	if (buf->len <= len)
  		return;					/* no need to truncate */
  	buf->len = len;
  	buf->data[len] = '\0';
  
! 	/*
! 	 * Add "..." to show we truncated the input
! 	 */
  	appendStringInfoString(buf, "...");
  }
  
+ 
  /*
   * Copy FROM file to relation.
   */
***************
*** 1510,1521 ****
  	 * code here that basically duplicated execUtils.c ...)
  	 */
  	resultRelInfo = makeNode(ResultRelInfo);
  	resultRelInfo->ri_RangeTableIndex = 1;		/* dummy */
  	resultRelInfo->ri_RelationDesc = rel;
  	resultRelInfo->ri_TrigDesc = CopyTriggerDesc(rel->trigdesc);
  	if (resultRelInfo->ri_TrigDesc)
  		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
! 			palloc0(resultRelInfo->ri_TrigDesc->numtriggers * sizeof(FmgrInfo));
  	resultRelInfo->ri_TrigInstrument = NULL;
  
  	ExecOpenIndices(resultRelInfo);
--- 1735,1748 ----
  	 * code here that basically duplicated execUtils.c ...)
  	 */
  	resultRelInfo = makeNode(ResultRelInfo);
+ 
  	resultRelInfo->ri_RangeTableIndex = 1;		/* dummy */
  	resultRelInfo->ri_RelationDesc = rel;
  	resultRelInfo->ri_TrigDesc = CopyTriggerDesc(rel->trigdesc);
  	if (resultRelInfo->ri_TrigDesc)
  		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
! 			palloc0(resultRelInfo->ri_TrigDesc->numtriggers *
! 					sizeof(FmgrInfo));
  	resultRelInfo->ri_TrigInstrument = NULL;
  
  	ExecOpenIndices(resultRelInfo);
***************
*** 1524,1530 ****
  	estate->es_num_result_relations = 1;
  	estate->es_result_relation_info = resultRelInfo;
  
! 	/* Set up a tuple slot too */
  	slot = MakeSingleTupleTableSlot(tupDesc);
  
  	econtext = GetPerTupleExprContext(estate);
--- 1751,1759 ----
  	estate->es_num_result_relations = 1;
  	estate->es_result_relation_info = resultRelInfo;
  
! 	/*
! 	 * Set up a tuple slot too
! 	 */
  	slot = MakeSingleTupleTableSlot(tupDesc);
  
  	econtext = GetPerTupleExprContext(estate);
***************
*** 1539,1557 ****
  	typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
  	defmap = (int *) palloc(num_phys_attrs * sizeof(int));
  	defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
! 	constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
  	force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
  
  	for (attnum = 1; attnum <= num_phys_attrs; attnum++)
  	{
! 		/* We don't need info for dropped attributes */
  		if (attr[attnum - 1]->attisdropped)
  			continue;
  
! 		/* Fetch the input function and typioparam info */
  		if (binary)
  			getTypeBinaryInputInfo(attr[attnum - 1]->atttypid,
! 								 &in_func_oid, &typioparams[attnum - 1]);
  		else
  			getTypeInputInfo(attr[attnum - 1]->atttypid,
  							 &in_func_oid, &typioparams[attnum - 1]);
--- 1768,1791 ----
  	typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
  	defmap = (int *) palloc(num_phys_attrs * sizeof(int));
  	defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
! 	constraintexprs =
! 		(ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
  	force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
  
  	for (attnum = 1; attnum <= num_phys_attrs; attnum++)
  	{
! 		/*
! 		 * We don't need info for dropped attributes 
! 		 */
  		if (attr[attnum - 1]->attisdropped)
  			continue;
  
! 		/*
! 		 * Fetch the input function and typioparam info
! 		 */
  		if (binary)
  			getTypeBinaryInputInfo(attr[attnum - 1]->atttypid,
! 								   &in_func_oid, &typioparams[attnum - 1]);
  		else
  			getTypeInputInfo(attr[attnum - 1]->atttypid,
  							 &in_func_oid, &typioparams[attnum - 1]);
***************
*** 1562,1572 ****
  		else
  			force_notnull[attnum - 1] = false;
  
! 		/* Get default info if needed */
  		if (!list_member_int(attnumlist, attnum))
  		{
! 			/* attribute is NOT to be copied from input */
! 			/* use default value if one exists */
  			Node	   *defexpr = build_column_default(rel, attnum);
  
  			if (defexpr != NULL)
--- 1796,1812 ----
  		else
  			force_notnull[attnum - 1] = false;
  
! 		/*
! 		 * Get default info if needed
! 		 */
  		if (!list_member_int(attnumlist, attnum))
  		{
! 			/*
! 			 * attribute is NOT to be copied from input
! 			 */
! 			/*
! 			 * use default value if one exists
! 			 */
  			Node	   *defexpr = build_column_default(rel, attnum);
  
  			if (defexpr != NULL)
***************
*** 1578,1584 ****
  			}
  		}
  
! 		/* If it's a domain type, set up to check domain constraints */
  		if (get_typtype(attr[attnum - 1]->atttypid) == 'd')
  		{
  			Param	   *prm;
--- 1818,1826 ----
  			}
  		}
  
! 		/*
! 		 * If it's a domain type, set up to check domain constraints 
! 		 */
  		if (get_typtype(attr[attnum - 1]->atttypid) == 'd')
  		{
  			Param	   *prm;
***************
*** 1593,1598 ****
--- 1835,1841 ----
  			 * actual datum during the data input loop.
  			 */
  			prm = makeNode(Param);
+ 
  			prm->paramkind = PARAM_EXEC;
  			prm->paramid = 0;
  			prm->paramtype = getBaseType(attr[attnum - 1]->atttypid);
***************
*** 1608,1614 ****
  		}
  	}
  
! 	/* Prepare to catch AFTER triggers. */
  	AfterTriggerBeginQuery();
  
  	/*
--- 1851,1859 ----
  		}
  	}
  
! 	/*
! 	 * Prepare to catch AFTER triggers.
! 	 */
  	AfterTriggerBeginQuery();
  
  	/*
***************
*** 1624,1640 ****
  		file_has_oids = oids;	/* must rely on user to tell us this... */
  	else
  	{
! 		/* Read and verify binary header */
  		char		readSig[11];
  		int32		tmp;
  
! 		/* Signature */
  		CopyGetData(readSig, 11);
  		if (CopyGetEof() || memcmp(readSig, BinarySignature, 11) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  					 errmsg("COPY file signature not recognized")));
! 		/* Flags field */
  		tmp = CopyGetInt32();
  		if (CopyGetEof())
  			ereport(ERROR,
--- 1869,1891 ----
  		file_has_oids = oids;	/* must rely on user to tell us this... */
  	else
  	{
! 		/*
! 		 * Read and verify binary header
! 		 */
  		char		readSig[11];
  		int32		tmp;
  
! 		/*
! 		 * Signature
! 		 */
  		CopyGetData(readSig, 11);
  		if (CopyGetEof() || memcmp(readSig, BinarySignature, 11) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  					 errmsg("COPY file signature not recognized")));
! 		/*
! 		 * Flags field
! 		 */
  		tmp = CopyGetInt32();
  		if (CopyGetEof())
  			ereport(ERROR,
***************
*** 1645,1684 ****
  		if ((tmp >> 16) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 			 errmsg("unrecognized critical flags in COPY file header")));
! 		/* Header extension length */
  		tmp = CopyGetInt32();
  		if (CopyGetEof() || tmp < 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 				   errmsg("invalid COPY file header (missing length)")));
! 		/* Skip extension header, if present */
  		while (tmp-- > 0)
  		{
  			CopyGetData(readSig, 1);
  			if (CopyGetEof())
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg("invalid COPY file header (wrong length)")));
  		}
  	}
  
  	if (file_has_oids && binary)
  	{
! 		getTypeBinaryInputInfo(OIDOID,
! 							   &in_func_oid, &oid_typioparam);
  		fmgr_info(in_func_oid, &oid_in_function);
  	}
  
  	values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
  	nulls = (char *) palloc(num_phys_attrs * sizeof(char));
  
! 	/* Make room for a PARAM_EXEC value for domain constraint checks */
  	if (hasConstraints)
  		econtext->ecxt_param_exec_vals = (ParamExecData *)
  			palloc0(sizeof(ParamExecData));
  
! 	/* Initialize static variables */
  	fe_eof = false;
  	eol_type = EOL_UNKNOWN;
  	copy_binary = binary;
--- 1896,1943 ----
  		if ((tmp >> 16) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg
! 					 ("unrecognized critical flags in COPY file header")));
! 		/*
! 		 * Header extension length
! 		 */
  		tmp = CopyGetInt32();
  		if (CopyGetEof() || tmp < 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg("invalid COPY file header (missing length)")));
! 		/*
! 		 * Skip extension header, if present
! 		 */
  		while (tmp-- > 0)
  		{
  			CopyGetData(readSig, 1);
  			if (CopyGetEof())
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("invalid COPY file header (wrong length)")));
  		}
  	}
  
  	if (file_has_oids && binary)
  	{
! 		getTypeBinaryInputInfo(OIDOID, &in_func_oid, &oid_typioparam);
  		fmgr_info(in_func_oid, &oid_in_function);
  	}
  
  	values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
  	nulls = (char *) palloc(num_phys_attrs * sizeof(char));
  
! 	/*
! 	 * Make room for a PARAM_EXEC value for domain constraint checks
! 	 */
  	if (hasConstraints)
  		econtext->ecxt_param_exec_vals = (ParamExecData *)
  			palloc0(sizeof(ParamExecData));
  
! 	/*
! 	 * Initialize static variables
! 	 */
  	fe_eof = false;
  	eol_type = EOL_UNKNOWN;
  	copy_binary = binary;
***************
*** 1686,1702 ****
  	copy_lineno = 0;
  	copy_attname = NULL;
  
! 	/* Set up callback to identify error line number */
  	errcontext.callback = copy_in_error_callback;
  	errcontext.arg = NULL;
  	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)
--- 1945,1965 ----
  	copy_lineno = 0;
  	copy_attname = NULL;
  
! 	/*
! 	 * Set up callback to identify error line number
! 	 */
  	errcontext.callback = copy_in_error_callback;
  	errcontext.arg = NULL;
  	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)
***************
*** 1708,1720 ****
  
  		copy_lineno++;
  
! 		/* Reset the per-tuple exprcontext */
  		ResetPerTupleExprContext(estate);
  
! 		/* Switch into its memory context */
  		MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
  
! 		/* Initialize all values for row to NULL */
  		MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  		MemSet(nulls, 'n', num_phys_attrs * sizeof(char));
  
--- 1971,1989 ----
  
  		copy_lineno++;
  
! 		/*
! 		 * Reset the per-tuple exprcontext
! 		 */
  		ResetPerTupleExprContext(estate);
  
! 		/*
! 		 * Switch into its memory context
! 		 */
  		MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
  
! 		/*
! 		 * Initialize all values for row to NULL
! 		 */
  		MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  		MemSet(nulls, 'n', num_phys_attrs * sizeof(char));
  
***************
*** 1724,1731 ****
  			char	   *string;
  			ListCell   *cur;
  
! 			/* Actually read the line into memory here */
! 			done = csv_mode ? 
  				CopyReadLine(quote, escape) : CopyReadLine(NULL, NULL);
  
  			/*
--- 1993,2002 ----
  			char	   *string;
  			ListCell   *cur;
  
! 			/*
! 			 * Actually read the line into memory here
! 			 */
! 			done = csv_mode ?
  				CopyReadLine(quote, escape) : CopyReadLine(NULL, NULL);
  
  			/*
***************
*** 1739,1745 ****
  
  			if (file_has_oids)
  			{
! 				/* can't be in CSV mode here */
  				string = CopyReadAttribute(delim, null_print,
  										   &result, &isnull);
  
--- 2010,2018 ----
  
  			if (file_has_oids)
  			{
! 				/*
! 				 * can't be in CSV mode here 
! 				 */
  				string = CopyReadAttribute(delim, null_print,
  										   &result, &isnull);
  
***************
*** 1751,1757 ****
  				{
  					copy_attname = "oid";
  					loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
! 											   CStringGetDatum(string)));
  					if (loaded_oid == InvalidOid)
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
--- 2024,2031 ----
  				{
  					copy_attname = "oid";
  					loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
! 																	  CStringGetDatum
! 																	  (string)));
  					if (loaded_oid == InvalidOid)
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
***************
*** 1760,1766 ****
  				}
  			}
  
! 			/* Loop to read the user attributes on the line. */
  			foreach(cur, attnumlist)
  			{
  				int			attnum = lfirst_int(cur);
--- 2034,2042 ----
  				}
  			}
  
! 			/*
! 			 * Loop to read the user attributes on the line.
! 			 */
  			foreach(cur, attnumlist)
  			{
  				int			attnum = lfirst_int(cur);
***************
*** 1779,1789 ****
  				if (csv_mode)
  				{
  					string = CopyReadAttributeCSV(delim, null_print, quote,
! 											   escape, &result, &isnull);
  					if (result == UNTERMINATED_FIELD)
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							   errmsg("unterminated CSV quoted field")));
  				}
  				else
  					string = CopyReadAttribute(delim, null_print,
--- 2055,2065 ----
  				if (csv_mode)
  				{
  					string = CopyReadAttributeCSV(delim, null_print, quote,
! 												  escape, &result, &isnull);
  					if (result == UNTERMINATED_FIELD)
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 								 errmsg("unterminated CSV quoted field")));
  				}
  				else
  					string = CopyReadAttribute(delim, null_print,
***************
*** 1795,1808 ****
  					isnull = false;
  				}
  
! 				/* we read an SQL NULL, no need to do anything */
  				if (!isnull)
  				{
  					copy_attname = NameStr(attr[m]->attname);
  					values[m] = FunctionCall3(&in_functions[m],
  											  CStringGetDatum(string),
! 										ObjectIdGetDatum(typioparams[m]),
! 									  Int32GetDatum(attr[m]->atttypmod));
  					nulls[m] = ' ';
  					copy_attname = NULL;
  				}
--- 2071,2087 ----
  					isnull = false;
  				}
  
! 				/*
! 				 * we read an SQL NULL, no need to do anything
! 				 */
  				if (!isnull)
  				{
  					copy_attname = NameStr(attr[m]->attname);
  					values[m] = FunctionCall3(&in_functions[m],
  											  CStringGetDatum(string),
! 											  ObjectIdGetDatum(typioparams[m]),
! 											  Int32GetDatum(attr[m]->
! 															atttypmod));
  					nulls[m] = ' ';
  					copy_attname = NULL;
  				}
***************
*** 1818,1828 ****
  			if (result == NORMAL_ATTR && line_buf.len != 0)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					   errmsg("extra data after last expected column")));
  		}
  		else
  		{
! 			/* binary */
  			int16		fld_count;
  			ListCell   *cur;
  
--- 2097,2109 ----
  			if (result == NORMAL_ATTR && line_buf.len != 0)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("extra data after last expected column")));
  		}
  		else
  		{
! 			/*
! 			 * binary
! 			 */
  			int16		fld_count;
  			ListCell   *cur;
  
***************
*** 1844,1851 ****
  				copy_attname = "oid";
  				loaded_oid =
  					DatumGetObjectId(CopyReadBinaryAttribute(0,
! 														&oid_in_function,
! 														  oid_typioparam,
  															 &isnull));
  				if (isnull || loaded_oid == InvalidOid)
  					ereport(ERROR,
--- 2125,2132 ----
  				copy_attname = "oid";
  				loaded_oid =
  					DatumGetObjectId(CopyReadBinaryAttribute(0,
! 															 &oid_in_function,
! 															 oid_typioparam,
  															 &isnull));
  				if (isnull || loaded_oid == InvalidOid)
  					ereport(ERROR,
***************
*** 1864,1871 ****
  				i++;
  				values[m] = CopyReadBinaryAttribute(i,
  													&in_functions[m],
! 													typioparams[m],
! 													&isnull);
  				nulls[m] = isnull ? 'n' : ' ';
  				copy_attname = NULL;
  			}
--- 2145,2151 ----
  				i++;
  				values[m] = CopyReadBinaryAttribute(i,
  													&in_functions[m],
! 													typioparams[m], &isnull);
  				nulls[m] = isnull ? 'n' : ' ';
  				copy_attname = NULL;
  			}
***************
*** 1884,1890 ****
  				nulls[defmap[i]] = ' ';
  		}
  
! 		/* Next apply any domain constraints */
  		if (hasConstraints)
  		{
  			ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0];
--- 2164,2172 ----
  				nulls[defmap[i]] = ' ';
  		}
  
! 		/*
! 		 * Next apply any domain constraints
! 		 */
  		if (hasConstraints)
  		{
  			ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0];
***************
*** 1896,1902 ****
  				if (exprstate == NULL)
  					continue;	/* no constraint for this attr */
  
! 				/* Insert current row's value into the Param value */
  				prmdata->value = values[i];
  				prmdata->isnull = (nulls[i] == 'n');
  
--- 2178,2186 ----
  				if (exprstate == NULL)
  					continue;	/* no constraint for this attr */
  
! 				/*
! 				 * Insert current row's value into the Param value 
! 				 */
  				prmdata->value = values[i];
  				prmdata->isnull = (nulls[i] == 'n');
  
***************
*** 1905,1928 ****
  				 * expression to replace the value (consider e.g. a
  				 * timestamp precision restriction).
  				 */
! 				values[i] = ExecEvalExpr(exprstate, econtext,
! 										 &isnull, NULL);
  				nulls[i] = isnull ? 'n' : ' ';
  			}
  		}
  
! 		/* And now we can form the input tuple. */
  		tuple = heap_formtuple(tupDesc, values, nulls);
  
  		if (oids && file_has_oids)
  			HeapTupleSetOid(tuple, loaded_oid);
  
! 		/* Triggers and stuff need to be invoked in query context. */
  		MemoryContextSwitchTo(oldcontext);
  
  		skip_tuple = false;
  
! 		/* BEFORE ROW INSERT Triggers */
  		if (resultRelInfo->ri_TrigDesc &&
  			resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
  		{
--- 2189,2217 ----
  				 * expression to replace the value (consider e.g. a
  				 * timestamp precision restriction).
  				 */
! 				values[i] = ExecEvalExpr(exprstate, econtext, &isnull, NULL);
  				nulls[i] = isnull ? 'n' : ' ';
  			}
  		}
  
! 		/*
! 		 * And now we can form the input tuple.
! 		 */
  		tuple = heap_formtuple(tupDesc, values, nulls);
  
  		if (oids && file_has_oids)
  			HeapTupleSetOid(tuple, loaded_oid);
  
! 		/*
! 		 * Triggers and stuff need to be invoked in query context.
! 		 */
  		MemoryContextSwitchTo(oldcontext);
  
  		skip_tuple = false;
  
! 		/*
! 		 * BEFORE ROW INSERT Triggers
! 		 */
  		if (resultRelInfo->ri_TrigDesc &&
  			resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
  		{
***************
*** 1941,1973 ****
  
  		if (!skip_tuple)
  		{
! 			/* Place tuple in tuple slot */
  			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
  
! 			/* Check the constraints of the tuple */
  			if (rel->rd_att->constr)
  				ExecConstraints(resultRelInfo, slot, estate);
  
! 			/* OK, store the tuple and create index entries for it */
  			simple_heap_insert(rel, tuple);
  
  			if (resultRelInfo->ri_NumIndices > 0)
  				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
  
! 			/* AFTER ROW INSERT Triggers */
  			ExecARInsertTriggers(estate, resultRelInfo, tuple);
  		}
  	}
  
! 	/* Done, clean up */
  	error_context_stack = errcontext.previous;
  
  	MemoryContextSwitchTo(oldcontext);
  
! 	/* Execute AFTER STATEMENT insertion triggers */
  	ExecASInsertTriggers(estate, resultRelInfo);
  
! 	/* Handle queued AFTER triggers */
  	AfterTriggerEndQuery(estate);
  
  	pfree(values);
--- 2230,2276 ----
  
  		if (!skip_tuple)
  		{
! 			/*
! 			 * Place tuple in tuple slot
! 			 */
  			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
  
! 			/*
! 			 * Check the constraints of the tuple
! 			 */
  			if (rel->rd_att->constr)
  				ExecConstraints(resultRelInfo, slot, estate);
  
! 			/*
! 			 * OK, store the tuple and create index entries for it
! 			 */
  			simple_heap_insert(rel, tuple);
  
  			if (resultRelInfo->ri_NumIndices > 0)
  				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
  
! 			/*
! 			 * AFTER ROW INSERT Triggers
! 			 */
  			ExecARInsertTriggers(estate, resultRelInfo, tuple);
  		}
  	}
  
! 	/*
! 	 * Done, clean up
! 	 */
  	error_context_stack = errcontext.previous;
  
  	MemoryContextSwitchTo(oldcontext);
  
! 	/*
! 	 * Execute AFTER STATEMENT insertion triggers
! 	 */
  	ExecASInsertTriggers(estate, resultRelInfo);
  
! 	/*
! 	 * Handle queued AFTER triggers
! 	 */
  	AfterTriggerEndQuery(estate);
  
  	pfree(values);
***************
*** 1987,2019 ****
  	FreeExecutorState(estate);
  }
  
- 
  /*
!  * Read the next input line and stash it in line_buf, with conversion to
!  * server encoding.
!  *
!  * Result is true if read was terminated by EOF, false if terminated
!  * by newline.
   */
! static bool
! CopyReadLine(char * quote, char * escape)
  {
! 	bool		result;
! 	bool		change_encoding = (client_encoding != server_encoding);
! 	int			c;
! 	int			mblen;
! 	int			j;
  	unsigned char s[2];
  	char	   *cvt;
! 	bool        in_quote = false, last_was_esc = false, csv_mode = false;
! 	char        quotec = '\0', escapec = '\0';
  
  	if (quote)
  	{
  		csv_mode = true;
  		quotec = quote[0];
  		escapec = escape[0];
! 		/* ignore special escape processing if it's the same as quotec */
  		if (quotec == escapec)
  			escapec = '\0';
  	}
--- 2290,2852 ----
  	FreeExecutorState(estate);
  }
  
  /*
!  * Copy FROM file to relation with faster processing.
   */
! static void
! CopyFromDelimited(Relation rel, List *attnumlist, bool binary, bool oids,
! 				  char *delim, char *null_print, bool csv_mode, char *quote,
! 				  char *escape, List *force_notnull_atts)
  {
! 	HeapTuple	tuple;
! 	TupleDesc	tupDesc;
! 	Form_pg_attribute *attr;
! 	AttrNumber	num_phys_attrs,
! 				attr_count,
! 				num_defaults;
! 	FmgrInfo   *in_functions;
! 	Oid		   *typioparams;
! 	ExprState **constraintexprs;
! 	bool	   *force_notnull;
! 	bool		hasConstraints = false;
! 	int			attnum;
! 	int			i;
! 	Oid			in_func_oid;
! 	Datum	   *values;
! 	char	   *nulls;
! 
! 	/*
! 	 * an array of offsets into attr_buf that point to beginning of attributes
! 	 */
! 	int		   *attr_offsets;
! 	bool		isnull;
! 	int			null_print_len; /* length of null print */
! 	ResultRelInfo *resultRelInfo;
! 	EState	   *estate = CreateExecutorState(); /* for ExecConstraints() */
! 	TupleTableSlot *slot;
! 	bool		file_has_oids;
! 	int		   *defmap;
! 	ExprState **defexprs;		/* array of default att expressions */
! 	ExprContext *econtext;		/* used for ExecEvalExpr for default atts */
! 	MemoryContext oldcontext = CurrentMemoryContext;
! 	ErrorContextCallback errcontext;
! 	bool		no_more_data;
! 	CopyReadResult result;
! 	ListCell   *cur;
! 
! 
! 	tupDesc = RelationGetDescr(rel);
! 	attr = tupDesc->attrs;
! 	num_phys_attrs = tupDesc->natts;
! 	attr_count = list_length(attnumlist);
! 	num_defaults = 0;
! 
! 	/*
! 	 * We need a ResultRelInfo so we can use the regular executor's
! 	 * index-entry-making machinery.  (There used to be a huge amount of
! 	 * code here that basically duplicated execUtils.c ...)
! 	 */
! 	resultRelInfo = makeNode(ResultRelInfo);
! 
! 	resultRelInfo->ri_RangeTableIndex = 1;		/* dummy */
! 	resultRelInfo->ri_RelationDesc = rel;
! 	resultRelInfo->ri_TrigDesc = CopyTriggerDesc(rel->trigdesc);
! 	if (resultRelInfo->ri_TrigDesc)
! 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
! 			palloc0(resultRelInfo->ri_TrigDesc->numtriggers *
! 					sizeof(FmgrInfo));
! 	resultRelInfo->ri_TrigInstrument = NULL;
! 
! 	ExecOpenIndices(resultRelInfo);
! 
! 	estate->es_result_relations = resultRelInfo;
! 	estate->es_num_result_relations = 1;
! 	estate->es_result_relation_info = resultRelInfo;
! 
! 	/*
! 	 * Set up a tuple slot too
! 	 */
! 	slot = MakeSingleTupleTableSlot(tupDesc);
! 
! 	econtext = GetPerTupleExprContext(estate);
! 
! 	/*
! 	 * Pick up the required catalog information for each attribute in the
! 	 * relation, including the input function, the element type (to pass
! 	 * to the input function), and info about defaults and constraints.
! 	 * (Which input function we use depends on text/binary format choice.)
! 	 */
! 	in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
! 	typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
! 	defmap = (int *) palloc(num_phys_attrs * sizeof(int));
! 	defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
! 	constraintexprs =
! 		(ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
! 	force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
! 
! 	for (attnum = 1; attnum <= num_phys_attrs; attnum++)
! 	{
! 		/*
! 		 * We don't need info for dropped attributes 
! 		 */
! 		if (attr[attnum - 1]->attisdropped)
! 			continue;
! 
! 		getTypeInputInfo(attr[attnum - 1]->atttypid,
! 						 &in_func_oid, &typioparams[attnum - 1]);
! 
! 		fmgr_info(in_func_oid, &in_functions[attnum - 1]);
! 
! 		if (list_member_int(force_notnull_atts, attnum))
! 			force_notnull[attnum - 1] = true;
! 		else
! 			force_notnull[attnum - 1] = false;
! 
! 		/*
! 		 * Get default info if needed
! 		 */
! 		if (!list_member_int(attnumlist, attnum))
! 		{
! 			/*
! 			 * attribute is NOT to be copied from input
! 			 */
! 			/*
! 			 * use default value if one exists
! 			 */
! 			Node	   *defexpr = build_column_default(rel, attnum);
! 
! 			if (defexpr != NULL)
! 			{
! 				defexprs[num_defaults] = ExecPrepareExpr((Expr *) defexpr,
! 														 estate);
! 				defmap[num_defaults] = attnum - 1;
! 				num_defaults++;
! 			}
! 		}
! 
! 		/*
! 		 * If it's a domain type, set up to check domain constraints 
! 		 */
! 		if (get_typtype(attr[attnum - 1]->atttypid) == 'd')
! 		{
! 			Param	   *prm;
! 			Node	   *node;
! 
! 			/*
! 			 * Easiest way to do this is to use parse_coerce.c to set up
! 			 * an expression that checks the constraints.  (At present,
! 			 * the expression might contain a length-coercion-function
! 			 * call and/or CoerceToDomain nodes.)  The bottom of the
! 			 * expression is a Param node so that we can fill in the
! 			 * actual datum during the data input loop.
! 			 */
! 			prm = makeNode(Param);
! 
! 			prm->paramkind = PARAM_EXEC;
! 			prm->paramid = 0;
! 			prm->paramtype = getBaseType(attr[attnum - 1]->atttypid);
! 
! 			node = coerce_to_domain((Node *) prm,
! 									prm->paramtype,
! 									attr[attnum - 1]->atttypid,
! 									COERCE_IMPLICIT_CAST, false, false);
! 
! 			constraintexprs[attnum - 1] =
! 				ExecPrepareExpr((Expr *) node, estate);
! 			hasConstraints = true;
! 		}
! 	}
! 
! 	/*
! 	 * Prepare to catch AFTER triggers.
! 	 */
! 	AfterTriggerBeginQuery();
! 
! 	/*
! 	 * Check BEFORE STATEMENT insertion triggers. It's debateable whether
! 	 * we should do this for COPY, since it's not really an "INSERT"
! 	 * statement as such. However, executing these triggers maintains
! 	 * consistency with the EACH ROW triggers that we already fire on
! 	 * COPY.
! 	 */
! 	ExecBSInsertTriggers(estate, resultRelInfo);
! 
! 	file_has_oids = oids;		/* must rely on user to tell us this... */
! 
! 	values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
! 	nulls = (char *) palloc(num_phys_attrs * sizeof(char));
! 	attr_offsets = (int *) palloc(num_phys_attrs * sizeof(int));
! 
! 	/*
! 	 * Make room for a PARAM_EXEC value for domain constraint checks
! 	 */
! 	if (hasConstraints)
! 		econtext->ecxt_param_exec_vals = (ParamExecData *)
! 			palloc0(sizeof(ParamExecData));
! 
! 	/*
! 	 * Initialize static variables
! 	 */
! 	fe_eof = false;
! 	eol_type = EOL_UNKNOWN;
! 	copy_binary = binary;
! 	copy_relname = RelationGetRelationName(rel);
! 	copy_lineno = 0;
! 	copy_attname = NULL;
! 	line_buf_converted = false;
! 
! 	/*
! 	 * it is MUCH faster to do this once here than in the attribute parse loop
! 	 */
! 	null_print_len = strlen(null_print);
! 
! 	/*
! 	 * Set up callback to identify error line number
! 	 */
! 	errcontext.callback = copy_in_error_callback;
! 	errcontext.arg = NULL;
! 	errcontext.previous = error_context_stack;
! 	error_context_stack = &errcontext;
! 
! 	/*
! 	 * Set up data buffer to hold a chunk of data
! 	 */
! 	MemSet(input_buf, ' ', COPY_BUF_SIZE * sizeof(char));
! 	input_buf[COPY_BUF_SIZE] = '\0';
! 
! 	no_more_data = false;		/* no more input data to read from file or FE */
! 	line_done = true;
! 
! 	do
! 	{
! 		/*
! 		 * read a chunk of data into the buffer
! 		 */
! 		size_t		bytesread = CopyGetData(input_buf, COPY_BUF_SIZE);
! 
! 		buf_done = false;
! 
! 		/*
! 		 * set buffer pointers to beginning of the buffer
! 		 */
! 		begloc = input_buf;
! 		buffer_index = 0;
! 
! 		/*
! 		 * continue if some bytes were read or if we didn't reach EOF. if we both *
! 		 * * reached EOF _and_ no bytes were read, quit the loop we are done
! 		 */
! 		if (bytesread > 0 || !fe_eof)
! 		{
! 
! 			while (!buf_done)
! 			{
! 				bool		skip_tuple;
! 				Oid			loaded_oid = InvalidOid;
! 
! 				CHECK_FOR_INTERRUPTS();
! 
! 				/*
! 				 * Reset the per-tuple exprcontext
! 				 */
! 				ResetPerTupleExprContext(estate);
! 
! 				/*
! 				 * Switch into its memory context
! 				 */
! 				MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
! 
! 				/*
! 				 * Initialize all values for row to NULL
! 				 */
! 				MemSet(values, 0, num_phys_attrs * sizeof(Datum));
! 				MemSet(nulls, 'n', num_phys_attrs * sizeof(char));
! 				/*
! 				 * reset attribute pointers
! 				 */
! 				MemSet(attr_offsets, 0, num_phys_attrs * sizeof(int));
! 
! 				result = NORMAL_ATTR;
! 
! 				/*
! 				 * Actually read the line into memory here
! 				 */
! 				line_done = CopyReadLineBuffered(bytesread);
! 				copy_lineno++;
! 
! 				/*
! 				 * if didn't finish processing data line, skip att parsing and read more data,
! 				 * * unless there is no more data to read... (which means that the original last
! 				 * * data line is missing attrs and we want to catch that error)
! 				 */
! 				if (!line_done)
! 				{
! 					copy_lineno--;
! 					if (!fe_eof || buf_done)
! 						break;
! 				}
! 
! 				if (file_has_oids)
! 				{
! 					char	   *oid_string;
! 
! 					/*
! 					 * can't be in CSV mode here 
! 					 */
! 					oid_string =
! 						CopyReadOidAttr(delim, null_print, null_print_len,
! 										&result, &isnull);
! 
! 					if (isnull)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 								 errmsg("null OID in COPY data")));
! 					else
! 					{
! 						copy_attname = "oid";
! 						loaded_oid =
! 							DatumGetObjectId(DirectFunctionCall1
! 											 (oidin,
! 											  CStringGetDatum(oid_string)));
! 						if (loaded_oid == InvalidOid)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 									 errmsg("invalid OID in COPY data")));
! 						copy_attname = NULL;
! 					}
! 				}
! 
! 				/*
! 				 * parse all the attribute in the data line
! 				 */
! 				CopyReadAllAttrs(delim, null_print, null_print_len,
! 								 nulls, attnumlist, attr_offsets,
! 								 num_phys_attrs, attr);
! 
! 				/*
! 				 * Loop to read the user attributes on the line.
! 				 */
! 				foreach(cur, attnumlist)
! 				{
! 					int			attnum = lfirst_int(cur);
! 					int			m = attnum - 1;
! 
! 					if (nulls[m] == ' ')
! 						isnull = false;
! 					else
! 						isnull = true;
! 
! 					/*
! 					 * we read an SQL NULL, no need to do anything
! 					 */
! 					if (!isnull)
! 					{
! 						copy_attname = NameStr(attr[m]->attname);
! 						values[m] = FunctionCall3(&in_functions[m],
! 												  CStringGetDatum(attr_buf.
! 																  data +
! 																  attr_offsets
! 																  [m]),
! 												  ObjectIdGetDatum(typioparams
! 																   [m]),
! 												  Int32GetDatum(attr[m]->
! 																atttypmod));
! 						copy_attname = NULL;
! 					}
! 				}
! 
! 				/*
! 				 * Now compute and insert any defaults available for the columns
! 				 * not provided by the input data.	Anything not processed here or
! 				 * above will remain NULL.
! 				 */
! 				for (i = 0; i < num_defaults; i++)
! 				{
! 					values[defmap[i]] =
! 						ExecEvalExpr(defexprs[i], econtext, &isnull, NULL);
! 					if (!isnull)
! 						nulls[defmap[i]] = ' ';
! 				}
! 
! 				/*
! 				 * Next apply any domain constraints
! 				 */
! 				if (hasConstraints)
! 				{
! 					ParamExecData *prmdata =
! 						&econtext->ecxt_param_exec_vals[0];
! 
! 					for (i = 0; i < num_phys_attrs; i++)
! 					{
! 						ExprState  *exprstate = constraintexprs[i];
! 
! 						if (exprstate == NULL)
! 							continue;	/* no constraint for this attr */
! 
! 						/*
! 						 * Insert current row's value into the Param value 
! 						 */
! 						prmdata->value = values[i];
! 						prmdata->isnull = (nulls[i] == 'n');
! 
! 						/*
! 						 * Execute the constraint expression.  Allow the
! 						 * expression to replace the value (consider e.g. a
! 						 * timestamp precision restriction).
! 						 */
! 						values[i] = ExecEvalExpr(exprstate, econtext,
! 												 &isnull, NULL);
! 						nulls[i] = isnull ? 'n' : ' ';
! 					}
! 				}
! 
! 				/*
! 				 * And now we can form the input tuple.
! 				 */
! 				tuple = heap_formtuple(tupDesc, values, nulls);
! 
! 				if (oids && file_has_oids)
! 					HeapTupleSetOid(tuple, loaded_oid);
! 
! 				/*
! 				 * Triggers and stuff need to be invoked in query context.
! 				 */
! 				MemoryContextSwitchTo(oldcontext);
! 
! 				skip_tuple = false;
! 
! 				/*
! 				 * BEFORE ROW INSERT Triggers
! 				 */
! 				if (resultRelInfo->ri_TrigDesc &&
! 					resultRelInfo->ri_TrigDesc->
! 					n_before_row[TRIGGER_EVENT_INSERT] > 0)
! 				{
! 					HeapTuple	newtuple;
! 
! 					newtuple =
! 						ExecBRInsertTriggers(estate, resultRelInfo, tuple);
! 
! 					if (newtuple == NULL)		/* "do nothing" */
! 						skip_tuple = true;
! 					else if (newtuple != tuple) /* modified by Trigger(s) */
! 					{
! 						heap_freetuple(tuple);
! 						tuple = newtuple;
! 					}
! 				}
! 
! 				if (!skip_tuple)
! 				{
! 					/*
! 					 * Place tuple in tuple slot
! 					 */
! 					ExecStoreTuple(tuple, slot, InvalidBuffer, false);
! 
! 					/*
! 					 * Check the constraints of the tuple
! 					 */
! 					if (rel->rd_att->constr)
! 						ExecConstraints(resultRelInfo, slot, estate);
! 
! 					/*
! 					 * OK, store the tuple and create index entries for it
! 					 */
! 					simple_heap_insert(rel, tuple);
! 
! 					if (resultRelInfo->ri_NumIndices > 0)
! 						ExecInsertIndexTuples(slot, &(tuple->t_self), estate,
! 											  false);
! 
! 					/*
! 					 * AFTER ROW INSERT Triggers
! 					 */
! 					ExecARInsertTriggers(estate, resultRelInfo, tuple);
! 				}
! 
! 				line_buf.len = 0;		/* we can reset line buffer now. */
! 				line_buf.data[0] = '\0';
! 				line_buf.cursor = 0;
! 			}					/* end while(!buf_done) */
! 		}						/* end if (bytesread > 0 || !fe_eof) */
! 		else
! 			/*
! 			 * no bytes read, end of data
! 			 */
! 		{
! 			no_more_data = TRUE;
! 		}
! 	}
! 	while (!no_more_data);
! 
! 	/*
! 	 * Done, clean up
! 	 */
! 	error_context_stack = errcontext.previous;
! 
! 	MemoryContextSwitchTo(oldcontext);
! 
! 	/*
! 	 * Execute AFTER STATEMENT insertion triggers
! 	 */
! 	ExecASInsertTriggers(estate, resultRelInfo);
! 
! 	/*
! 	 * Handle queued AFTER triggers
! 	 */
! 	AfterTriggerEndQuery(estate);
! 
! 	pfree(values);
! 	pfree(nulls);
! 
! 	pfree(attr_offsets);
! 
! 	pfree(in_functions);
! 	pfree(typioparams);
! 	pfree(defmap);
! 	pfree(defexprs);
! 	pfree(constraintexprs);
! 	pfree(force_notnull);
! 
! 	ExecDropSingleTupleTableSlot(slot);
! 
! 	ExecCloseIndices(resultRelInfo);
! 
! 	FreeExecutorState(estate);
! }
! 
! 
! /*
!  * Read the next input line and stash it in line_buf, with conversion to
!  * server encoding.
!  *
!  * Result is true if read was terminated by EOF, false if terminated
!  * by newline.
!  */
! static bool
! CopyReadLine(char *quote, char *escape)
! {
! 	bool		result;
! 	bool		change_encoding = (client_encoding != server_encoding);
! 	int			c;
! 	int			mblen;
! 	int			j;
  	unsigned char s[2];
  	char	   *cvt;
! 	bool		in_quote = false,
! 		last_was_esc = false,
! 		csv_mode = false;
! 	char		quotec = '\0',
! 		escapec = '\0';
  
  	if (quote)
  	{
  		csv_mode = true;
  		quotec = quote[0];
  		escapec = escape[0];
! 		/*
! 		 * ignore special escape processing if it's the same as quotec 
! 		 */
  		if (quotec == escapec)
  			escapec = '\0';
  	}
***************
*** 2021,2051 ****
  
  	s[1] = 0;
  
! 	/* reset line_buf to empty */
  	line_buf.len = 0;
  	line_buf.data[0] = '\0';
  	line_buf.cursor = 0;
  
! 	/* mark that encoding conversion hasn't occurred yet */
  	line_buf_converted = false;
  
! 	/* set default status */
  	result = false;
  
  	/*
  	 * In this loop we only care for detecting newlines (\r and/or \n) and
! 	 * the end-of-copy marker (\.).  
  	 *
  	 * In Text mode, for backwards compatibility we allow
  	 * backslashes to escape newline characters.  Backslashes other than
  	 * the end marker get put into the line_buf, since CopyReadAttribute
! 	 * does its own escape processing.	
  	 *
  	 * In CSV mode, CR and NL inside q quoted field are just part of the
  	 * data value and are put in line_buf. We keep just enough state
  	 * to know if we are currently in a quoted field or not.
  	 *
! 	 * These four characters, and only these four, are assumed the same in 
  	 * frontend and backend encodings.
  	 *
  	 * We do not assume that second and later bytes of a frontend
--- 2854,2890 ----
  
  	s[1] = 0;
  
! 	/*
! 	 * reset line_buf to empty
! 	 */
  	line_buf.len = 0;
  	line_buf.data[0] = '\0';
  	line_buf.cursor = 0;
  
! 	/*
! 	 * mark that encoding conversion hasn't occurred yet 
! 	 */
  	line_buf_converted = false;
  
! 	/*
! 	 * set default status
! 	 */
  	result = false;
  
  	/*
  	 * In this loop we only care for detecting newlines (\r and/or \n) and
! 	 * the end-of-copy marker (\.).
  	 *
  	 * In Text mode, for backwards compatibility we allow
  	 * backslashes to escape newline characters.  Backslashes other than
  	 * the end marker get put into the line_buf, since CopyReadAttribute
! 	 * does its own escape processing.
  	 *
  	 * In CSV mode, CR and NL inside q quoted field are just part of the
  	 * data value and are put in line_buf. We keep just enough state
  	 * to know if we are currently in a quoted field or not.
  	 *
! 	 * These four characters, and only these four, are assumed the same in
  	 * frontend and backend encodings.
  	 *
  	 * We do not assume that second and later bytes of a frontend
***************
*** 2062,2108 ****
  
  		if (csv_mode)
  		{
! 			/*  
  			 * Dealing with quotes and escapes here is mildly tricky. If the
  			 * quote char is also the escape char, there's no problem - we  
  			 * just use the char as a toggle. If they are different, we need
  			 * to ensure that we only take account of an escape inside a quoted
  			 * field and immediately preceding a quote char, and not the
  			 * second in a escape-escape sequence.
! 			 */ 
  
  			if (in_quote && c == escapec)
! 				last_was_esc = ! last_was_esc;
! 			if (c == quotec && ! last_was_esc)
! 				in_quote = ! in_quote;
  			if (c != escapec)
  				last_was_esc = false;
  
  			/*
! 			 * updating the line count for embedded CR and/or LF chars is 
! 			 * necessarily a little fragile - this test is probably about 
  			 * the best we can do.
! 			 */ 
! 			if (in_quote && c == (eol_type == EOL_CR ? '\r' : '\n')) 
! 				copy_lineno++; 
  		}
  
  		if (!in_quote && c == '\r')
  		{
  			if (eol_type == EOL_NL)
  			{
! 				if (! csv_mode)
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  							 errmsg("literal carriage return found in data"),
! 							 errhint("Use \"\\r\" to represent carriage return.")));
  				else
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg("unquoted carriage return found in CSV data"),
! 							 errhint("Use quoted CSV field to represent carriage return.")));
  			}
! 			/* Check for \r\n on first line, _and_ handle \r\n. */
  			if (eol_type == EOL_UNKNOWN || eol_type == EOL_CRNL)
  			{
  				int			c2 = CopyPeekChar();
--- 2901,2952 ----
  
  		if (csv_mode)
  		{
! 			/*
  			 * Dealing with quotes and escapes here is mildly tricky. If the
  			 * quote char is also the escape char, there's no problem - we  
  			 * just use the char as a toggle. If they are different, we need
  			 * to ensure that we only take account of an escape inside a quoted
  			 * field and immediately preceding a quote char, and not the
  			 * second in a escape-escape sequence.
! 			 */
  
  			if (in_quote && c == escapec)
! 				last_was_esc = !last_was_esc;
! 			if (c == quotec && !last_was_esc)
! 				in_quote = !in_quote;
  			if (c != escapec)
  				last_was_esc = false;
  
  			/*
! 			 * updating the line count for embedded CR and/or LF chars is
! 			 * necessarily a little fragile - this test is probably about
  			 * the best we can do.
! 			 */
! 			if (in_quote && c == (eol_type == EOL_CR ? '\r' : '\n'))
! 				copy_lineno++;
  		}
  
  		if (!in_quote && c == '\r')
  		{
  			if (eol_type == EOL_NL)
  			{
! 				if (!csv_mode)
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  							 errmsg("literal carriage return found in data"),
! 							 errhint
! 							 ("Use \"\\r\" to represent carriage return.")));
  				else
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg
! 							 ("unquoted carriage return found in CSV data"),
! 							 errhint
! 							 ("Use quoted CSV field to represent carriage return.")));
  			}
! 			/*
! 			 * Check for \r\n on first line, _and_ handle \r\n.
! 			 */
  			if (eol_type == EOL_UNKNOWN || eol_type == EOL_CRNL)
  			{
  				int			c2 = CopyPeekChar();
***************
*** 2114,2132 ****
  				}
  				else
  				{
! 					/* found \r, but no \n */
  					if (eol_type == EOL_CRNL)
  					{
  						if (!csv_mode)
  							ereport(ERROR,
  									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 									 errmsg("literal carriage return found in data"),
! 									 errhint("Use \"\\r\" to represent carriage return.")));
  						else
  							ereport(ERROR,
  									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 									 errmsg("unquoted carriage return found in data"),
! 									 errhint("Use quoted CSV field to represent carriage return.")));
  
  					}
  
--- 2958,2982 ----
  				}
  				else
  				{
! 					/*
! 					 * found \r, but no \n
! 					 */
  					if (eol_type == EOL_CRNL)
  					{
  						if (!csv_mode)
  							ereport(ERROR,
  									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 									 errmsg
! 									 ("literal carriage return found in data"),
! 									 errhint
! 									 ("Use \"\\r\" to represent carriage return.")));
  						else
  							ereport(ERROR,
  									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 									 errmsg
! 									 ("unquoted carriage return found in data"),
! 									 errhint
! 									 ("Use quoted CSV field to represent carriage return.")));
  
  					}
  
***************
*** 2153,2160 ****
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  							 errmsg("unquoted newline found in data"),
! 							 errhint("Use quoted CSV field to represent newline.")));
! 					
  			}
  			eol_type = EOL_NL;
  			break;
--- 3003,3011 ----
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  							 errmsg("unquoted newline found in data"),
! 							 errhint
! 							 ("Use quoted CSV field to represent newline.")));
! 
  			}
  			eol_type = EOL_NL;
  			break;
***************
*** 2162,2169 ****
  
  		if ((line_buf.len == 0 || !csv_mode) && c == '\\')
  		{
! 			int c2;
! 			
  			if (csv_mode)
  				c2 = CopyPeekChar();
  			else
--- 3013,3020 ----
  
  		if ((line_buf.len == 0 || !csv_mode) && c == '\\')
  		{
! 			int			c2;
! 
  			if (csv_mode)
  				c2 = CopyPeekChar();
  			else
***************
*** 2179,2185 ****
  			if (c2 == '.')
  			{
  				if (csv_mode)
! 					CopyDonePeek(c2, true); /* allow keep calling GetChar() */
  
  				if (eol_type == EOL_CRNL)
  				{
--- 3030,3036 ----
  			if (c2 == '.')
  			{
  				if (csv_mode)
! 					CopyDonePeek(c2, true);		/* allow keep calling GetChar() */
  
  				if (eol_type == EOL_CRNL)
  				{
***************
*** 2187,2193 ****
  					if (c == '\n')
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 								 errmsg("end-of-copy marker does not match previous newline style")));
  					if (c != '\r')
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
--- 3038,3045 ----
  					if (c == '\n')
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 								 errmsg
! 								 ("end-of-copy marker does not match previous newline style")));
  					if (c != '\r')
  						ereport(ERROR,
  								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
***************
*** 2203,2209 ****
  					(eol_type == EOL_CR && c != '\r'))
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg("end-of-copy marker does not match previous newline style")));
  
  				/*
  				 * In protocol version 3, we should ignore anything after
--- 3055,3062 ----
  					(eol_type == EOL_CR && c != '\r'))
  					ereport(ERROR,
  							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg
! 							 ("end-of-copy marker does not match previous newline style")));
  
  				/*
  				 * In protocol version 3, we should ignore anything after
***************
*** 2218,2228 ****
  				result = true;	/* report EOF */
  				break;
  			}
! 			
  			if (csv_mode)
! 				CopyDonePeek(c2, false); /* not a dot, so put it back */ 
  			else
! 				/* not EOF mark, so emit \ and following char literally */
  				appendStringInfoCharMacro(&line_buf, '\\');
  		}
  
--- 3071,3083 ----
  				result = true;	/* report EOF */
  				break;
  			}
! 
  			if (csv_mode)
! 				CopyDonePeek(c2, false);		/* not a dot, so put it back */
  			else
! 				/*
! 				 * not EOF mark, so emit \ and following char literally
! 				 */
  				appendStringInfoCharMacro(&line_buf, '\\');
  		}
  
***************
*** 2254,2290 ****
  		}
  	}							/* end of outer loop */
  
! 	/* Done reading the line.  Convert it to server encoding. */
! 	if (change_encoding)
  	{
! 		cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
! 										   line_buf.len);
! 		if (cvt != line_buf.data)
! 		{
! 			/* transfer converted data back to line_buf */
! 			line_buf.len = 0;
! 			line_buf.data[0] = '\0';
! 			appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
! 		}
  	}
  
! 	/* Now it's safe to use the buffer in error messages */
! 	line_buf_converted = true;
! 
! 	return result;
  }
  
- /*
-  *	Return decimal value for a hexadecimal digit
-  */
- static
- int GetDecimalFromHex(char hex)
- {
- 	if (isdigit(hex))
- 		return hex - '0';
- 	else
- 		return tolower(hex) - 'a' + 10;
- }
  
  /*----------
   * Read the value of a single attribute, performing de-escaping as needed.
--- 3109,3502 ----
  		}
  	}							/* end of outer loop */
  
! 	/*
! 	 * Done reading the line.  Convert it to server encoding.
! 	 */
! 	if (change_encoding)
! 	{
! 		cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
! 										   line_buf.len);
! 		if (cvt != line_buf.data)
! 		{
! 			/*
! 			 * transfer converted data back to line_buf
! 			 */
! 			line_buf.len = 0;
! 			line_buf.data[0] = '\0';
! 			line_buf.cursor = 0;
! 			appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
! 		}
! 	}
! 
! 	/*
! 	 * Now it's safe to use the buffer in error messages 
! 	 */
! 	line_buf_converted = true;
! 
! 	return result;
! }
! 
! /*
!  *	Return decimal value for a hexadecimal digit
!  */
! static int
! GetDecimalFromHex(char hex)
! {
! 	if (isdigit(hex))
! 		return hex - '0';
! 	else
! 		return tolower(hex) - 'a' + 10;
! }
! 
! /*
!  * Detected the eol type by looking at the first data row.
!  * Possible eol types are NL, CR, or CRNL. If eol type was
!  * detected, it is set and a boolean true is returned to
!  * indicated detection was successful. If the first data row
!  * is longer than the input buffer, we return false and will
!  * try again in the next buffer.
!  */
! static bool
! DetectLineEnd(size_t bytesread)
! {
! 	int			bytes_remaining = COPY_BUF_SIZE;
! 	char		ch_found;		/* the character found in the scan ('\n' or '\r' or escapec) */
! 	char	   *start = input_buf;
! 	char	   *end;
! 
! 	while (true)
! 	{
! 		bytes_remaining = COPY_BUF_SIZE - (input_buf - start);
! 
! 		if (bytes_remaining <= 0)
! 			return false;
! 
! 		if ((end =
! 			 str2chrlen(start, '\n', '\r', bytes_remaining,
! 						&ch_found)) == NULL)
! 		{
! 			return false;
! 		}
! 		else
! 		{
! 			if (ch_found == '\n')
! 			{
! 				eol_type = EOL_NL;
! 				eol_ch[0] = '\n';
! 				eol_ch[1] = '\0';
! 
! 				return true;
! 			}
! 			if (ch_found == '\r')
! 			{
! 				if (*(end + 1) == '\n')
! 				{
! 					eol_type = EOL_CRNL;
! 					eol_ch[0] = '\r';
! 					eol_ch[1] = '\n';
! 				}
! 				else
! 				{
! 					eol_type = EOL_CR;
! 					eol_ch[0] = '\r';
! 					eol_ch[1] = '\0';
! 				}
! 
! 				return true;
! 			}
! 		}
! 	}
! 
! }
! 
! 
! /*
!  * Finds the next data line that is in the input buffer and loads it into line_buf.
!  * Returns an indication if the line that was read is complete (if an unescaped line-end was
!  * encountered). If we reached the end of buffer before the whole line was written into the
!  * line buffer then returns false.
!  */
! static bool
! CopyReadLineBuffered(size_t bytesread)
! {
! 	int			linesize;
! 	bool		transcode = (client_encoding != server_encoding);
! 	char	   *cvt;
! 	bool		end_marker;
! 
! 	/*
! 	 * mark that encoding conversion hasn't occurred yet 
! 	 */
! 	line_buf_converted = false;
! 
! 	/*
! 	 * Detect end of line type if not already detected.
! 	 */
! 	if (eol_type == EOL_UNKNOWN)
! 	{
! 		bool		eol_detected = DetectLineEnd(bytesread);
! 
! 		if (!eol_detected)
! 		{
! 			/*
! 			 * load entire input buffer into line buf, and quit
! 			 */
! 			appendBinaryStringInfo(&line_buf, input_buf, COPY_BUF_SIZE);
! 			line_done = false;
! 			buf_done = true;
! 
! 			return line_done;
! 		}
! 	}
! 
! 	/*
! 	 * Special case: eol is CRNL, last byte of previous buffer was an
! 	 * unescaped CR and 1st byte of current buffer is NL. We check for
! 	 * that here.
! 	 */
! 	if (eol_type == EOL_CRNL)
! 	{
! 		/*
! 		 * if we started scanning from the 1st byte of the buffer
! 		 */
! 		if (begloc == input_buf)
! 		{
! 			/*
! 			 * and had a CR in last byte of prev buf
! 			 */
! 			if (cr_in_prevbuf)
! 			{
! 				/*
! 				 * if this 1st byte in buffer is 2nd byte of line end sequence (linefeed)
! 				 */
! 				if (*begloc == eol_ch[1])
! 				{
! 					/*
! 					 * load that one linefeed byte and indicate we are done with the data line
! 					 */
! 					appendBinaryStringInfo(&line_buf, begloc, 1);
! 					buffer_index++;
! 					begloc++;
! 
! 					line_done = true;
! 					esc_in_prevbuf = false;
! 					cr_in_prevbuf = false;
! 
! 					return line_done;
! 				}
! 			}
! 
! 			cr_in_prevbuf = false;
! 		}
! 	}
! 
! 	/*
! 	 * (we need a loop so that if eol_ch is found, but prev ch is backslash, we can search
! 	 * for the next eol_ch)
! 	 */
! 	while (true)
! 	{
! 		/*
! 		 * reached end of buffer
! 		 */
! 		if ((endloc =
! 			 strchrlen(begloc, eol_ch[0], bytesread - buffer_index)) == NULL)
! 		{
! 			linesize = COPY_BUF_SIZE - (begloc - input_buf);
! 			appendBinaryStringInfo(&line_buf, begloc, linesize);
! 
! 			if (line_buf.len > 1)
! 			{
! 				char	   *last_ch = line_buf.data + line_buf.len - 1; /* before terminating \0 */
! 
! 				if (*last_ch == escapec)
! 				{
! 					esc_in_prevbuf = true;
! 
! 					if (line_buf.len > 2)
! 					{
! 						last_ch--;
! 						if (*last_ch == escapec)
! 							esc_in_prevbuf = false;
! 					}
! 				}
! 				else if (*last_ch == '\r')
! 				{
! 					if (eol_type == EOL_CRNL)
! 						cr_in_prevbuf = true;
! 				}
! 			}
! 
! 			line_done = false;
! 			buf_done = true;
! 			break;
! 		}
! 		else
! 			/*
! 			 * found the 1st eol ch in input_buf.
! 			 */
! 		{
! 			bool		eol_found = true;
! 			bool		eol_escaped = true;
! 
! 			/*
! 			 * Load that piece of data (potentially a data line) into the line buffer,
! 			 * and update the pointers for the next scan.
! 			 */
! 			linesize = endloc - begloc + 1;
! 			appendBinaryStringInfo(&line_buf, begloc, linesize);
! 			buffer_index += linesize;
! 			begloc = endloc + 1;
! 
! 			if (eol_type == EOL_CRNL)
! 			{
! 				/*
! 				 * check if there is a '\n' after the '\r'
! 				 */
! 				if (*(endloc + 1) == '\n')
! 				{
! 					/*
! 					 * this is a line end
! 					 */
! 					appendBinaryStringInfo(&line_buf, begloc, 1);		/* load that '\n' */
! 					buffer_index++;
! 					begloc++;
! 				}
! 				else
! 					/*
! 					 * just a CR, not a line end
! 					 */
! 					eol_found = false;
! 			}
! 
! 			/*
! 			 * in some cases, this end of line char happens to be the
! 			 * last character in the buffer. we need to catch that.
! 			 */
! 			if (buffer_index >= bytesread)
! 				buf_done = true;
! 
! 			/*
! 			 * Check if the 1st end of line ch is escaped.
! 			 */
! 			if (endloc != input_buf)	/* can we look 1 char back? */
! 			{
! 				if (*(endloc - 1) != escapec)	/* prev char is not an escape */
! 					eol_escaped = false;
! 				else
! 					/*
! 					 * prev char is an escape
! 					 */
! 				{
! 					if (endloc != (input_buf + 1))		/* can we look another char back? */
! 					{
! 						/*
! 						 * it's a double escape char, so it's not an escape
! 						 */
! 						if (*(endloc - 2) == escapec)
! 							eol_escaped = false;
! 						/*
! 						 * else it's a single escape char, so EOL is ascaped 
! 						 */
! 					}
! 					else
! 					{
! 						/*
! 						 * we need to check in the last buffer
! 						 */
! 						if (esc_in_prevbuf)		/* double escape char, so not an escape */
! 							eol_escaped = false;
! 					}
! 				}
! 			}
! 			else
! 				/*
! 				 * this eol ch is first ch in buffer, check for escape in prev buf
! 				 */
! 			{
! 				if (!esc_in_prevbuf)
! 					eol_escaped = false;
! 			}
! 
! 			esc_in_prevbuf = false;		/* reset variable */
! 
! 			/*
! 			 * if eol was found, and it isn't escaped, line is done
! 			 */
! 			if ((eol_escaped == false) && eol_found)
! 			{
! 				line_done = true;
! 				break;
! 			}
! 			else
! 				/*
! 				 * stay in the loop and process some more data.
! 				 */
! 				line_done = false;
! 
! 		}						/* end of found eol_ch */
! 	}
! 
! 	/*
! 	 * Done reading the line. Convert it to server encoding.
! 	 */
! 	if (transcode)
! 	{
! 		cvt = (char *) pg_client_to_server((unsigned char *) line_buf.data,
! 										   line_buf.len);
! 		if (cvt != line_buf.data)
! 		{
! 			/*
! 			 * transfer converted data back to line_buf
! 			 */
! 			line_buf.len = 0;
! 			line_buf.data[0] = '\0';
! 			line_buf.cursor = 0;
! 			appendBinaryStringInfo(&line_buf, cvt, strlen(cvt));
! 		}
! 	}
! 
! 	/*
! 	 * indicate that conversion had occured
! 	 */
! 	line_buf_converted = true;
! 
! 	/*
! 	 * check if this line is an end marker -- "\."
! 	 */
! 	end_marker = false;
! 
! 	switch (eol_type)
! 	{
! 		case EOL_NL:
! 			if (!strcmp(line_buf.data, "\\.\n"))
! 				end_marker = true;
! 			break;
! 		case EOL_CR:
! 			if (!strcmp(line_buf.data, "\\.\r"))
! 				end_marker = true;
! 			break;
! 		case EOL_CRNL:
! 			if (!strcmp(line_buf.data, "\\.\r\n"))
! 				end_marker = true;
! 			break;
! 		case EOL_UNKNOWN:
! 			break;
! 	}
! 
! 	if (end_marker)
  	{
! 		fe_eof = true;
! 		/*
! 		 * we don't want to process a \. as data line, want to quit. 
! 		 */
! 		line_done = false;
! 		buf_done = true;
  	}
  
! 	return line_done;
  }
  
  
  /*----------
   * Read the value of a single attribute, performing de-escaping as needed.
***************
*** 2314,2324 ****
  	int			end_cursor;
  	int			input_len;
  
! 	/* reset attribute_buf to empty */
! 	attribute_buf.len = 0;
! 	attribute_buf.data[0] = '\0';
  
! 	/* set default status */
  	*result = END_OF_LINE;
  
  	for (;;)
--- 3526,3541 ----
  	int			end_cursor;
  	int			input_len;
  
! 	/*
! 	 * reset attr_buf to empty
! 	 */
! 	attr_buf.len = 0;
! 	attr_buf.data[0] = '\0';
! 	attr_buf.cursor = 0;
  
! 	/*
! 	 * set default status
! 	 */
  	*result = END_OF_LINE;
  
  	for (;;)
***************
*** 2347,2353 ****
  				case '5':
  				case '6':
  				case '7':
! 					/* handle \013 */
  					{
  						int			val;
  
--- 3564,3572 ----
  				case '5':
  				case '6':
  				case '7':
! 					/*
! 					 * handle \013
! 					 */
  					{
  						int			val;
  
***************
*** 2374,2387 ****
  					}
  					break;
  				case 'x':
! 					/* Handle \x3F */
  					if (line_buf.cursor < line_buf.len)
  					{
! 						char hexchar = line_buf.data[line_buf.cursor];
  
  						if (isxdigit(hexchar))
  						{
! 							int val = GetDecimalFromHex(hexchar);
  
  							line_buf.cursor++;
  							if (line_buf.cursor < line_buf.len)
--- 3593,3608 ----
  					}
  					break;
  				case 'x':
! 					/*
! 					 * Handle \x3F
! 					 */
  					if (line_buf.cursor < line_buf.len)
  					{
! 						char		hexchar = line_buf.data[line_buf.cursor];
  
  						if (isxdigit(hexchar))
  						{
! 							int			val = GetDecimalFromHex(hexchar);
  
  							line_buf.cursor++;
  							if (line_buf.cursor < line_buf.len)
***************
*** 2390,2396 ****
  								if (isxdigit(hexchar))
  								{
  									line_buf.cursor++;
! 									val = (val << 4) + GetDecimalFromHex(hexchar);
  								}
  							}
  							c = val & 0xff;
--- 3611,3619 ----
  								if (isxdigit(hexchar))
  								{
  									line_buf.cursor++;
! 									val =
! 										(val << 4) +
! 										GetDecimalFromHex(hexchar);
  								}
  							}
  							c = val & 0xff;
***************
*** 2422,2431 ****
  					 */
  			}
  		}
! 		appendStringInfoCharMacro(&attribute_buf, c);
  	}
  
! 	/* check whether raw input matched null marker */
  	input_len = end_cursor - start_cursor;
  	if (input_len == strlen(null_print) &&
  		strncmp(&line_buf.data[start_cursor], null_print, input_len) == 0)
--- 3645,3656 ----
  					 */
  			}
  		}
! 		appendStringInfoCharMacro(&attr_buf, c);
  	}
  
! 	/*
! 	 * check whether raw input matched null marker
! 	 */
  	input_len = end_cursor - start_cursor;
  	if (input_len == strlen(null_print) &&
  		strncmp(&line_buf.data[start_cursor], null_print, input_len) == 0)
***************
*** 2433,2439 ****
  	else
  		*isnull = false;
  
! 	return attribute_buf.data;
  }
  
  
--- 3658,4066 ----
  	else
  		*isnull = false;
  
! 	return attr_buf.data;
! }
! 
! /*
!  * Read the first attribute. This is mainly used to maintain support
!  * for an OID column. All the rest of the columns will be read at once with
!  * CopyReadAllAttrs().
!  */
! static char *
! CopyReadOidAttr(const char *delim, const char *null_print, int null_print_len,
! 				CopyReadResult *result, bool *isnull)
! {
! 	char		delimc = delim[0];
! 	char	   *start_loc = line_buf.data + line_buf.cursor;
! 	char	   *end_loc;
! 	int			attr_len = 0;
! 	int			bytes_remaining;
! 
! 	/*
! 	 * reset attribute buf to empty
! 	 */
! 	attr_buf.len = 0;
! 	attr_buf.data[0] = '\0';
! 	attr_buf.cursor = 0;
! 
! 	/*
! 	 * set default status
! 	 */
! 	*result = END_OF_LINE;
! 
! 	/*
! 	 * # of bytes that were not yet processed in this line
! 	 */
! 	bytes_remaining = line_buf.len - line_buf.cursor;
! 
! 	/*
! 	 * got to end of line
! 	 */
! 	if ((end_loc = strchrlen(start_loc, delimc, bytes_remaining)) == NULL)
! 	{
! 		attr_len = bytes_remaining - 1; /* don't count '\n' in len calculation */
! 		appendBinaryStringInfo(&attr_buf, start_loc, attr_len);
! 		line_buf.cursor += attr_len + 2;		/* skip '\n' and '\0' */
! 
! 		*result = END_OF_LINE;
! 	}
! 	else
! 		/*
! 		 * found a delimiter
! 		 */
! 	{
! 		/*
! 		 * (we don't care if delim was preceded with a backslash, because it's an
! 		 * invalid OID anyway)
! 		 */
! 
! 		attr_len = end_loc - start_loc; /* we don't include the delimiter ch */
! 
! 		appendBinaryStringInfo(&attr_buf, start_loc, attr_len);
! 		line_buf.cursor += attr_len + 1;
! 
! 		*result = NORMAL_ATTR;
! 	}
! 
! 
! 	/*
! 	 * check whether raw input matched null marker
! 	 */
! 	if (attr_len == null_print_len
! 		&& strncmp(start_loc, null_print, attr_len) == 0)
! 		*isnull = true;
! 	else
! 		*isnull = false;
! 
! 	return attr_buf.data;
! }
! 
! 
! /*
!  * Read all attributes. Attributes are parsed from line_buf and
!  * inserted (all at once) to attr_buf, while saving pointers to
!  * each attribute's starting position.
!  *
!  * When this routine finishes execution both the nulls array and
!  * the attr_offsets array are updated. The attr_offsets will include
!  * the offset from the beginning of the attribute array of which
!  * each attribute begins. If a specific attribute is not used for this
!  * COPY command (ommitted from the column list), a value of 0 will be assigned.
!  * For example: for table foo(a,b,c,d,e) and COPY foo(a,b,e)
!  * attr_offsets may look something like this after this routine
!  * returns: [0,20,0,0,55]. That means that column "a" value starts
!  * at byte offset 0, "b" in 20 and "e" in 55, in attr_buf.
!  *
!  * In the attribute buffer (attr_buf) each attribute
!  * is terminated with a '\0', and therefore by using the attr_offsets
!  * array we could point to a beginning of an attribute and have it
!  * behave as a C string, much like previously done in COPY.
!  *
!  * Another aspect to improving performance is reducing the frequency
!  * of data load into buffers. The original COPY read attribute code
!  * loaded a character at a time. In here we try to load a chunk of data
!  * at a time. Usually a chunk will include a full data row
!  * (unless we have an escaped delim). That effectively reduces the number of
!  * loads by a factor of number of bytes per row. This improves performance
!  * greatly, unfortunately it add more complexity to the code.
!  *
!  * Global participants in parsing logic:
!  *
!  * line_buf.cursor -- an offset from beginning of the line buffer
!  * that indicates where we are about to begin the next scan. Note that
!  * if we have WITH OIDS this cursor is already shifted past the first
!  * OID attribute.
!  *
!  * attr_buf.cursor -- an offset from the beginning of the
!  * attribute buffer that indicates where the current attribute begins.
!  */
! static void
! CopyReadAllAttrs(const char *delim, const char *null_print, int null_print_len,
! 				 char *nulls, List *attnumlist, int *attr_offsets,
! 				 int num_phys_attrs, Form_pg_attribute *attr)
! {
! 	char		delimc = delim[0];		/* delimiter character */
! 	char	   *scan_start;		/* pointer to line buffer where scan should start. */
! 	char	   *scan_end;		/* pointer to line buffer where char was found */
! 	char		ch_found;		/* the character found in the scan (delimc or escape) */
! 	int			attr_pre_len;	/* current attr raw len, before processing escapes */
! 	int			attr_post_len;	/* current attr len after escaping */
! 	int			m;				/* attribute index being parsed */
! 	int			bytes_remaining;		/* num of bytes remaining to be scanned in line buf */
! 	int			chunk_start;	/* offset to beginning of line chunk to load */
! 	int			chunk_len;		/* length of chunk of data to load to attr buf */
! 	int			oct_val;		/* byte value for octal escapes */
! 	int			attnum;			/* attribute number being parsed */
! 	ListCell   *cur;			/* cursor to attribute list used for this COPY */
! 	int			attribute;
! 
! 	/*
! 	 * init variables for attribute scan
! 	 */
! 	attr_buf.len = 0;
! 	attr_buf.data[0] = '\0';
! 	attr_buf.cursor = 0;
! 	/*
! 	 * cursor is now > 0 if we copy WITH OIDS
! 	 */
! 	scan_start = line_buf.data + line_buf.cursor;
! 	cur = list_head(attnumlist);
! 	attnum = lfirst_int(cur);
! 	m = attnum - 1;
! 	chunk_start = line_buf.cursor;
! 	chunk_len = 0;
! 	attr_pre_len = 0;
! 	attr_post_len = 0;
! 
! 	/*
! 	 * Scan through the line buffer to read all attributes data
! 	 */
! 	while (line_buf.cursor < line_buf.len)
! 	{
! 		bytes_remaining = line_buf.len - line_buf.cursor;
! 		ch_found = '\0';
! 
! 		if ((scan_end =
! 			 str2chrlen(scan_start, delimc, escapec, bytes_remaining,
! 						&ch_found)) == NULL)
! 		{
! 			/*
! 			 * GOT TO END OF LINE BUFFER
! 			 */
! 
! 			if (cur == NULL)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("extra data after last expected column")));
! 
! 			attnum = lfirst_int(cur);
! 			m = attnum - 1;
! 
! 			/*
! 			 * don't count eol char(s) in attr and chunk len calculation 
! 			 */
! 			if (eol_type == EOL_CRNL)
! 			{
! 				attr_pre_len += bytes_remaining - 2;
! 				chunk_len = line_buf.len - chunk_start - 2;
! 			}
! 			else
! 			{
! 				attr_pre_len += bytes_remaining - 1;
! 				chunk_len = line_buf.len - chunk_start - 1;
! 			}
! 
! 			/*
! 			 * check if this is a NULL value or data value (assumed NULL)
! 			 */
! 			if (attr_pre_len == null_print_len
! 				&&
! 				strncmp(line_buf.data + line_buf.len - attr_pre_len - 1,
! 						null_print, attr_pre_len) == 0)
! 				nulls[m] = 'n';
! 			else
! 				nulls[m] = ' ';
! 
! 			attr_offsets[m] = attr_buf.cursor;
! 
! 
! 			/*
! 			 * load the last chunk, the whole buffer in most cases
! 			 */
! 			appendBinaryStringInfo(&attr_buf, line_buf.data + chunk_start,
! 								   chunk_len);
! 
! 			line_buf.cursor += attr_pre_len + 2;		/* skip eol char and '\0' to exit loop */
! 
! 			if (lnext(cur) != NULL)
! 			{
! 				/*
! 				 * For an empty data line, the previous COPY code will
! 				 * fail it during the conversion stage. We can fail it here
! 				 * already, but then we will fail the regression tests b/c
! 				 * of a different error message. that's why we return so we
! 				 * can get the same error message that regress expects. ahh...
! 				 * this conditional is unnecessary and should be removed soon.
! 				 */
! 				if (line_buf.len > 1)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg("missing data for column \"%s\"",
! 									NameStr(attr[m + 1]->attname))));
! 				else
! 					return;
! 			}
! 		}
! 		else
! 			/*
! 			 * FOUND A DELIMITER OR ESCAPE
! 			 */
! 		{
! 			if (cur == NULL)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("extra data after last expected column")));
! 
! 			if (ch_found == delimc)		/* found a delimiter */
! 			{
! 				attnum = lfirst_int(cur);
! 				m = attnum - 1;
! 
! 				/*
! 				 * (we don't include the delimiter ch in length) 
! 				 */
! 				attr_pre_len += scan_end - scan_start;
! 				/*
! 				 * (we don't include the delimiter ch in length) 
! 				 */
! 				attr_post_len += scan_end - scan_start;
! 
! 				/*
! 				 * check if this is a null print or data (assumed NULL)
! 				 */
! 				if (attr_pre_len == null_print_len
! 					&&
! 					strncmp(scan_end - attr_pre_len, null_print, attr_pre_len)
! 					== 0)
! 					nulls[m] = 'n';
! 				else
! 					nulls[m] = ' ';
! 
! 				/*
! 				 * set the pointer to next attribute position
! 				 */
! 				attr_offsets[m] = attr_buf.cursor;
! 
! 				/*
! 				 * update buffer cursors to our current location, +1 to skip the delimc
! 				 */
! 				line_buf.cursor = scan_end - line_buf.data + 1;
! 				attr_buf.cursor += attr_post_len + 1;
! 
! 				/*
! 				 * prepare scan for next attr
! 				 */
! 				scan_start = line_buf.data + line_buf.cursor;
! 				cur = lnext(cur);
! 				attr_pre_len = 0;
! 				attr_post_len = 0;
! 			}
! 			else
! 				/*
! 				 * found an escape character
! 				 */
! 			{
! 				char		nextc = *(scan_end + 1);
! 				char		newc;
! 				int			skip = 2;
! 
! 				chunk_len = (scan_end - line_buf.data) - chunk_start + 1;
! 
! 				/*
! 				 * load a chunk of data
! 				 */
! 				appendBinaryStringInfo(&attr_buf, line_buf.data + chunk_start,
! 									   chunk_len);
! 
! 				switch (nextc)
! 				{
! 					case '0':
! 					case '1':
! 					case '2':
! 					case '3':
! 					case '4':
! 					case '5':
! 					case '6':
! 					case '7':
! 						oct_val = OCTVALUE(nextc);
! 						nextc = *(scan_end + 2);
! 						/*
! 						 * (no need for out bad access check since line if buffered)
! 						 */
! 						if (ISOCTAL(nextc))
! 						{
! 							skip++;
! 							oct_val = (oct_val << 3) + OCTVALUE(nextc);
! 							nextc = *(scan_end + 3);
! 							if (ISOCTAL(nextc))
! 							{
! 								skip++;
! 								oct_val = (oct_val << 3) + OCTVALUE(nextc);
! 							}
! 						}
! 						newc = oct_val & 0377;	/* the escaped byte value */
! 						break;
! 					case 'b':
! 						newc = '\b';
! 						break;
! 					case 'f':
! 						newc = '\f';
! 						break;
! 					case 'n':
! 						newc = '\n';
! 						break;
! 					case 'r':
! 						newc = '\r';
! 						break;
! 					case 't':
! 						newc = '\t';
! 						break;
! 					case 'v':
! 						newc = '\v';
! 						break;
! 					default:
! 						if (nextc == delimc)
! 							newc = delimc;
! 						else if (nextc == escapec)
! 							newc = escapec;
! 						else
! 							/*
! 							 * no escape sequence, take next char literaly
! 							 */
! 							newc = nextc;
! 						break;
! 				}
! 
! 				/*
! 				 * update to current length, add escape and escaped chars
! 				 */
! 				attr_pre_len += scan_end - scan_start + 2;
! 				/*
! 				 * update to current length, escaped char
! 				 */
! 				attr_post_len += scan_end - scan_start + 1;
! 
! 				/*
! 				 * Need to get rid of the escape character. This is done by
! 				 * loading the chunk up to including the escape character
! 				 * into the attribute buffer. Then overwritting the backslash
! 				 * with the escaped sequence or char, and continuing to scan
! 				 * from *after* the char than is after the escape in line buf.
! 				 */
! 				*(attr_buf.data + attr_buf.len - 1) = newc;
! 				line_buf.cursor = scan_end - line_buf.data + skip;
! 				scan_start = scan_end + skip;
! 				chunk_start = line_buf.cursor;
! 				chunk_len = 0;
! 			}
! 
! 		}						/* end delimiter/backslash */
! 
! 	}							/* end line buffer scan. */
! 
! 	/*
! 	 * Replace all delimiters with NULL for string termination.
! 	 * NOTE: only delimiters (NOT necessarily all delimc) are replaced.
! 	 * Example (delimc = '|'):
! 	 * - Before:  f  1	|  f  \|  2  |	f  3
! 	 * - After :  f  1 \0  f   |  2 \0	f  3
! 	 */
! 	for (attribute = 1; attribute < num_phys_attrs; attribute++)
! 	{
! 		if (attr_offsets[attribute] != 0)
! 			*(attr_buf.data + attr_offsets[attribute] - 1) = '\0';
! 	}
! 
  }
  
  
***************
*** 2477,2487 ****
  	bool		in_quote = false;
  	bool		saw_quote = false;
  
! 	/* reset attribute_buf to empty */
! 	attribute_buf.len = 0;
! 	attribute_buf.data[0] = '\0';
  
! 	/* set default status */
  	*result = END_OF_LINE;
  
  	for (;;)
--- 4104,4119 ----
  	bool		in_quote = false;
  	bool		saw_quote = false;
  
! 	/*
! 	 * reset attr_buf to empty
! 	 */
! 	attr_buf.len = 0;
! 	attr_buf.data[0] = '\0';
! 	attr_buf.cursor = 0;
  
! 	/*
! 	 * set default status
! 	 */
  	*result = END_OF_LINE;
  
  	for (;;)
***************
*** 2491,2504 ****
  			break;
  		c = line_buf.data[line_buf.cursor++];
  
! 		/* unquoted field delimiter  */
  		if (!in_quote && c == delimc)
  		{
  			*result = NORMAL_ATTR;
  			break;
  		}
  
! 		/* start of quoted field (or part of field) */
  		if (!in_quote && c == quotec)
  		{
  			saw_quote = true;
--- 4123,4140 ----
  			break;
  		c = line_buf.data[line_buf.cursor++];
  
! 		/*
! 		 * unquoted field delimiter
! 		 */
  		if (!in_quote && c == delimc)
  		{
  			*result = NORMAL_ATTR;
  			break;
  		}
  
! 		/*
! 		 * start of quoted field (or part of field)
! 		 */
  		if (!in_quote && c == quotec)
  		{
  			saw_quote = true;
***************
*** 2506,2512 ****
  			continue;
  		}
  
! 		/* escape within a quoted field */
  		if (in_quote && c == escapec)
  		{
  			/*
--- 4142,4150 ----
  			continue;
  		}
  
! 		/*
! 		 * escape within a quoted field
! 		 */
  		if (in_quote && c == escapec)
  		{
  			/*
***************
*** 2519,2525 ****
  
  				if (nextc == escapec || nextc == quotec)
  				{
! 					appendStringInfoCharMacro(&attribute_buf, nextc);
  					line_buf.cursor++;
  					continue;
  				}
--- 4157,4163 ----
  
  				if (nextc == escapec || nextc == quotec)
  				{
! 					appendStringInfoCharMacro(&attr_buf, nextc);
  					line_buf.cursor++;
  					continue;
  				}
***************
*** 2536,2548 ****
  			in_quote = false;
  			continue;
  		}
! 		appendStringInfoCharMacro(&attribute_buf, c);
  	}
  
  	if (in_quote)
  		*result = UNTERMINATED_FIELD;
  
! 	/* check whether raw input matched null marker */
  	input_len = end_cursor - start_cursor;
  	if (!saw_quote && input_len == strlen(null_print) &&
  		strncmp(&line_buf.data[start_cursor], null_print, input_len) == 0)
--- 4174,4188 ----
  			in_quote = false;
  			continue;
  		}
! 		appendStringInfoCharMacro(&attr_buf, c);
  	}
  
  	if (in_quote)
  		*result = UNTERMINATED_FIELD;
  
! 	/*
! 	 * check whether raw input matched null marker
! 	 */
  	input_len = end_cursor - start_cursor;
  	if (!saw_quote && input_len == strlen(null_print) &&
  		strncmp(&line_buf.data[start_cursor], null_print, input_len) == 0)
***************
*** 2550,2556 ****
  	else
  		*isnull = false;
  
! 	return attribute_buf.data;
  }
  
  /*
--- 4190,4196 ----
  	else
  		*isnull = false;
  
! 	return attr_buf.data;
  }
  
  /*
***************
*** 2578,2606 ****
  				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  				 errmsg("invalid field size")));
  
! 	/* reset attribute_buf to empty, and load raw data in it */
! 	attribute_buf.len = 0;
! 	attribute_buf.data[0] = '\0';
! 	attribute_buf.cursor = 0;
  
! 	enlargeStringInfo(&attribute_buf, fld_size);
  
! 	CopyGetData(attribute_buf.data, fld_size);
  	if (CopyGetEof())
  		ereport(ERROR,
  				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  				 errmsg("unexpected EOF in COPY data")));
  
! 	attribute_buf.len = fld_size;
! 	attribute_buf.data[fld_size] = '\0';
  
! 	/* Call the column type's binary input converter */
  	result = FunctionCall2(flinfo,
! 						   PointerGetDatum(&attribute_buf),
  						   ObjectIdGetDatum(typioparam));
  
! 	/* Trouble if it didn't eat the whole buffer */
! 	if (attribute_buf.cursor != attribute_buf.len)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
  				 errmsg("incorrect binary data format")));
--- 4218,4252 ----
  				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  				 errmsg("invalid field size")));
  
! 	/*
! 	 * reset attr_buf to empty, and load raw data in it
! 	 */
! 	attr_buf.len = 0;
! 	attr_buf.data[0] = '\0';
! 	attr_buf.cursor = 0;
  
! 	enlargeStringInfo(&attr_buf, fld_size);
  
! 	CopyGetData(attr_buf.data, fld_size);
  	if (CopyGetEof())
  		ereport(ERROR,
  				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  				 errmsg("unexpected EOF in COPY data")));
  
! 	attr_buf.len = fld_size;
! 	attr_buf.data[fld_size] = '\0';
  
! 	/*
! 	 * Call the column type's binary input converter 
! 	 */
  	result = FunctionCall2(flinfo,
! 						   PointerGetDatum(&attr_buf),
  						   ObjectIdGetDatum(typioparam));
  
! 	/*
! 	 * Trouble if it didn't eat the whole buffer 
! 	 */
! 	if (attr_buf.cursor != attr_buf.len)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
  				 errmsg("incorrect binary data format")));
***************
*** 2668,2674 ****
  				 */
  				if (!same_encoding)
  				{
! 					/* send additional bytes of the char, if any */
  					mblen = pg_encoding_mblen(client_encoding, string);
  					for (i = 1; i < mblen; i++)
  						CopySendChar(string[i]);
--- 4314,4322 ----
  				 */
  				if (!same_encoding)
  				{
! 					/*
! 					 * send additional bytes of the char, if any
! 					 */
  					mblen = pg_encoding_mblen(client_encoding, string);
  					for (i = 1; i < mblen; i++)
  						CopySendChar(string[i]);
***************
*** 2709,2716 ****
  	 */
  
  	for (test_string = string;
! 		 !use_quote && (c = *test_string) != '\0';
! 		 test_string += mblen)
  	{
  		if (c == delimc || c == quotec || c == '\n' || c == '\r')
  			use_quote = true;
--- 4357,4363 ----
  	 */
  
  	for (test_string = string;
! 		 !use_quote && (c = *test_string) != '\0'; test_string += mblen)
  	{
  		if (c == delimc || c == quotec || c == '\n' || c == '\r')
  			use_quote = true;
***************
*** 2732,2738 ****
  
  		if (!same_encoding)
  		{
! 			/* send additional bytes of the char, if any */
  			mblen = pg_encoding_mblen(client_encoding, string);
  			for (i = 1; i < mblen; i++)
  				CopySendChar(string[i]);
--- 4379,4387 ----
  
  		if (!same_encoding)
  		{
! 			/*
! 			 * send additional bytes of the char, if any
! 			 */
  			mblen = pg_encoding_mblen(client_encoding, string);
  			for (i = 1; i < mblen; i++)
  				CopySendChar(string[i]);
***************
*** 2759,2765 ****
  
  	if (attnamelist == NIL)
  	{
! 		/* Generate default column list */
  		TupleDesc	tupDesc = RelationGetDescr(rel);
  		Form_pg_attribute *attr = tupDesc->attrs;
  		int			attr_count = tupDesc->natts;
--- 4408,4416 ----
  
  	if (attnamelist == NIL)
  	{
! 		/*
! 		 * Generate default column list
! 		 */
  		TupleDesc	tupDesc = RelationGetDescr(rel);
  		Form_pg_attribute *attr = tupDesc->attrs;
  		int			attr_count = tupDesc->natts;
***************
*** 2774,2780 ****
  	}
  	else
  	{
! 		/* Validate the user-supplied list and extract attnums */
  		ListCell   *l;
  
  		foreach(l, attnamelist)
--- 4425,4433 ----
  	}
  	else
  	{
! 		/*
! 		 * Validate the user-supplied list and extract attnums
! 		 */
  		ListCell   *l;
  
  		foreach(l, attnamelist)
***************
*** 2782,2791 ****
  			char	   *name = strVal(lfirst(l));
  			int			attnum;
  
! 			/* Lookup column name, ereport on failure */
! 			/* Note we disallow system columns here */
  			attnum = attnameAttNum(rel, name, false);
! 			/* Check for duplicates */
  			if (list_member_int(attnums, attnum))
  				ereport(ERROR,
  						(errcode(ERRCODE_DUPLICATE_COLUMN),
--- 4435,4450 ----
  			char	   *name = strVal(lfirst(l));
  			int			attnum;
  
! 			/*
! 			 * Lookup column name, ereport on failure
! 			 */
! 			/*
! 			 * Note we disallow system columns here
! 			 */
  			attnum = attnameAttNum(rel, name, false);
! 			/*
! 			 * Check for duplicates
! 			 */
  			if (list_member_int(attnums, attnum))
  				ereport(ERROR,
  						(errcode(ERRCODE_DUPLICATE_COLUMN),
***************
*** 2797,2799 ****
--- 4456,4524 ----
  
  	return attnums;
  }
+ 
+ /******************************************************
+  * utility functions, should be placed elsewhere in src
+  ******************************************************/
+ 
+ /*
+  * These are custom versions of the string function strchr().
+  * As opposed to the original strchr which searches through
+  * a string until the target character is found, or a NULL is
+  * found, this version will not return when a NULL is found.
+  * Instead it will search through a pre-defined length of
+  * bytes and will return only if the target character(s) is reached.
+  *
+  * If our client encoding is not a supported server encoding, we
+  * know that it is not safe to look at each character as trailing
+  * byte in a multibyte character may be a 7-bit ASCII equivalent.
+  * Therefore we use pg_encoding_mblen to skip to the end of the
+  * character.
+  *
+  * parameters:
+  *	 s	  - string being searched.
+  *	 c(n) - char we are searching for.
+  *	 len  - maximum # of bytes to search.
+  *
+  * returns:
+  *	 pointer to c - if c is located within the string.
+  *	 NULL - if c was not found in specified length of search. Note:
+  *			this DOESN'T mean that a '\0' was reached.
+  */
+ char *
+ strchrlen(const char *s, int c, size_t len)
+ {
+ 	const char *start;
+ 
+ 	if (client_encoding_only)
+ 	{
+ 		int			mblen = pg_encoding_mblen(client_encoding, s);
+ 
+ 		for (start = s; *s != c && s < start + len; s += mblen)
+ 			mblen = pg_encoding_mblen(client_encoding, s);
+ 	}
+ 	else
+ 		/*
+ 		 * safe to scroll byte by byte
+ 		 */
+ 	{
+ 		for (start = s; *s != c && s < start + len; s++)
+ 			;
+ 	}
+ 
+ 	return ((*s == c) ? (char *) s : NULL);
+ }
+ 
+ char *
+ str2chrlen(const char *s, int c1, int c2, size_t len, char *c_found)
+ {
+ 	const char *start;
+ 
+ 	*c_found = '\0';
+ 
+ 	for (start = s; *s != c1 && *s != c2 && s < start + len; s++)
+ 		;
+ 
+ 	*c_found = *s;
+ 	return (*c_found != '\0' ? (char *) s : NULL);
+ }