Hi

here is cleaned/finished previous implementation of RAW_TEXT/RAW_BINARY
formats for COPY statements.

The RAW with text formats means unescaped data, but with correct encoding -
input/output is realised with input/output function. RAW binary means
content produced/received by sending/received functions.

Now both directions (input/output) working well

Some examples of expected usage:

copy (select xmlelement(name foo, 'hello')) to stdout (format raw_binary,
encoding 'latin2');

create table avatars(id serial, picture bytea);
\copy avatars(picture) from ~/images/foo.jpg (format raw_binary);
select lastval();

create table doc(id serial, txt text);
\copy doc(txt) from ~/files/aaa.txt (format raw_text, encoding 'latin2');
select lastval();

Regards

Pavel
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
new file mode 100644
index 3829a14..7b9ed73
*** a/doc/src/sgml/libpq.sgml
--- b/doc/src/sgml/libpq.sgml
*************** int PQfformat(const PGresult *res,
*** 3226,3233 ****
  
        <para>
         Format code zero indicates textual data representation, while format
!        code one indicates binary representation.  (Other codes are reserved
!        for future definition.)
        </para>
       </listitem>
      </varlistentry>
--- 3226,3234 ----
  
        <para>
         Format code zero indicates textual data representation, while format
!        code one indicates binary representation. Format code two indicates
!        raw_text representation and format code three indicates raw_binary
!        representation (Other codes are reserved for future definition.)
        </para>
       </listitem>
      </varlistentry>
*************** typedef struct
*** 3557,3562 ****
--- 3558,3583 ----
     </para>
  
     <variablelist>
+     <varlistentry id="libpq-pqcopyformat">
+      <term>
+       <function>PQcopyFormat</function>
+       <indexterm>
+        <primary>PQcopyFormat</primary>
+       </indexterm>
+      </term>
+ 
+      <listitem>
+       <para>
+        Format code zero indicates textual data representation, format one
+        indicates binary representation, format two indicates raw
+        representation.
+ <synopsis>
+ int PQcopyFormat(PGresult *res);
+ </synopsis>
+       </para>
+      </listitem>
+     </varlistentry>
+ 
      <varlistentry id="libpq-pqcmdstatus">
       <term>
        <function>PQcmdStatus</function>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
new file mode 100644
index 522128e..e783b30
*** a/doc/src/sgml/protocol.sgml
--- b/doc/src/sgml/protocol.sgml
*************** CopyInResponse (B)
*** 3239,3244 ****
--- 3239,3245 ----
                  characters, etc).
                  1 indicates the overall copy format is binary (similar
                  to DataRow format).
+                 2 indicates the overall copy format is raw.
                  See <xref linkend="sql-copy">
                  for more information.
  </para>
*************** CopyInResponse (B)
*** 3262,3269 ****
  <listitem>
  <para>
                  The format codes to be used for each column.
!                 Each must presently be zero (text) or one (binary).
!                 All must be zero if the overall copy format is textual.
  </para>
  </listitem>
  </varlistentry>
--- 3263,3271 ----
  <listitem>
  <para>
                  The format codes to be used for each column.
!                 Each must be zero (text), one (binary), two (raw_text)
!                 or three (raw_binary). All must be zero if the overall
!                 copy format is textual.
  </para>
  </listitem>
  </varlistentry>
*************** CopyOutResponse (B)
*** 3313,3319 ****
                  is textual (rows separated by newlines, columns
                  separated by separator characters, etc). 1 indicates
                  the overall copy format is binary (similar to DataRow
!                 format). See <xref linkend="sql-copy"> for more information.
  </para>
  </listitem>
  </varlistentry>
--- 3315,3322 ----
                  is textual (rows separated by newlines, columns
                  separated by separator characters, etc). 1 indicates
                  the overall copy format is binary (similar to DataRow
!                 format). 2 indicates raw_text or raw_binary format.
!                 See <xref linkend="sql-copy"> for more information.
  </para>
  </listitem>
  </varlistentry>
*************** CopyOutResponse (B)
*** 3335,3342 ****
  <listitem>
  <para>
                  The format codes to be used for each column.
!                 Each must presently be zero (text) or one (binary).
!                 All must be zero if the overall copy format is textual.
  </para>
  </listitem>
  </varlistentry>
--- 3338,3346 ----
  <listitem>
  <para>
                  The format codes to be used for each column.
!                 Each must be zero (text), one (binary), two (raw_text)
!                 or three (raw_binary). All must be zero if the overall
!                 copy format is textual.
  </para>
  </listitem>
  </varlistentry>
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
new file mode 100644
index 07e2f45..4e339e4
*** 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,205 ----
        Selects the data format to be read or written:
        <literal>text</>,
        <literal>csv</> (Comma Separated Values),
!       <literal>binary</>,
!       <literal>raw_text</>
!       or <literal>raw_binary</>.
        The default is <literal>text</>.
       </para>
      </listitem>
*************** OIDs to be shown as null if that ever pr
*** 888,893 ****
--- 890,933 ----
      </para>
     </refsect3>
    </refsect2>
+ 
+   <refsect2>
+      <title>Raw_text/raw_binary Format</title>
+ 
+    <para>
+     The <literal>raw_text</literal> format option causes all data to be
+     stored/read as one text value. This format doesn't use any metadata
+     - only raw data are exported or imported.
+    </para>
+ 
+    <para>
+     The <literal>raw_binary</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_binary</literal> format can be used for export or import
+     bytea values.
+ <programlisting>
+ COPY images(data) FROM '/usr1/proj/img/01.jpg' (FORMAT raw_binary);
+ </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_binary);
+ <?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..7600829
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** typedef struct CopyStateData
*** 110,115 ****
--- 110,116 ----
  	char	   *filename;		/* filename, or NULL for STDIN/STDOUT */
  	bool		is_program;		/* is 'filename' a program to popen? */
  	bool		binary;			/* binary format? */
+ 	bool		raw;			/* raw mode? */
  	bool		oids;			/* include OIDs? */
  	bool		freeze;			/* freeze rows on loading? */
  	bool		csv_mode;		/* Comma Separated Value format? */
*************** SendCopyBegin(CopyState cstate)
*** 342,353 ****
  		/* 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);
  		for (i = 0; i < natts; i++)
  			pq_sendint(&buf, format, 2);		/* per-column formats */
  		pq_endmessage(&buf);
--- 343,369 ----
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format;
! 		int			mode;
  		int			i;
  
  		pq_beginmessage(&buf, 'H');
! 
! 		if (cstate->raw)
! 			mode = 2;
! 		else if (cstate->binary)
! 			mode = 1;
! 		else
! 			mode = 0;
! 
! 		pq_sendbyte(&buf, mode);		/* overall mode */
  		pq_sendint(&buf, natts, 2);
+ 
+ 		if (!cstate->raw)
+ 			format = cstate->binary ? 1 : 0;
+ 		else
+ 			format = cstate->binary ? 3 : 2;
+ 
  		for (i = 0; i < natts; i++)
  			pq_sendint(&buf, format, 2);		/* per-column formats */
  		pq_endmessage(&buf);
*************** SendCopyBegin(CopyState cstate)
*** 356,365 ****
  	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")));
  		pq_putemptymessage('H');
  		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
