From: Tom Lane Date: Wed, 27 Feb 2002 19:36:13 +0000 (+0000) Subject: Clean up BeginCommand and related routines. BeginCommand and EndCommand X-Git-Tag: REL7_3~2018 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6779c55c228ba5904dc767a7882b47a2b4c74ff7;p=postgresql Clean up BeginCommand and related routines. BeginCommand and EndCommand are now both invoked once per received SQL command (raw parsetree) from pg_exec_query_string. BeginCommand is actually just an empty routine at the moment --- all its former operations have been pushed into tuple receiver setup routines in printtup.c. This makes for a clean distinction between BeginCommand/EndCommand (once per command) and the tuple receiver setup/teardown routines (once per ExecutorRun call), whereas the old code was quite ad hoc. Along the way, clean up the calling conventions for ExecutorRun a little bit. --- diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index ea10330bf8..95e79b9569 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -1,15 +1,15 @@ /*------------------------------------------------------------------------- * * printtup.c - * Routines to print out tuples to the destination (binary or non-binary - * portals, frontend/interactive backend, etc.). + * Routines to print out tuples to the destination (both frontend + * clients and interactive backends are supported here). + * * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.60 2001/10/25 05:49:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.61 2002/02/27 19:34:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,10 +18,12 @@ #include "access/heapam.h" #include "access/printtup.h" #include "catalog/pg_type.h" +#include "libpq/libpq.h" #include "libpq/pqformat.h" #include "utils/syscache.h" -static void printtup_setup(DestReceiver *self, TupleDesc typeinfo); +static void printtup_setup(DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo); static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); static void printtup_cleanup(DestReceiver *self); @@ -97,17 +99,56 @@ printtup_create_DR(bool isBinary) } static void -printtup_setup(DestReceiver *self, TupleDesc typeinfo) +printtup_setup(DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo) { + /* + * Send portal name to frontend. + * + * If portal name not specified, use "blank" portal. + */ + if (portalName == NULL) + portalName = "blank"; + + pq_puttextmessage('P', portalName); + + /* + * if this is a retrieve, then we send back the tuple + * descriptor of the tuples. + */ + if (operation == CMD_SELECT) + { + Form_pg_attribute *attrs = typeinfo->attrs; + int natts = typeinfo->natts; + int i; + StringInfoData buf; + + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'T'); /* tuple descriptor message type */ + pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ + + for (i = 0; i < natts; ++i) + { + pq_sendstring(&buf, NameStr(attrs[i]->attname)); + pq_sendint(&buf, (int) attrs[i]->atttypid, + sizeof(attrs[i]->atttypid)); + pq_sendint(&buf, attrs[i]->attlen, + sizeof(attrs[i]->attlen)); + if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) + pq_sendint(&buf, attrs[i]->atttypmod, + sizeof(attrs[i]->atttypmod)); + } + pq_endmessage(&buf); + } + /* ---------------- * We could set up the derived attr info at this time, but we postpone it - * until the first call of printtup, for 3 reasons: + * until the first call of printtup, for 2 reasons: * 1. We don't waste time (compared to the old way) if there are no * tuples at all to output. * 2. Checking in printtup allows us to handle the case that the tuples * change type midway through (although this probably can't happen in * the current executor). - * 3. Right now, ExecutorRun passes a NULL for typeinfo anyway :-( * ---------------- */ } @@ -267,12 +308,12 @@ printatt(unsigned attributeId, * showatts * ---------------- */ -void -showatts(char *name, TupleDesc tupleDesc) +static void +showatts(const char *name, TupleDesc tupleDesc) { - int i; int natts = tupleDesc->natts; Form_pg_attribute *attinfo = tupleDesc->attrs; + int i; puts(name); for (i = 0; i < natts; ++i) @@ -281,7 +322,24 @@ showatts(char *name, TupleDesc tupleDesc) } /* ---------------- - * debugtup + * debugSetup - prepare to print tuples for an interactive backend + * ---------------- + */ +void +debugSetup(DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo) +{ + /* + * show the return type of the tuples + */ + if (portalName == NULL) + portalName = "blank"; + + showatts(portalName, typeinfo); +} + +/* ---------------- + * debugtup - print one tuple for an interactive backend * ---------------- */ void diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4b9d53df34..81f996b8a0 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.76 2001/10/25 05:49:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.77 2002/02/27 19:34:11 tgl Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -432,7 +432,7 @@ TupleDescInitEntry(TupleDesc desc, * * (Why not just make the atttypid point to the OID type, instead of the * type the query returns? Because the executor uses the atttypid to - * tell the front end what type will be returned (in BeginCommand), + * tell the front end what type will be returned, * and in the end the type returned will be the result of the query, * not an OID.) * diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 676caba22d..64554aa2da 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.155 2002/02/26 22:47:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.156 2002/02/27 19:34:38 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -113,6 +113,7 @@ PerformPortalFetch(char *name, QueryDesc *queryDesc; EState *estate; MemoryContext oldcontext; + ScanDirection direction; CommandId savedId; bool temp_desc = false; @@ -145,6 +146,9 @@ PerformPortalFetch(char *name, */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + queryDesc = PortalGetQueryDesc(portal); + estate = PortalGetState(portal); + /* * If the requested destination is not the same as the query's * original destination, make a temporary QueryDesc with the proper @@ -156,9 +160,6 @@ PerformPortalFetch(char *name, * original dest. This is necessary since a FETCH command will pass * dest = Remote, not knowing whether the cursor is binary or not. */ - queryDesc = PortalGetQueryDesc(portal); - estate = PortalGetState(portal); - if (dest != queryDesc->dest && !(queryDesc->dest == RemoteInternal && dest == Remote)) { @@ -170,19 +171,6 @@ PerformPortalFetch(char *name, temp_desc = true; } - /* - * Tell the destination to prepare to receive some tuples. - */ - BeginCommand(name, - queryDesc->operation, - PortalGetTupleDesc(portal), - false, /* portal fetches don't end up in - * relations */ - false, /* this is a portal fetch, not a "retrieve - * portal" */ - NULL, /* not used */ - queryDesc->dest); - /* * Restore the scanCommandId that was current when the cursor was * opened. This ensures that we see the same tuples throughout the @@ -194,47 +182,49 @@ PerformPortalFetch(char *name, /* * Determine which direction to go in, and check to see if we're * already at the end of the available tuples in that direction. If - * so, do nothing. (This check exists because not all plan node types + * so, set the direction to NoMovement to avoid trying to fetch any + * tuples. (This check exists because not all plan node types * are robust about being called again if they've already returned - * NULL once.) If it's OK to do the fetch, call the executor. Then, - * update the atStart/atEnd state depending on the number of tuples - * that were retrieved. + * NULL once.) Then call the executor (we must not skip this, because + * the destination needs to see a setup and shutdown even if no tuples + * are available). Finally, update the atStart/atEnd state depending + * on the number of tuples that were retrieved. */ if (forward) { - if (!portal->atEnd) - { - ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count); + if (portal->atEnd) + direction = NoMovementScanDirection; + else + direction = ForwardScanDirection; - if (estate->es_processed > 0) - portal->atStart = false; /* OK to back up now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atEnd = true; /* we retrieved 'em all */ + ExecutorRun(queryDesc, estate, direction, (long) count); - if (completionTag) - snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", - (dest == None) ? "MOVE" : "FETCH", - estate->es_processed); - } + if (estate->es_processed > 0) + portal->atStart = false; /* OK to back up now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atEnd = true; /* we retrieved 'em all */ } else { - if (!portal->atStart) - { - ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count); + if (portal->atStart) + direction = NoMovementScanDirection; + else + direction = BackwardScanDirection; - if (estate->es_processed > 0) - portal->atEnd = false; /* OK to go forward now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atStart = true; /* we retrieved 'em all */ + ExecutorRun(queryDesc, estate, direction, (long) count); - if (completionTag) - snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", - (dest == None) ? "MOVE" : "FETCH", - estate->es_processed); - } + if (estate->es_processed > 0) + portal->atEnd = false; /* OK to go forward now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atStart = true; /* we retrieved 'em all */ } + /* Return command status if wanted */ + if (completionTag) + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", + (dest == None) ? "MOVE" : "FETCH", + estate->es_processed); + /* * Restore outer command ID. */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 6e1145ac77..18b81849ac 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -19,7 +19,7 @@ * query plan and ExecutorEnd() should always be called at the end of * execution of a plan. * - * ExecutorRun accepts 'feature' and 'count' arguments that specify whether + * ExecutorRun accepts direction and count arguments that specify whether * the plan is to be executed forwards, backwards, and for how many tuples. * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.149 2001/10/25 05:49:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.150 2002/02/27 19:34:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,7 +88,7 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); * query plan * * returns a TupleDesc which describes the attributes of the tuples to - * be returned by the query. + * be returned by the query. (Same value is saved in queryDesc) * * NB: the CurrentMemoryContext when this is called must be the context * to be used as the per-query context for the query plan. ExecutorRun() @@ -137,6 +137,8 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) queryDesc->plantree, estate); + queryDesc->tupDesc = result; + return result; } @@ -149,25 +151,23 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) * * ExecutorStart must have been called already. * - * the different features supported are: - * EXEC_RUN: retrieve all tuples in the forward direction - * EXEC_FOR: retrieve 'count' number of tuples in the forward dir - * EXEC_BACK: retrieve 'count' number of tuples in the backward dir - * EXEC_RETONE: return one tuple but don't 'retrieve' it - * used in postquel function processing + * If direction is NoMovementScanDirection then nothing is done + * except to start up/shut down the destination. Otherwise, + * we retrieve up to 'count' tuples in the specified direction. * * Note: count = 0 is interpreted as "no limit". * * ---------------------------------------------------------------- */ TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count) +ExecutorRun(QueryDesc *queryDesc, EState *estate, + ScanDirection direction, long count) { CmdType operation; Plan *plan; - TupleTableSlot *result; CommandDest dest; DestReceiver *destfunc; + TupleTableSlot *result; /* * sanity checks @@ -181,69 +181,33 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count) operation = queryDesc->operation; plan = queryDesc->plantree; dest = queryDesc->dest; - destfunc = DestToFunction(dest); - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; /* - * FIXME: the dest setup function ought to be handed the tuple desc - * for the tuples to be output, but I'm not quite sure how to get that - * info at this point. For now, passing NULL is OK because no - * existing dest setup function actually uses the pointer. + * startup tuple receiver */ - (*destfunc->setup) (destfunc, (TupleDesc) NULL); - - switch (feature) - { - case EXEC_RUN: - result = ExecutePlan(estate, - plan, - operation, - count, - ForwardScanDirection, - destfunc); - break; - - case EXEC_FOR: - result = ExecutePlan(estate, - plan, - operation, - count, - ForwardScanDirection, - destfunc); - break; - - /* - * retrieve next n "backward" tuples - */ - case EXEC_BACK: - result = ExecutePlan(estate, - plan, - operation, - count, - BackwardScanDirection, - destfunc); - break; + estate->es_processed = 0; + estate->es_lastoid = InvalidOid; - /* - * return one tuple but don't "retrieve" it. (this is used by - * the rule manager..) -cim 9/14/89 - */ - case EXEC_RETONE: - result = ExecutePlan(estate, - plan, - operation, - ONE_TUPLE, - ForwardScanDirection, - destfunc); - break; + destfunc = DestToFunction(dest); + (*destfunc->setup) (destfunc, (int) operation, + queryDesc->portalName, queryDesc->tupDesc); - default: - elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); - result = NULL; - break; - } + /* + * run plan + */ + if (direction == NoMovementScanDirection) + result = NULL; + else + result = ExecutePlan(estate, + plan, + operation, + count, + direction, + destfunc); + /* + * shutdown receiver + */ (*destfunc->cleanup) (destfunc); return result; @@ -916,7 +880,7 @@ EndPlan(Plan *plan, EState *estate) * * processes the query plan to retrieve 'numberTuples' tuples in the * direction specified. - * Retrieves all tuples if tupleCount is 0 + * Retrieves all tuples if numberTuples is 0 * * result is either a slot containing the last tuple in the case * of a RETRIEVE or NULL otherwise. diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index c38b8077f5..885d93a2af 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.48 2002/02/26 22:47:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.49 2002/02/27 19:34:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -111,9 +111,8 @@ init_execution_state(char *src, Oid *argOidVect, int nargs) nextes->next = NULL; nextes->status = F_EXEC_START; - nextes->qd = CreateQueryDesc(queryTree, - planTree, - None); + + nextes->qd = CreateQueryDesc(queryTree, planTree, None, NULL); estate = CreateExecutorState(); if (nargs > 0) @@ -268,7 +267,7 @@ postquel_start(execution_state *es) static TupleTableSlot * postquel_getnext(execution_state *es) { - int feature; + long count; if (es->qd->operation == CMD_UTILITY) { @@ -281,9 +280,10 @@ postquel_getnext(execution_state *es) return (TupleTableSlot *) NULL; } - feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; + /* If it's not the last command, just run it to completion */ + count = (LAST_POSTQUEL_COMMAND(es)) ? 1L : 0L; - return ExecutorRun(es->qd, es->estate, feature, 0L); + return ExecutorRun(es->qd, es->estate, ForwardScanDirection, count); } static void diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 05044d6cd3..c8c65b1cee 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.66 2002/02/26 22:47:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.67 2002/02/27 19:34:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -779,7 +779,7 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls) queryTree->isBinary = false; /* Create the QueryDesc object and the executor state */ - queryDesc = CreateQueryDesc(queryTree, planTree, SPI); + queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL); eState = CreateExecutorState(); /* If the plan has parameters, put them into the executor state */ @@ -1023,7 +1023,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan) else if (plan == NULL) { qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None); + islastquery ? SPI : None, NULL); state = CreateExecutorState(); res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0); if (res < 0 || islastquery) @@ -1033,7 +1033,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan) else { qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None); + islastquery ? SPI : None, NULL); res = _SPI_pquery(qdesc, NULL, islastquery ? tcount : 0); if (res < 0) return res; @@ -1094,7 +1094,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount) else { qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None); + islastquery ? SPI : None, NULL); state = CreateExecutorState(); if (nargs > 0) { @@ -1132,7 +1132,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) Query *parseTree = queryDesc->parsetree; int operation = queryDesc->operation; CommandDest dest = queryDesc->dest; - TupleDesc tupdesc; bool isRetrieveIntoPortal = false; bool isRetrieveIntoRelation = false; char *intoName = NULL; @@ -1174,11 +1173,13 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) if (state == NULL) /* plan preparation */ return res; + #ifdef SPI_EXECUTOR_STATS if (ShowExecutorStats) ResetUsage(); #endif - tupdesc = ExecutorStart(queryDesc, state); + + ExecutorStart(queryDesc, state); /* * Don't work currently --- need to rearrange callers so that we @@ -1188,7 +1189,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) if (isRetrieveIntoPortal) elog(FATAL, "SPI_select: retrieve into portal not implemented"); - ExecutorRun(queryDesc, state, EXEC_FOR, (long) tcount); + ExecutorRun(queryDesc, state, ForwardScanDirection, (long) tcount); _SPI_current->processed = state->es_processed; save_lastoid = state->es_lastoid; @@ -1230,6 +1231,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, QueryDesc *querydesc; EState *estate; MemoryContext oldcontext; + ScanDirection direction; CommandId savedId; CommandDest olddest; @@ -1268,29 +1270,35 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, /* Run the executor like PerformPortalFetch and remember states */ if (forward) { - if (!portal->atEnd) - { - ExecutorRun(querydesc, estate, EXEC_FOR, (long) count); - _SPI_current->processed = estate->es_processed; - if (estate->es_processed > 0) - portal->atStart = false; - if (count <= 0 || (int) estate->es_processed < count) - portal->atEnd = true; - } + if (portal->atEnd) + direction = NoMovementScanDirection; + else + direction = ForwardScanDirection; + + ExecutorRun(querydesc, estate, direction, (long) count); + + if (estate->es_processed > 0) + portal->atStart = false; /* OK to back up now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atEnd = true; /* we retrieved 'em all */ } else { - if (!portal->atStart) - { - ExecutorRun(querydesc, estate, EXEC_BACK, (long) count); - _SPI_current->processed = estate->es_processed; - if (estate->es_processed > 0) - portal->atEnd = false; - if (count <= 0 || estate->es_processed < count) - portal->atStart = true; - } + if (portal->atStart) + direction = NoMovementScanDirection; + else + direction = BackwardScanDirection; + + ExecutorRun(querydesc, estate, direction, (long) count); + + if (estate->es_processed > 0) + portal->atEnd = false; /* OK to go forward now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atStart = true; /* we retrieved 'em all */ } + _SPI_current->processed = estate->es_processed; + /* * Restore outer command ID. */ diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c index f055bc3137..d56f032a02 100644 --- a/src/backend/tcop/dest.c +++ b/src/backend/tcop/dest.c @@ -1,22 +1,22 @@ /*------------------------------------------------------------------------- * * dest.c - * support for various communication destinations - see include/tcop/dest.h + * support for communication destinations + * * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.47 2002/02/26 22:47:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.48 2002/02/27 19:35:09 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * BeginCommand - prepare destination for tuples of the given type + * BeginCommand - initialize the destination at start of command * DestToFunction - identify per-tuple processing routines - * EndCommand - tell destination that no more tuples will arrive + * EndCommand - clean up the destination at end of command * NullCommand - tell dest that an empty query string was recognized * ReadyForQuery - tell dest that we are ready for a new query * @@ -24,14 +24,6 @@ * These routines do the appropriate work before and after * tuples are returned by a query to keep the backend and the * "destination" portals synchronized. - * - * There is a second level of initialization/cleanup performed by the - * setup/cleanup routines identified by DestToFunction. This could - * probably be merged with the work done by BeginCommand/EndCommand, - * but as of right now BeginCommand/EndCommand are used in a rather - * unstructured way --- some places call Begin without End, some vice - * versa --- so I think I'll just leave 'em alone for now. tgl 1/99. - * */ #include "postgres.h" @@ -51,7 +43,8 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) } static void -donothingSetup(DestReceiver *self, TupleDesc typeinfo) +donothingSetup(DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo) { } @@ -68,97 +61,20 @@ static DestReceiver donothingDR = { donothingReceive, donothingSetup, donothingCleanup }; static DestReceiver debugtupDR = { - debugtup, donothingSetup, donothingCleanup + debugtup, debugSetup, donothingCleanup }; static DestReceiver spi_printtupDR = { spi_printtup, donothingSetup, donothingCleanup }; /* ---------------- - * BeginCommand - prepare destination for tuples of the given type + * BeginCommand - initialize the destination at start of command * ---------------- */ void -BeginCommand(char *pname, - int operation, - TupleDesc tupdesc, - bool isIntoRel, - bool isIntoPortal, - char *tag, - CommandDest dest) +BeginCommand(const char *commandTag, CommandDest dest) { - Form_pg_attribute *attrs = tupdesc->attrs; - int natts = tupdesc->natts; - int i; - - switch (dest) - { - case Remote: - case RemoteInternal: - - /* - * if this is a "retrieve into portal" query, done because - * nothing needs to be sent to the fe. - */ - if (isIntoPortal) - break; - - /* - * if portal name not specified for remote query, use the - * "blank" portal. - */ - if (pname == NULL) - pname = "blank"; - - /* - * send fe info on tuples we're about to send - */ - pq_puttextmessage('P', pname); - - /* - * if this is a retrieve, then we send back the tuple - * descriptor of the tuples. "retrieve into" is an exception - * because no tuples are returned in that case. - */ - if (operation == CMD_SELECT && !isIntoRel) - { - StringInfoData buf; - - pq_beginmessage(&buf); - pq_sendbyte(&buf, 'T'); /* tuple descriptor message type */ - pq_sendint(&buf, natts, 2); /* # of attributes in - * tuples */ - - for (i = 0; i < natts; ++i) - { - pq_sendstring(&buf, NameStr(attrs[i]->attname)); - pq_sendint(&buf, (int) attrs[i]->atttypid, - sizeof(attrs[i]->atttypid)); - pq_sendint(&buf, attrs[i]->attlen, - sizeof(attrs[i]->attlen)); - if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) - pq_sendint(&buf, attrs[i]->atttypmod, - sizeof(attrs[i]->atttypmod)); - } - pq_endmessage(&buf); - } - break; - - case Debug: - - /* - * show the return type of the tuples - */ - if (pname == NULL) - pname = "blank"; - - showatts(pname, tupdesc); - break; - - case None: - default: - break; - } + /* Nothing to do at present */ } /* ---------------- @@ -183,19 +99,15 @@ DestToFunction(CommandDest dest) return &spi_printtupDR; case None: - default: return &donothingDR; } - /* - * never gets here, but DECstation lint appears to be stupid... - */ - + /* should never get here */ return &donothingDR; } /* ---------------- - * EndCommand - tell destination that query is complete + * EndCommand - clean up the destination at end of command * ---------------- */ void diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 61fdf9c5ac..cfaaf556dd 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.248 2002/02/26 22:47:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.249 2002/02/27 19:35:12 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -645,6 +645,18 @@ pg_exec_query_string(char *query_string, /* string to execute */ /* Transaction control statements need some special handling */ isTransactionStmt = IsA(parsetree, TransactionStmt); + /* + * First we set the command-completion tag to the main query + * (as opposed to each of the others that may be generated by + * analyze and rewrite). Also set ps_status and do any special + * start-of-SQL-command processing needed by the destination. + */ + commandTag = CreateCommandTag(parsetree); + + set_ps_display(commandTag); + + BeginCommand(commandTag, dest); + /* * If we are in an aborted transaction, ignore all commands except * COMMIT/ABORT. It is important that this test occur before we @@ -707,18 +719,7 @@ pg_exec_query_string(char *query_string, /* string to execute */ /* * OK to analyze and rewrite this query. - */ - - /* - * First we set the command-completion tag to the main query - * (as opposed to each of the others that may be generated by - * analyze and rewrite). Also set ps_status to the main query tag. - */ - commandTag = CreateCommandTag(parsetree); - - set_ps_display(commandTag); - - /* + * * Switch to appropriate context for constructing querytrees (again, * these must outlive the execution context). */ @@ -1688,7 +1689,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.248 $ $Date: 2002/02/26 22:47:08 $\n"); + puts("$Revision: 1.249 $ $Date: 2002/02/27 19:35:12 $\n"); } /* diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 75b99c21fa..80658a61d6 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.47 2002/02/26 22:47:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.48 2002/02/27 19:35:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,8 @@ QueryDesc * CreateQueryDesc(Query *parsetree, Plan *plantree, - CommandDest dest) + CommandDest dest, + const char *portalName) { QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc)); @@ -38,6 +39,9 @@ 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->tupDesc = NULL; /* until set by ExecutorStart */ + return qd; } @@ -138,8 +142,7 @@ ProcessQuery(Query *parsetree, char *completionTag) { int operation = parsetree->commandType; - bool isRetrieveIntoPortal; - bool isRetrieveIntoRelation; + bool isRetrieveIntoPortal = false; char *intoName = NULL; Portal portal = NULL; MemoryContext oldContext = NULL; @@ -148,31 +151,28 @@ ProcessQuery(Query *parsetree, TupleDesc attinfo; /* - * initialize portal/into relation status + * Check for special-case destinations */ - isRetrieveIntoPortal = false; - isRetrieveIntoRelation = false; - if (operation == CMD_SELECT) { if (parsetree->isPortal) { isRetrieveIntoPortal = true; intoName = parsetree->into; - if (parsetree->isBinary) - { - /* - * For internal format portals, we change Remote - * (externalized form) to RemoteInternal (internalized - * form) - */ + /* If binary portal, switch to alternate output format */ + if (dest == Remote && parsetree->isBinary) dest = RemoteInternal; - } } else if (parsetree->into != NULL) { - /* select into table */ - isRetrieveIntoRelation = true; + /* + * SELECT INTO table (a/k/a CREATE AS ... SELECT). + * + * Override the normal communication destination; execMain.c + * special-cases this case. (Perhaps would be cleaner to + * have an additional destination type?) + */ + dest = None; } } @@ -197,16 +197,7 @@ ProcessQuery(Query *parsetree, /* * Now we can create the QueryDesc object. */ - queryDesc = CreateQueryDesc(parsetree, plan, dest); - - /* - * When performing a retrieve into, we override the normal - * communication destination during the processing of the the query. - * This only affects the tuple-output function - the correct - * destination will still see the BeginCommand() call. - */ - if (isRetrieveIntoRelation) - queryDesc->dest = None; + queryDesc = CreateQueryDesc(parsetree, plan, dest, intoName); /* * create a default executor state. @@ -218,18 +209,6 @@ ProcessQuery(Query *parsetree, */ attinfo = ExecutorStart(queryDesc, state); - /* - * report the query's result type information back to the front end or - * to whatever destination we're dealing with. - */ - BeginCommand(NULL, - operation, - attinfo, - isRetrieveIntoRelation, - isRetrieveIntoPortal, - NULL, /* not used */ - dest); - /* * If retrieve into portal, stop now; we do not run the plan until a * FETCH command is received. @@ -256,7 +235,7 @@ ProcessQuery(Query *parsetree, * Now we get to the important call to ExecutorRun() where we actually * run the plan.. */ - ExecutorRun(queryDesc, state, EXEC_RUN, 0L); + ExecutorRun(queryDesc, state, ForwardScanDirection, 0L); /* * Build command completion status string, if caller wants one. diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 7682752922..7b04a0e036 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.45 2002/02/14 15:24:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.46 2002/02/27 19:35:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,8 +22,8 @@ * sees a * fetch 1 from FOO * the system looks up the portal named "FOO" in the portal table, - * gets the planned query and then calls the executor with a feature of - * '(EXEC_FOR 1). The executor then runs the query and returns a single + * gets the planned query and then calls the executor with a count + * of 1. The executor then runs the query and returns a single * tuple. The problem is that we have to hold onto the state of the * portal query until we see a "close". This means we have to be * careful about memory management. diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h index ff775eb269..c1d8db17ad 100644 --- a/src/include/access/printtup.h +++ b/src/include/access/printtup.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: printtup.h,v 1.18 2001/11/05 17:46:31 momjian Exp $ + * $Id: printtup.h,v 1.19 2002/02/27 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,9 +18,10 @@ extern DestReceiver *printtup_create_DR(bool isBinary); -extern void showatts(char *name, TupleDesc attinfo); +extern void debugSetup(DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo); extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, - DestReceiver *self); + DestReceiver *self); /* XXX this one is really in executor/spi.c */ extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc, diff --git a/src/include/executor/execdefs.h b/src/include/executor/execdefs.h index b6e7195771..bdb4dce13c 100644 --- a/src/include/executor/execdefs.h +++ b/src/include/executor/execdefs.h @@ -7,30 +7,13 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execdefs.h,v 1.11 2001/11/05 17:46:33 momjian Exp $ + * $Id: execdefs.h,v 1.12 2002/02/27 19:35:51 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef EXECDEFS_H #define EXECDEFS_H -/* ---------------- - * ExecutePlan() tuplecount definitions - * ---------------- - */ -#define ALL_TUPLES 0 /* return all tuples */ -#define ONE_TUPLE 1 /* return only one tuple */ - -/* ---------------- - * constants used by ExecMain - * ---------------- - */ -#define EXEC_RUN 3 -#define EXEC_FOR 4 -#define EXEC_BACK 5 -#define EXEC_RETONE 6 -#define EXEC_RESULT 7 - /* ---------------- * Merge Join states * ---------------- diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h index 55bf52ed38..87915e9c07 100644 --- a/src/include/executor/execdesc.h +++ b/src/include/executor/execdesc.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execdesc.h,v 1.17 2001/11/05 17:46:33 momjian Exp $ + * $Id: execdesc.h,v 1.18 2002/02/27 19:35:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "nodes/plannodes.h" #include "tcop/dest.h" + /* ---------------- * query descriptor: * a QueryDesc encapsulates everything that the executor @@ -31,10 +32,14 @@ typedef struct QueryDesc Query *parsetree; Plan *plantree; CommandDest dest; /* the destination output of the execution */ + const char *portalName; /* name of portal, or NULL */ + + TupleDesc tupDesc; /* set by ExecutorStart */ } QueryDesc; /* in pquery.c */ extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree, - CommandDest dest); + CommandDest dest, const char *portalName); + #endif /* EXECDESC_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index c089a47731..63bc10f79d 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.62 2002/02/19 20:11:19 tgl Exp $ + * $Id: executor.h,v 1.63 2002/02/27 19:35:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,7 +50,7 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot); */ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate); extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, - int feature, long count); + ScanDirection direction, long count); extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate); extern void ExecConstraints(char *caller, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h index d5ac420ce6..c96133759d 100644 --- a/src/include/tcop/dest.h +++ b/src/include/tcop/dest.h @@ -1,11 +1,13 @@ /*------------------------------------------------------------------------- * * dest.h - * Whenever the backend executes a query, the results - * have to go someplace. + * support for communication destinations + * + * Whenever the backend executes a query, the results + * have to go someplace. * * - stdout is the destination only when we are running a - * backend without a postmaster and are returning results + * standalone backend (no postmaster) and are returning results * back to an interactive user. * * - a remote process is the destination when we are @@ -14,15 +16,21 @@ * to the frontend via the functions in backend/libpq. * * - None is the destination when the system executes - * a query internally. This is not used now but it may be - * useful for the parallel optimiser/executor. + * a query internally. The results are discarded. * * dest.c defines three functions that implement destination management: * - * BeginCommand: initialize the destination. + * BeginCommand: initialize the destination at start of command. * DestToFunction: return a pointer to a struct of destination-specific * receiver functions. - * EndCommand: clean up the destination when output is complete. + * EndCommand: clean up the destination at end of command. + * + * BeginCommand/EndCommand are executed once per received SQL query. + * + * DestToFunction, and the receiver functions it links to, are executed + * each time we run the executor to produce tuples, which may occur + * multiple times per received query (eg, due to additional queries produced + * by rewrite rules). * * The DestReceiver object returned by DestToFunction may be a statically * allocated object (for destination types that require no local state) @@ -32,14 +40,11 @@ * by casting the DestReceiver* pointer passed to them. * The palloc'd object is pfree'd by the DestReceiver's cleanup function. * - * XXX FIXME: the initialization and cleanup code that currently appears - * in-line in BeginCommand and EndCommand probably should be moved out - * to routines associated with each destination receiver type. * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: dest.h,v 1.29 2002/02/26 22:47:11 tgl Exp $ + * $Id: dest.h,v 1.30 2002/02/27 19:36:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -80,18 +85,17 @@ struct _DestReceiver { /* Called for each tuple to be output: */ void (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo, - DestReceiver *self); + DestReceiver *self); /* Initialization and teardown: */ - void (*setup) (DestReceiver *self, TupleDesc typeinfo); + void (*setup) (DestReceiver *self, int operation, + const char *portalName, TupleDesc typeinfo); void (*cleanup) (DestReceiver *self); /* Private fields might appear beyond this point... */ }; /* The primary destination management functions */ -extern void BeginCommand(char *pname, int operation, TupleDesc attinfo, - bool isIntoRel, bool isIntoPortal, char *tag, - CommandDest dest); +extern void BeginCommand(const char *commandTag, CommandDest dest); extern DestReceiver *DestToFunction(CommandDest dest); extern void EndCommand(const char *commandTag, CommandDest dest);