Hi

2015-08-06 10:37 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi,
>
> Psql based implementation needs new infrastructure (more than few lines)
>
> Missing:
>
> * binary mode support
> * parametrized query support,
>
> I am not against, but both points I proposed, and both was rejected.
>
> So why dont use current infrastructure? Raw copy is trivial patch.
>

I was asked by Daniel Verite about reopening this patch in opened
commitfest.

I am sending rebased patch

Regards

Pavel
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
new file mode 100644
index 07e2f45..68fbfd8
*** a/doc/src/sgml/ref/copy.sgml
--- b/doc/src/sgml/ref/copy.sgml
*************** COPY { <replaceable class="parameter">ta
*** 197,203 ****
        Selects the data format to be read or written:
        <literal>text</>,
        <literal>csv</> (Comma Separated Values),
!       or <literal>binary</>.
        The default is <literal>text</>.
       </para>
      </listitem>
--- 197,203 ----
        Selects the data format to be read or written:
        <literal>text</>,
        <literal>csv</> (Comma Separated Values),
!       <literal>binary</> or <literal>raw</literal>.
        The default is <literal>text</>.
       </para>
      </listitem>
*************** OIDs to be shown as null if that ever pr
*** 888,893 ****
--- 888,925 ----
      </para>
     </refsect3>
    </refsect2>
+ 
+   <refsect2>
+      <title>Raw Format</title>
+ 
+    <para>
+     The <literal>raw</literal> format option causes all data to be
+     stored/read as binary format rather than as text. It shares format
+     for data with <literal>binary</literal> format. This format doesn't
+     use any metadata - only row data in network byte order are exported
+     or imported.
+    </para>
+ 
+    <para>
+     Because this format doesn't support any delimiter, only one value
+     can be exported or imported. NULL values are not allowed.
+    </para>
+    <para>
+     The <literal>raw</literal> format can be used for export or import
+     bytea values.
+ <programlisting>
+ COPY images(data) FROM '/usr1/proj/img/01.jpg' (FORMAT raw);
+ </programlisting>
+     It can be used successfully for export XML in different encoding
+     or import valid XML document with any supported encoding:
+ <screen><![CDATA[
+ SET client_encoding TO latin2;
+ 
+ COPY (SELECT xmlelement(NAME data, 'Hello')) TO stdout (FORMAT raw);
+ <?xml version="1.0" encoding="LATIN2"?><data>Hello</data>
+ ]]></screen>
+    </para>
+   </refsect2>
   </refsect1>
  
   <refsect1>
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
new file mode 100644
index 3201476..beb9152
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** typedef enum EolType
*** 89,94 ****
--- 89,99 ----
   * it's faster to make useless comparisons to trailing bytes than it is to
   * invoke pg_encoding_mblen() to skip over them. encoding_embeds_ascii is TRUE
   * when we have to do it the hard way.
+  *
+  * COPY supports three modes: text, binary and raw. The text format is plain
+  * text multiline format with specified delimiter. The binary format holds
+  * metadata (numbers, sizes) and data. The raw format holds data only and
+  * only one non NULL value can be processed.
   */
  typedef struct CopyStateData
  {
*************** typedef struct CopyStateData
*** 110,115 ****
--- 115,121 ----
  	char	   *filename;		/* filename, or NULL for STDIN/STDOUT */
  	bool		is_program;		/* is 'filename' a program to popen? */
  	bool		binary;			/* binary format? */
+ 	bool		raw;			/* required raw binary? */
  	bool		oids;			/* include OIDs? */
  	bool		freeze;			/* freeze rows on loading? */
  	bool		csv_mode;		/* Comma Separated Value format? */
*************** typedef struct CopyStateData
*** 199,204 ****
--- 205,213 ----
  	char	   *raw_buf;
  	int			raw_buf_index;	/* next byte to process */
  	int			raw_buf_len;	/* total # of bytes stored */
+ 
+ 	/* field for RAW mode */
+ 	bool		row_processed;		/* true, when first row was processed */
  } CopyStateData;
  
  /* DestReceiver for COPY (query) TO */
*************** SendCopyBegin(CopyState cstate)
*** 342,350 ****
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format = (cstate->binary ? 1 : 0);
  		int			i;
  
  		pq_beginmessage(&buf, 'H');
  		pq_sendbyte(&buf, format);		/* overall format */
  		pq_sendint(&buf, natts, 2);
--- 351,366 ----
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format;
  		int			i;
  
+ 		if (cstate->raw)
+ 			format = 2;
+ 		else if (cstate->binary)
+ 			format = 1;
+ 		else
+ 			format = 0;
+ 
  		pq_beginmessage(&buf, 'H');
  		pq_sendbyte(&buf, format);		/* overall format */
  		pq_sendint(&buf, natts, 2);
*************** SendCopyBegin(CopyState cstate)
*** 356,362 ****
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
--- 372,378 ----
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary && cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
*************** SendCopyBegin(CopyState cstate)
*** 368,374 ****
  	else
  	{
  		/* very old way */
! 		if (cstate->binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
--- 384,390 ----
  	else
  	{
  		/* very old way */
! 		if (cstate->binary && cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
*************** ReceiveCopyBegin(CopyState cstate)
*** 387,395 ****
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format = (cstate->binary ? 1 : 0);
  		int			i;
  
  		pq_beginmessage(&buf, 'G');
  		pq_sendbyte(&buf, format);		/* overall format */
  		pq_sendint(&buf, natts, 2);
--- 403,418 ----
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format;
  		int			i;
  
+ 		if (cstate->raw)
+ 			format = 2;
+ 		else if (cstate->binary)
+ 			format = 1;
+ 		else
+ 			format = 0;
+ 
  		pq_beginmessage(&buf, 'G');
  		pq_sendbyte(&buf, format);		/* overall format */
  		pq_sendint(&buf, natts, 2);
*************** ReceiveCopyBegin(CopyState cstate)
*** 402,408 ****
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
--- 425,431 ----
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
*************** ReceiveCopyBegin(CopyState cstate)
*** 414,420 ****
  	else
  	{
  		/* very old way */
! 		if (cstate->binary)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
--- 437,443 ----
  	else
  	{
  		/* very old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			errmsg("COPY BINARY is not supported to stdout or from stdin")));
*************** CopySendEndOfRow(CopyState cstate)
*** 482,488 ****
  	switch (cstate->copy_dest)
  	{
  		case COPY_FILE:
! 			if (!cstate->binary)
  			{
  				/* Default line termination depends on platform */
  #ifndef WIN32
--- 505,511 ----
  	switch (cstate->copy_dest)
  	{
  		case COPY_FILE:
! 			if (!cstate->binary && !cstate->raw)
  			{
  				/* Default line termination depends on platform */
  #ifndef WIN32
*************** CopySendEndOfRow(CopyState cstate)
*** 527,533 ****
  			break;
  		case COPY_OLD_FE:
  			/* The FE/BE protocol uses \n as newline for all platforms */
! 			if (!cstate->binary)
  				CopySendChar(cstate, '\n');
  
  			if (pq_putbytes(fe_msgbuf->data, fe_msgbuf->len))
--- 550,556 ----
  			break;
  		case COPY_OLD_FE:
  			/* The FE/BE protocol uses \n as newline for all platforms */
! 			if (!cstate->binary && !cstate->raw)
  				CopySendChar(cstate, '\n');
  
  			if (pq_putbytes(fe_msgbuf->data, fe_msgbuf->len))
*************** CopySendEndOfRow(CopyState cstate)
*** 540,546 ****
  			break;
  		case COPY_NEW_FE:
  			/* The FE/BE protocol uses \n as newline for all platforms */
! 			if (!cstate->binary)
  				CopySendChar(cstate, '\n');
  
  			/* Dump the accumulated row as one CopyData message */
--- 563,569 ----
  			break;
  		case COPY_NEW_FE:
  			/* The FE/BE protocol uses \n as newline for all platforms */
! 			if (!cstate->binary && !cstate->raw)
  				CopySendChar(cstate, '\n');
  
  			/* Dump the accumulated row as one CopyData message */
*************** CopyGetData(CopyState cstate, void *data
*** 597,602 ****
--- 620,626 ----
  			bytesread = minread;
  			break;
  		case COPY_NEW_FE:
+ 
  			while (maxread > 0 && bytesread < minread && !cstate->fe_eof)
  			{
  				int			avail;
*************** CopyGetData(CopyState cstate, void *data
*** 619,624 ****
--- 643,649 ----
  								(errcode(ERRCODE_CONNECTION_FAILURE),
  								 errmsg("unexpected EOF on client connection with an open transaction")));
  					RESUME_CANCEL_INTERRUPTS();
+ 
  					switch (mtype)
  					{
  						case 'd':		/* CopyData */
*************** CopyLoadRawBuf(CopyState cstate)
*** 766,771 ****
--- 791,827 ----
  	return (inbytes > 0);
  }
  
+ /*
+  * CopyLoadallRawBuf load all file into raw_buf.
+  *
+  * It is used for reading content in raw mode. If original RAW_BUF_SIZE is not
+  * enough, the buffer is enlarged.
+  */
+ static void
+ CopyLoadallRawBuf(CopyState cstate)
+ {
+ 	int			nbytes = 0;
+ 	int			inbytes;
+ 	Size			raw_buf_size = RAW_BUF_SIZE;
+ 
+ 	do
+ 	{
+ 		/* hold enough space for one data packet */
+ 		if ((raw_buf_size - nbytes - 1) < 8 * 1024)
+ 		{
+ 			raw_buf_size += RAW_BUF_SIZE;
+ 			cstate->raw_buf = repalloc(cstate->raw_buf, raw_buf_size);
+ 		}
+ 
+ 		inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes, 1, raw_buf_size - nbytes - 1);
+ 		nbytes += inbytes;
+ 	}
+ 	while (inbytes > 0);
+ 
+ 	cstate->raw_buf[nbytes] = '\0';
+ 	cstate->raw_buf_index = 0;
+ 	cstate->raw_buf_len = nbytes;
+ }
  
  /*
   *	 DoCopy executes the SQL COPY statement
*************** ProcessCopyOptions(CopyState cstate,
*** 1013,1018 ****
--- 1069,1076 ----
  				cstate->csv_mode = true;
  			else if (strcmp(fmt, "binary") == 0)
  				cstate->binary = true;
+ 			else if (strcmp(fmt, "raw") == 0)
+ 				cstate->raw = true;
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
*************** ProcessCopyOptions(CopyState cstate,
*** 1162,1176 ****
  	 * Check for incompatible options (must do these two before inserting
  	 * defaults)
  	 */
! 	if (cstate->binary && cstate->delim)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("cannot specify DELIMITER in BINARY mode")));
  
! 	if (cstate->binary && cstate->null_print)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("cannot specify NULL in BINARY mode")));
  
  	/* Set defaults for omitted options */
  	if (!cstate->delim)
--- 1220,1239 ----
  	 * Check for incompatible options (must do these two before inserting
  	 * defaults)
  	 */
! 	if ((cstate->binary || cstate->raw) && cstate->delim)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("cannot specify DELIMITER in BINARY or RAW mode")));
  
! 	if ((cstate->binary || cstate->raw) && cstate->null_print)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("cannot specify NULL in BINARY or RAW mode")));
! 
! 	if (cstate->raw && cstate->oids)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("cannot specify OIDS in RAW mode")));
  
  	/* Set defaults for omitted options */
  	if (!cstate->delim)
