mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Restructure command destination handling so that we pass around
DestReceiver pointers instead of just CommandDest values. The DestReceiver is made at the point where the destination is selected, rather than deep inside the executor. This cleans up the original kluge implementation of tstoreReceiver.c, and makes it easy to support retrieving results from utility statements inside portals. Thus, you can now do fun things like Bind and Execute a FETCH or EXPLAIN command, and it'll all work as expected (e.g., you can Describe the portal, or use Execute's count parameter to suspend the output partway through). Implementation involves stuffing the utility command's output into a Tuplestore, which would be kind of annoying for huge output sets, but should be quite acceptable for typical uses of utility commands.
This commit is contained in:
@ -26,7 +26,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.207 2003/05/06 00:20:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.208 2003/05/06 20:26:26 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -72,9 +72,9 @@ static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate,
|
||||
CmdType operation,
|
||||
long numberTuples,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc);
|
||||
DestReceiver *dest);
|
||||
static void ExecSelect(TupleTableSlot *slot,
|
||||
DestReceiver *destfunc,
|
||||
DestReceiver *dest,
|
||||
EState *estate);
|
||||
static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
EState *estate);
|
||||
@ -188,8 +188,7 @@ ExecutorRun(QueryDesc *queryDesc,
|
||||
{
|
||||
EState *estate;
|
||||
CmdType operation;
|
||||
CommandDest dest;
|
||||
DestReceiver *destfunc;
|
||||
DestReceiver *dest;
|
||||
TupleTableSlot *result;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
@ -218,11 +217,10 @@ ExecutorRun(QueryDesc *queryDesc,
|
||||
estate->es_processed = 0;
|
||||
estate->es_lastoid = InvalidOid;
|
||||
|
||||
destfunc = DestToFunction(dest);
|
||||
(*destfunc->setup) (destfunc, operation,
|
||||
queryDesc->portalName,
|
||||
queryDesc->tupDesc,
|
||||
queryDesc->planstate->plan->targetlist);
|
||||
(*dest->startup) (dest, operation,
|
||||
queryDesc->portalName,
|
||||
queryDesc->tupDesc,
|
||||
queryDesc->planstate->plan->targetlist);
|
||||
|
||||
/*
|
||||
* run plan
|
||||
@ -235,12 +233,12 @@ ExecutorRun(QueryDesc *queryDesc,
|
||||
operation,
|
||||
count,
|
||||
direction,
|
||||
destfunc);
|
||||
dest);
|
||||
|
||||
/*
|
||||
* shutdown receiver
|
||||
*/
|
||||
(*destfunc->cleanup) (destfunc);
|
||||
(*dest->shutdown) (dest);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
@ -962,7 +960,7 @@ ExecutePlan(EState *estate,
|
||||
CmdType operation,
|
||||
long numberTuples,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc)
|
||||
DestReceiver *dest)
|
||||
{
|
||||
JunkFilter *junkfilter;
|
||||
TupleTableSlot *slot;
|
||||
@ -1162,8 +1160,7 @@ lnext: ;
|
||||
{
|
||||
case CMD_SELECT:
|
||||
ExecSelect(slot, /* slot containing tuple */
|
||||
destfunc, /* destination's tuple-receiver
|
||||
* obj */
|
||||
dest, /* destination's tuple-receiver obj */
|
||||
estate);
|
||||
result = slot;
|
||||
break;
|
||||
@ -1237,7 +1234,7 @@ lnext: ;
|
||||
*/
|
||||
static void
|
||||
ExecSelect(TupleTableSlot *slot,
|
||||
DestReceiver *destfunc,
|
||||
DestReceiver *dest,
|
||||
EState *estate)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
@ -1251,6 +1248,8 @@ ExecSelect(TupleTableSlot *slot,
|
||||
|
||||
/*
|
||||
* insert the tuple into the "into relation"
|
||||
*
|
||||
* XXX this probably ought to be replaced by a separate destination
|
||||
*/
|
||||
if (estate->es_into_relation_descriptor != NULL)
|
||||
{
|
||||
@ -1260,9 +1259,9 @@ ExecSelect(TupleTableSlot *slot,
|
||||
}
|
||||
|
||||
/*
|
||||
* send the tuple to the front end (or the screen)
|
||||
* send the tuple to the destination
|
||||
*/
|
||||
(*destfunc->receiveTuple) (tuple, attrtype, destfunc);
|
||||
(*dest->receiveTuple) (tuple, attrtype, dest);
|
||||
IncrRetrieved();
|
||||
(estate->es_processed)++;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.65 2003/05/06 20:26:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -591,6 +591,49 @@ ExecTypeFromTL(List *targetList, bool hasoid)
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecCleanTypeFromTL
|
||||
*
|
||||
* Same as above, but resjunk columns are omitted from the result.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
ExecCleanTypeFromTL(List *targetList, bool hasoid)
|
||||
{
|
||||
TupleDesc typeInfo;
|
||||
List *tlitem;
|
||||
int len;
|
||||
int cleanresno;
|
||||
|
||||
/*
|
||||
* allocate a new typeInfo
|
||||
*/
|
||||
len = ExecCleanTargetListLength(targetList);
|
||||
typeInfo = CreateTemplateTupleDesc(len, hasoid);
|
||||
|
||||
/*
|
||||
* scan list, generate type info for each entry
|
||||
*/
|
||||
cleanresno = 1;
|
||||
foreach(tlitem, targetList)
|
||||
{
|
||||
TargetEntry *tle = lfirst(tlitem);
|
||||
Resdom *resdom = tle->resdom;
|
||||
|
||||
if (resdom->resjunk)
|
||||
continue;
|
||||
TupleDescInitEntry(typeInfo,
|
||||
cleanresno++,
|
||||
resdom->resname,
|
||||
resdom->restype,
|
||||
resdom->restypmod,
|
||||
0,
|
||||
false);
|
||||
}
|
||||
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
|
||||
*/
|
||||
@ -713,17 +756,17 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
|
||||
* Table Function capability. Currently used by EXPLAIN and SHOW ALL
|
||||
*/
|
||||
TupOutputState *
|
||||
begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc)
|
||||
begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
|
||||
{
|
||||
TupOutputState *tstate;
|
||||
|
||||
tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
|
||||
|
||||
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
|
||||
tstate->destfunc = DestToFunction(dest);
|
||||
tstate->dest = dest;
|
||||
|
||||
(*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
|
||||
NULL, tupdesc, NIL);
|
||||
(*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT,
|
||||
NULL, tupdesc, NIL);
|
||||
|
||||
return tstate;
|
||||
}
|
||||
@ -741,9 +784,9 @@ do_tup_output(TupOutputState *tstate, char **values)
|
||||
HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values);
|
||||
|
||||
/* send the tuple to the receiver */
|
||||
(*tstate->destfunc->receiveTuple) (tuple,
|
||||
tstate->metadata->tupdesc,
|
||||
tstate->destfunc);
|
||||
(*tstate->dest->receiveTuple) (tuple,
|
||||
tstate->metadata->tupdesc,
|
||||
tstate->dest);
|
||||
/* clean up */
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
@ -766,7 +809,7 @@ do_text_output_multiline(TupOutputState *tstate, char *text)
|
||||
if (eol)
|
||||
*eol++ = '\0';
|
||||
else
|
||||
eol = text +strlen(text);
|
||||
eol = text + strlen(text);
|
||||
|
||||
do_tup_output(tstate, &text);
|
||||
text = eol;
|
||||
@ -776,7 +819,8 @@ do_text_output_multiline(TupOutputState *tstate, char *text)
|
||||
void
|
||||
end_tup_output(TupOutputState *tstate)
|
||||
{
|
||||
(*tstate->destfunc->cleanup) (tstate->destfunc);
|
||||
(*tstate->dest->shutdown) (tstate->dest);
|
||||
/* note that destroying the dest is not ours to do */
|
||||
/* XXX worth cleaning up the attinmetadata? */
|
||||
pfree(tstate);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.63 2003/05/06 00:20:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.64 2003/05/06 20:26:27 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, NULL,
|
||||
None_Receiver, NULL,
|
||||
fcache->paramLI, false);
|
||||
|
||||
/* Utility commands don't need Executor. */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.95 2003/05/06 00:20:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.96 2003/05/06 20:26:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -38,7 +38,7 @@ static int _SPI_execute_plan(_SPI_plan *plan,
|
||||
Datum *Values, const char *Nulls, int tcount);
|
||||
|
||||
static void _SPI_cursor_operation(Portal portal, bool forward, int count,
|
||||
CommandDest dest);
|
||||
DestReceiver *dest);
|
||||
|
||||
static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
|
||||
|
||||
@ -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, SPI);
|
||||
_SPI_cursor_operation(portal, forward, count, CreateDestReceiver(SPI));
|
||||
/* we know that the SPI receiver doesn't need a destroy call */
|
||||
}
|
||||
|
||||
|
||||
@ -853,7 +854,7 @@ SPI_cursor_fetch(Portal portal, bool forward, int count)
|
||||
void
|
||||
SPI_cursor_move(Portal portal, bool forward, int count)
|
||||
{
|
||||
_SPI_cursor_operation(portal, forward, count, None);
|
||||
_SPI_cursor_operation(portal, forward, count, None_Receiver);
|
||||
}
|
||||
|
||||
|
||||
@ -874,13 +875,13 @@ SPI_cursor_close(Portal portal)
|
||||
/* =================== private functions =================== */
|
||||
|
||||
/*
|
||||
* spi_dest_setup
|
||||
* spi_dest_startup
|
||||
* Initialize to receive tuples from Executor into SPITupleTable
|
||||
* of current SPI procedure
|
||||
*/
|
||||
void
|
||||
spi_dest_setup(DestReceiver *self, int operation,
|
||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
||||
spi_dest_startup(DestReceiver *self, int operation,
|
||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
||||
{
|
||||
SPITupleTable *tuptable;
|
||||
MemoryContext oldcxt;
|
||||
@ -891,12 +892,12 @@ spi_dest_setup(DestReceiver *self, int operation,
|
||||
* _SPI_connected
|
||||
*/
|
||||
if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
|
||||
elog(FATAL, "SPI: improper call to spi_dest_setup");
|
||||
elog(FATAL, "SPI: improper call to spi_dest_startup");
|
||||
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
|
||||
elog(FATAL, "SPI: stack corrupted in spi_dest_setup");
|
||||
elog(FATAL, "SPI: stack corrupted in spi_dest_startup");
|
||||
|
||||
if (_SPI_current->tuptable != NULL)
|
||||
elog(FATAL, "SPI: improper call to spi_dest_setup");
|
||||
elog(FATAL, "SPI: improper call to spi_dest_startup");
|
||||
|
||||
oldcxt = _SPI_procmem(); /* switch to procedure memory context */
|
||||
|
||||
@ -1029,10 +1030,12 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
||||
Query *queryTree = (Query *) lfirst(query_list_item);
|
||||
Plan *planTree;
|
||||
QueryDesc *qdesc;
|
||||
DestReceiver *dest;
|
||||
|
||||
planTree = pg_plan_query(queryTree);
|
||||
plan_list = lappend(plan_list, planTree);
|
||||
|
||||
dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
|
||||
if (queryTree->commandType == CMD_UTILITY)
|
||||
{
|
||||
if (IsA(queryTree->utilityStmt, CopyStmt))
|
||||
@ -1051,14 +1054,13 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
||||
res = SPI_OK_UTILITY;
|
||||
if (plan == NULL)
|
||||
{
|
||||
ProcessUtility(queryTree->utilityStmt, None, NULL);
|
||||
ProcessUtility(queryTree->utilityStmt, dest, NULL);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
}
|
||||
else if (plan == NULL)
|
||||
{
|
||||
qdesc = CreateQueryDesc(queryTree, planTree,
|
||||
queryTree->canSetTag ? SPI : None,
|
||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||
NULL, NULL, false);
|
||||
res = _SPI_pquery(qdesc, true,
|
||||
queryTree->canSetTag ? tcount : 0);
|
||||
@ -1068,8 +1070,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
||||
}
|
||||
else
|
||||
{
|
||||
qdesc = CreateQueryDesc(queryTree, planTree,
|
||||
queryTree->canSetTag ? SPI : None,
|
||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||
NULL, NULL, false);
|
||||
res = _SPI_pquery(qdesc, false, 0);
|
||||
if (res < 0)
|
||||
@ -1144,20 +1145,21 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||
Query *queryTree = (Query *) lfirst(query_list_item);
|
||||
Plan *planTree;
|
||||
QueryDesc *qdesc;
|
||||
DestReceiver *dest;
|
||||
|
||||
planTree = lfirst(plan_list);
|
||||
plan_list = lnext(plan_list);
|
||||
|
||||
dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
|
||||
if (queryTree->commandType == CMD_UTILITY)
|
||||
{
|
||||
ProcessUtility(queryTree->utilityStmt, None, NULL);
|
||||
ProcessUtility(queryTree->utilityStmt, dest, NULL);
|
||||
res = SPI_OK_UTILITY;
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
else
|
||||
{
|
||||
qdesc = CreateQueryDesc(queryTree, planTree,
|
||||
queryTree->canSetTag ? SPI : None,
|
||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||
NULL, paramLI, false);
|
||||
res = _SPI_pquery(qdesc, true,
|
||||
queryTree->canSetTag ? tcount : 0);
|
||||
@ -1185,7 +1187,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
|
||||
if (queryDesc->parsetree->into != NULL) /* select into table */
|
||||
{
|
||||
res = SPI_OK_SELINTO;
|
||||
queryDesc->dest = None; /* don't output results anywhere */
|
||||
queryDesc->dest = None_Receiver; /* don't output results */
|
||||
}
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
@ -1216,13 +1218,13 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
|
||||
_SPI_current->processed = queryDesc->estate->es_processed;
|
||||
save_lastoid = queryDesc->estate->es_lastoid;
|
||||
|
||||
if (operation == CMD_SELECT && queryDesc->dest == SPI)
|
||||
if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
|
||||
{
|
||||
if (_SPI_checktuples())
|
||||
elog(FATAL, "SPI_select: # of processed tuples check failed");
|
||||
}
|
||||
|
||||
if (queryDesc->dest == SPI)
|
||||
if (queryDesc->dest->mydest == SPI)
|
||||
{
|
||||
SPI_processed = _SPI_current->processed;
|
||||
SPI_lastoid = save_lastoid;
|
||||
@ -1253,7 +1255,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
|
||||
*/
|
||||
static void
|
||||
_SPI_cursor_operation(Portal portal, bool forward, int count,
|
||||
CommandDest dest)
|
||||
DestReceiver *dest)
|
||||
{
|
||||
/* Check that the portal is valid */
|
||||
if (!PortalIsValid(portal))
|
||||
@ -1275,7 +1277,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
|
||||
(long) count,
|
||||
dest);
|
||||
|
||||
if (dest == SPI && _SPI_checktuples())
|
||||
if (dest->mydest == SPI && _SPI_checktuples())
|
||||
elog(FATAL, "SPI_fetch: # of processed tuples check failed");
|
||||
|
||||
/* Put the result into place for access by caller */
|
||||
@ -1343,7 +1345,7 @@ _SPI_checktuples(void)
|
||||
SPITupleTable *tuptable = _SPI_current->tuptable;
|
||||
bool failed = false;
|
||||
|
||||
if (tuptable == NULL) /* spi_dest_setup was not called */
|
||||
if (tuptable == NULL) /* spi_dest_startup was not called */
|
||||
failed = true;
|
||||
else if (processed != (tuptable->alloced - tuptable->free))
|
||||
failed = true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tstore_receiver.c
|
||||
* tstoreReceiver.c
|
||||
* an implementation of DestReceiver that stores the result tuples in
|
||||
* a Tuplestore
|
||||
*
|
||||
@ -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.4 2003/05/06 00:20:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.5 2003/05/06 20:26:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,8 +17,7 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/tstoreReceiver.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/portal.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -30,32 +29,13 @@ typedef struct
|
||||
|
||||
/*
|
||||
* Prepare to receive tuples from executor.
|
||||
*
|
||||
* XXX: As currently implemented, this routine is a hack: there should
|
||||
* be no tie between this code and the portal system. Instead, the
|
||||
* receiver function that is part of DestFunction should be passed a
|
||||
* QueryDesc, so that the call site of ExecutorRun can "sub-class"
|
||||
* QueryDesc and pass in any necessary addition information (in this
|
||||
* case, the Tuplestore to use).
|
||||
*/
|
||||
static void
|
||||
tstoreSetupReceiver(DestReceiver *self, int operation,
|
||||
const char *portalname,
|
||||
TupleDesc typeinfo, List *targetlist)
|
||||
tstoreStartupReceiver(DestReceiver *self, int operation,
|
||||
const char *portalname,
|
||||
TupleDesc typeinfo, List *targetlist)
|
||||
{
|
||||
TStoreState *myState = (TStoreState *) self;
|
||||
|
||||
/* Should only be called within a suitably-prepped portal */
|
||||
if (CurrentPortal == NULL ||
|
||||
CurrentPortal->holdStore == NULL)
|
||||
elog(ERROR, "Tuplestore destination used in wrong context");
|
||||
|
||||
/* Debug check: make sure portal's result tuple desc is correct */
|
||||
Assert(CurrentPortal->tupDesc != NULL);
|
||||
Assert(equalTupleDescs(CurrentPortal->tupDesc, typeinfo));
|
||||
|
||||
myState->tstore = CurrentPortal->holdStore;
|
||||
myState->cxt = CurrentPortal->holdContext;
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -73,28 +53,40 @@ tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up
|
||||
* Clean up at end of an executor run
|
||||
*/
|
||||
static void
|
||||
tstoreCleanupReceiver(DestReceiver *self)
|
||||
tstoreShutdownReceiver(DestReceiver *self)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy receiver when done with it
|
||||
*/
|
||||
static void
|
||||
tstoreDestroyReceiver(DestReceiver *self)
|
||||
{
|
||||
pfree(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initially create a DestReceiver object.
|
||||
*/
|
||||
DestReceiver *
|
||||
tstoreReceiverCreateDR(void)
|
||||
CreateTuplestoreDestReceiver(Tuplestorestate *tStore,
|
||||
MemoryContext tContext)
|
||||
{
|
||||
TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState));
|
||||
|
||||
self->pub.receiveTuple = tstoreReceiveTuple;
|
||||
self->pub.setup = tstoreSetupReceiver;
|
||||
self->pub.cleanup = tstoreCleanupReceiver;
|
||||
self->pub.startup = tstoreStartupReceiver;
|
||||
self->pub.shutdown = tstoreShutdownReceiver;
|
||||
self->pub.destroy = tstoreDestroyReceiver;
|
||||
self->pub.mydest = Tuplestore;
|
||||
|
||||
self->tstore = NULL;
|
||||
self->cxt = NULL;
|
||||
self->tstore = tStore;
|
||||
self->cxt = tContext;
|
||||
|
||||
return (DestReceiver *) self;
|
||||
}
|
||||
|
Reference in New Issue
Block a user