Hi

I wrote a prototype of this patch, and it works well

postgres=# set client_encoding to 'latin2';
SET
Time: 1.488 ms
postgres=# \copy (select xmlelement(name xx, d) from d) to ~/d.xml (format
'raw')
COPY 1
Time: 1.108 ms
postgres=# copy (select xmlelement(name xx, d) from d) to stdout (format
'raw') ;
<?xml version="1.0" encoding="LATIN2"?><xx>příliš žluťoučký kůň</xx>Time:
1.000 ms

Regards

Pavel

2015-04-09 20:48 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi
>
> This thread was finished without real work. I have a real use case -
> export XML doc in non utf8 encoding.
>
> http://www.postgresql.org/message-id/16174.1319228...@sss.pgh.pa.us
>
> I propose to implement new format option "RAW" like Tom proposed.
>
> It requires only one row, one column result - and result is just raw
> binary data without size.
>
> Objections? Ideas?
>
> Regards
>
> Pavel
>
commit 60c6701fe5a91c41e9ed0db99676c8b1a27e85e3
Author: Pavel Stehule <pavel.steh...@gooddata.com>
Date:   Fri Apr 10 23:22:39 2015 +0200

    initial

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 92ff632..5701f8b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -113,6 +113,7 @@ typedef struct CopyStateData
 	char	   *filename;		/* filename, or NULL for STDIN/STDOUT */
 	bool		is_program;		/* is 'filename' a program to popen? */
 	bool		binary;			/* binary format? */
+	bool		raw;			/* raw format - data only */
 	bool		oids;			/* include OIDs? */
 	bool		freeze;			/* freeze rows on loading? */
 	bool		csv_mode;		/* Comma Separated Value format? */
@@ -348,6 +349,13 @@ SendCopyBegin(CopyState cstate)
 		int16		format = (cstate->binary ? 1 : 0);
 		int			i;
 
+		if (cstate->binary)
+			format = 1;
+		else if (cstate->raw)
+			format = 2;
+		else
+			format = 0;
+
 		pq_beginmessage(&buf, 'H');
 		pq_sendbyte(&buf, format);		/* overall format */
 		pq_sendint(&buf, natts, 2);
@@ -359,7 +367,7 @@ SendCopyBegin(CopyState cstate)
 	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
 	{
 		/* old way */
-		if (cstate->binary)
+		if (cstate->binary || cstate->raw)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			errmsg("COPY BINARY is not supported to stdout or from stdin")));
@@ -371,7 +379,7 @@ SendCopyBegin(CopyState cstate)
 	else
 	{
 		/* very old way */
-		if (cstate->binary)
+		if (cstate->binary || cstate->raw)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			errmsg("COPY BINARY is not supported to stdout or from stdin")));
@@ -485,7 +493,7 @@ CopySendEndOfRow(CopyState cstate)
 	switch (cstate->copy_dest)
 	{
 		case COPY_FILE:
-			if (!cstate->binary)
+			if (!(cstate->binary || cstate->raw))
 			{
 				/* Default line termination depends on platform */
 #ifndef WIN32
@@ -543,7 +551,7 @@ CopySendEndOfRow(CopyState cstate)
 			break;
 		case COPY_NEW_FE:
 			/* The FE/BE protocol uses \n as newline for all platforms */
-			if (!cstate->binary)
+			if (!(cstate->binary || cstate->raw))
 				CopySendChar(cstate, '\n');
 
 			/* Dump the accumulated row as one CopyData message */
@@ -1005,6 +1013,8 @@ ProcessCopyOptions(CopyState cstate,
 				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),
@@ -1808,6 +1818,10 @@ CopyTo(CopyState cstate)
 	num_phys_attrs = tupDesc->natts;
 	cstate->null_print_client = cstate->null_print;		/* default */
 
+	/* don't allow more columns for raw format */
+	if (tupDesc->natts > 1)
+		elog(ERROR, "too much columns for RAW output");
+
 	/* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
 	cstate->fe_msgbuf = makeStringInfo();
 
@@ -1819,7 +1833,7 @@ CopyTo(CopyState cstate)
 		Oid			out_func_oid;
 		bool		isvarlena;
 
-		if (cstate->binary)
+		if ((cstate->binary || cstate->raw))
 			getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
 									&out_func_oid,
 									&isvarlena);
@@ -1858,7 +1872,7 @@ CopyTo(CopyState cstate)
 		tmp = 0;
 		CopySendInt32(cstate, tmp);
 	}
-	else
+	else if (!cstate->raw)
 	{
 		/*
 		 * For non-binary copy, we need to convert null_print to file
@@ -1970,7 +1984,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
 			CopySendInt32(cstate, tupleOid);
 		}
 	}
-	else
+	else if (!cstate->raw)
 	{
 		/* Text format has no per-tuple header, but send OID if wanted */
 		/* Assume digits don't need any quoting or encoding conversion */
@@ -1998,14 +2012,16 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
 
 		if (isnull)
 		{
-			if (!cstate->binary)
+			if (cstate->raw)
+				elog(ERROR, "cannot to push NULL in raw output");
+			else if (!cstate->binary)
 				CopySendString(cstate, cstate->null_print_client);
 			else
 				CopySendInt32(cstate, -1);
 		}
 		else
 		{
-			if (!cstate->binary)
+			if (!(cstate->binary || cstate->raw))
 			{
 				string = OutputFunctionCall(&out_functions[attnum - 1],
 											value);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index a847f08..135c052 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1352,6 +1352,8 @@ getCopyStart(PGconn *conn, ExecStatusType copytype)
 		 */
 		format = (int) ((int16) format);
 		result->attDescs[i].format = format;
+
+		conn->copy_raw_mode = format == 2;
 	}
 
 	/* Success! */
@@ -1544,13 +1546,38 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async)
 								  libpq_gettext("out of memory\n"));
 				return -2;
 			}
-			memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
-			(*buffer)[msgLength] = '\0';		/* Add terminating null */
 
-			/* Mark message consumed */
-			conn->inStart = conn->inCursor + msgLength;
+			if (!conn->copy_raw_mode)
+			{
+				memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
+				(*buffer)[msgLength] = '\0';		/* Add terminating null */
 
-			return msgLength;
+				conn->inStart = conn->inCursor + msgLength;
+
+				return msgLength;
+			}
+			else
+			{
+				if (msgLength < 4)
+				{
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("broken format\n"));
+					return -2;
+				}
+
+				/*
+				 * raw format is same as binary without addtional info. Every
+				 * binary data starts with length 4B
+				 */
+				memcpy(*buffer, &conn->inBuffer[conn->inCursor + 4], msgLength - 4);
+				(*buffer)[msgLength] = '\0';		/* Add terminating null */
+
+				conn->inStart = conn->inCursor + msgLength;
+
+				return msgLength - 4;
+			}
+
+			/* Mark message consumed */
 		}
 
 		/* Empty, so drop it and loop around for another */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 2175957..23bde61 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -421,6 +421,8 @@ struct pg_conn
 	PGresult   *result;			/* result being constructed */
 	PGresult   *next_result;	/* next result (used in single-row mode) */
 
+	bool		copy_raw_mode;		/* true, when one value is passed by copy protocol */
+
 	/* Assorted state for SSL, GSS, etc */
 
 #ifdef USE_SSL
-- 
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