*************** BeginCopy(bool is_from,
*** 1608,1613 ****
--- 1671,1690 ----
  		}
  	}
  
+ 	/*
+ 	 * Initializaze the field "row_processed" for one row output in RAW mode,
+ 	 * and ensure only one output column.
+ 	 */
+ 	if (cstate->raw)
+ 	{
+ 		cstate->row_processed = false;
+ 
+ 		if (num_phys_attrs > 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("only single column result is allowed in RAW mode")));
+ 	}
+ 
  	/* Use client encoding when ENCODING option is not specified. */
  	if (cstate->file_encoding < 0)
  		cstate->file_encoding = pg_get_client_encoding();
*************** CopyTo(CopyState cstate)
*** 1876,1882 ****
  		Oid			out_func_oid;
  		bool		isvarlena;
  
! 		if (cstate->binary)
  			getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
  									&out_func_oid,
  									&isvarlena);
--- 1953,1959 ----
  		Oid			out_func_oid;
  		bool		isvarlena;
  
! 		if (cstate->binary || cstate->raw)
  			getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
  									&out_func_oid,
  									&isvarlena);
*************** CopyTo(CopyState cstate)
*** 1915,1921 ****
  		tmp = 0;
  		CopySendInt32(cstate, tmp);
  	}
! 	else
  	{
  		/*
  		 * For non-binary copy, we need to convert null_print to file
--- 1992,1998 ----
  		tmp = 0;
  		CopySendInt32(cstate, tmp);
  	}
! 	else if (!cstate->raw)
  	{
  		/*
  		 * For non-binary copy, we need to convert null_print to file
*************** CopyTo(CopyState cstate)
*** 1983,1989 ****
  	else
  	{
  		/* run the plan --- the dest receiver will send tuples */
! 		ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L);
  		processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
  	}
  
