mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
221 lines
5.4 KiB
C
221 lines
5.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* tstoreReceiver.c
|
|
* An implementation of DestReceiver that stores the result tuples in
|
|
* a Tuplestore.
|
|
*
|
|
* Optionally, we can force detoasting (but not decompression) of out-of-line
|
|
* toasted values. This is to support cursors WITH HOLD, which must retain
|
|
* data even if the underlying table is dropped.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/executor/tstoreReceiver.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/tuptoaster.h"
|
|
#include "executor/tstoreReceiver.h"
|
|
|
|
|
|
typedef struct
|
|
{
|
|
DestReceiver pub;
|
|
/* parameters: */
|
|
Tuplestorestate *tstore; /* where to put the data */
|
|
MemoryContext cxt; /* context containing tstore */
|
|
bool detoast; /* were we told to detoast? */
|
|
/* workspace: */
|
|
Datum *outvalues; /* values array for result tuple */
|
|
Datum *tofree; /* temp values to be pfree'd */
|
|
} TStoreState;
|
|
|
|
|
|
static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
|
|
static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
|
|
|
|
|
|
/*
|
|
* Prepare to receive tuples from executor.
|
|
*/
|
|
static void
|
|
tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|
{
|
|
TStoreState *myState = (TStoreState *) self;
|
|
bool needtoast = false;
|
|
int natts = typeinfo->natts;
|
|
int i;
|
|
|
|
/* Check if any columns require detoast work */
|
|
if (myState->detoast)
|
|
{
|
|
for (i = 0; i < natts; i++)
|
|
{
|
|
Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
|
|
|
|
if (attr->attisdropped)
|
|
continue;
|
|
if (attr->attlen == -1)
|
|
{
|
|
needtoast = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set up appropriate callback */
|
|
if (needtoast)
|
|
{
|
|
myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
|
|
/* Create workspace */
|
|
myState->outvalues = (Datum *)
|
|
MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
|
|
myState->tofree = (Datum *)
|
|
MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
|
|
}
|
|
else
|
|
{
|
|
myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
|
|
myState->outvalues = NULL;
|
|
myState->tofree = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Receive a tuple from the executor and store it in the tuplestore.
|
|
* This is for the easy case where we don't have to detoast.
|
|
*/
|
|
static bool
|
|
tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
|
|
{
|
|
TStoreState *myState = (TStoreState *) self;
|
|
|
|
tuplestore_puttupleslot(myState->tstore, slot);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Receive a tuple from the executor and store it in the tuplestore.
|
|
* This is for the case where we have to detoast any toasted values.
|
|
*/
|
|
static bool
|
|
tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
|
|
{
|
|
TStoreState *myState = (TStoreState *) self;
|
|
TupleDesc typeinfo = slot->tts_tupleDescriptor;
|
|
int natts = typeinfo->natts;
|
|
int nfree;
|
|
int i;
|
|
MemoryContext oldcxt;
|
|
|
|
/* Make sure the tuple is fully deconstructed */
|
|
slot_getallattrs(slot);
|
|
|
|
/*
|
|
* Fetch back any out-of-line datums. We build the new datums array in
|
|
* myState->outvalues[] (but we can re-use the slot's isnull array). Also,
|
|
* remember the fetched values to free afterwards.
|
|
*/
|
|
nfree = 0;
|
|
for (i = 0; i < natts; i++)
|
|
{
|
|
Datum val = slot->tts_values[i];
|
|
Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
|
|
|
|
if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
|
|
{
|
|
if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
|
|
{
|
|
val = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
|
|
DatumGetPointer(val)));
|
|
myState->tofree[nfree++] = val;
|
|
}
|
|
}
|
|
|
|
myState->outvalues[i] = val;
|
|
}
|
|
|
|
/*
|
|
* Push the modified tuple into the tuplestore.
|
|
*/
|
|
oldcxt = MemoryContextSwitchTo(myState->cxt);
|
|
tuplestore_putvalues(myState->tstore, typeinfo,
|
|
myState->outvalues, slot->tts_isnull);
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
/* And release any temporary detoasted values */
|
|
for (i = 0; i < nfree; i++)
|
|
pfree(DatumGetPointer(myState->tofree[i]));
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Clean up at end of an executor run
|
|
*/
|
|
static void
|
|
tstoreShutdownReceiver(DestReceiver *self)
|
|
{
|
|
TStoreState *myState = (TStoreState *) self;
|
|
|
|
/* Release workspace if any */
|
|
if (myState->outvalues)
|
|
pfree(myState->outvalues);
|
|
myState->outvalues = NULL;
|
|
if (myState->tofree)
|
|
pfree(myState->tofree);
|
|
myState->tofree = NULL;
|
|
}
|
|
|
|
/*
|
|
* Destroy receiver when done with it
|
|
*/
|
|
static void
|
|
tstoreDestroyReceiver(DestReceiver *self)
|
|
{
|
|
pfree(self);
|
|
}
|
|
|
|
/*
|
|
* Initially create a DestReceiver object.
|
|
*/
|
|
DestReceiver *
|
|
CreateTuplestoreDestReceiver(void)
|
|
{
|
|
TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
|
|
|
|
self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
|
|
self->pub.rStartup = tstoreStartupReceiver;
|
|
self->pub.rShutdown = tstoreShutdownReceiver;
|
|
self->pub.rDestroy = tstoreDestroyReceiver;
|
|
self->pub.mydest = DestTuplestore;
|
|
|
|
/* private fields will be set by SetTuplestoreDestReceiverParams */
|
|
|
|
return (DestReceiver *) self;
|
|
}
|
|
|
|
/*
|
|
* Set parameters for a TuplestoreDestReceiver
|
|
*/
|
|
void
|
|
SetTuplestoreDestReceiverParams(DestReceiver *self,
|
|
Tuplestorestate *tStore,
|
|
MemoryContext tContext,
|
|
bool detoast)
|
|
{
|
|
TStoreState *myState = (TStoreState *) self;
|
|
|
|
Assert(myState->pub.mydest == DestTuplestore);
|
|
myState->tstore = tStore;
|
|
myState->cxt = tContext;
|
|
myState->detoast = detoast;
|
|
}
|