From: Andres Freund Date: Wed, 6 Mar 2019 01:07:53 +0000 (-0800) Subject: tableam: introduce slot based table getnext and use where necessary. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=c81a6dd5c9fad6e6886ace7e630bec70c9725ff4;p=users%2Fandresfreund%2Fpostgres.git tableam: introduce slot based table getnext and use where necessary. Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: --- diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 9d06ba55b5..7ace8e7d25 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -170,6 +170,7 @@ static const TableAmRoutine heapam_methods = { .scan_end = heap_endscan, .scan_rescan = heap_rescan, .scan_update_snapshot = heap_update_snapshot, + .scan_getnextslot = heap_getnextslot, .parallelscan_estimate = table_block_parallelscan_estimate, .parallelscan_initialize = table_block_parallelscan_initialize, diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 1b57e0f85d..5d0700744a 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -430,7 +430,7 @@ systable_getnext(SysScanDesc sysscan) } else { - if (heap_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot)) + if (table_scan_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot)) { bool shouldFree; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index ae68e01098..ae522b663d 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2969,9 +2969,7 @@ IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo) { - HeapTuple heapTuple; TableScanDesc scan; - HeapScanDesc hscan; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; ExprState *predicate; @@ -3012,17 +3010,11 @@ IndexCheckExclusion(Relation heapRelation, NULL, /* scan key */ true, /* buffer access strategy OK */ true); /* syncscan OK */ - hscan = (HeapScanDesc) scan; - while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, slot)) { CHECK_FOR_INTERRUPTS(); - MemoryContextReset(econtext->ecxt_per_tuple_memory); - - /* Set up for predicate or expression evaluation */ - ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf); - /* * In a partial index, ignore tuples that don't satisfy the predicate. */ @@ -3046,8 +3038,10 @@ IndexCheckExclusion(Relation heapRelation, */ check_exclusion_constraint(heapRelation, indexRelation, indexInfo, - &(heapTuple->t_self), values, isnull, + &(slot->tts_tid), values, isnull, estate, true); + + MemoryContextReset(econtext->ecxt_per_tuple_memory); } table_endscan(scan); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 81ff6a59ee..3a04a26f01 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4732,11 +4732,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) if (newrel || needscan) { ExprContext *econtext; - Datum *values; - bool *isnull; TupleTableSlot *oldslot; TupleTableSlot *newslot; - HeapTuple tuple; TableScanDesc scan; MemoryContext oldCxt; List *dropped_attrs = NIL; @@ -4765,19 +4762,32 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) econtext = GetPerTupleExprContext(estate); /* - * Make tuple slots for old and new tuples. Note that even when the - * tuples are the same, the tupDescs might not be (consider ADD COLUMN - * without a default). + * Create necessary tuple slots. When rewriting, two slots are needed, + * otherwise one suffices. In the case where one slot suffices, we + * need to use the new tuple descriptor, otherwise some constraints + * can't be evaluated. Note that even when the tuple layout is the + * same and no rewrite is required, the tupDescs might not be + * (consider ADD COLUMN without a default). */ - oldslot = MakeSingleTupleTableSlot(oldTupDesc, &TTSOpsHeapTuple); - newslot = MakeSingleTupleTableSlot(newTupDesc, &TTSOpsHeapTuple); + if (tab->rewrite) + { + Assert(newrel != NULL); + oldslot = MakeSingleTupleTableSlot(oldTupDesc, + table_slot_callbacks(oldrel)); + newslot = MakeSingleTupleTableSlot(newTupDesc, + table_slot_callbacks(newrel)); - /* Preallocate values/isnull arrays */ - i = Max(newTupDesc->natts, oldTupDesc->natts); - values = (Datum *) palloc(i * sizeof(Datum)); - isnull = (bool *) palloc(i * sizeof(bool)); - memset(values, 0, i * sizeof(Datum)); - memset(isnull, true, i * sizeof(bool)); + memset(newslot->tts_values, 0, + sizeof(Datum) * newTupDesc->natts); + memset(newslot->tts_isnull, 0, + sizeof(bool) * newTupDesc->natts); + } + else + { + oldslot = MakeSingleTupleTableSlot(newTupDesc, + table_slot_callbacks(oldrel)); + newslot = NULL; + } /* * Any attributes that are dropped according to the new tuple @@ -4803,55 +4813,69 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) */ oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot)) { + TupleTableSlot *insertslot; + if (tab->rewrite > 0) { /* Extract data from old tuple */ - heap_deform_tuple(tuple, oldTupDesc, values, isnull); + slot_getallattrs(oldslot); + ExecClearTuple(newslot); + + /* copy attributes */ + memcpy(newslot->tts_values, oldslot->tts_values, + sizeof(Datum) * oldslot->tts_nvalid); + memcpy(newslot->tts_isnull, oldslot->tts_isnull, + sizeof(bool) * oldslot->tts_nvalid); /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) - isnull[lfirst_int(lc)] = true; + newslot->tts_isnull[lfirst_int(lc)] = true; /* * Process supplied expressions to replace selected columns. * Expression inputs come from the old tuple. */ - ExecStoreHeapTuple(tuple, oldslot, false); econtext->ecxt_scantuple = oldslot; foreach(l, tab->newvals) { NewColumnValue *ex = lfirst(l); - values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, - econtext, - &isnull[ex->attnum - 1]); + newslot->tts_values[ex->attnum - 1] + = ExecEvalExpr(ex->exprstate, + econtext, + &newslot->tts_isnull[ex->attnum - 1]); } - /* - * Form the new tuple. Note that we don't explicitly pfree it, - * since the per-tuple memory context will be reset shortly. - */ - tuple = heap_form_tuple(newTupDesc, values, isnull); + ExecStoreVirtualTuple(newslot); /* * Constraints might reference the tableoid column, so * initialize t_tableOid before evaluating them. */ - tuple->t_tableOid = RelationGetRelid(oldrel); + newslot->tts_tableOid = RelationGetRelid(oldrel); + insertslot = newslot; + } + else + { + /* + * If there's no rewrite, old and new table are guaranteed to + * have the same AM, so we can just use the old slot to + * verify new constraints etc. + */ + insertslot = oldslot; } /* Now check any constraints on the possibly-changed tuple */ - ExecStoreHeapTuple(tuple, newslot, false); - econtext->ecxt_scantuple = newslot; + econtext->ecxt_scantuple = insertslot; foreach(l, notnull_attrs) { int attn = lfirst_int(l); - if (heap_attisnull(tuple, attn + 1, newTupDesc)) + if (slot_attisnull(insertslot, attn + 1)) { Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn); @@ -4901,6 +4925,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) /* Write the tuple out to the new relation */ if (newrel) { + HeapTuple tuple; + + tuple = ExecFetchSlotHeapTuple(newslot, true, NULL); heap_insert(newrel, tuple, mycid, hi_options, bistate); ItemPointerCopy(&tuple->t_self, &newslot->tts_tid); } @@ -4915,7 +4942,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) UnregisterSnapshot(snapshot); ExecDropSingleTupleTableSlot(oldslot); - ExecDropSingleTupleTableSlot(newslot); + if (newslot) + ExecDropSingleTupleTableSlot(newslot); } FreeExecutorState(estate); @@ -8818,9 +8846,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) char *conbin; Expr *origexpr; ExprState *exprstate; - HeapTuple tuple; TableScanDesc scan; - HeapScanDesc hscan; ExprContext *econtext; MemoryContext oldcxt; TupleTableSlot *slot; @@ -8860,7 +8886,6 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) snapshot = RegisterSnapshot(GetLatestSnapshot()); scan = table_beginscan(rel, snapshot, 0, NULL); - hscan = (HeapScanDesc) scan; /* * Switch to per-tuple memory context and reset it for each tuple @@ -8868,11 +8893,8 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) */ oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, slot)) { - ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf); - - if (!ExecCheck(exprstate, econtext)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), @@ -8903,7 +8925,7 @@ validateForeignKeyConstraint(char *conname, Oid pkindOid, Oid constraintOid) { - HeapTuple tuple; + TupleTableSlot *slot; TableScanDesc scan; Trigger trig; Snapshot snapshot; @@ -8939,9 +8961,10 @@ validateForeignKeyConstraint(char *conname, * ereport(ERROR) and that's that. */ snapshot = RegisterSnapshot(GetLatestSnapshot()); + slot = table_gimmegimmeslot(rel, NULL); scan = table_beginscan(rel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, slot)) { LOCAL_FCINFO(fcinfo, 0); TriggerData trigdata; @@ -8959,7 +8982,8 @@ validateForeignKeyConstraint(char *conname, trigdata.type = T_TriggerData; trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; trigdata.tg_relation = rel; - trigdata.tg_trigtuple = tuple; + trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, true, NULL); + trigdata.tg_trigslot = slot; trigdata.tg_newtuple = NULL; trigdata.tg_trigger = &trig; @@ -8970,6 +8994,7 @@ validateForeignKeyConstraint(char *conname, table_endscan(scan); UnregisterSnapshot(snapshot); + ExecDropSingleTupleTableSlot(slot); } static void diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index e0c17d10b9..ad77acb596 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2363,14 +2363,15 @@ AlterDomainNotNull(List *names, bool notNull) RelToCheck *rtc = (RelToCheck *) lfirst(rt); Relation testrel = rtc->rel; TupleDesc tupdesc = RelationGetDescr(testrel); - HeapTuple tuple; + TupleTableSlot *slot; TableScanDesc scan; Snapshot snapshot; /* Scan all tuples in this relation */ snapshot = RegisterSnapshot(GetLatestSnapshot()); scan = table_beginscan(testrel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + slot = table_gimmegimmeslot(testrel, NULL); + while (table_scan_getnextslot(scan, ForwardScanDirection, slot)) { int i; @@ -2380,7 +2381,7 @@ AlterDomainNotNull(List *names, bool notNull) int attnum = rtc->atts[i]; Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); - if (heap_attisnull(tuple, attnum, tupdesc)) + if (slot_attisnull(slot, attnum)) { /* * In principle the auxiliary information for this @@ -2399,6 +2400,7 @@ AlterDomainNotNull(List *names, bool notNull) } } } + ExecDropSingleTupleTableSlot(slot); table_endscan(scan); UnregisterSnapshot(snapshot); @@ -2777,14 +2779,15 @@ validateDomainConstraint(Oid domainoid, char *ccbin) RelToCheck *rtc = (RelToCheck *) lfirst(rt); Relation testrel = rtc->rel; TupleDesc tupdesc = RelationGetDescr(testrel); - HeapTuple tuple; + TupleTableSlot *slot; TableScanDesc scan; Snapshot snapshot; /* Scan all tuples in this relation */ snapshot = RegisterSnapshot(GetLatestSnapshot()); scan = table_beginscan(testrel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + slot = table_gimmegimmeslot(testrel, NULL); + while (table_scan_getnextslot(scan, ForwardScanDirection, slot)) { int i; @@ -2797,7 +2800,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin) Datum conResult; Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); - d = heap_getattr(tuple, attnum, tupdesc, &isNull); + d = slot_getattr(slot, attnum, &isNull); econtext->domainValue_datum = d; econtext->domainValue_isNull = isNull; @@ -2827,6 +2830,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin) ResetExprContext(econtext); } + ExecDropSingleTupleTableSlot(slot); table_endscan(scan); UnregisterSnapshot(snapshot); diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 275b927cb3..311d2aadc0 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -221,19 +221,21 @@ retry: } /* - * Compare the tuple and slot and check if they have equal values. + * Compare the tuples in the slots by checking if they have equal values. */ static bool -tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot) +tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2) { - Datum values[MaxTupleAttributeNumber]; - bool isnull[MaxTupleAttributeNumber]; - int attrnum; + int attrnum; - heap_deform_tuple(tup, desc, values, isnull); + Assert(slot1->tts_tupleDescriptor->natts == + slot2->tts_tupleDescriptor->natts); + + slot_getallattrs(slot1); + slot_getallattrs(slot2); /* Check equality of the attributes. */ - for (attrnum = 0; attrnum < desc->natts; attrnum++) + for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++) { Form_pg_attribute att; TypeCacheEntry *typentry; @@ -242,16 +244,16 @@ tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot) * If one value is NULL and other is not, then they are certainly not * equal */ - if (isnull[attrnum] != slot->tts_isnull[attrnum]) + if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum]) return false; /* * If both are NULL, they can be considered equal. */ - if (isnull[attrnum]) + if (slot1->tts_isnull[attrnum] || slot2->tts_isnull[attrnum]) continue; - att = TupleDescAttr(desc, attrnum); + att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum); typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO); if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) @@ -261,8 +263,8 @@ tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot) format_type_be(att->atttypid)))); if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo, - values[attrnum], - slot->tts_values[attrnum]))) + slot1->tts_values[attrnum], + slot2->tts_values[attrnum]))) return false; } @@ -283,18 +285,19 @@ bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot) { - HeapTuple scantuple; + TupleTableSlot *scanslot; TableScanDesc scan; SnapshotData snap; TransactionId xwait; bool found; - TupleDesc desc = RelationGetDescr(rel); + TupleDesc desc PG_USED_FOR_ASSERTS_ONLY = RelationGetDescr(rel); Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor)); /* Start a heap scan. */ InitDirtySnapshot(snap); scan = table_beginscan(rel, &snap, 0, NULL); + scanslot = table_gimmegimmeslot(rel, NULL); retry: found = false; @@ -302,16 +305,13 @@ retry: table_rescan(scan, NULL); /* Try to find the tuple */ - while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot)) { - HeapScanDesc hscan = (HeapScanDesc) scan; - - if (!tuple_equals_slot(desc, scantuple, searchslot)) + if (!tuples_equal(scanslot, searchslot)) continue; found = true; - ExecStoreBufferHeapTuple(scantuple, outslot, hscan->rs_cbuf); - ExecMaterializeSlot(outslot); + ExecCopySlot(outslot, scanslot); xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax; @@ -377,6 +377,7 @@ retry: } table_endscan(scan); + ExecDropSingleTupleTableSlot(scanslot); return found; } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 2272f93b09..525e84c047 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -27,7 +27,6 @@ */ #include "postgres.h" -#include "access/heapam.h" #include "access/relscan.h" #include "access/tableam.h" #include "executor/execdebug.h" @@ -50,9 +49,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node); static TupleTableSlot * SeqNext(SeqScanState *node) { - HeapTuple tuple; TableScanDesc scandesc; - HeapScanDesc hscandesc; EState *estate; ScanDirection direction; TupleTableSlot *slot; @@ -77,30 +74,10 @@ SeqNext(SeqScanState *node) node->ss.ss_currentScanDesc = scandesc; } - hscandesc = (HeapScanDesc) scandesc; - /* * get the next tuple from the table */ - tuple = heap_getnext(scandesc, direction); - - /* - * save the tuple and the buffer returned to us by the access methods in - * our scan tuple slot and return the slot. Note: we pass 'false' because - * tuples returned by heap_getnext() are pointers onto disk pages and were - * not created with palloc() and so should not be pfree()'d. Note also - * that ExecStoreHeapTuple will increment the refcount of the buffer; the - * refcount will not be dropped until the tuple table slot is cleared. - */ - if (tuple) - ExecStoreBufferHeapTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - hscandesc->rs_cbuf); /* buffer associated - * with this tuple */ - else - ExecClearTuple(slot); - - return slot; + return table_scan_getnextslot(scandesc, direction, slot); } /* diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 5d9e4dc171..30b0d15895 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -14,7 +14,8 @@ #include "postgres.h" -#include "access/heapam.h" +#include "access/relation.h" +#include "access/table.h" #include "access/tableam.h" #include "catalog/partition.h" #include "catalog/pg_inherits.h" @@ -1203,12 +1204,10 @@ check_default_partition_contents(Relation parent, Relation default_rel, Expr *constr; Expr *partition_constraint; EState *estate; - HeapTuple tuple; ExprState *partqualstate = NULL; Snapshot snapshot; ExprContext *econtext; TableScanDesc scan; - HeapScanDesc hscan; MemoryContext oldCxt; TupleTableSlot *tupslot; @@ -1268,7 +1267,6 @@ check_default_partition_contents(Relation parent, Relation default_rel, snapshot = RegisterSnapshot(GetLatestSnapshot()); tupslot = table_gimmegimmeslot(part_rel, &estate->es_tupleTable); scan = table_beginscan(part_rel, snapshot, 0, NULL); - hscan = (HeapScanDesc) scan; /* * Switch to per-tuple memory context and reset it for each tuple @@ -1276,9 +1274,8 @@ check_default_partition_contents(Relation parent, Relation default_rel, */ oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot)) { - ExecStoreBufferHeapTuple(tuple, tupslot, hscan->rs_cbuf); econtext->ecxt_scantuple = tupslot; if (!ExecCheck(partqualstate, econtext)) diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index a83f4cd26c..599f98be4d 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -426,6 +426,7 @@ DefineQueryRewrite(const char *rulename, { TableScanDesc scanDesc; Snapshot snapshot; + TupleTableSlot *slot; if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ereport(ERROR, @@ -441,11 +442,13 @@ DefineQueryRewrite(const char *rulename, snapshot = RegisterSnapshot(GetLatestSnapshot()); scanDesc = table_beginscan(event_relation, snapshot, 0, NULL); - if (heap_getnext(scanDesc, ForwardScanDirection) != NULL) + slot = table_gimmegimmeslot(event_relation, NULL); + if (table_scan_getnextslot(scanDesc, ForwardScanDirection, slot)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not convert table \"%s\" to a view because it is not empty", RelationGetRelationName(event_relation)))); + ExecDropSingleTupleTableSlot(slot); table_endscan(scanDesc); UnregisterSnapshot(snapshot); diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index 3e422f84b0..8f500e55d1 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -62,6 +62,8 @@ typedef struct TableAmRoutine void (*scan_rescan) (TableScanDesc scan, struct ScanKeyData *key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode); void (*scan_update_snapshot) (TableScanDesc scan, Snapshot snapshot); + TupleTableSlot *(*scan_getnextslot) (TableScanDesc scan, + ScanDirection direction, TupleTableSlot *slot); /* ------------------------------------------------------------------------ @@ -231,6 +233,13 @@ table_scan_update_snapshot(TableScanDesc scan, Snapshot snapshot) scan->rs_rd->rd_tableam->scan_update_snapshot(scan, snapshot); } +static inline TupleTableSlot * +table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot) +{ + slot->tts_tableOid = RelationGetRelid(sscan->rs_rd); + return sscan->rs_rd->rd_tableam->scan_getnextslot(sscan, direction, slot); +} + /* ---------------------------------------------------------------------------- * Parallel table scan related functions.