diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/access/common/printtup.c | 228 | ||||
| -rw-r--r-- | src/backend/commands/copy.c | 28 | ||||
| -rw-r--r-- | src/backend/commands/explain.c | 4 | ||||
| -rw-r--r-- | src/backend/commands/portalcmds.c | 27 | ||||
| -rw-r--r-- | src/backend/commands/prepare.c | 4 | ||||
| -rw-r--r-- | src/backend/executor/execMain.c | 7 | ||||
| -rw-r--r-- | src/backend/executor/execTuples.c | 5 | ||||
| -rw-r--r-- | src/backend/executor/functions.c | 4 | ||||
| -rw-r--r-- | src/backend/executor/spi.c | 18 | ||||
| -rw-r--r-- | src/backend/executor/tstoreReceiver.c | 6 | ||||
| -rw-r--r-- | src/backend/libpq/pqformat.c | 23 | ||||
| -rw-r--r-- | src/backend/tcop/dest.c | 37 | ||||
| -rw-r--r-- | src/backend/tcop/fastpath.c | 260 | ||||
| -rw-r--r-- | src/backend/tcop/postgres.c | 207 | ||||
| -rw-r--r-- | src/backend/tcop/pquery.c | 82 |
15 files changed, 647 insertions, 293 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 = "<unprintable>"; + 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 = "<unprintable>"; - 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); } |