--- 372,381 ----
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			errmsg("COPY BINARY or COPY RAW_TEXT/RAW_BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('H');
  		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
*************** SendCopyBegin(CopyState cstate)
*** 368,377 ****
  	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")));
  		pq_putemptymessage('B');
  		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
--- 384,393 ----
  	else
  	{
  		/* very old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			errmsg("COPY BINARY or COPY RAW_TEXT/RAW_BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('B');
  		/* grottiness needed for old COPY OUT protocol */
  		pq_startcopyout();
*************** ReceiveCopyBegin(CopyState cstate)
*** 387,398 ****
  		/* 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);
  		for (i = 0; i < natts; i++)
  			pq_sendint(&buf, format, 2);		/* per-column formats */
  		pq_endmessage(&buf);
--- 403,429 ----
  		/* new way */
  		StringInfoData buf;
  		int			natts = list_length(cstate->attnumlist);
! 		int16		format;
! 		int			mode;
  		int			i;
  
  		pq_beginmessage(&buf, 'G');
! 
! 		if (cstate->raw)
! 			mode = 2;
! 		else if (cstate->binary)
! 			mode = 1;
! 		else
! 			mode = 0;
! 
! 		pq_sendbyte(&buf, mode);		/* overall format */
  		pq_sendint(&buf, natts, 2);
+ 
+ 		if (!cstate->raw)
+ 			format = cstate->binary ? 1 : 0;
+ 		else
+ 			format = cstate->binary ? 3 : 2;
+ 
  		for (i = 0; i < natts; i++)
  			pq_sendint(&buf, format, 2);		/* per-column formats */
  		pq_endmessage(&buf);
*************** ReceiveCopyBegin(CopyState cstate)
*** 402,411 ****
  	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")));
  		pq_putemptymessage('G');
  		/* any error in old protocol will make us lose sync */
  		pq_startmsgread();
--- 433,442 ----
  	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
  	{
  		/* old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			errmsg("COPY BINARY or COPY RAW_TEXT/RAW_BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('G');
  		/* any error in old protocol will make us lose sync */
  		pq_startmsgread();
*************** ReceiveCopyBegin(CopyState cstate)
*** 414,423 ****
  	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")));
  		pq_putemptymessage('D');
  		/* any error in old protocol will make us lose sync */
  		pq_startmsgread();
--- 445,454 ----
  	else
  	{
  		/* very old way */
! 		if (cstate->binary || cstate->raw)
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 			errmsg("COPY BINARY or COPY RAW_TEXT/RAW_BINARY is not supported to stdout or from stdin")));
  		pq_putemptymessage('D');
  		/* any error in old protocol will make us lose sync */
  		pq_startmsgread();
*************** CopySendEndOfRow(CopyState cstate)
*** 482,488 ****
  	switch (cstate->copy_dest)
  	{
  		case COPY_FILE:
! 			if (!cstate->binary)
  			{
  				/* Default line termination depends on platform */
  #ifndef WIN32
--- 513,519 ----
  	switch (cstate->copy_dest)
  	{
  		case COPY_FILE:
! 			if (!cstate->binary && !cstate->raw)
  			{
  				/* Default line termination depends on platform */
  #ifndef WIN32
*************** CopySendEndOfRow(CopyState cstate)
*** 526,531 ****
--- 557,565 ----
  			}
  			break;
  		case COPY_OLD_FE:
+ 			/* This old protocol doesn't allow RAW_TEXT/RAW_BINARY */
+ 			Assert(!cstate->raw);
+ 
  			/* The FE/BE protocol uses \n as newline for all platforms */
  			if (!cstate->binary)
  				CopySendChar(cstate, '\n');
*************** 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 */
--- 574,580 ----
  			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 */
*************** CopyLoadRawBuf(CopyState cstate)
*** 766,771 ****
--- 800,837 ----
  	return (inbytes > 0);
  }
  
+ /*
+  * CopyLoadallRawBuf loads all content into raw_buf.
+  *
+  * This routine is used in raw_text/raw_binary mode. If original RAW_BUF_SIZE is not
+  * enough, then 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 ****
--- 1079,1091 ----
  				cstate->csv_mode = true;
  			else if (strcmp(fmt, "binary") == 0)
  				cstate->binary = true;
+ 			else if (strcmp(fmt, "raw_text") == 0)
+ 				cstate->raw = true;
+ 			else if (strcmp(fmt, "raw_binary") == 0)
+ 			{
+ 				cstate->binary = true;
+ 				cstate->raw = true;
+ 			}
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
*************** ProcessCopyOptions(CopyState cstate,
*** 1162,1177 ****
  	 * 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)
  		cstate->delim = cstate->csv_mode ? "," : "\t";
--- 1235,1255 ----
  	 * 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 mode")));
  
! 	if ((cstate->binary || cstate->raw) && cstate->null_print)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("cannot specify NULL in BINARY mode")));
  
+ 	if (cstate->raw && cstate->oids)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_SYNTAX_ERROR),
+ 				 errmsg("cannot specify OIDS in RAW_TEXT/RAW_BINARY mode")));
+ 
  	/* Set defaults for omitted options */
  	if (!cstate->delim)
  		cstate->delim = cstate->csv_mode ? "," : "\t";
*************** BeginCopy(bool is_from,
*** 1608,1613 ****
--- 1686,1697 ----
  		}
  	}
  
+ 	/* No more columns are allowed in RAW mode */
+ 	if (cstate->raw && list_length(cstate->attnumlist) > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 		 errmsg("Single column result/target is required in RAW_TEXT/RAW_BINARY 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)
*** 1899,1905 ****
  											   ALLOCSET_DEFAULT_INITSIZE,
  											   ALLOCSET_DEFAULT_MAXSIZE);
  
! 	if (cstate->binary)
  	{
  		/* Generate header for a binary copy */
  		int32		tmp;
--- 1983,1989 ----
  											   ALLOCSET_DEFAULT_INITSIZE,
  											   ALLOCSET_DEFAULT_MAXSIZE);
  
! 	if (cstate->binary && !cstate->raw)
  	{
  		/* Generate header for a binary copy */
  		int32		tmp;
*************** CopyTo(CopyState cstate)
*** 1931,1936 ****
--- 2015,2023 ----
  		{
  			bool		hdr_delim = false;
  
+ 			/* raw_text/raw_binary mode is not allowed here */
+ 			Assert(!cstate->raw);
+ 
  			foreach(cur, cstate->attnumlist)
  			{
  				int			attnum = lfirst_int(cur);
*************** CopyTo(CopyState cstate)
*** 1967,1972 ****
--- 2054,2063 ----
  		{
  			CHECK_FOR_INTERRUPTS();
  
+ 			/* stop quickly in raw_text/raw_binary when more rows is detected */
+ 			if (cstate->raw && processed > 0)
+ 				break;
+ 
  			/* Deconstruct the tuple ... faster than repeated heap_getattr */
  			heap_deform_tuple(tuple, tupDesc, values, nulls);
  
*************** CopyTo(CopyState cstate)
*** 1983,1993 ****
  	else
  	{
  		/* run the plan --- the dest receiver will send tuples */
! 		ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L);
  		processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
  	}
  
! 	if (cstate->binary)
  	{
  		/* Generate trailer for a binary copy */
  		CopySendInt16(cstate, -1);
--- 2074,2098 ----
  	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;
  	}
  
! 	/* raw_text/raw_binary requires exactly one row */
! 	if (cstate->raw)
! 	{
! 		if (processed > 1)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_TOO_MANY_ROWS),
! 					 errmsg("single row result is required by RAW_TEXT/RAW_BINARY mode")));
! 
! 		if (processed == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_NO_DATA_FOUND),
! 					 errmsg("single row result is required by RAW_TEXT/RAW_BINARY mode")));
! 	}
! 
! 	if (cstate->binary && !cstate->raw)
  	{
  		/* Generate trailer for a binary copy */
  		CopySendInt16(cstate, -1);
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2015,2042 ****
  	MemoryContextReset(cstate->rowcontext);
  	oldcontext = MemoryContextSwitchTo(cstate->rowcontext);
  
! 	if (cstate->binary)
  	{
! 		/* Binary per-tuple header */
! 		CopySendInt16(cstate, list_length(cstate->attnumlist));
! 		/* Send OID if wanted --- note attnumlist doesn't include it */
! 		if (cstate->oids)
  		{
! 			/* Hack --- assume Oid is same size as int32 */
! 			CopySendInt32(cstate, sizeof(int32));
! 			CopySendInt32(cstate, tupleOid);
  		}
! 	}
! 	else
! 	{
! 		/* Text format has no per-tuple header, but send OID if wanted */
! 		/* Assume digits don't need any quoting or encoding conversion */
! 		if (cstate->oids)
  		{
! 			string = DatumGetCString(DirectFunctionCall1(oidout,
! 												ObjectIdGetDatum(tupleOid)));
! 			CopySendString(cstate, string);
! 			need_delim = true;
  		}
  	}
  
--- 2120,2150 ----
  	MemoryContextReset(cstate->rowcontext);
  	oldcontext = MemoryContextSwitchTo(cstate->rowcontext);
  
! 	if (!cstate->raw)
  	{
! 		if (cstate->binary)
  		{
! 			/* Binary per-tuple header */
! 			CopySendInt16(cstate, list_length(cstate->attnumlist));
! 			/* Send OID if wanted --- note attnumlist doesn't include it */
! 			if (cstate->oids)
! 			{
! 				/* Hack --- assume Oid is same size as int32 */
! 				CopySendInt32(cstate, sizeof(int32));
! 				CopySendInt32(cstate, tupleOid);
! 			}
  		}
! 		else
  		{
! 			/* Text format has no per-tuple header, but send OID if wanted */
! 			/* Assume digits don't need any quoting or encoding conversion */
! 			if (cstate->oids)
! 			{
! 				string = DatumGetCString(DirectFunctionCall1(oidout,
! 													ObjectIdGetDatum(tupleOid)));
! 				CopySendString(cstate, string);
! 				need_delim = true;
! 			}
  		}
  	}
  
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2055,2060 ****
--- 2163,2173 ----
  
  		if (isnull)
  		{
+ 			if (cstate->raw)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 						 errmsg("NULL value is not allowed in RAW_TEXT/RAW_BINARY mode")));
+ 
  			if (!cstate->binary)
  				CopySendString(cstate, cstate->null_print_client);
  			else
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 2062,2068 ****
  		}
  		else
  		{
! 			if (!cstate->binary)
  			{
  				string = OutputFunctionCall(&out_functions[attnum - 1],
  											value);
--- 2175,2246 ----
  		}
  		else
  		{
! 			if (cstate->raw)
! 			{
! 				const void *content;
! 				int size;
! 			
! 				if (!cstate->binary)
! 				{
! 					string = OutputFunctionCall(&out_functions[attnum - 1],
! 												value);
! 
! 					/* We would to transcode, but without escaping */
! 					if (cstate->need_transcoding)
! 						content = pg_server_to_any(string, strlen(string), cstate->file_encoding);
! 					else
! 						content = string;
! 
! 					size = strlen((const char *) content);
! 				}
! 				else
! 				{
! 					bytea *outputbytes;
! 
! 					/*
! 					 * Some binary output functions depends can depends on client encoding.
! 					 * The binary output of xml is good example. Set client_encoding
! 					 * temporaly before out function execution.
! 					 */
! 					if (cstate->need_transcoding)
! 					{
! 						int		old_server_encoding = pg_get_client_encoding();
! 						volatile bool reset_encoding = false;
! 
! 						PG_TRY();
! 						{
! 							/* We don't expect an error, because encoding was checked before */
! 							if (PrepareClientEncoding(cstate->file_encoding) < 0)
! 								elog(ERROR, "PrepareClientEncoding(%d) failed", cstate->file_encoding);
! 
! 							SetClientEncoding(cstate->file_encoding);
! 							reset_encoding = true;
! 
! 							outputbytes = SendFunctionCall(&out_functions[attnum - 1],
! 													   value);
! 							SetClientEncoding(old_server_encoding);
! 						}
! 						PG_CATCH();
! 						{
! 							if (reset_encoding)
! 								SetClientEncoding(old_server_encoding);
! 							PG_RE_THROW();
! 						}
! 						PG_END_TRY();
! 					}
! 					else
! 					{
! 						outputbytes = SendFunctionCall(&out_functions[attnum - 1],
! 												   value);
! 					}
! 					content = VARDATA(outputbytes);
! 					size = VARSIZE(outputbytes) - VARHDRSZ;
! 				}
! 
! 				/* Send only content in RAW_TEXT/RAW_BINARY mode */
! 				CopySendData(cstate, content, size);
! 			}
! 			else if (!cstate->binary)
  			{
  				string = OutputFunctionCall(&out_functions[attnum - 1],
  											value);
*************** BeginCopyFrom(Relation rel,
*** 2811,2875 ****
  		}
  	}
  
! 	if (!cstate->binary)
! 	{
! 		/* must rely on user to tell us... */
! 		cstate->file_has_oids = cstate->oids;
! 	}
! 	else
  	{
! 		/* Read and verify binary header */
! 		char		readSig[11];
! 		int32		tmp;
! 
! 		/* Signature */
! 		if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
! 			memcmp(readSig, BinarySignature, 11) != 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg("COPY file signature not recognized")));
! 		/* Flags field */
! 		if (!CopyGetInt32(cstate, &tmp))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg("invalid COPY file header (missing flags)")));
! 		cstate->file_has_oids = (tmp & (1 << 16)) != 0;
! 		tmp &= ~(1 << 16);
! 		if ((tmp >> 16) != 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 				 errmsg("unrecognized critical flags in COPY file header")));
! 		/* Header extension length */
! 		if (!CopyGetInt32(cstate, &tmp) ||
! 			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)
  		{
! 			if (CopyGetData(cstate, readSig, 1, 1) != 1)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("invalid COPY file header (wrong length)")));
  		}
