From c0a8c3ac13f84602a46ba25b9a2fd5427514f61a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 8 May 2003 18:16:37 +0000 Subject: [PATCH] Update 3.0 protocol support to match recent agreements about how to handle multiple 'formats' for data I/O. Restructure CommandDest and DestReceiver stuff one more time (it's finally starting to look a bit clean though). Code now matches latest 3.0 protocol document as far as message formats go --- but there is no support for binary I/O yet. --- src/backend/access/common/printtup.c | 228 ++++++++---- src/backend/commands/copy.c | 28 +- src/backend/commands/explain.c | 4 +- src/backend/commands/portalcmds.c | 27 +- src/backend/commands/prepare.c | 4 +- src/backend/executor/execMain.c | 7 +- src/backend/executor/execTuples.c | 5 +- src/backend/executor/functions.c | 4 +- src/backend/executor/spi.c | 18 +- src/backend/executor/tstoreReceiver.c | 6 +- src/backend/libpq/pqformat.c | 23 +- src/backend/tcop/dest.c | 37 +- src/backend/tcop/fastpath.c | 260 +++++++++---- src/backend/tcop/postgres.c | 207 ++++++++--- src/backend/tcop/pquery.c | 82 +++-- src/include/access/printtup.h | 11 +- src/include/executor/execdesc.h | 5 +- src/include/libpq/pqcomm.h | 4 +- src/include/libpq/pqformat.h | 5 +- src/include/tcop/dest.h | 25 +- src/include/tcop/pquery.h | 6 +- src/include/utils/portal.h | 9 +- src/interfaces/libpq/fe-exec.c | 670 ++++++++++++++++------------------ src/interfaces/libpq/libpq-int.h | 4 +- 24 files changed, 999 insertions(+), 680 deletions(-) diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 35355b8b9b..d5a29ef16c 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.70 2003/05/06 20:26:26 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.71 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,12 +20,17 @@ #include "libpq/libpq.h" #include "libpq/pqformat.h" #include "utils/lsyscache.h" +#include "utils/portal.h" static void printtup_startup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist); -static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); -static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); + TupleDesc typeinfo); +static void printtup(HeapTuple tuple, TupleDesc typeinfo, + DestReceiver *self); +static void printtup_20(HeapTuple tuple, TupleDesc typeinfo, + DestReceiver *self); +static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, + DestReceiver *self); static void printtup_shutdown(DestReceiver *self); static void printtup_destroy(DestReceiver *self); @@ -50,6 +55,7 @@ typedef struct typedef struct { DestReceiver pub; /* publicly-known function pointers */ + Portal portal; /* the Portal we are printing from */ bool sendDescrip; /* send RowDescription at startup? */ TupleDesc attrinfo; /* The attr info we are set up for */ int nattrs; @@ -61,43 +67,33 @@ typedef struct * ---------------- */ DestReceiver * -printtup_create_DR(CommandDest dest) +printtup_create_DR(CommandDest dest, Portal portal) { DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup)); - bool isBinary; - bool sendDescrip; - switch (dest) + if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) + self->pub.receiveTuple = printtup; + else { - case Remote: - isBinary = false; - sendDescrip = true; - break; - case RemoteInternal: - isBinary = true; - sendDescrip = true; - break; - case RemoteExecute: - isBinary = false; - sendDescrip = false; /* no T message for Execute */ - break; - case RemoteExecuteInternal: - isBinary = true; - sendDescrip = false; /* no T message for Execute */ - break; - - default: - elog(ERROR, "printtup_create_DR: unsupported dest"); - return NULL; + /* + * In protocol 2.0 the Bind message does not exist, so there is + * no way for the columns to have different print formats; it's + * sufficient to look at the first one. + */ + if (portal->formats && portal->formats[0] != 0) + self->pub.receiveTuple = printtup_internal_20; + else + self->pub.receiveTuple = printtup_20; } - - self->pub.receiveTuple = isBinary ? printtup_internal : printtup; self->pub.startup = printtup_startup; self->pub.shutdown = printtup_shutdown; self->pub.destroy = printtup_destroy; self->pub.mydest = dest; - self->sendDescrip = sendDescrip; + self->portal = portal; + + /* Send T message automatically if Remote, but not if RemoteExecute */ + self->sendDescrip = (dest == Remote); self->attrinfo = NULL; self->nattrs = 0; @@ -107,10 +103,10 @@ printtup_create_DR(CommandDest dest) } static void -printtup_startup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist) +printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { DR_printtup *myState = (DR_printtup *) self; + Portal portal = myState->portal; if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { @@ -119,7 +115,9 @@ printtup_startup(DestReceiver *self, int operation, * * If portal name not specified, use "blank" portal. */ - if (portalName == NULL) + const char *portalName = portal->name; + + if (portalName == NULL || portalName[0] == '\0') portalName = "blank"; pq_puttextmessage('P', portalName); @@ -130,7 +128,16 @@ printtup_startup(DestReceiver *self, int operation, * then we send back the tuple descriptor of the tuples. */ if (operation == CMD_SELECT && myState->sendDescrip) - SendRowDescriptionMessage(typeinfo, targetlist); + { + List *targetlist; + + if (portal->strategy == PORTAL_ONE_SELECT) + targetlist = ((Query *) lfirst(portal->parseTrees))->targetList; + else + targetlist = NIL; + + SendRowDescriptionMessage(typeinfo, targetlist, portal->formats); + } /* ---------------- * We could set up the derived attr info at this time, but we postpone it @@ -150,11 +157,13 @@ printtup_startup(DestReceiver *self, int operation, * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does - * not have a plan. If the targetlist isn't NIL then it is a Plan node's - * targetlist; it is up to us to ignore resjunk columns in it. + * not have a plan. If the targetlist isn't NIL then it is a Query node's + * targetlist; it is up to us to ignore resjunk columns in it. The formats[] + * array pointer might be NULL (if we are doing Describe on a prepared stmt); + * send zeroes for the format codes in that case. */ void -SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist) +SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; @@ -198,6 +207,14 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist) if (proto >= 2) pq_sendint(&buf, attrs[i]->atttypmod, sizeof(attrs[i]->atttypmod)); + /* format info appears in protocol 3.0 and up */ + if (proto >= 3) + { + if (formats) + pq_sendint(&buf, formats[i], 2); + else + pq_sendint(&buf, 0, 2); + } } pq_endmessage(&buf); } @@ -228,13 +245,100 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) } /* ---------------- - * printtup + * printtup --- print a tuple in protocol 3.0 * ---------------- */ static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) { DR_printtup *myState = (DR_printtup *) self; + int16 *formats = myState->portal->formats; + StringInfoData buf; + int natts = tuple->t_data->t_natts; + int i; + + /* Set or update my derived attribute info, if needed */ + if (myState->attrinfo != typeinfo || myState->nattrs != natts) + printtup_prepare_info(myState, typeinfo, natts); + + /* + * Prepare a DataRow message + */ + pq_beginmessage(&buf, 'D'); + + pq_sendint(&buf, natts, 2); + + /* + * send the attributes of this tuple + */ + for (i = 0; i < natts; ++i) + { + PrinttupAttrInfo *thisState = myState->myinfo + i; + int16 format = (formats ? formats[i] : 0); + Datum origattr, + attr; + bool isnull; + char *outputstr; + + origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); + if (isnull) + { + pq_sendint(&buf, -1, 4); + continue; + } + if (format == 0) + { + if (OidIsValid(thisState->typoutput)) + { + /* + * If we have a toasted datum, forcibly detoast it here to + * avoid memory leakage inside the type's output routine. + */ + if (thisState->typisvarlena) + attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); + else + attr = origattr; + + outputstr = DatumGetCString(FunctionCall3(&thisState->finfo, + attr, + ObjectIdGetDatum(thisState->typelem), + Int32GetDatum(typeinfo->attrs[i]->atttypmod))); + + pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); + + /* Clean up detoasted copy, if any */ + if (attr != origattr) + pfree(DatumGetPointer(attr)); + pfree(outputstr); + } + else + { + outputstr = ""; + pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); + } + } + else if (format == 1) + { + /* XXX something similar to above */ + elog(ERROR, "Binary transmission not implemented yet"); + } + else + { + elog(ERROR, "Invalid format code %d", format); + } + } + + pq_endmessage(&buf); +} + +/* ---------------- + * printtup_20 --- print a tuple in protocol 2.0 + * ---------------- + */ +static void +printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) +{ + DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = tuple->t_data->t_natts; int i, @@ -300,7 +404,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ObjectIdGetDatum(thisState->typelem), Int32GetDatum(typeinfo->attrs[i]->atttypmod))); - pq_sendcountedtext(&buf, outputstr, strlen(outputstr)); + pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true); /* Clean up detoasted copy, if any */ if (attr != origattr) @@ -310,7 +414,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) else { outputstr = ""; - pq_sendcountedtext(&buf, outputstr, strlen(outputstr)); + pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true); } } @@ -364,37 +468,22 @@ printatt(unsigned attributeId, } /* ---------------- - * showatts - * ---------------- - */ -static void -showatts(const char *name, TupleDesc tupleDesc) -{ - int natts = tupleDesc->natts; - Form_pg_attribute *attinfo = tupleDesc->attrs; - int i; - - puts(name); - for (i = 0; i < natts; ++i) - printatt((unsigned) i + 1, attinfo[i], (char *) NULL); - printf("\t----\n"); -} - -/* ---------------- * debugStartup - prepare to print tuples for an interactive backend * ---------------- */ void -debugStartup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist) +debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo) { + int natts = typeinfo->natts; + Form_pg_attribute *attinfo = typeinfo->attrs; + int i; + /* * show the return type of the tuples */ - if (portalName == NULL) - portalName = "blank"; - - showatts(portalName, typeinfo); + for (i = 0; i < natts; ++i) + printatt((unsigned) i + 1, attinfo[i], (char *) NULL); + printf("\t----\n"); } /* ---------------- @@ -448,15 +537,16 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) } /* ---------------- - * printtup_internal - * We use a different data prefix, e.g. 'B' instead of 'D' to - * indicate a tuple in internal (binary) form. + * printtup_internal_20 --- print a binary tuple in protocol 2.0 + * + * We use a different message type, i.e. 'B' instead of 'D' to + * indicate a tuple in internal (binary) form. * - * This is largely same as printtup, except we don't use the typout func. + * This is largely same as printtup_20, except we don't use the typout func. * ---------------- */ static void -printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) +printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) { DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index ec6e771aed..93d4b8406e 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.197 2003/04/25 02:28:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.198 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -125,8 +125,8 @@ static int server_encoding; /* * Internal communications functions */ -static void SendCopyBegin(bool binary); -static void ReceiveCopyBegin(bool binary); +static void SendCopyBegin(bool binary, int natts); +static void ReceiveCopyBegin(bool binary, int natts); static void SendCopyEnd(bool binary); static void CopySendData(void *databuf, int datasize); static void CopySendString(const char *str); @@ -143,15 +143,20 @@ static void CopyDonePeek(int c, bool pickup); * in past protocol redesigns. */ static void -SendCopyBegin(bool binary) +SendCopyBegin(bool binary, int natts) { if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* new way */ StringInfoData buf; + int16 format = (binary ? 1 : 0); + int i; pq_beginmessage(&buf, 'H'); - pq_sendbyte(&buf, binary ? 1 : 0); + 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); copy_dest = COPY_NEW_FE; copy_msgbuf = makeStringInfo(); @@ -179,15 +184,20 @@ SendCopyBegin(bool binary) } static void -ReceiveCopyBegin(bool binary) +ReceiveCopyBegin(bool binary, int natts) { if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* new way */ StringInfoData buf; + int16 format = (binary ? 1 : 0); + int i; pq_beginmessage(&buf, 'G'); - pq_sendbyte(&buf, binary ? 1 : 0); + 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); copy_dest = COPY_NEW_FE; copy_msgbuf = makeStringInfo(); @@ -682,7 +692,7 @@ DoCopy(const CopyStmt *stmt) if (pipe) { if (IsUnderPostmaster) - ReceiveCopyBegin(binary); + ReceiveCopyBegin(binary, length(attnumlist)); else copy_file = stdin; } @@ -724,7 +734,7 @@ DoCopy(const CopyStmt *stmt) if (pipe) { if (IsUnderPostmaster) - SendCopyBegin(binary); + SendCopyBegin(binary, length(attnumlist)); else copy_file = stdout; } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 28a6e0378c..879a7e6d68 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.108 2003/05/06 20:26:26 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.109 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -179,7 +179,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) plan = planner(query, isCursor, cursorOptions); /* Create a QueryDesc requesting no output */ - queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, NULL, + queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, stmt->analyze); ExplainOnePlan(queryDesc, stmt, tstate); diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 1c51a1bb89..f9e31c3aaa 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.15 2003/05/06 20:26:26 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.16 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ #include "commands/portalcmds.h" #include "executor/executor.h" -#include "executor/tstoreReceiver.h" #include "optimizer/planner.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" @@ -145,7 +144,6 @@ PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, char *completionTag) { - DestReceiver *mydest = dest; Portal portal; long nprocessed; @@ -168,35 +166,21 @@ PerformPortalFetch(FetchStmt *stmt, return; } - /* - * Adjust dest if needed. MOVE wants destination None. - * - * If fetching from a binary cursor and the requested destination is - * Remote, change it to RemoteInternal. Note we do NOT change if the - * destination is RemoteExecute --- so the Execute message's format - * specification wins out over the cursor's type. - */ + /* Adjust dest if needed. MOVE wants destination None */ if (stmt->ismove) - mydest = CreateDestReceiver(None); - else if (dest->mydest == Remote && - (portal->cursorOptions & CURSOR_OPT_BINARY)) - mydest = CreateDestReceiver(RemoteInternal); + dest = None_Receiver; /* Do it */ nprocessed = PortalRunFetch(portal, stmt->direction, stmt->howMany, - mydest); + dest); /* Return command status if wanted */ if (completionTag) snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld", stmt->ismove ? "MOVE" : "FETCH", nprocessed); - - /* Clean up if we created a local destination */ - if (mydest != dest) - (mydest->destroy) (mydest); } /* @@ -329,8 +313,7 @@ PersistHoldablePortal(Portal portal) ExecutorRewind(queryDesc); /* Change the destination to output to the tuplestore */ - queryDesc->dest = CreateTuplestoreDestReceiver(portal->holdStore, - portal->holdContext); + queryDesc->dest = CreateDestReceiver(Tuplestore, portal); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 6e16853fd7..433fd8e049 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -10,7 +10,7 @@ * Copyright (c) 2002-2003, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.17 2003/05/06 21:51:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.18 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -528,7 +528,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate) } /* Create a QueryDesc requesting no output */ - qdesc = CreateQueryDesc(query, plan, None_Receiver, NULL, + qdesc = CreateQueryDesc(query, plan, None_Receiver, paramLI, stmt->analyze); ExplainOnePlan(qdesc, stmt, tstate); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 3d1b950e21..8b720c110f 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.208 2003/05/06 20:26:26 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.209 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -217,10 +217,7 @@ ExecutorRun(QueryDesc *queryDesc, estate->es_processed = 0; estate->es_lastoid = InvalidOid; - (*dest->startup) (dest, operation, - queryDesc->portalName, - queryDesc->tupDesc, - queryDesc->planstate->plan->targetlist); + (*dest->startup) (dest, operation, queryDesc->tupDesc); /* * run plan diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 2e040f7890..cb85be630f 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.65 2003/05/06 20:26:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.66 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -765,8 +765,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc) tstate->metadata = TupleDescGetAttInMetadata(tupdesc); tstate->dest = dest; - (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, - NULL, tupdesc, NIL); + (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, tupdesc); return tstate; } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index e89e5f32d6..46d1e51c4b 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.64 2003/05/06 20:26:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.65 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -245,7 +245,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) { Assert(es->qd == NULL); es->qd = CreateQueryDesc(es->query, es->plan, - None_Receiver, NULL, + None_Receiver, fcache->paramLI, false); /* Utility commands don't need Executor. */ diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index c3b3ec3530..fe420ce978 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.96 2003/05/06 20:26:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.97 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -841,7 +841,8 @@ SPI_cursor_find(const char *name) void SPI_cursor_fetch(Portal portal, bool forward, int count) { - _SPI_cursor_operation(portal, forward, count, CreateDestReceiver(SPI)); + _SPI_cursor_operation(portal, forward, count, + CreateDestReceiver(SPI, NULL)); /* we know that the SPI receiver doesn't need a destroy call */ } @@ -880,8 +881,7 @@ SPI_cursor_close(Portal portal) * of current SPI procedure */ void -spi_dest_startup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist) +spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { SPITupleTable *tuptable; MemoryContext oldcxt; @@ -1035,7 +1035,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) planTree = pg_plan_query(queryTree); plan_list = lappend(plan_list, planTree); - dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None); + dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL); if (queryTree->commandType == CMD_UTILITY) { if (IsA(queryTree->utilityStmt, CopyStmt)) @@ -1061,7 +1061,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) else if (plan == NULL) { qdesc = CreateQueryDesc(queryTree, planTree, dest, - NULL, NULL, false); + NULL, false); res = _SPI_pquery(qdesc, true, queryTree->canSetTag ? tcount : 0); if (res < 0) @@ -1071,7 +1071,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) else { qdesc = CreateQueryDesc(queryTree, planTree, dest, - NULL, NULL, false); + NULL, false); res = _SPI_pquery(qdesc, false, 0); if (res < 0) return res; @@ -1150,7 +1150,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, planTree = lfirst(plan_list); plan_list = lnext(plan_list); - dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None); + dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL); if (queryTree->commandType == CMD_UTILITY) { ProcessUtility(queryTree->utilityStmt, dest, NULL); @@ -1160,7 +1160,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, else { qdesc = CreateQueryDesc(queryTree, planTree, dest, - NULL, paramLI, false); + paramLI, false); res = _SPI_pquery(qdesc, true, queryTree->canSetTag ? tcount : 0); if (res < 0) diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c index bcab8154a3..3d8479faee 100644 --- a/src/backend/executor/tstoreReceiver.c +++ b/src/backend/executor/tstoreReceiver.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.5 2003/05/06 20:26:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.6 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,9 +31,7 @@ typedef struct * Prepare to receive tuples from executor. */ static void -tstoreStartupReceiver(DestReceiver *self, int operation, - const char *portalname, - TupleDesc typeinfo, List *targetlist) +tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo) { /* do nothing */ } diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c index dacfa93ecc..6f574e383b 100644 --- a/src/backend/libpq/pqformat.c +++ b/src/backend/libpq/pqformat.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.29 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -104,27 +104,32 @@ pq_sendbytes(StringInfo buf, const char *data, int datalen) * pq_sendcountedtext - append a text string (with character set conversion) * * The data sent to the frontend by this routine is a 4-byte count field - * (the count includes itself, by convention) followed by the string. + * followed by the string. The count includes itself or not, as per the + * countincludesself flag (pre-3.0 protocol requires it to include itself). * The passed text string need not be null-terminated, and the data sent * to the frontend isn't either. * -------------------------------- */ void -pq_sendcountedtext(StringInfo buf, const char *str, int slen) +pq_sendcountedtext(StringInfo buf, const char *str, int slen, + bool countincludesself) { + int extra = countincludesself ? 4 : 0; char *p; p = (char *) pg_server_to_client((unsigned char *) str, slen); if (p != str) /* actual conversion has been done? */ { slen = strlen(p); - pq_sendint(buf, slen + 4, 4); + pq_sendint(buf, slen + extra, 4); appendBinaryStringInfo(buf, p, slen); pfree(p); - return; } - pq_sendint(buf, slen + 4, 4); - appendBinaryStringInfo(buf, str, slen); + else + { + pq_sendint(buf, slen + extra, 4); + appendBinaryStringInfo(buf, str, slen); + } } /* -------------------------------- @@ -296,7 +301,7 @@ pq_getmsgbytes(StringInfo msg, int datalen) { const char *result; - if (datalen > (msg->len - msg->cursor)) + if (datalen < 0 || datalen > (msg->len - msg->cursor)) elog(ERROR, "pq_getmsgbytes: insufficient data left in message"); result = &msg->data[msg->cursor]; msg->cursor += datalen; @@ -312,7 +317,7 @@ pq_getmsgbytes(StringInfo msg, int datalen) void pq_copymsgbytes(StringInfo msg, char *buf, int datalen) { - if (datalen > (msg->len - msg->cursor)) + if (datalen < 0 || datalen > (msg->len - msg->cursor)) elog(ERROR, "pq_copymsgbytes: insufficient data left in message"); memcpy(buf, &msg->data[msg->cursor], datalen); msg->cursor += datalen; diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c index bce77603f5..a590cffd35 100644 --- a/src/backend/tcop/dest.c +++ b/src/backend/tcop/dest.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.57 2003/05/06 20:26:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.58 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,8 +30,10 @@ #include "access/printtup.h" #include "access/xact.h" +#include "executor/tstoreReceiver.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" +#include "utils/portal.h" /* ---------------- @@ -44,8 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) } static void -donothingStartup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist) +donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo) { } @@ -90,18 +91,21 @@ BeginCommand(const char *commandTag, CommandDest dest) /* ---------------- * CreateDestReceiver - return appropriate receiver function set for dest + * + * Note: a Portal must be specified for destinations Remote, RemoteExecute, + * and Tuplestore. It can be NULL for the others. * ---------------- */ DestReceiver * -CreateDestReceiver(CommandDest dest) +CreateDestReceiver(CommandDest dest, Portal portal) { switch (dest) { case Remote: - case RemoteInternal: case RemoteExecute: - case RemoteExecuteInternal: - return printtup_create_DR(dest); + if (portal == NULL) + elog(ERROR, "CreateDestReceiver: no portal specified"); + return printtup_create_DR(dest, portal); case None: return &donothingDR; @@ -113,12 +117,13 @@ CreateDestReceiver(CommandDest dest) return &spi_printtupDR; case Tuplestore: - /* - * This is disallowed, you must use tstoreReceiver.c's - * specialized function to create a Tuplestore DestReceiver - */ - elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore"); - break; + if (portal == NULL) + elog(ERROR, "CreateDestReceiver: no portal specified"); + if (portal->holdStore == NULL || + portal->holdContext == NULL) + elog(ERROR, "CreateDestReceiver: portal has no holdStore"); + return CreateTuplestoreDestReceiver(portal->holdStore, + portal->holdContext); } /* should never get here */ @@ -135,9 +140,7 @@ EndCommand(const char *commandTag, CommandDest dest) switch (dest) { case Remote: - case RemoteInternal: case RemoteExecute: - case RemoteExecuteInternal: pq_puttextmessage('C', commandTag); break; @@ -167,9 +170,7 @@ NullCommand(CommandDest dest) switch (dest) { case Remote: - case RemoteInternal: case RemoteExecute: - case RemoteExecuteInternal: /* * tell the fe that we saw an empty query string. In protocols @@ -206,9 +207,7 @@ ReadyForQuery(CommandDest dest) switch (dest) { case Remote: - case RemoteInternal: case RemoteExecute: - case RemoteExecuteInternal: if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { StringInfoData buf; diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index 65161c54ff..0720f4e971 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $ * * NOTES * This cruft is the server side of PQfn. @@ -47,6 +47,34 @@ #include "utils/tqual.h" +/* + * Formerly, this code attempted to cache the function and type info + * looked up by fetch_fp_info, but only for the duration of a single + * transaction command (since in theory the info could change between + * commands). This was utterly useless, because postgres.c executes + * each fastpath call as a separate transaction command, and so the + * cached data could never actually have been reused. If it had worked + * as intended, it would have had problems anyway with dangling references + * in the FmgrInfo struct. So, forget about caching and just repeat the + * syscache fetches on each usage. They're not *that* expensive. + */ +struct fp_info +{ + Oid funcid; + FmgrInfo flinfo; /* function lookup info for funcid */ + int16 arglen[FUNC_MAX_ARGS]; + bool argbyval[FUNC_MAX_ARGS]; + int16 retlen; + bool retbyval; +}; + + +static void parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip, + FunctionCallInfo fcinfo); +static void parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip, + FunctionCallInfo fcinfo); + + /* ---------------- * GetOldFunctionMessage * @@ -121,57 +149,73 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen) pq_beginmessage(&buf, 'V'); - if (retlen != 0) + if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { - pq_sendbyte(&buf, 'G'); - if (retbyval) - { /* by-value */ - pq_sendint(&buf, retlen, 4); - pq_sendint(&buf, DatumGetInt32(retval), retlen); + /* New-style message */ + /* XXX replace this with standard binary (or text!) output */ + if (retlen != 0) + { + if (retbyval) + { /* by-value */ + pq_sendint(&buf, retlen, 4); + pq_sendint(&buf, DatumGetInt32(retval), retlen); + } + else + { /* by-reference ... */ + if (retlen == -1) + { /* ... varlena */ + struct varlena *v = PG_DETOAST_DATUM(retval); + + pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ); + } + else + { /* ... fixed */ + pq_sendint(&buf, retlen, 4); + pq_sendbytes(&buf, DatumGetPointer(retval), retlen); + } + } } else - { /* by-reference ... */ - if (retlen == -1) - { /* ... varlena */ - struct varlena *v = PG_DETOAST_DATUM(retval); - - pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ); - pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ); + { + /* NULL marker */ + pq_sendint(&buf, -1, 4); + } + } + else + { + /* Old-style message */ + if (retlen != 0) + { + pq_sendbyte(&buf, 'G'); + if (retbyval) + { /* by-value */ + pq_sendint(&buf, retlen, 4); + pq_sendint(&buf, DatumGetInt32(retval), retlen); } else - { /* ... fixed */ - pq_sendint(&buf, retlen, 4); - pq_sendbytes(&buf, DatumGetPointer(retval), retlen); + { /* by-reference ... */ + if (retlen == -1) + { /* ... varlena */ + struct varlena *v = PG_DETOAST_DATUM(retval); + + pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ); + } + else + { /* ... fixed */ + pq_sendint(&buf, retlen, 4); + pq_sendbytes(&buf, DatumGetPointer(retval), retlen); + } } } + pq_sendbyte(&buf, '0'); } - pq_sendbyte(&buf, '0'); pq_endmessage(&buf); } /* - * Formerly, this code attempted to cache the function and type info - * looked up by fetch_fp_info, but only for the duration of a single - * transaction command (since in theory the info could change between - * commands). This was utterly useless, because postgres.c executes - * each fastpath call as a separate transaction command, and so the - * cached data could never actually have been reused. If it had worked - * as intended, it would have had problems anyway with dangling references - * in the FmgrInfo struct. So, forget about caching and just repeat the - * syscache fetches on each usage. They're not *that* expensive. - */ -struct fp_info -{ - Oid funcid; - FmgrInfo flinfo; /* function lookup info for funcid */ - int16 arglen[FUNC_MAX_ARGS]; - bool argbyval[FUNC_MAX_ARGS]; - int16 retlen; - bool retbyval; -}; - -/* * fetch_fp_info * * Performs catalog lookups to load a struct fp_info 'fip' for the @@ -262,11 +306,9 @@ int HandleFunctionRequest(StringInfo msgBuf) { Oid fid; - int nargs; AclResult aclresult; FunctionCallInfoData fcinfo; Datum retval; - int i; struct fp_info my_fp; struct fp_info *fip; @@ -294,9 +336,10 @@ HandleFunctionRequest(StringInfo msgBuf) /* * Parse the buffer contents. */ - (void) pq_getmsgstring(msgBuf); /* dummy string */ + if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) + (void) pq_getmsgstring(msgBuf); /* dummy string */ + fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */ - nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */ /* * There used to be a lame attempt at caching lookup info here. Now we @@ -316,19 +359,61 @@ HandleFunctionRequest(StringInfo msgBuf) SetQuerySnapshot(); /* - * Prepare function call info block. + * Prepare function call info block and insert arguments. */ + MemSet(&fcinfo, 0, sizeof(fcinfo)); + fcinfo.flinfo = &fip->flinfo; + + if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) + parse_fcall_arguments(msgBuf, fip, &fcinfo); + else + parse_fcall_arguments_20(msgBuf, fip, &fcinfo); + + /* Verify we reached the end of the message where expected. */ + pq_getmsgend(msgBuf); + + /* Okay, do it ... */ + retval = FunctionCallInvoke(&fcinfo); + + if (fcinfo.isnull) + SendFunctionResult(retval, fip->retbyval, 0); + else + SendFunctionResult(retval, fip->retbyval, fip->retlen); + + return 0; +} + +/* + * Parse function arguments in a 3.0 protocol message + */ +static void +parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip, + FunctionCallInfo fcinfo) +{ + int nargs; + int i; + int numAFormats; + int16 *aformats = NULL; + + /* Get the argument format codes */ + numAFormats = pq_getmsgint(msgBuf, 2); + if (numAFormats > 0) + { + aformats = (int16 *) palloc(numAFormats * sizeof(int16)); + for (i = 0; i < numAFormats; i++) + aformats[i] = pq_getmsgint(msgBuf, 2); + } + + nargs = pq_getmsgint(msgBuf, 2); /* # of arguments */ + if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS) elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)", nargs, fip->flinfo.fn_nargs); - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &fip->flinfo; - fcinfo.nargs = nargs; + fcinfo->nargs = nargs; /* - * Copy supplied arguments into arg vector. Note there is no way for - * frontend to specify a NULL argument --- this protocol is misdesigned. + * Copy supplied arguments into arg vector. */ for (i = 0; i < nargs; ++i) { @@ -342,7 +427,7 @@ HandleFunctionRequest(StringInfo msgBuf) elog(ERROR, "HandleFunctionRequest: bogus argsize %d", argsize); /* XXX should we demand argsize == fip->arglen[i] ? */ - fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize); + fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize); } else { /* by-reference ... */ @@ -363,25 +448,70 @@ HandleFunctionRequest(StringInfo msgBuf) p = palloc(argsize + 1); /* +1 in case argsize is 0 */ pq_copymsgbytes(msgBuf, p, argsize); } - fcinfo.arg[i] = PointerGetDatum(p); + fcinfo->arg[i] = PointerGetDatum(p); } } - /* Verify we reached the end of the message where expected. */ - pq_getmsgend(msgBuf); + /* XXX for the moment, ignore result format code */ + (void) pq_getmsgint(msgBuf, 2); +} -#ifdef NO_FASTPATH - /* force a NULL return */ - retval = (Datum) 0; - fcinfo.isnull = true; -#else - retval = FunctionCallInvoke(&fcinfo); -#endif /* NO_FASTPATH */ +/* + * Parse function arguments in a 2.0 protocol message + */ +static void +parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip, + FunctionCallInfo fcinfo) +{ + int nargs; + int i; - if (fcinfo.isnull) - SendFunctionResult(retval, fip->retbyval, 0); - else - SendFunctionResult(retval, fip->retbyval, fip->retlen); + nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */ - return 0; + if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS) + elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)", + nargs, fip->flinfo.fn_nargs); + + fcinfo->nargs = nargs; + + /* + * Copy supplied arguments into arg vector. Note there is no way for + * frontend to specify a NULL argument --- this protocol is misdesigned. + */ + for (i = 0; i < nargs; ++i) + { + int argsize; + char *p; + + argsize = pq_getmsgint(msgBuf, 4); + if (fip->argbyval[i]) + { /* by-value */ + if (argsize < 1 || argsize > 4) + elog(ERROR, "HandleFunctionRequest: bogus argsize %d", + argsize); + /* XXX should we demand argsize == fip->arglen[i] ? */ + fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize); + } + else + { /* by-reference ... */ + if (fip->arglen[i] == -1) + { /* ... varlena */ + if (argsize < 0) + elog(ERROR, "HandleFunctionRequest: bogus argsize %d", + argsize); + p = palloc(argsize + VARHDRSZ); + VARATT_SIZEP(p) = argsize + VARHDRSZ; + pq_copymsgbytes(msgBuf, VARDATA(p), argsize); + } + else + { /* ... fixed */ + if (argsize != fip->arglen[i]) + elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d", + argsize, fip->arglen[i]); + p = palloc(argsize + 1); /* +1 in case argsize is 0 */ + pq_copymsgbytes(msgBuf, p, argsize); + } + fcinfo->arg[i] = PointerGetDatum(p); + } + } } diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 8956455870..63b08dc969 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.339 2003/05/08 14:49:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.340 2003/05/08 18:16:36 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -658,7 +658,6 @@ static void exec_simple_query(const char *query_string) { CommandDest dest = whereToSendOutput; - DestReceiver *receiver; MemoryContext oldcontext; List *parsetree_list, *parsetree_item; @@ -686,12 +685,6 @@ exec_simple_query(const char *query_string) ResetUsage(); /* - * Create destination receiver object --- we can reuse it for all - * queries in the string. Note it is created in MessageContext. - */ - receiver = CreateDestReceiver(dest); - - /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command @@ -743,6 +736,8 @@ exec_simple_query(const char *query_string) List *querytree_list, *plantree_list; Portal portal; + DestReceiver *receiver; + int16 format; /* * Get the command name for use in status display (it also becomes the @@ -804,11 +799,6 @@ exec_simple_query(const char *query_string) CHECK_FOR_INTERRUPTS(); /* - * Switch back to transaction context for execution. - */ - MemoryContextSwitchTo(oldcontext); - - /* * Create unnamed portal to run the query or queries in. * If there already is one, silently drop it. */ @@ -822,16 +812,53 @@ exec_simple_query(const char *query_string) MessageContext); /* - * Run the portal to completion, and then drop it. + * Start the portal. No parameters here. */ PortalStart(portal, NULL); + /* + * Select the appropriate output format: text unless we are doing + * a FETCH from a binary cursor. (Pretty grotty to have to do this + * here --- but it avoids grottiness in other places. Ah, the joys + * of backward compatibility...) + */ + format = 0; /* TEXT is default */ + if (IsA(parsetree, FetchStmt)) + { + FetchStmt *stmt = (FetchStmt *) parsetree; + + if (!stmt->ismove) + { + Portal fportal = GetPortalByName(stmt->portalname); + + if (PortalIsValid(fportal) && + (fportal->cursorOptions & CURSOR_OPT_BINARY)) + format = 1; /* BINARY */ + } + } + PortalSetResultFormat(portal, 1, &format); + + /* + * Now we can create the destination receiver object. + */ + receiver = CreateDestReceiver(dest, portal); + + /* + * Switch back to transaction context for execution. + */ + MemoryContextSwitchTo(oldcontext); + + /* + * Run the portal to completion, and then drop it (and the receiver). + */ (void) PortalRun(portal, FETCH_ALL, receiver, receiver, completionTag); + (*receiver->destroy) (receiver); + PortalDrop(portal, false); @@ -886,8 +913,6 @@ exec_simple_query(const char *query_string) if (!parsetree_list) NullCommand(dest); - (*receiver->destroy) (receiver); - QueryContext = NULL; /* @@ -1156,8 +1181,12 @@ exec_bind_message(StringInfo input_message) { const char *portal_name; const char *stmt_name; - int is_binary; + int numPFormats; + int16 *pformats = NULL; int numParams; + int numRFormats; + int16 *rformats = NULL; + int i; PreparedStatement *pstmt; Portal portal; ParamListInfo params; @@ -1173,14 +1202,28 @@ exec_bind_message(StringInfo input_message) */ start_xact_command(); + /* Switch back to message context */ + MemoryContextSwitchTo(MessageContext); + /* Get the fixed part of the message */ portal_name = pq_getmsgstring(input_message); stmt_name = pq_getmsgstring(input_message); - is_binary = pq_getmsgbyte(input_message); - numParams = pq_getmsgint(input_message, 4); - if (is_binary) - elog(ERROR, "Binary BIND not implemented yet"); + /* Get the parameter format codes */ + numPFormats = pq_getmsgint(input_message, 2); + if (numPFormats > 0) + { + pformats = (int16 *) palloc(numPFormats * sizeof(int16)); + for (i = 0; i < numPFormats; i++) + pformats[i] = pq_getmsgint(input_message, 2); + } + + /* Get the parameter value count */ + numParams = pq_getmsgint(input_message, 2); + + if (numPFormats > 1 && numPFormats != numParams) + elog(ERROR, "BIND message has %d parameter formats but %d parameters", + numPFormats, numParams); /* Find prepared statement */ if (stmt_name[0] != '\0') @@ -1217,45 +1260,98 @@ exec_bind_message(StringInfo input_message) * Fetch parameters, if any, and store in the portal's memory context. * * In an aborted transaction, we can't risk calling user-defined functions, - * so bind all parameters to null values. + * but we can't fail to Bind either, so bind all parameters to null values. */ if (numParams > 0) { bool isaborted = IsAbortedTransactionBlockState(); - int i = 0; + StringInfoData pbuf; List *l; MemoryContext oldContext; + /* Note that the string buffer lives in MessageContext */ + initStringInfo(&pbuf); + oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); params = (ParamListInfo) palloc0((numParams + 1) * sizeof(ParamListInfoData)); + i = 0; foreach(l, pstmt->argtype_list) { Oid ptype = lfirsto(l); + int32 plength; bool isNull; - isNull = (pq_getmsgbyte(input_message) != 0) ? false : true; + plength = pq_getmsgint(input_message, 4); + isNull = (plength == -1); + if (!isNull) { - const char *ptext = pq_getmsgstring(input_message); + /* Reset pbuf to empty, and insert raw data into it */ + pbuf.len = 0; + pbuf.data[0] = '\0'; + pbuf.cursor = 0; + + appendBinaryStringInfo(&pbuf, + pq_getmsgbytes(input_message, plength), + plength); if (isaborted) + { + /* We don't bother to check the format in this case */ isNull = true; + } else { - Oid typInput; - Oid typElem; - - getTypeInputInfo(ptype, &typInput, &typElem); - params[i].value = - OidFunctionCall3(typInput, - CStringGetDatum(ptext), - ObjectIdGetDatum(typElem), - Int32GetDatum(-1)); + int16 pformat; + + if (numPFormats > 1) + pformat = pformats[i]; + else if (numPFormats > 0) + pformat = pformats[0]; + else + pformat = 0; /* default = text */ + + if (pformat == 0) + { + Oid typInput; + Oid typElem; + char *pstring; + + getTypeInputInfo(ptype, &typInput, &typElem); + /* + * Since stringinfo.c keeps a trailing null in + * place even for binary data, the contents of + * pbuf are a valid C string. We have to do + * encoding conversion before calling the typinput + * routine, though. + */ + pstring = (char *) + pg_client_to_server((unsigned char *) pbuf.data, + plength); + params[i].value = + OidFunctionCall3(typInput, + CStringGetDatum(pstring), + ObjectIdGetDatum(typElem), + Int32GetDatum(-1)); + /* Free result of encoding conversion, if any */ + if (pstring != pbuf.data) + pfree(pstring); + } + else if (pformat == 1) + { + /* XXX something similar to above */ + elog(ERROR, "Binary BIND not implemented yet"); + } + else + { + elog(ERROR, "Invalid format code %d", pformat); + } } } + params[i].kind = PARAM_NUM; params[i].id = i + 1; params[i].isnull = isNull; @@ -1270,6 +1366,15 @@ exec_bind_message(StringInfo input_message) else params = NULL; + /* Get the result format codes */ + numRFormats = pq_getmsgint(input_message, 2); + if (numRFormats > 0) + { + rformats = (int16 *) palloc(numRFormats * sizeof(int16)); + for (i = 0; i < numRFormats; i++) + rformats[i] = pq_getmsgint(input_message, 2); + } + pq_getmsgend(input_message); /* @@ -1278,6 +1383,11 @@ exec_bind_message(StringInfo input_message) PortalStart(portal, params); /* + * Apply the result format requests to the portal. + */ + PortalSetResultFormat(portal, numRFormats, rformats); + + /* * Send BindComplete. */ if (whereToSendOutput == Remote) @@ -1290,7 +1400,7 @@ exec_bind_message(StringInfo input_message) * Process an "Execute" message for a portal */ static void -exec_execute_message(const char *portal_name, int is_binary, long max_rows) +exec_execute_message(const char *portal_name, long max_rows) { CommandDest dest; DestReceiver *receiver; @@ -1303,7 +1413,7 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows) /* Adjust destination to tell printtup.c what to do */ dest = whereToSendOutput; if (dest == Remote) - dest = is_binary ? RemoteExecuteInternal : RemoteExecute; + dest = RemoteExecute; portal = GetPortalByName(portal_name); if (!PortalIsValid(portal)) @@ -1353,6 +1463,12 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows) } /* + * Create dest receiver in MessageContext (we don't want it in transaction + * context, because that may get deleted if portal contains VACUUM). + */ + receiver = CreateDestReceiver(dest, portal); + + /* * Ensure we are in a transaction command (this should normally be * the case already due to prior BIND). */ @@ -1375,8 +1491,6 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows) /* * Okay to run the portal. */ - receiver = CreateDestReceiver(dest); - if (max_rows <= 0) max_rows = FETCH_ALL; @@ -1451,7 +1565,7 @@ exec_describe_statement_message(const char *stmt_name) * First describe the parameters... */ pq_beginmessage(&buf, 't'); /* parameter description message type */ - pq_sendint(&buf, length(pstmt->argtype_list), 4); + pq_sendint(&buf, length(pstmt->argtype_list), 2); foreach(l, pstmt->argtype_list) { @@ -1473,7 +1587,7 @@ exec_describe_statement_message(const char *stmt_name) targetlist = ((Query *) lfirst(pstmt->query_list))->targetList; else targetlist = NIL; - SendRowDescriptionMessage(tupdesc, targetlist); + SendRowDescriptionMessage(tupdesc, targetlist, NULL); } else pq_putemptymessage('n'); /* NoData */ @@ -1502,10 +1616,11 @@ exec_describe_portal_message(const char *portal_name) List *targetlist; if (portal->strategy == PORTAL_ONE_SELECT) - targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist; + targetlist = ((Query *) lfirst(portal->parseTrees))->targetList; else targetlist = NIL; - SendRowDescriptionMessage(portal->tupDesc, targetlist); + SendRowDescriptionMessage(portal->tupDesc, targetlist, + portal->formats); } else pq_putemptymessage('n'); /* NoData */ @@ -2397,7 +2512,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.339 $ $Date: 2003/05/08 14:49:04 $\n"); + puts("$Revision: 1.340 $ $Date: 2003/05/08 18:16:36 $\n"); } /* @@ -2613,7 +2728,7 @@ PostgresMain(int argc, char *argv[], const char *username) stmt_name = pq_getmsgstring(input_message); query_string = pq_getmsgstring(input_message); - numParams = pq_getmsgint(input_message, 4); + numParams = pq_getmsgint(input_message, 2); if (numParams > 0) { int i; @@ -2640,15 +2755,13 @@ PostgresMain(int argc, char *argv[], const char *username) case 'E': /* execute */ { const char *portal_name; - int is_binary; int max_rows; portal_name = pq_getmsgstring(input_message); - is_binary = pq_getmsgbyte(input_message); max_rows = pq_getmsgint(input_message, 4); pq_getmsgend(input_message); - exec_execute_message(portal_name, is_binary, max_rows); + exec_execute_message(portal_name, max_rows); } break; diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index e3a37b7310..bf63dcbc29 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,14 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.63 2003/05/06 21:01:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.64 2003/05/08 18:16:36 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "executor/executor.h" -#include "executor/tstoreReceiver.h" #include "miscadmin.h" #include "tcop/tcopprot.h" #include "tcop/pquery.h" @@ -47,7 +46,6 @@ QueryDesc * CreateQueryDesc(Query *parsetree, Plan *plantree, DestReceiver *dest, - const char *portalName, ParamListInfo params, bool doInstrument) { @@ -57,7 +55,6 @@ CreateQueryDesc(Query *parsetree, qd->parsetree = parsetree; /* parse tree */ qd->plantree = plantree; /* plan */ qd->dest = dest; /* output dest */ - qd->portalName = portalName; /* name, if dest is a portal */ qd->params = params; /* parameter values passed into query */ qd->doInstrument = doInstrument; /* instrumentation wanted? */ @@ -89,7 +86,6 @@ FreeQueryDesc(QueryDesc *qdesc) * parsetree: the query tree * plan: the plan tree for the query * params: any parameters needed - * portalName: name of portal being used * dest: where to send results * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE * in which to store a command completion status string. @@ -103,7 +99,6 @@ void ProcessQuery(Query *parsetree, Plan *plan, ParamListInfo params, - const char *portalName, DestReceiver *dest, char *completionTag) { @@ -131,8 +126,7 @@ ProcessQuery(Query *parsetree, /* * Create the QueryDesc object */ - queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params, - false); + queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false); /* * Call ExecStart to prepare the plan for execution @@ -269,7 +263,6 @@ PortalStart(Portal portal, ParamListInfo params) queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees), (Plan *) lfirst(portal->planTrees), None_Receiver, - portal->name, params, false); /* @@ -281,7 +274,7 @@ PortalStart(Portal portal, ParamListInfo params) */ portal->queryDesc = queryDesc; /* - * Remember tuple descriptor + * Remember tuple descriptor (computed by ExecutorStart) */ portal->tupDesc = queryDesc->tupDesc; /* @@ -321,6 +314,53 @@ PortalStart(Portal portal, ParamListInfo params) } /* + * PortalSetResultFormat + * Select the format codes for a portal's output. + * + * This must be run after PortalStart for a portal that will be read by + * a Remote or RemoteExecute destination. It is not presently needed for + * other destination types. + * + * formats[] is the client format request, as per Bind message conventions. + */ +void +PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) +{ + int natts; + int i; + + /* Do nothing if portal won't return tuples */ + if (portal->tupDesc == NULL) + return; + natts = portal->tupDesc->natts; + /* +1 avoids palloc(0) if no columns */ + portal->formats = (int16 *) + MemoryContextAlloc(PortalGetHeapMemory(portal), + (natts + 1) * sizeof(int16)); + if (nFormats > 1) + { + /* format specified for each column */ + if (nFormats != natts) + elog(ERROR, "BIND message has %d result formats but query has %d columns", + nFormats, natts); + memcpy(portal->formats, formats, natts * sizeof(int16)); + } else if (nFormats > 0) + { + /* single format specified, use for all columns */ + int16 format1 = formats[0]; + + for (i = 0; i < natts; i++) + portal->formats[i] = format1; + } + else + { + /* use default format for all columns */ + for (i = 0; i < natts; i++) + portal->formats[i] = 0; + } +} + +/* * PortalRun * Run a portal's query or queries. * @@ -399,8 +439,7 @@ PortalRun(Portal portal, long count, DestReceiver *treceiver; PortalCreateHoldStore(portal); - treceiver = CreateTuplestoreDestReceiver(portal->holdStore, - portal->holdContext); + treceiver = CreateDestReceiver(Tuplestore, portal); PortalRunUtility(portal, lfirst(portal->parseTrees), treceiver, NULL); (*treceiver->destroy) (treceiver); @@ -604,16 +643,9 @@ static uint32 RunFromStore(Portal portal, ScanDirection direction, long count, DestReceiver *dest) { - List *targetlist; long current_tuple_count = 0; - if (portal->strategy == PORTAL_ONE_SELECT) - targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist; - else - targetlist = NIL; - - (*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc, - targetlist); + (*dest->startup) (dest, CMD_SELECT, portal->tupDesc); if (direction == NoMovementScanDirection) { @@ -737,11 +769,9 @@ PortalRunMulti(Portal portal, * but the results will be discarded unless you use "simple Query" * protocol. */ - if (dest->mydest == RemoteExecute || - dest->mydest == RemoteExecuteInternal) + if (dest->mydest == RemoteExecute) dest = None_Receiver; - if (altdest->mydest == RemoteExecute || - altdest->mydest == RemoteExecuteInternal) + if (altdest->mydest == RemoteExecute) altdest = None_Receiver; /* @@ -791,14 +821,14 @@ PortalRunMulti(Portal portal, { /* statement can set tag string */ ProcessQuery(query, plan, - portal->portalParams, portal->name, + portal->portalParams, dest, completionTag); } else { /* stmt added by rewrite cannot set tag */ ProcessQuery(query, plan, - portal->portalParams, portal->name, + portal->portalParams, altdest, NULL); } diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h index 9d10fe4607..981b0f2648 100644 --- a/src/include/access/printtup.h +++ b/src/include/access/printtup.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: printtup.h,v 1.26 2003/05/06 20:26:27 tgl Exp $ + * $Id: printtup.h,v 1.27 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,18 +16,19 @@ #include "tcop/dest.h" -extern DestReceiver *printtup_create_DR(CommandDest dest); +extern DestReceiver *printtup_create_DR(CommandDest dest, Portal portal); -extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist); +extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, + int16 *formats); extern void debugStartup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist); + TupleDesc typeinfo); extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); /* XXX these are really in executor/spi.c */ extern void spi_dest_startup(DestReceiver *self, int operation, - const char *portalName, TupleDesc typeinfo, List *targetlist); + TupleDesc typeinfo); extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h index 9c54ed1d64..5eecb53b8d 100644 --- a/src/include/executor/execdesc.h +++ b/src/include/executor/execdesc.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execdesc.h,v 1.23 2003/05/06 20:26:28 tgl Exp $ + * $Id: execdesc.h,v 1.24 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ typedef struct QueryDesc Query *parsetree; /* rewritten parsetree */ Plan *plantree; /* planner's output */ DestReceiver *dest; /* the destination for tuple output */ - const char *portalName; /* name of portal, or NULL */ ParamListInfo params; /* param values being passed in */ bool doInstrument; /* TRUE requests runtime instrumentation */ @@ -46,7 +45,7 @@ typedef struct QueryDesc /* in pquery.c */ extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree, - DestReceiver *dest, const char *portalName, + DestReceiver *dest, ParamListInfo params, bool doInstrument); diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h index 998b3a391f..e23b4563ed 100644 --- a/src/include/libpq/pqcomm.h +++ b/src/include/libpq/pqcomm.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.h,v 1.83 2003/05/06 21:51:42 tgl Exp $ + * $Id: pqcomm.h,v 1.84 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,7 +106,7 @@ typedef union SockAddr /* The earliest and latest frontend/backend protocol version supported. */ #define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) -#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,107) /* XXX temporary value */ +#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,108) /* XXX temporary value */ typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h index 229de38c9b..7167c6d63a 100644 --- a/src/include/libpq/pqformat.h +++ b/src/include/libpq/pqformat.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqformat.h,v 1.15 2003/04/22 00:08:07 tgl Exp $ + * $Id: pqformat.h,v 1.16 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,7 +18,8 @@ extern void pq_beginmessage(StringInfo buf, char msgtype); extern void pq_sendbyte(StringInfo buf, int byt); extern void pq_sendbytes(StringInfo buf, const char *data, int datalen); -extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen); +extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen, + bool countincludesself); extern void pq_sendstring(StringInfo buf, const char *str); extern void pq_sendint(StringInfo buf, int i, int b); extern void pq_endmessage(StringInfo buf); diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h index d2162e9976..b5721f4593 100644 --- a/src/include/tcop/dest.h +++ b/src/include/tcop/dest.h @@ -54,7 +54,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: dest.h,v 1.37 2003/05/06 20:26:28 tgl Exp $ + * $Id: dest.h,v 1.38 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -82,12 +82,9 @@ typedef enum None, /* results are discarded */ Debug, /* results go to debugging output */ Remote, /* results sent to frontend process */ - RemoteInternal, /* results sent to frontend process in - * internal (binary) form */ - SPI, /* results sent to SPI manager */ - Tuplestore, /* results sent to Tuplestore */ RemoteExecute, /* sent to frontend, in Execute command */ - RemoteExecuteInternal /* same, but binary format */ + SPI, /* results sent to SPI manager */ + Tuplestore /* results sent to Tuplestore */ } CommandDest; /* ---------------- @@ -106,13 +103,13 @@ typedef struct _DestReceiver DestReceiver; struct _DestReceiver { /* Called for each tuple to be output: */ - void (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo, + void (*receiveTuple) (HeapTuple tuple, + TupleDesc typeinfo, DestReceiver *self); /* Per-executor-run initialization and shutdown: */ - void (*startup) (DestReceiver *self, int operation, - const char *portalName, - TupleDesc typeinfo, - List *targetlist); + void (*startup) (DestReceiver *self, + int operation, + TupleDesc typeinfo); void (*shutdown) (DestReceiver *self); /* Destroy the receiver object itself (if dynamically allocated) */ void (*destroy) (DestReceiver *self); @@ -123,10 +120,14 @@ struct _DestReceiver extern DestReceiver *None_Receiver; /* permanent receiver for None */ +/* This is a forward reference to utils/portal.h */ + +typedef struct PortalData *Portal; + /* The primary destination management functions */ extern void BeginCommand(const char *commandTag, CommandDest dest); -extern DestReceiver *CreateDestReceiver(CommandDest dest); +extern DestReceiver *CreateDestReceiver(CommandDest dest, Portal portal); extern void EndCommand(const char *commandTag, CommandDest dest); /* Additional functions that go with destination management, more or less. */ diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index d9d6a6221d..ff9cc9d76a 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pquery.h,v 1.26 2003/05/06 20:26:28 tgl Exp $ + * $Id: pquery.h,v 1.27 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ extern void ProcessQuery(Query *parsetree, Plan *plan, ParamListInfo params, - const char *portalName, DestReceiver *dest, char *completionTag); @@ -28,6 +27,9 @@ extern PortalStrategy ChoosePortalStrategy(List *parseTrees); extern void PortalStart(Portal portal, ParamListInfo params); +extern void PortalSetResultFormat(Portal portal, int nFormats, + int16 *formats); + extern bool PortalRun(Portal portal, long count, DestReceiver *dest, DestReceiver *altdest, char *completionTag); diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h index e2617f321e..7da359cbe7 100644 --- a/src/include/utils/portal.h +++ b/src/include/utils/portal.h @@ -39,7 +39,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: portal.h,v 1.43 2003/05/06 20:26:28 tgl Exp $ + * $Id: portal.h,v 1.44 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,7 +79,10 @@ typedef enum PortalStrategy PORTAL_MULTI_QUERY } PortalStrategy; -typedef struct PortalData *Portal; +/* + * Note: typedef Portal is declared in tcop/dest.h as + * typedef struct PortalData *Portal; + */ typedef struct PortalData { @@ -119,6 +122,8 @@ typedef struct PortalData /* If portal returns tuples, this is their tupdesc: */ TupleDesc tupDesc; /* descriptor for result tuples */ + /* and these are the format codes to use for the columns: */ + int16 *formats; /* a format code for each column */ /* * Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query. diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index db513d64ef..7d0eb39f96 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.134 2003/04/26 20:22:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.135 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,250 +55,10 @@ static void parseInput(PGconn *conn); static void handleSendFailure(PGconn *conn); static void handleSyncLoss(PGconn *conn, char id, int msgLength); static int getRowDescriptions(PGconn *conn); -static int getAnotherTuple(PGconn *conn, int binary); +static int getAnotherTuple(PGconn *conn); static int getParameterStatus(PGconn *conn); static int getNotify(PGconn *conn); -/* --------------- - * Escaping arbitrary strings to get valid SQL strings/identifiers. - * - * Replaces "\\" with "\\\\" and "'" with "''". - * length is the length of the buffer pointed to by - * from. The buffer at to must be at least 2*length + 1 characters - * long. A terminating NUL character is written. - * --------------- - */ - -size_t -PQescapeString(char *to, const char *from, size_t length) -{ - const char *source = from; - char *target = to; - unsigned int remaining = length; - - while (remaining > 0) - { - switch (*source) - { - case '\\': - *target = '\\'; - target++; - *target = '\\'; - /* target and remaining are updated below. */ - break; - - case '\'': - *target = '\''; - target++; - *target = '\''; - /* target and remaining are updated below. */ - break; - - default: - *target = *source; - /* target and remaining are updated below. */ - } - source++; - target++; - remaining--; - } - - /* Write the terminating NUL character. */ - *target = '\0'; - - return target - to; -} - -/* - * PQescapeBytea - converts from binary string to the - * minimal encoding necessary to include the string in an SQL - * INSERT statement with a bytea type column as the target. - * - * The following transformations are applied - * '\0' == ASCII 0 == \\000 - * '\'' == ASCII 39 == \' - * '\\' == ASCII 92 == \\\\ - * anything >= 0x80 ---> \\ooo (where ooo is an octal expression) - */ -unsigned char * -PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen) -{ - const unsigned char *vp; - unsigned char *rp; - unsigned char *result; - size_t i; - size_t len; - - /* - * empty string has 1 char ('\0') - */ - len = 1; - - vp = bintext; - for (i = binlen; i > 0; i--, vp++) - { - if (*vp == 0 || *vp >= 0x80) - len += 5; /* '5' is for '\\ooo' */ - else if (*vp == '\'') - len += 2; - else if (*vp == '\\') - len += 4; - else - len++; - } - - rp = result = (unsigned char *) malloc(len); - if (rp == NULL) - return NULL; - - vp = bintext; - *bytealen = len; - - for (i = binlen; i > 0; i--, vp++) - { - if (*vp == 0 || *vp >= 0x80) - { - (void) sprintf(rp, "\\\\%03o", *vp); - rp += 5; - } - else if (*vp == '\'') - { - rp[0] = '\\'; - rp[1] = '\''; - rp += 2; - } - else if (*vp == '\\') - { - rp[0] = '\\'; - rp[1] = '\\'; - rp[2] = '\\'; - rp[3] = '\\'; - rp += 4; - } - else - *rp++ = *vp; - } - *rp = '\0'; - - return result; -} - -/* - * PQunescapeBytea - converts the null terminated string representation - * of a bytea, strtext, into binary, filling a buffer. It returns a - * pointer to the buffer which is NULL on error, and the size of the - * buffer in retbuflen. The pointer may subsequently be used as an - * argument to the function free(3). It is the reverse of PQescapeBytea. - * - * The following transformations are reversed: - * '\0' == ASCII 0 == \000 - * '\'' == ASCII 39 == \' - * '\\' == ASCII 92 == \\ - * - * States: - * 0 normal 0->1->2->3->4 - * 1 \ 1->5 - * 2 \0 1->6 - * 3 \00 - * 4 \000 - * 5 \' - * 6 \\ - */ -unsigned char * -PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen) -{ - size_t buflen; - unsigned char *buffer, - *bp; - const unsigned char *sp; - unsigned int state = 0; - - if (strtext == NULL) - return NULL; - buflen = strlen(strtext); /* will shrink, also we discover if - * strtext */ - buffer = (unsigned char *) malloc(buflen); /* isn't NULL terminated */ - if (buffer == NULL) - return NULL; - for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++) - { - switch (state) - { - case 0: - if (*sp == '\\') - state = 1; - *bp = *sp; - break; - case 1: - if (*sp == '\'') /* state=5 */ - { /* replace \' with 39 */ - bp--; - *bp = '\''; - buflen--; - state = 0; - } - else if (*sp == '\\') /* state=6 */ - { /* replace \\ with 92 */ - bp--; - *bp = '\\'; - buflen--; - state = 0; - } - else - { - if (isdigit(*sp)) - state = 2; - else - state = 0; - *bp = *sp; - } - break; - case 2: - if (isdigit(*sp)) - state = 3; - else - state = 0; - *bp = *sp; - break; - case 3: - if (isdigit(*sp)) /* state=4 */ - { - int v; - - bp -= 3; - sscanf(sp - 2, "%03o", &v); - *bp = v; - buflen -= 3; - state = 0; - } - else - { - *bp = *sp; - state = 0; - } - break; - } - } - buffer = realloc(buffer, buflen); - if (buffer == NULL) - return NULL; - - *retbuflen = buflen; - return buffer; -} - - -/* - * PQfreemem - safely frees memory allocated - * - * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6) - * Used for freeing memory from PQescapeByte()a/PQunescapeBytea() - */ -void PQfreemem(void *ptr) -{ - free(ptr); -} - /* ---------------- * Space management for PGresult. @@ -909,7 +669,7 @@ parseInput(PGconn *conn) return; } if (msgLength > 30000 && - !(id == 'T' || id == 'D' || id == 'B' || id == 'd')) + !(id == 'T' || id == 'D' || id == 'd')) { handleSyncLoss(conn, id, msgLength); return; @@ -1074,11 +834,11 @@ parseInput(PGconn *conn) return; } break; - case 'D': /* ASCII data tuple */ + case 'D': /* Data Row */ if (conn->result != NULL) { /* Read another tuple of a normal query response */ - if (getAnotherTuple(conn, FALSE)) + if (getAnotherTuple(conn)) return; } else @@ -1090,30 +850,18 @@ parseInput(PGconn *conn) conn->inCursor += msgLength; } break; - case 'B': /* Binary data tuple */ - if (conn->result != NULL) - { - /* Read another tuple of a normal query response */ - if (getAnotherTuple(conn, TRUE)) - return; - } - else - { - snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n")); - DONOTICE(conn, noticeWorkspace); - /* Discard the unexpected message */ - conn->inCursor += msgLength; - } - break; case 'G': /* Start Copy In */ if (pqGetc(&conn->copy_is_binary, conn)) return; + /* XXX we currently ignore the rest of the message */ + conn->inCursor = conn->inStart + 5 + msgLength; conn->asyncStatus = PGASYNC_COPY_IN; break; case 'H': /* Start Copy Out */ if (pqGetc(&conn->copy_is_binary, conn)) return; + /* XXX we currently ignore the rest of the message */ + conn->inCursor = conn->inStart + 5 + msgLength; conn->asyncStatus = PGASYNC_COPY_OUT; conn->copy_already_done = 0; break; @@ -1229,13 +977,15 @@ getRowDescriptions(PGconn *conn) int typid; int typlen; int atttypmod; + int format; if (pqGets(&conn->workBuffer, conn) || pqGetInt(&tableid, 4, conn) || pqGetInt(&columnid, 2, conn) || pqGetInt(&typid, 4, conn) || pqGetInt(&typlen, 2, conn) || - pqGetInt(&atttypmod, 4, conn)) + pqGetInt(&atttypmod, 4, conn) || + pqGetInt(&format, 2, conn)) { PQclear(result); return EOF; @@ -1247,13 +997,14 @@ getRowDescriptions(PGconn *conn) */ columnid = (int) ((int16) columnid); typlen = (int) ((int16) typlen); + format = (int) ((int16) format); result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; - /* XXX todo: save tableid/columnid too */ + /* XXX todo: save tableid/columnid, format too */ } /* Success! */ @@ -1262,7 +1013,7 @@ getRowDescriptions(PGconn *conn) } /* - * parseInput subroutine to read a 'B' or 'D' (row data) message. + * parseInput subroutine to read a 'D' (row data) message. * We add another tuple to the existing PGresult structure. * Returns: 0 if completed message, EOF if error or not enough data yet. * @@ -1272,23 +1023,14 @@ getRowDescriptions(PGconn *conn) */ static int -getAnotherTuple(PGconn *conn, int binary) +getAnotherTuple(PGconn *conn) { PGresult *result = conn->result; int nfields = result->numAttributes; PGresAttValue *tup; - - /* the backend sends us a bitmap of which attributes are null */ - char std_bitmap[64]; /* used unless it doesn't fit */ - char *bitmap = std_bitmap; - int i; - size_t nbytes; /* the number of bytes in bitmap */ - char bmap; /* One byte of the bitmap */ - int bitmap_index; /* Its index */ - int bitcnt; /* number of bits examined in current byte */ + int tupnfields; /* # fields from tuple */ int vlen; /* length of the current field value */ - - result->binary = binary; + int i; /* Allocate tuple space if first time for this data message */ if (conn->curTuple == NULL) @@ -1301,61 +1043,50 @@ getAnotherTuple(PGconn *conn, int binary) } tup = conn->curTuple; - /* Get the null-value bitmap */ - nbytes = (nfields + BYTELEN - 1) / BYTELEN; - /* malloc() only for unusually large field counts... */ - if (nbytes > sizeof(std_bitmap)) - bitmap = (char *) malloc(nbytes); + /* Get the field count and make sure it's what we expect */ + if (pqGetInt(&tupnfields, 2, conn)) + return EOF; - if (pqGetnchar(bitmap, nbytes, conn)) - goto EOFexit; + if (tupnfields != nfields) + { + /* Replace partially constructed result with an error result */ + pqClearAsyncResult(conn); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unexpected field count in D message\n")); + saveErrorResult(conn); + conn->asyncStatus = PGASYNC_READY; + /* Discard the failed message by pretending we read it */ + return 0; + } /* Scan the fields */ - bitmap_index = 0; - bmap = bitmap[bitmap_index]; - bitcnt = 0; - for (i = 0; i < nfields; i++) { - if (!(bmap & 0200)) + /* get the value length */ + if (pqGetInt(&vlen, 4, conn)) + return EOF; + if (vlen == -1) { - /* if the field value is absent, make it a null string */ + /* null field */ tup[i].value = result->null_field; tup[i].len = NULL_LEN; + continue; } - else + if (vlen < 0) + vlen = 0; + if (tup[i].value == NULL) { - /* get the value length (the first four bytes are for length) */ - if (pqGetInt(&vlen, 4, conn)) - goto EOFexit; - if (binary == 0) - vlen = vlen - 4; - if (vlen < 0) - vlen = 0; + tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false); if (tup[i].value == NULL) - { - tup[i].value = (char *) pqResultAlloc(result, vlen + 1, (bool) binary); - if (tup[i].value == NULL) - goto outOfMemory; - } - tup[i].len = vlen; - /* read in the value */ - if (vlen > 0) - if (pqGetnchar((char *) (tup[i].value), vlen, conn)) - goto EOFexit; - /* we have to terminate this ourselves */ - tup[i].value[vlen] = '\0'; - } - /* advance the bitmap stuff */ - bitcnt++; - if (bitcnt == BYTELEN) - { - bitmap_index++; - bmap = bitmap[bitmap_index]; - bitcnt = 0; + goto outOfMemory; } - else - bmap <<= 1; + tup[i].len = vlen; + /* read in the value */ + if (vlen > 0) + if (pqGetnchar((char *) (tup[i].value), vlen, conn)) + return EOF; + /* we have to terminate this ourselves */ + tup[i].value[vlen] = '\0'; } /* Success! Store the completed tuple in the result */ @@ -1364,8 +1095,6 @@ getAnotherTuple(PGconn *conn, int binary) /* and reset for a new message */ conn->curTuple = NULL; - if (bitmap != std_bitmap) - free(bitmap); return 0; outOfMemory: @@ -1380,13 +1109,8 @@ outOfMemory: libpq_gettext("out of memory\n")); conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); conn->asyncStatus = PGASYNC_READY; - /* Discard the failed message --- good idea? */ - conn->inStart = conn->inEnd; - -EOFexit: - if (bitmap != std_bitmap) - free(bitmap); - return EOF; + /* Discard the failed message by pretending we read it */ + return 0; } @@ -2129,10 +1853,11 @@ PQfn(PGconn *conn, return NULL; } - if (pqPutMsgStart('F', conn) < 0 || /* function call msg */ - pqPuts("", conn) < 0 || /* useless string */ - pqPutInt(fnid, 4, conn) < 0 || /* function id */ - pqPutInt(nargs, 4, conn) < 0) /* # of args */ + if (pqPutMsgStart('F', conn) < 0 || /* function call msg */ + pqPutInt(fnid, 4, conn) < 0 || /* function id */ + pqPutInt(1, 2, conn) < 0 || /* # of format codes */ + pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ + pqPutInt(nargs, 2, conn) < 0) /* # of args */ { handleSendFailure(conn); return NULL; @@ -2145,10 +1870,12 @@ PQfn(PGconn *conn, handleSendFailure(conn); return NULL; } + if (args[i].len == -1) + continue; /* it's NULL */ if (args[i].isint) { - if (pqPutInt(args[i].u.integer, 4, conn)) + if (pqPutInt(args[i].u.integer, args[i].len, conn)) { handleSendFailure(conn); return NULL; @@ -2164,6 +1891,12 @@ PQfn(PGconn *conn, } } + if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ + { + handleSendFailure(conn); + return NULL; + } + if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { @@ -2204,7 +1937,7 @@ PQfn(PGconn *conn, break; } if (msgLength > 30000 && - !(id == 'T' || id == 'D' || id == 'B' || id == 'd' || id == 'V')) + !(id == 'T' || id == 'D' || id == 'd' || id == 'V')) { handleSyncLoss(conn, id, msgLength); break; @@ -2243,16 +1976,13 @@ PQfn(PGconn *conn, switch (id) { case 'V': /* function result */ - if (pqGetc(&id, conn)) + if (pqGetInt(actual_result_len, 4, conn)) continue; - if (id == 'G') + if (*actual_result_len != -1) { - /* function returned nonempty value */ - if (pqGetInt(actual_result_len, 4, conn)) - continue; if (result_is_int) { - if (pqGetInt(result_buf, 4, conn)) + if (pqGetInt(result_buf, *actual_result_len, conn)) continue; } else @@ -2262,24 +1992,9 @@ PQfn(PGconn *conn, conn)) continue; } - if (pqGetc(&id, conn)) /* get the last '0' */ - continue; - } - if (id == '0') - { - /* correctly finished function result message */ - status = PGRES_COMMAND_OK; - } - else - { - /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("protocol error: id=0x%x\n"), - id); - saveErrorResult(conn); - conn->inStart += 5 + msgLength; - return prepareAsyncResult(conn); } + /* correctly finished function result message */ + status = PGRES_COMMAND_OK; break; case 'E': /* error return */ if (pqGetErrorNotice(conn, true)) @@ -2744,6 +2459,18 @@ PQflush(PGconn *conn) return (pqFlush(conn)); } + +/* + * PQfreemem - safely frees memory allocated + * + * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6) + * Used for freeing memory from PQescapeByte()a/PQunescapeBytea() + */ +void PQfreemem(void *ptr) +{ + free(ptr); +} + /* * PQfreeNotify - free's the memory associated with a PGnotify * @@ -2760,3 +2487,232 @@ PQfreeNotify(PGnotify *notify) { PQfreemem(notify); } + + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * --------------- + */ + +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +/* + * PQescapeBytea - converts from binary string to the + * minimal encoding necessary to include the string in an SQL + * INSERT statement with a bytea type column as the target. + * + * The following transformations are applied + * '\0' == ASCII 0 == \\000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\\\ + * anything >= 0x80 ---> \\ooo (where ooo is an octal expression) + */ +unsigned char * +PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen) +{ + const unsigned char *vp; + unsigned char *rp; + unsigned char *result; + size_t i; + size_t len; + + /* + * empty string has 1 char ('\0') + */ + len = 1; + + vp = bintext; + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + len += 5; /* '5' is for '\\ooo' */ + else if (*vp == '\'') + len += 2; + else if (*vp == '\\') + len += 4; + else + len++; + } + + rp = result = (unsigned char *) malloc(len); + if (rp == NULL) + return NULL; + + vp = bintext; + *bytealen = len; + + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + { + (void) sprintf(rp, "\\\\%03o", *vp); + rp += 5; + } + else if (*vp == '\'') + { + rp[0] = '\\'; + rp[1] = '\''; + rp += 2; + } + else if (*vp == '\\') + { + rp[0] = '\\'; + rp[1] = '\\'; + rp[2] = '\\'; + rp[3] = '\\'; + rp += 4; + } + else + *rp++ = *vp; + } + *rp = '\0'; + + return result; +} + +/* + * PQunescapeBytea - converts the null terminated string representation + * of a bytea, strtext, into binary, filling a buffer. It returns a + * pointer to the buffer which is NULL on error, and the size of the + * buffer in retbuflen. The pointer may subsequently be used as an + * argument to the function free(3). It is the reverse of PQescapeBytea. + * + * The following transformations are reversed: + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\ + * + * States: + * 0 normal 0->1->2->3->4 + * 1 \ 1->5 + * 2 \0 1->6 + * 3 \00 + * 4 \000 + * 5 \' + * 6 \\ + */ +unsigned char * +PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen) +{ + size_t buflen; + unsigned char *buffer, + *bp; + const unsigned char *sp; + unsigned int state = 0; + + if (strtext == NULL) + return NULL; + buflen = strlen(strtext); /* will shrink, also we discover if + * strtext */ + buffer = (unsigned char *) malloc(buflen); /* isn't NULL terminated */ + if (buffer == NULL) + return NULL; + for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++) + { + switch (state) + { + case 0: + if (*sp == '\\') + state = 1; + *bp = *sp; + break; + case 1: + if (*sp == '\'') /* state=5 */ + { /* replace \' with 39 */ + bp--; + *bp = '\''; + buflen--; + state = 0; + } + else if (*sp == '\\') /* state=6 */ + { /* replace \\ with 92 */ + bp--; + *bp = '\\'; + buflen--; + state = 0; + } + else + { + if (isdigit(*sp)) + state = 2; + else + state = 0; + *bp = *sp; + } + break; + case 2: + if (isdigit(*sp)) + state = 3; + else + state = 0; + *bp = *sp; + break; + case 3: + if (isdigit(*sp)) /* state=4 */ + { + int v; + + bp -= 3; + sscanf(sp - 2, "%03o", &v); + *bp = v; + buflen -= 3; + state = 0; + } + else + { + *bp = *sp; + state = 0; + } + break; + } + } + buffer = realloc(buffer, buflen); + if (buffer == NULL) + return NULL; + + *retbuflen = buflen; + return buffer; +} diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 1a25105ead..b65f47efbf 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.68 2003/05/06 21:51:42 tgl Exp $ + * $Id: libpq-int.h,v 1.69 2003/05/08 18:16:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast * pqcomm.h describe what the backend knows, not what libpq knows. */ -#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,107) /* XXX temporary value */ +#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,108) /* XXX temporary value */ /* * POSTGRES backend dependent Constants. -- 2.11.0