.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,
}
else
{
- if (heap_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot))
+ if (table_scan_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot))
{
bool shouldFree;
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapTuple heapTuple;
TableScanDesc scan;
- HeapScanDesc hscan;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
ExprState *predicate;
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.
*/
*/
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);
if (newrel || needscan)
{
ExprContext *econtext;
- Datum *values;
- bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapTuple tuple;
TableScanDesc scan;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
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
*/
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);
/* 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);
}
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
- ExecDropSingleTupleTableSlot(newslot);
+ if (newslot)
+ ExecDropSingleTupleTableSlot(newslot);
}
FreeExecutorState(estate);
char *conbin;
Expr *origexpr;
ExprState *exprstate;
- HeapTuple tuple;
TableScanDesc scan;
- HeapScanDesc hscan;
ExprContext *econtext;
MemoryContext oldcxt;
TupleTableSlot *slot;
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
*/
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),
Oid pkindOid,
Oid constraintOid)
{
- HeapTuple tuple;
+ TupleTableSlot *slot;
TableScanDesc scan;
Trigger trig;
Snapshot snapshot;
* 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;
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;
table_endscan(scan);
UnregisterSnapshot(snapshot);
+ ExecDropSingleTupleTableSlot(slot);
}
static void
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;
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
}
}
}
+ ExecDropSingleTupleTableSlot(slot);
table_endscan(scan);
UnregisterSnapshot(snapshot);
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;
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;
ResetExprContext(econtext);
}
+ ExecDropSingleTupleTableSlot(slot);
table_endscan(scan);
UnregisterSnapshot(snapshot);
}
/*
- * 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;
* 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))
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;
}
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;
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;
}
table_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
*/
#include "postgres.h"
-#include "access/heapam.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "executor/execdebug.h"
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
TableScanDesc scandesc;
- HeapScanDesc hscandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
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);
}
/*
#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"
Expr *constr;
Expr *partition_constraint;
EState *estate;
- HeapTuple tuple;
ExprState *partqualstate = NULL;
Snapshot snapshot;
ExprContext *econtext;
TableScanDesc scan;
- HeapScanDesc hscan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
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
*/
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))
{
TableScanDesc scanDesc;
Snapshot snapshot;
+ TupleTableSlot *slot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
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);
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);
/* ------------------------------------------------------------------------
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.