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