--- 2060,2066 ----
  	else
  	{
  		/* run the plan --- the dest receiver will send tuples */
! 		ExecutorRun(cstate->queryDesc, ForwardScanDirection, cstate->raw ? 2L : 0L);
  		processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
  	}
  
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2027,2032 ****
--- 2104,2117 ----
  			CopySendInt32(cstate, tupleOid);
  		}
  	}
+ 	else if (cstate->raw)
+ 	{
+ 		if (cstate->row_processed)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_TOO_MANY_ROWS),
+ 					 errmsg("only single row result is allowed in RAW mode")));
+ 		cstate->row_processed = true;
+ 	}
  	else
  	{
  		/* Text format has no per-tuple header, but send OID if wanted */
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2046,2052 ****
  		Datum		value = values[attnum - 1];
  		bool		isnull = nulls[attnum - 1];
  
! 		if (!cstate->binary)
  		{
  			if (need_delim)
  				CopySendChar(cstate, cstate->delim[0]);
--- 2131,2137 ----
  		Datum		value = values[attnum - 1];
  		bool		isnull = nulls[attnum - 1];
  
! 		if (!cstate->binary && !cstate->raw)
  		{
  			if (need_delim)
  				CopySendChar(cstate, cstate->delim[0]);
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2055,2068 ****
  
  		if (isnull)
  		{
! 			if (!cstate->binary)
  				CopySendString(cstate, cstate->null_print_client);
  			else
  				CopySendInt32(cstate, -1);
  		}
  		else
  		{
! 			if (!cstate->binary)
  			{
  				string = OutputFunctionCall(&out_functions[attnum - 1],
  											value);
--- 2140,2171 ----
  
  		if (isnull)
  		{
! 			if (cstate->raw)
! 					ereport(ERROR,
! 						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 						  errmsg("cannot to copy NULL value in RAW mode.")));
! 			else if (!cstate->binary)
  				CopySendString(cstate, cstate->null_print_client);
  			else
  				CopySendInt32(cstate, -1);
  		}
  		else
  		{
! 			if (cstate->binary || cstate->raw)
! 			{
! 				bytea	   *outputbytes;
! 
! 				outputbytes = SendFunctionCall(&out_functions[attnum - 1],
! 											   value);
! 
! 				/* send the size only in binary mode */
! 				if (cstate->binary)
! 					CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
! 
! 				CopySendData(cstate, VARDATA(outputbytes),
! 							 VARSIZE(outputbytes) - VARHDRSZ);
! 			}
! 			else
  			{
  				string = OutputFunctionCall(&out_functions[attnum - 1],
  											value);
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2073,2088 ****
  				else
  					CopyAttributeOutText(cstate, string);
  			}
- 			else
- 			{
- 				bytea	   *outputbytes;
- 
- 				outputbytes = SendFunctionCall(&out_functions[attnum - 1],
- 											   value);
- 				CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
- 				CopySendData(cstate, VARDATA(outputbytes),
- 							 VARSIZE(outputbytes) - VARHDRSZ);
- 			}
  		}
  	}
  
--- 2176,2181 ----
*************** BeginCopyFrom(Relation rel,
*** 2712,2718 ****
  			continue;
  
  		/* Fetch the input function and typioparam info */
! 		if (cstate->binary)
  			getTypeBinaryInputInfo(attr[attnum - 1]->atttypid,
  								   &in_func_oid, &typioparams[attnum - 1]);
  		else
--- 2805,2811 ----
  			continue;
  
  		/* Fetch the input function and typioparam info */
! 		if (cstate->binary || cstate->raw)
  			getTypeBinaryInputInfo(attr[attnum - 1]->atttypid,
  								   &in_func_oid, &typioparams[attnum - 1]);
  		else
*************** BeginCopyFrom(Relation rel,
*** 2811,2817 ****
  		}
  	}
  
! 	if (!cstate->binary)
  	{
  		/* must rely on user to tell us... */
  		cstate->file_has_oids = cstate->oids;
--- 2904,2910 ----
  		}
  	}
  
! 	if (!cstate->binary || cstate->raw)
  	{
  		/* must rely on user to tell us... */
  		cstate->file_has_oids = cstate->oids;
*************** BeginCopyFrom(Relation rel,
*** 2863,2869 ****
  	}
  
  	/* create workspace for CopyReadAttributes results */
! 	if (!cstate->binary)
  	{
  		AttrNumber	attr_count = list_length(cstate->attnumlist);
  		int			nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count;
--- 2956,2962 ----
  	}
  
  	/* create workspace for CopyReadAttributes results */
! 	if (!cstate->binary && !cstate->raw)
  	{
  		AttrNumber	attr_count = list_length(cstate->attnumlist);
  		int			nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count;
*************** NextCopyFrom(CopyState cstate, ExprConte
*** 2968,2975 ****
  	MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  	MemSet(nulls, true, num_phys_attrs * sizeof(bool));
  
! 	if (!cstate->binary)
  	{
  		char	  **field_strings;
  		ListCell   *cur;
  		int			fldct;
--- 3061,3180 ----
  	MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  	MemSet(nulls, true, num_phys_attrs * sizeof(bool));
  
! 	if (cstate->binary)
  	{
+ 		int16		fld_count;
+ 		ListCell   *cur;
+ 
+ 		cstate->cur_lineno++;
+ 
+ 		if (!CopyGetInt16(cstate, &fld_count))
+ 		{
+ 			/* EOF detected (end of file, or protocol-level EOF) */
+ 			return false;
+ 		}
+ 
+ 		if (fld_count == -1)
+ 		{
+ 			/*
+ 			 * Received EOF marker.  In a V3-protocol copy, wait for the
+ 			 * protocol-level EOF, and complain if it doesn't come
+ 			 * immediately.  This ensures that we correctly handle CopyFail,
+ 			 * if client chooses to send that now.
+ 			 *
+ 			 * Note that we MUST NOT try to read more data in an old-protocol
+ 			 * copy, since there is no protocol-level EOF marker then.  We
+ 			 * could go either way for copy from file, but choose to throw
+ 			 * error if there's data after the EOF marker, for consistency
+ 			 * with the new-protocol case.
+ 			 */
+ 			char		dummy;
+ 
+ 			if (cstate->copy_dest != COPY_OLD_FE &&
+ 				CopyGetData(cstate, &dummy, 1, 1) > 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ 						 errmsg("received copy data after EOF marker")));
+ 			return false;
+ 		}
+ 
+ 		if (fld_count != attr_count)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ 					 errmsg("row field count is %d, expected %d",
+ 							(int) fld_count, attr_count)));
+ 
+ 		if (file_has_oids)
+ 		{
+ 			Oid			loaded_oid;
+ 
+ 			cstate->cur_attname = "oid";
+ 			loaded_oid =
+ 				DatumGetObjectId(CopyReadBinaryAttribute(cstate,
+ 														 0,
+ 													&cstate->oid_in_function,
+ 													  cstate->oid_typioparam,
+ 														 -1,
+ 														 &isnull));
+ 			if (isnull || loaded_oid == InvalidOid)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ 						 errmsg("invalid OID in COPY data")));
+ 			cstate->cur_attname = NULL;
+ 			if (cstate->oids && tupleOid != NULL)
+ 				*tupleOid = loaded_oid;
+ 		}
+ 
+ 		i = 0;
+ 		foreach(cur, cstate->attnumlist)
+ 		{
+ 			int			attnum = lfirst_int(cur);
+ 			int			m = attnum - 1;
+ 
+ 			cstate->cur_attname = NameStr(attr[m]->attname);
+ 			i++;
+ 			values[m] = CopyReadBinaryAttribute(cstate,
+ 												i,
+ 												&in_functions[m],
+ 												typioparams[m],
+ 												attr[m]->atttypmod,
+ 												&nulls[m]);
+ 			cstate->cur_attname = NULL;
+ 		}
+ 	}
+ 	else if (cstate->raw)
+ 	{
+ 		if (cstate->row_processed)
+ 			return false;
+ 
+ 		CopyLoadallRawBuf(cstate);
+ 		cstate->cur_attname = NameStr(attr[0]->attname);
+ 
+ 		if (cstate->attribute_buf.data != NULL)
+ 			pfree(cstate->attribute_buf.data);
+ 
+ 		cstate->attribute_buf.data = cstate->raw_buf;
+ 		cstate->attribute_buf.len = cstate->raw_buf_len;
+ 		cstate->attribute_buf.cursor = 0;
+ 
+ 		cstate->raw_buf = NULL;
+ 
+ 		/* Call the column type's binary input converter */
+ 		values[0] = ReceiveFunctionCall(&in_functions[0], &cstate->attribute_buf,
+ 								 typioparams[0], attr[0]->atttypmod);
+ 		nulls[0] = false;
+ 
+ 		/* Trouble if it didn't eat the whole buffer */
+ 		if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ 					 errmsg("incorrect binary data format")));
+ 
+ 		cstate->row_processed = true;
+ 	}
+ 	else
+ 	{
+ 		/* text */
  		char	  **field_strings;
  		ListCell   *cur;
  		int			fldct;
*************** NextCopyFrom(CopyState cstate, ExprConte
*** 3074,3161 ****
  
  		Assert(fieldno == nfields);
  	}
- 	else
- 	{
- 		/* binary */
- 		int16		fld_count;
- 		ListCell   *cur;
- 
- 		cstate->cur_lineno++;
- 
- 		if (!CopyGetInt16(cstate, &fld_count))
- 		{
- 			/* EOF detected (end of file, or protocol-level EOF) */
- 			return false;
- 		}
- 
- 		if (fld_count == -1)
- 		{
- 			/*
- 			 * Received EOF marker.  In a V3-protocol copy, wait for the
- 			 * protocol-level EOF, and complain if it doesn't come
- 			 * immediately.  This ensures that we correctly handle CopyFail,
- 			 * if client chooses to send that now.
- 			 *
- 			 * Note that we MUST NOT try to read more data in an old-protocol
- 			 * copy, since there is no protocol-level EOF marker then.  We
- 			 * could go either way for copy from file, but choose to throw
- 			 * error if there's data after the EOF marker, for consistency
- 			 * with the new-protocol case.
- 			 */
- 			char		dummy;
- 
- 			if (cstate->copy_dest != COPY_OLD_FE &&
- 				CopyGetData(cstate, &dummy, 1, 1) > 0)
- 				ereport(ERROR,
- 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- 						 errmsg("received copy data after EOF marker")));
- 			return false;
- 		}
- 
- 		if (fld_count != attr_count)
- 			ereport(ERROR,
- 					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- 					 errmsg("row field count is %d, expected %d",
- 							(int) fld_count, attr_count)));
- 
- 		if (file_has_oids)
- 		{
- 			Oid			loaded_oid;
- 
- 			cstate->cur_attname = "oid";
- 			loaded_oid =
- 				DatumGetObjectId(CopyReadBinaryAttribute(cstate,
- 														 0,
- 													&cstate->oid_in_function,
- 													  cstate->oid_typioparam,
- 														 -1,
- 														 &isnull));
- 			if (isnull || loaded_oid == InvalidOid)
- 				ereport(ERROR,
- 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- 						 errmsg("invalid OID in COPY data")));
- 			cstate->cur_attname = NULL;
- 			if (cstate->oids && tupleOid != NULL)
- 				*tupleOid = loaded_oid;
- 		}
- 
- 		i = 0;
- 		foreach(cur, cstate->attnumlist)
- 		{
- 			int			attnum = lfirst_int(cur);
- 			int			m = attnum - 1;
- 
- 			cstate->cur_attname = NameStr(attr[m]->attname);
- 			i++;
- 			values[m] = CopyReadBinaryAttribute(cstate,
- 												i,
- 												&in_functions[m],
- 												typioparams[m],
- 												attr[m]->atttypmod,
- 												&nulls[m]);
- 			cstate->cur_attname = NULL;
- 		}
- 	}
  
  	/*
  	 * Now compute and insert any defaults available for the columns not
--- 3279,3284 ----
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
new file mode 100644
index 5f6260a..90d2aa5
*** a/src/test/regress/expected/copy2.out
--- b/src/test/regress/expected/copy2.out
*************** DROP FUNCTION truncate_in_subxact();
*** 466,468 ****
--- 466,481 ----
  DROP TABLE x, y;
  DROP FUNCTION fn_x_before();
  DROP FUNCTION fn_x_after();
+ CREATE TABLE x(a bytea);
+ INSERT INTO x VALUES('\x41484f4a0a');
+ INSERT INTO x VALUES('\x41484f4a0a');
+ -- should to fail
+ COPY (SELECT a,a FROM x LIMIT 1) TO STDOUT (FORMAT raw);
+ ERROR:  only single column result is allowed in RAW mode
+ COPY (SELECT a FROM x) TO STDOUT (FORMAT raw);
+ AHOJ
+ ERROR:  only single row result is allowed in RAW mode
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO STDOUT (FORMAT raw);
+ AHOJ
+ DROP TABLE x;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
new file mode 100644
index cb13606..d351b43
*** a/src/test/regress/input/copy.source
--- b/src/test/regress/input/copy.source
*************** this is just a line full of junk that wo
*** 133,135 ****
--- 133,195 ----
  \.
  
  copy copytest3 to stdout csv header;
+ 
+ -- copy raw
+ CREATE TABLE x(a bytea);
+ INSERT INTO x VALUES('\x41484f4a0a');
+ SELECT length(a) FROM x;
+ 
+ INSERT INTO x VALUES('\x41484f4a0a');
+ 
+ -- should to fail
+ COPY (SELECT a,a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ COPY (SELECT a FROM x) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ 
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw);
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw);
+ 
+ TRUNCATE x;
+ 
+ \COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw)
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw);
+ 
+ \COPY x TO '@abs_builddir@/results/raw2.data' (FORMAT raw)
+ TRUNCATE x;
+ 
+ \COPY x FROM '@abs_builddir@/results/raw2.data' (FORMAT raw)
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw);
+ 
+ -- test big file
+ TRUNCATE x;
+ -- use different mechanism for load to bytea
+ \lo_import @abs_builddir@/data/hash.data
+ \set lo_oid :LASTOID
+ INSERT INTO x VALUES(lo_get(:lo_oid));
+ \lo_unlink :lo_oid
+ 
+ COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw)
+ 
+ SELECT md5(a), length(a) FROM x;
+ 
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw);
+ COPY x TO '@abs_builddir@/results/hash2.data' (FORMAT raw);
+ \COPY x TO '@abs_builddir@/results/hash3.data' (FORMAT raw)
+ 
+ -- read again
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw)
+ -- cross
+ COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw)
+ 
+ SELECT md5(a), length(a) FROM x;
+ 
+ DROP TABLE x;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
new file mode 100644
index b7e372d..f555f58
*** a/src/test/regress/output/copy.source
--- b/src/test/regress/output/copy.source
*************** copy copytest3 to stdout csv header;
*** 95,97 ****
--- 95,183 ----
  c1,"col with , comma","col with "" quote"
  1,a,1
  2,b,2
+ -- copy raw
+ CREATE TABLE x(a bytea);
+ INSERT INTO x VALUES('\x41484f4a0a');
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ INSERT INTO x VALUES('\x41484f4a0a');
+ -- should to fail
+ COPY (SELECT a,a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ ERROR:  only single column result is allowed in RAW mode
+ COPY (SELECT a FROM x) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ ERROR:  only single row result is allowed in RAW mode
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw);
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw);
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw);
+ AHOJ
+ TRUNCATE x;
+ \COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw)
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw);
+ AHOJ
+ \COPY x TO '@abs_builddir@/results/raw2.data' (FORMAT raw)
+ TRUNCATE x;
+ \COPY x FROM '@abs_builddir@/results/raw2.data' (FORMAT raw)
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw);
+ AHOJ
+ -- test big file
+ TRUNCATE x;
+ -- use different mechanism for load to bytea
+ \lo_import @abs_builddir@/data/hash.data
+ \set lo_oid :LASTOID
+ INSERT INTO x VALUES(lo_get(:lo_oid));
+ \lo_unlink :lo_oid
+ COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw)
+ SELECT md5(a), length(a) FROM x;
+                md5                | length 
+ ----------------------------------+--------
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+ (3 rows)
+ 
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw);
+ COPY x TO '@abs_builddir@/results/hash2.data' (FORMAT raw);
+ \COPY x TO '@abs_builddir@/results/hash3.data' (FORMAT raw)
+ -- read again
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw)
+ -- cross
+ COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw);
+ \COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw)
+ SELECT md5(a), length(a) FROM x;
+                md5                | length 
+ ----------------------------------+--------
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+ (5 rows)
+ 
+ DROP TABLE x;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
new file mode 100644
index 39a9deb..e5703a5
*** a/src/test/regress/sql/copy2.sql
--- b/src/test/regress/sql/copy2.sql
*************** DROP FUNCTION truncate_in_subxact();
*** 333,335 ****
--- 333,348 ----
  DROP TABLE x, y;
  DROP FUNCTION fn_x_before();
  DROP FUNCTION fn_x_after();
+ 
+ CREATE TABLE x(a bytea);
+ INSERT INTO x VALUES('\x41484f4a0a');
+ INSERT INTO x VALUES('\x41484f4a0a');
+ 
+ -- should to fail
+ COPY (SELECT a,a FROM x LIMIT 1) TO STDOUT (FORMAT raw);
+ COPY (SELECT a FROM x) TO STDOUT (FORMAT raw);
+ 
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO STDOUT (FORMAT raw);
+ 
+ DROP TABLE x;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to