mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Make TupleTableSlots extensible, finish split of existing slot type.
This commit completes the work prepared in 1a0586de36
, splitting the
old TupleTableSlot implementation (which could store buffer, heap,
minimal and virtual slots) into four different slot types. As
described in the aforementioned commit, this is done with the goal of
making tuple table slots extensible, to allow for pluggable table
access methods.
To achieve runtime extensibility for TupleTableSlots, operations on
slots that can differ between types of slots are performed using the
TupleTableSlotOps struct provided at slot creation time. That
includes information from the size of TupleTableSlot struct to be
allocated, initialization, deforming etc. See the struct's definition
for more detailed information about callbacks TupleTableSlotOps.
I decided to rename TTSOpsBufferTuple to TTSOpsBufferHeapTuple and
ExecCopySlotTuple to ExecCopySlotHeapTuple, as that seems more
consistent with other naming introduced in recent patches.
There's plenty optimization potential in the slot implementation, but
according to benchmarking the state after this commit has similar
performance characteristics to before this set of changes, which seems
sufficient.
There's a few changes in execReplication.c that currently need to poke
through the slot abstraction, that'll be repaired once the pluggable
storage patchset provides the necessary infrastructure.
Author: Andres Freund and Ashutosh Bapat, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
This commit is contained in:
@ -218,27 +218,25 @@ execCurrentOf(CurrentOfExpr *cexpr,
|
||||
ItemPointer tuple_tid;
|
||||
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
|
||||
TableOidAttributeNumber,
|
||||
&ldatum,
|
||||
&lisnull))
|
||||
ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
|
||||
TableOidAttributeNumber,
|
||||
&lisnull);
|
||||
if (lisnull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_STATE),
|
||||
errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
|
||||
cursor_name, table_name)));
|
||||
Assert(!lisnull);
|
||||
Assert(DatumGetObjectId(ldatum) == table_oid);
|
||||
#endif
|
||||
|
||||
if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
|
||||
SelfItemPointerAttributeNumber,
|
||||
&ldatum,
|
||||
&lisnull))
|
||||
ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
|
||||
SelfItemPointerAttributeNumber,
|
||||
&lisnull);
|
||||
if (lisnull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_STATE),
|
||||
errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
|
||||
cursor_name, table_name)));
|
||||
Assert(!lisnull);
|
||||
tuple_tid = (ItemPointer) DatumGetPointer(ldatum);
|
||||
|
||||
*current_tid = *tuple_tid;
|
||||
|
@ -1875,11 +1875,11 @@ CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot)
|
||||
* Should probably fixed at some point, but for now it's easier to allow
|
||||
* buffer and heap tuples to be used interchangably.
|
||||
*/
|
||||
if (slot->tts_ops == &TTSOpsBufferTuple &&
|
||||
if (slot->tts_ops == &TTSOpsBufferHeapTuple &&
|
||||
op->d.fetch.kind == &TTSOpsHeapTuple)
|
||||
return;
|
||||
if (slot->tts_ops == &TTSOpsHeapTuple &&
|
||||
op->d.fetch.kind == &TTSOpsBufferTuple)
|
||||
op->d.fetch.kind == &TTSOpsBufferHeapTuple)
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -4025,15 +4025,15 @@ void
|
||||
ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
|
||||
TupleTableSlot *slot)
|
||||
{
|
||||
bool success;
|
||||
Datum d;
|
||||
|
||||
/* slot_getsysattr has sufficient defenses against bad attnums */
|
||||
success = slot_getsysattr(slot,
|
||||
op->d.var.attnum,
|
||||
op->resvalue,
|
||||
op->resnull);
|
||||
d = slot_getsysattr(slot,
|
||||
op->d.var.attnum,
|
||||
op->resnull);
|
||||
*op->resvalue = d;
|
||||
/* this ought to be unreachable, but it's cheap enough to check */
|
||||
if (unlikely(!success))
|
||||
if (unlikely(*op->resnull))
|
||||
elog(ERROR, "failed to fetch attribute from slot");
|
||||
}
|
||||
|
||||
|
@ -170,8 +170,11 @@ retry:
|
||||
HeapUpdateFailureData hufd;
|
||||
HTSU_Result res;
|
||||
HeapTupleData locktup;
|
||||
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)outslot;
|
||||
|
||||
ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
|
||||
/* Only a heap tuple has item pointers. */
|
||||
Assert(TTS_IS_HEAPTUPLE(outslot) || TTS_IS_BUFFERTUPLE(outslot));
|
||||
ItemPointerCopy(&hslot->tuple->t_self, &locktup.t_self);
|
||||
|
||||
PushActiveSnapshot(GetLatestSnapshot());
|
||||
|
||||
@ -334,8 +337,11 @@ retry:
|
||||
HeapUpdateFailureData hufd;
|
||||
HTSU_Result res;
|
||||
HeapTupleData locktup;
|
||||
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)outslot;
|
||||
|
||||
ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
|
||||
/* Only a heap tuple has item pointers. */
|
||||
Assert(TTS_IS_HEAPTUPLE(outslot) || TTS_IS_BUFFERTUPLE(outslot));
|
||||
ItemPointerCopy(&hslot->tuple->t_self, &locktup.t_self);
|
||||
|
||||
PushActiveSnapshot(GetLatestSnapshot());
|
||||
|
||||
@ -456,6 +462,12 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
|
||||
HeapTuple tuple;
|
||||
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
|
||||
Relation rel = resultRelInfo->ri_RelationDesc;
|
||||
HeapTupleTableSlot *hsearchslot = (HeapTupleTableSlot *)searchslot;
|
||||
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)slot;
|
||||
|
||||
/* We expect both searchslot and the slot to contain a heap tuple. */
|
||||
Assert(TTS_IS_HEAPTUPLE(searchslot) || TTS_IS_BUFFERTUPLE(searchslot));
|
||||
Assert(TTS_IS_HEAPTUPLE(slot) || TTS_IS_BUFFERTUPLE(slot));
|
||||
|
||||
/* For now we support only tables. */
|
||||
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
|
||||
@ -467,8 +479,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
|
||||
resultRelInfo->ri_TrigDesc->trig_update_before_row)
|
||||
{
|
||||
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
|
||||
&searchslot->tts_tuple->t_self,
|
||||
NULL, slot);
|
||||
&hsearchslot->tuple->t_self, NULL, slot);
|
||||
|
||||
if (slot == NULL) /* "do nothing" */
|
||||
skip_tuple = true;
|
||||
@ -488,19 +499,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
|
||||
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
|
||||
|
||||
/* OK, update the tuple and index entries for it */
|
||||
simple_heap_update(rel, &searchslot->tts_tuple->t_self,
|
||||
slot->tts_tuple);
|
||||
simple_heap_update(rel, &hsearchslot->tuple->t_self, hslot->tuple);
|
||||
|
||||
if (resultRelInfo->ri_NumIndices > 0 &&
|
||||
!HeapTupleIsHeapOnly(slot->tts_tuple))
|
||||
!HeapTupleIsHeapOnly(hslot->tuple))
|
||||
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
|
||||
estate, false, NULL,
|
||||
NIL);
|
||||
|
||||
/* AFTER ROW UPDATE Triggers */
|
||||
ExecARUpdateTriggers(estate, resultRelInfo,
|
||||
&searchslot->tts_tuple->t_self,
|
||||
NULL, tuple, recheckIndexes, NULL);
|
||||
&hsearchslot->tuple->t_self, NULL, tuple,
|
||||
recheckIndexes, NULL);
|
||||
|
||||
list_free(recheckIndexes);
|
||||
}
|
||||
@ -519,9 +529,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
|
||||
bool skip_tuple = false;
|
||||
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
|
||||
Relation rel = resultRelInfo->ri_RelationDesc;
|
||||
HeapTupleTableSlot *hsearchslot = (HeapTupleTableSlot *)searchslot;
|
||||
|
||||
/* For now we support only tables. */
|
||||
/* For now we support only tables and heap tuples. */
|
||||
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
|
||||
Assert(TTS_IS_HEAPTUPLE(searchslot) || TTS_IS_BUFFERTUPLE(searchslot));
|
||||
|
||||
CheckCmdReplicaIdentity(rel, CMD_DELETE);
|
||||
|
||||
@ -530,8 +542,8 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
|
||||
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
|
||||
{
|
||||
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
|
||||
&searchslot->tts_tuple->t_self,
|
||||
NULL, NULL);
|
||||
&hsearchslot->tuple->t_self, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (!skip_tuple)
|
||||
@ -539,11 +551,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
|
||||
List *recheckIndexes = NIL;
|
||||
|
||||
/* OK, delete the tuple */
|
||||
simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
|
||||
simple_heap_delete(rel, &hsearchslot->tuple->t_self);
|
||||
|
||||
/* AFTER ROW DELETE Triggers */
|
||||
ExecARDeleteTriggers(estate, resultRelInfo,
|
||||
&searchslot->tts_tuple->t_self, NULL, NULL);
|
||||
&hsearchslot->tuple->t_self, NULL, NULL);
|
||||
|
||||
list_free(recheckIndexes);
|
||||
}
|
||||
|
@ -78,8 +78,8 @@ ExecScanFetch(ScanState *node,
|
||||
return ExecClearTuple(slot);
|
||||
|
||||
/* Store test tuple in the plan node's scan slot */
|
||||
ExecStoreHeapTuple(estate->es_epqTuple[scanrelid - 1],
|
||||
slot, false);
|
||||
ExecForceStoreHeapTuple(estate->es_epqTuple[scanrelid - 1],
|
||||
slot);
|
||||
|
||||
/* Check if it meets the access-method conditions */
|
||||
if (!(*recheckMtd) (node, slot))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1741,7 +1741,7 @@ agg_retrieve_direct(AggState *aggstate)
|
||||
* Make a copy of the first input tuple; we will use this
|
||||
* for comparisons (in group mode) and for projection.
|
||||
*/
|
||||
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
aggstate->grp_firstTuple = ExecCopySlotHeapTuple(outerslot);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1800,9 +1800,8 @@ agg_retrieve_direct(AggState *aggstate)
|
||||
* reserved for it. The tuple will be deleted when it is
|
||||
* cleared from the slot.
|
||||
*/
|
||||
ExecStoreHeapTuple(aggstate->grp_firstTuple,
|
||||
firstSlot,
|
||||
true);
|
||||
ExecForceStoreHeapTuple(aggstate->grp_firstTuple,
|
||||
firstSlot);
|
||||
aggstate->grp_firstTuple = NULL; /* don't keep two pointers */
|
||||
|
||||
/* set up for first advance_aggregates call */
|
||||
@ -1858,7 +1857,7 @@ agg_retrieve_direct(AggState *aggstate)
|
||||
if (!ExecQual(aggstate->phase->eqfunctions[node->numCols - 1],
|
||||
tmpcontext))
|
||||
{
|
||||
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
aggstate->grp_firstTuple = ExecCopySlotHeapTuple(outerslot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -914,7 +914,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &scanstate->ss,
|
||||
RelationGetDescr(currentRelation),
|
||||
&TTSOpsBufferTuple);
|
||||
&TTSOpsBufferHeapTuple);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -931,9 +931,10 @@ ExecParallelHashJoinOuterGetTuple(PlanState *outerNode,
|
||||
hashvalue);
|
||||
if (tuple != NULL)
|
||||
{
|
||||
slot = ExecStoreMinimalTuple(tuple,
|
||||
hjstate->hj_OuterTupleSlot,
|
||||
false);
|
||||
ExecForceStoreMinimalTuple(tuple,
|
||||
hjstate->hj_OuterTupleSlot,
|
||||
false);
|
||||
slot = hjstate->hj_OuterTupleSlot;
|
||||
return slot;
|
||||
}
|
||||
else
|
||||
@ -1160,9 +1161,10 @@ ExecParallelHashJoinNewBatch(HashJoinState *hjstate)
|
||||
while ((tuple = sts_parallel_scan_next(inner_tuples,
|
||||
&hashvalue)))
|
||||
{
|
||||
slot = ExecStoreMinimalTuple(tuple,
|
||||
hjstate->hj_HashTupleSlot,
|
||||
false);
|
||||
ExecForceStoreMinimalTuple(tuple,
|
||||
hjstate->hj_HashTupleSlot,
|
||||
false);
|
||||
slot = hjstate->hj_HashTupleSlot;
|
||||
ExecParallelHashTableInsertCurrentBatch(hashtable, slot,
|
||||
hashvalue);
|
||||
}
|
||||
@ -1296,7 +1298,8 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read from hash-join temporary file: %m")));
|
||||
return ExecStoreMinimalTuple(tuple, tupleSlot, true);
|
||||
ExecForceStoreMinimalTuple(tuple, tupleSlot, true);
|
||||
return tupleSlot;
|
||||
}
|
||||
|
||||
|
||||
|
@ -950,7 +950,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &indexstate->ss,
|
||||
RelationGetDescr(currentRelation),
|
||||
&TTSOpsBufferTuple);
|
||||
&TTSOpsBufferHeapTuple);
|
||||
|
||||
/*
|
||||
* Initialize result type and projection.
|
||||
|
@ -2384,7 +2384,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
mtstate->mt_existing =
|
||||
ExecInitExtraTupleSlot(mtstate->ps.state,
|
||||
mtstate->mt_partition_tuple_routing ?
|
||||
NULL : relationDesc, &TTSOpsBufferTuple);
|
||||
NULL : relationDesc, &TTSOpsBufferHeapTuple);
|
||||
|
||||
/* carried forward solely for the benefit of explain */
|
||||
mtstate->mt_excludedtlist = node->exclRelTlist;
|
||||
|
@ -147,7 +147,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
|
||||
/* and create slot with appropriate rowtype */
|
||||
ExecInitScanTupleSlot(estate, &scanstate->ss,
|
||||
RelationGetDescr(scanstate->ss.ss_currentRelation),
|
||||
&TTSOpsBufferTuple);
|
||||
&TTSOpsBufferHeapTuple);
|
||||
|
||||
/*
|
||||
* Initialize result type and projection.
|
||||
|
@ -173,7 +173,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
|
||||
/* and create slot with the appropriate rowtype */
|
||||
ExecInitScanTupleSlot(estate, &scanstate->ss,
|
||||
RelationGetDescr(scanstate->ss.ss_currentRelation),
|
||||
&TTSOpsBufferTuple);
|
||||
&TTSOpsBufferHeapTuple);
|
||||
|
||||
/*
|
||||
* Initialize result type and projection.
|
||||
|
@ -252,7 +252,7 @@ setop_retrieve_direct(SetOpState *setopstate)
|
||||
if (!TupIsNull(outerslot))
|
||||
{
|
||||
/* Make a copy of the first input tuple */
|
||||
setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
setopstate->grp_firstTuple = ExecCopySlotHeapTuple(outerslot);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -303,7 +303,7 @@ setop_retrieve_direct(SetOpState *setopstate)
|
||||
/*
|
||||
* Save the first input tuple of the next group.
|
||||
*/
|
||||
setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
setopstate->grp_firstTuple = ExecCopySlotHeapTuple(outerslot);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -357,7 +357,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
*/
|
||||
if (node->curTuple)
|
||||
heap_freetuple(node->curTuple);
|
||||
node->curTuple = ExecCopySlotTuple(slot);
|
||||
node->curTuple = ExecCopySlotHeapTuple(slot);
|
||||
|
||||
result = heap_getattr(node->curTuple, 1, tdesc, isNull);
|
||||
/* keep scanning subplan to make sure there's only one tuple */
|
||||
@ -1137,7 +1137,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
||||
*/
|
||||
if (node->curTuple)
|
||||
heap_freetuple(node->curTuple);
|
||||
node->curTuple = ExecCopySlotTuple(slot);
|
||||
node->curTuple = ExecCopySlotHeapTuple(slot);
|
||||
|
||||
/*
|
||||
* Now set all the setParam params from the columns of the tuple
|
||||
|
@ -544,7 +544,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &tidstate->ss,
|
||||
RelationGetDescr(currentRelation),
|
||||
&TTSOpsBufferTuple);
|
||||
&TTSOpsBufferHeapTuple);
|
||||
|
||||
/*
|
||||
* Initialize result type and projection.
|
||||
|
@ -1857,7 +1857,7 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
||||
}
|
||||
|
||||
tuptable->vals[tuptable->alloced - tuptable->free] =
|
||||
ExecCopySlotTuple(slot);
|
||||
ExecCopySlotHeapTuple(slot);
|
||||
(tuptable->free)--;
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
Reference in New Issue
Block a user