- 	}
  
! 	if (cstate->file_has_oids && cstate->binary)
! 	{
! 		getTypeBinaryInputInfo(OIDOID,
! 							   &in_func_oid, &cstate->oid_typioparam);
! 		fmgr_info(in_func_oid, &cstate->oid_in_function);
! 	}
  
! 	/* 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;
  
! 		cstate->max_fields = nfields;
! 		cstate->raw_fields = (char **) palloc(nfields * sizeof(char *));
  	}
  
  	MemoryContextSwitchTo(oldcontext);
--- 2989,3057 ----
  		}
  	}
  
! 	/* The raw mode hasn't any header information */
! 	if (!cstate->raw)
  	{
! 		if (!cstate->binary)
  		{
! 			/* must rely on user to tell us... */
! 			cstate->file_has_oids = cstate->oids;
! 		}
! 		else
! 		{
! 			/* Read and verify binary header */
! 			char		readSig[11];
! 			int32		tmp;
! 
! 			/* Signature */
! 			if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
! 				memcmp(readSig, BinarySignature, 11) != 0)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("COPY file signature not recognized")));
! 			/* Flags field */
! 			if (!CopyGetInt32(cstate, &tmp))
! 				ereport(ERROR,
! 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 						 errmsg("invalid COPY file header (missing flags)")));
! 			cstate->file_has_oids = (tmp & (1 << 16)) != 0;
! 			tmp &= ~(1 << 16);
! 			if ((tmp >> 16) != 0)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 					 errmsg("unrecognized critical flags in COPY file header")));
! 			/* Header extension length */
! 			if (!CopyGetInt32(cstate, &tmp) ||
! 				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)
! 			{
! 				if (CopyGetData(cstate, readSig, 1, 1) != 1)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
! 							 errmsg("invalid COPY file header (wrong length)")));
! 			}
  		}
  
