Commit 6779c55c authored by Tom Lane's avatar Tom Lane

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.
parent e22c9c44
/*-------------------------------------------------------------------------
*
* 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
......
......@@ -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.)
*
......
......@@ -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.
*/
......
......@@ -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.
......
......@@ -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
......
......@@ -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.
*/
......
/*-------------------------------------------------------------------------
*
* 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
......
......@@ -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");
}
/*
......
......@@ -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.
......
......@@ -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.
......
......@@ -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,
......
......@@ -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
* ----------------
......
......@@ -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 */
......@@ -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);
......
/*-------------------------------------------------------------------------
*
* 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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment