if (fcache->funcResultStore)
{
Assert(isDone); /* it was provided before ... */
- if (tuplestore_gettupleslot(fcache->funcResultStore, true,
+ if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
fcache->funcResultSlot))
{
*isDone = ExprMultipleResult;
/* Re-use the junkfilter's output slot to fetch back the tuple */
Assert(fcache->junkFilter);
slot = fcache->junkFilter->jf_resultSlot;
- if (!tuplestore_gettupleslot(fcache->tstore, true, slot))
+ if (!tuplestore_gettupleslot(fcache->tstore, true, false, slot))
elog(ERROR, "failed to fetch lazy-eval tuple");
/* Extract the result as a datum, and copy out from the slot */
result = postquel_get_single_result(slot, fcinfo,
{
/* Re-use the junkfilter's output slot to fetch back the tuple */
slot = fcache->junkFilter->jf_resultSlot;
- if (tuplestore_gettupleslot(fcache->tstore, true, slot))
+ if (tuplestore_gettupleslot(fcache->tstore, true, false, slot))
result = postquel_get_single_result(slot, fcinfo,
fcache, oldcontext);
else
/*
* If we can fetch another tuple from the tuplestore, return it.
+ *
+ * Note: we have to use copy=true in the tuplestore_gettupleslot call,
+ * because we are sharing the tuplestore with other nodes that might
+ * write into the tuplestore before we get called again.
*/
if (!eof_tuplestore)
{
- if (tuplestore_gettupleslot(tuplestorestate, forward, slot))
+ if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
return slot;
if (forward)
eof_tuplestore = true;
slot = node->ss.ss_ScanTupleSlot;
(void) tuplestore_gettupleslot(tuplestorestate,
ScanDirectionIsForward(direction),
+ false,
slot);
return slot;
}
slot = node->ss.ps.ps_ResultTupleSlot;
if (!eof_tuplestore)
{
- if (tuplestore_gettupleslot(tuplestorestate, forward, slot))
+ if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))
return slot;
if (forward)
eof_tuplestore = true;
spool_tuples(winstate, winstate->aggregatedupto);
tuplestore_select_read_pointer(winstate->buffer,
winstate->agg_ptr);
- if (!tuplestore_gettupleslot(winstate->buffer, true, agg_row_slot))
+ if (!tuplestore_gettupleslot(winstate->buffer, true, true,
+ agg_row_slot))
break; /* must be end of partition */
}
/*
* Read the current row from the tuplestore, and save in ScanTupleSlot.
* (We can't rely on the outerplan's output slot because we may have to
- * read beyond the current row.)
+ * read beyond the current row. Also, we have to actually copy the row
+ * out of the tuplestore, since window function evaluation might cause
+ * the tuplestore to dump its state to disk.)
*
* Current row must be in the tuplestore, since we spooled it above.
*/
tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
- if (!tuplestore_gettupleslot(winstate->buffer, true,
+ if (!tuplestore_gettupleslot(winstate->buffer, true, true,
winstate->ss.ss_ScanTupleSlot))
elog(ERROR, "unexpected end of tuplestore");
while (winobj->seekpos > pos)
{
- if (!tuplestore_gettupleslot(winstate->buffer, false, slot))
+ if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
elog(ERROR, "unexpected end of tuplestore");
winobj->seekpos--;
}
while (winobj->seekpos < pos)
{
- if (!tuplestore_gettupleslot(winstate->buffer, true, slot))
+ if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
elog(ERROR, "unexpected end of tuplestore");
winobj->seekpos++;
}
* worktable plan node, since it cannot appear high enough in the plan
* tree of a scrollable cursor to be exposed to a backward-scan
* requirement. So it's not worth expending effort to support it.
+ *
+ * Note: we are also assuming that this node is the only reader of the
+ * worktable. Therefore, we don't need a private read pointer for the
+ * tuplestore, nor do we need to tell tuplestore_gettupleslot to copy.
*/
estate = node->ss.ps.state;
Assert(ScanDirectionIsForward(estate->es_direction));
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
- (void) tuplestore_gettupleslot(tuplestorestate, true, slot);
+ (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot);
return slot;
}
oldcontext = MemoryContextSwitchTo(portal->holdContext);
- ok = tuplestore_gettupleslot(portal->holdStore, forward, slot);
+ ok = tuplestore_gettupleslot(portal->holdStore, forward, false,
+ slot);
MemoryContextSwitchTo(oldcontext);
*
* If successful, put tuple in slot and return TRUE; else, clear the slot
* and return FALSE.
+ *
+ * If copy is TRUE, the slot receives a copied tuple (allocated in current
+ * memory context) that will stay valid regardless of future manipulations of
+ * the tuplestore's state. If copy is FALSE, the slot may just receive a
+ * pointer to a tuple held within the tuplestore. The latter is more
+ * efficient but the slot contents may be corrupted if additional writes to
+ * the tuplestore occur. (If using tuplestore_trim, see comments therein.)
*/
bool
tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
- TupleTableSlot *slot)
+ bool copy, TupleTableSlot *slot)
{
MinimalTuple tuple;
bool should_free;
if (tuple)
{
+ if (copy && !should_free)
+ {
+ tuple = heap_copy_minimal_tuple(tuple);
+ should_free = true;
+ }
ExecStoreMinimalTuple(tuple, slot, should_free);
return true;
}
* since tuplestore_gettuple returns a direct pointer to our
* internal copy of the tuple, it's likely that the caller has
* still got the tuple just before "current" referenced in a slot.
- * So we keep one extra tuple before the oldest "current".
+ * So we keep one extra tuple before the oldest "current". (Strictly
+ * speaking, we could require such callers to use the "copy" flag to
+ * tuplestore_gettupleslot, but for efficiency we allow this one case
+ * to not use "copy".)
*/
nremove = oldest - 1;
if (nremove <= 0)
extern bool tuplestore_in_memory(Tuplestorestate *state);
extern bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
- TupleTableSlot *slot);
+ bool copy, TupleTableSlot *slot);
extern bool tuplestore_advance(Tuplestorestate *state, bool forward);
extern bool tuplestore_ateof(Tuplestorestate *state);