! 		if (cstate->file_has_oids && cstate->binary)
! 		{
! 			getTypeBinaryInputInfo(OIDOID,
! 								   &in_func_oid, &cstate->oid_typioparam);
! 			fmgr_info(in_func_oid, &cstate->oid_in_function);
! 		}
  
! 		/* 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;
  
! 			cstate->max_fields = nfields;
! 			cstate->raw_fields = (char **) palloc(nfields * sizeof(char *));
! 		}
  	}
  
  	MemoryContextSwitchTo(oldcontext);
*************** NextCopyFrom(CopyState cstate, ExprConte
*** 2968,2974 ****
  	MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  	MemSet(nulls, true, num_phys_attrs * sizeof(bool));
  
! 	if (!cstate->binary)
  	{
  		char	  **field_strings;
  		ListCell   *cur;
--- 3150,3203 ----
  	MemSet(values, 0, num_phys_attrs * sizeof(Datum));
  	MemSet(nulls, true, num_phys_attrs * sizeof(bool));
  
! 	if (cstate->raw)
! 	{
! 		int		m = linitial_int(cstate->attnumlist) - 1;
! 
! 		/* All content was read in first cycle */
! 		if (++cstate->cur_lineno > 1)
! 			return false;
! 
! 		CopyLoadallRawBuf(cstate);
! 
! 		cstate->cur_attname = NameStr(attr[m]->attname);
! 
! 		if (!cstate->binary)
! 		{
! 			char	   *cvt;
! 
! 			cvt = pg_any_to_server(cstate->raw_buf,
! 								   cstate->raw_buf_len,
! 								   cstate->file_encoding);
! 
! 			values[m] = InputFunctionCall(&in_functions[m],
! 										  cvt,
! 										  typioparams[m],
! 										  attr[m]->atttypmod);
! 		}
! 		else
! 		{
! 			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[m] = ReceiveFunctionCall(&in_functions[m], &cstate->attribute_buf,
! 									 typioparams[m], attr[m]->atttypmod);
! 
! 			/* 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")));
! 		}
! 
! 		nulls[m] = false;
! 
! 		cstate->cur_attname = NULL;
! 	}
! 	else if (!cstate->binary)
  	{
  		char	  **field_strings;
  		ListCell   *cur;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
new file mode 100644
index cb8a06d..4dab119
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** psql_completion(const char *text, int st
*** 1969,1976 ****
  	/* Handle COPY [BINARY] <sth> FROM|TO filename */
  	else if (Matches4("COPY|\\copy", MatchAny, "FROM|TO", MatchAny) ||
  			 Matches5("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny))
! 		COMPLETE_WITH_LIST6("BINARY", "OIDS", "DELIMITER", "NULL", "CSV",
! 							"ENCODING");
  
  	/* Handle COPY [BINARY] <sth> FROM|TO filename CSV */
  	else if (Matches5("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "CSV") ||
--- 1969,1976 ----
  	/* Handle COPY [BINARY] <sth> FROM|TO filename */
  	else if (Matches4("COPY|\\copy", MatchAny, "FROM|TO", MatchAny) ||
  			 Matches5("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny))
! 		COMPLETE_WITH_LIST8("BINARY", "RAW_TEXT", "RAW_BINARY", "OIDS",
! 							"DELIMITER", "NULL", "CSV", "ENCODING");
  
  	/* Handle COPY [BINARY] <sth> FROM|TO filename CSV */
  	else if (Matches5("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "CSV") ||
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
new file mode 100644
index 21dd772..a2754f1
*** a/src/interfaces/libpq/exports.txt
--- b/src/interfaces/libpq/exports.txt
*************** PQsslAttributeNames       168
*** 171,173 ****
--- 171,174 ----
  PQsslAttribute            169
  PQsetErrorContextVisibility 170
  PQresultVerboseErrorMessage 171
+ PQcopyFormat              172
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
new file mode 100644
index 2621767..09967e9
*** a/src/interfaces/libpq/fe-exec.c
--- b/src/interfaces/libpq/fe-exec.c
*************** PQmakeEmptyPGresult(PGconn *conn, ExecSt
*** 155,160 ****
--- 155,161 ----
  	result->resultStatus = status;
  	result->cmdStatus[0] = '\0';
  	result->binary = 0;
+ 	result->raw = 0;
  	result->events = NULL;
  	result->nEvents = 0;
  	result->errMsg = NULL;
*************** PQsetResultAttrs(PGresult *res, int numA
*** 256,263 ****
  		if (!res->attDescs[i].name)
  			return FALSE;
  
! 		if (res->attDescs[i].format == 0)
  			res->binary = 0;
  	}
  
  	return TRUE;
--- 257,266 ----
  		if (!res->attDescs[i].name)
  			return FALSE;
  
! 		if (res->attDescs[i].format == 0 || res->attDescs[i].format == 2)
  			res->binary = 0;
+ 		if (res->attDescs[i].format == 2 || res->attDescs[i].format == 3)
+ 			res->raw = 1;
  	}
  
  	return TRUE;
*************** PQcmdStatus(PGresult *res)
*** 2932,2937 ****
--- 2935,2955 ----
  }
  
  /*
+  * PQcopyFormat
+  *
+  * Returns a info about copy mode:
+  * -1 signalize a error, 0 = text mode, 1 = binary mode, 2 = raw mode
+  */
+ int
+ PQcopyFormat(const PGresult *res)
+ {
+ 	if (res->raw)
+ 		return 2;
+ 	else
+ 		return res->binary;
+ }
+ 
+ /*
   * PQoidStatus -
   *	if the last command was an INSERT, return the oid string
   *	if not, return ""
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
new file mode 100644
index 0b8c62f..1783844
*** a/src/interfaces/libpq/fe-protocol3.c
--- b/src/interfaces/libpq/fe-protocol3.c
*************** getCopyStart(PGconn *conn, ExecStatusTyp
*** 1486,1491 ****
--- 1486,1495 ----
  		 */
  		format = (int) ((int16) format);
  		result->attDescs[i].format = format;
+ 
+ 		/* when any field uses raw format, then COPY RAW_* was used */
+ 		if (format == 2 || format == 3)
+ 			result->raw = true;
  	}
  
  	/* Success! */
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644
index 9ca0756..7984666
*** a/src/interfaces/libpq/libpq-fe.h
--- b/src/interfaces/libpq/libpq-fe.h
*************** extern Oid	PQftype(const PGresult *res,
*** 479,484 ****
--- 479,485 ----
  extern int	PQfsize(const PGresult *res, int field_num);
  extern int	PQfmod(const PGresult *res, int field_num);
  extern char *PQcmdStatus(PGresult *res);
+ extern int	PQcopyFormat(const PGresult *res);
  extern char *PQoidStatus(const PGresult *res);	/* old and ugly */
  extern Oid	PQoidValue(const PGresult *res);	/* new and improved */
  extern char *PQcmdTuples(PGresult *res);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
new file mode 100644
index 1183323..8fc4b04
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
*************** struct pg_result
*** 180,185 ****
--- 180,186 ----
  	char		cmdStatus[CMDSTATUS_LEN];		/* cmd status from the query */
  	int			binary;			/* binary tuple values if binary == 1,
  								 * otherwise text */
+ 	int			raw;			/* raw mode for COPY */
  
  	/*
  	 * These fields are copied from the originating PGconn, so that operations
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
new file mode 100644
index 5f6260a..3d36dd3
*** a/src/test/regress/expected/copy2.out
--- b/src/test/regress/expected/copy2.out
*************** DROP FUNCTION truncate_in_subxact();
*** 466,468 ****
--- 466,482 ----
  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_binary);
+ ERROR:  Single column result/target is required in RAW_TEXT/RAW_BINARY mode
+ COPY (SELECT a FROM x) TO STDOUT (FORMAT raw_binary);
+ AHOJ
+ AHOJ
+ ERROR:  single row result is required by RAW_TEXT/RAW_BINARY mode
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO STDOUT (FORMAT raw_binary);
+ 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..085ae36
*** 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,214 ----
  \.
  
  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_binary);
+ COPY (SELECT a FROM x) TO '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ 
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw_binary);
+ 
+ TRUNCATE x;
+ 
+ \COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw_binary)
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw_binary);
+ 
+ \COPY x TO '@abs_builddir@/results/raw2.data' (FORMAT raw_binary)
+ TRUNCATE x;
+ 
+ \COPY x FROM '@abs_builddir@/results/raw2.data' (FORMAT raw_binary)
+ SELECT length(a) FROM x;
+ COPY x TO stdout (FORMAT raw_binary);
+ 
+ -- 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_binary);
+ \COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw_binary)
+ 
+ SELECT md5(a), length(a) FROM x;
+ 
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw_binary);
+ COPY x TO '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ \COPY x TO '@abs_builddir@/results/hash3.data' (FORMAT raw_binary)
+ 
+ -- read again
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ \COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw_binary)
+ -- cross
+ COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw_binary);
+ \COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary)
+ 
+ SELECT md5(a), length(a) FROM x;
+ 
+ DROP TABLE x;
+ 
+ -- insert into multicolumn table
+ CREATE TABLE x(id serial, a bytea, b bytea);
+ 
+ -- should fail, too much columns
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ 
+ -- should work
+ COPY x(a) FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ COPY x(b) FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ SELECT id, md5(a), md5(b) FROM x;
+ 
+ -- test raw_text
+ COPY (SELECT a FROM x WHERE id = 1) TO '@abs_builddir@/results/hash4.data' (FORMAT raw_text);
+ COPY x(a) FROM '@abs_builddir@/results/hash4.data' (FORMAT raw_text);
+ SELECT id, md5(a) FROM x WHERE id = lastval();
+ 
+ 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..e34bbab
*** a/src/test/regress/output/copy.source
--- b/src/test/regress/output/copy.source
*************** copy copytest3 to stdout csv header;
*** 95,97 ****
--- 95,208 ----
  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_binary);
+ ERROR:  Single column result/target is required in RAW_TEXT/RAW_BINARY mode
+ COPY (SELECT a FROM x) TO '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ ERROR:  single row result is required by RAW_TEXT/RAW_BINARY mode
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ TRUNCATE x;
+ COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw_binary);
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw_binary);
+ AHOJ
+ TRUNCATE x;
+ \COPY x FROM '@abs_builddir@/results/raw.data' (FORMAT raw_binary)
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw_binary);
+ AHOJ
+ \COPY x TO '@abs_builddir@/results/raw2.data' (FORMAT raw_binary)
+ TRUNCATE x;
+ \COPY x FROM '@abs_builddir@/results/raw2.data' (FORMAT raw_binary)
+ SELECT length(a) FROM x;
+  length 
+ --------
+       5
+ (1 row)
+ 
+ COPY x TO stdout (FORMAT raw_binary);
+ 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_binary);
+ \COPY x FROM '@abs_builddir@/data/hash.data' (FORMAT raw_binary)
+ 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_binary);
+ COPY x TO '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ \COPY x TO '@abs_builddir@/results/hash3.data' (FORMAT raw_binary)
+ -- read again
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ \COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw_binary)
+ -- cross
+ COPY x FROM '@abs_builddir@/results/hash3.data' (FORMAT raw_binary);
+ \COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary)
+ SELECT md5(a), length(a) FROM x;
+                md5                | length 
+ ----------------------------------+--------
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+  e446fe6ea5a347e69670633412c7f8cb | 153749
+ (5 rows)
+ 
+ DROP TABLE x;
+ -- insert into multicolumn table
+ CREATE TABLE x(id serial, a bytea, b bytea);
+ -- should fail, too much columns
+ COPY x FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ ERROR:  Single column result/target is required in RAW_TEXT/RAW_BINARY mode
+ -- should work
+ COPY x(a) FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ COPY x(b) FROM '@abs_builddir@/results/hash2.data' (FORMAT raw_binary);
+ SELECT id, md5(a), md5(b) FROM x;
+  id |               md5                |               md5                
+ ----+----------------------------------+----------------------------------
+   1 | e446fe6ea5a347e69670633412c7f8cb | 
+   2 |                                  | e446fe6ea5a347e69670633412c7f8cb
+ (2 rows)
+ 
+ -- test raw_text
+ COPY (SELECT a FROM x WHERE id = 1) TO '@abs_builddir@/results/hash4.data' (FORMAT raw_text);
+ COPY x(a) FROM '@abs_builddir@/results/hash4.data' (FORMAT raw_text);
+ SELECT id, md5(a) FROM x WHERE id = lastval();
+  id |               md5                
+ ----+----------------------------------
+   3 | e446fe6ea5a347e69670633412c7f8cb
+ (1 row)
+ 
+ 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..7e22ee4
*** 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_binary);
+ COPY (SELECT a FROM x) TO STDOUT (FORMAT raw_binary);
+ 
+ -- should be ok
+ COPY (SELECT a FROM x LIMIT 1) TO STDOUT (FORMAT raw_binary);
+ 
+ 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