BlockNumber block, int lines,
bool all_visible, bool check_serializable)
{
+ Oid relid = RelationGetRelid(scan->rs_base.rs_rd);
int ntup = 0;
- OffsetNumber lineoff;
+ int nvis = 0;
+ BatchMVCCState batchmvcc;
+
+ /* page at a time should have been disabled otherwise */
+ Assert(IsMVCCSnapshot(snapshot));
- for (lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++)
+ /* first find all tuples on the page */
+ for (OffsetNumber lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++)
{
ItemId lpp = PageGetItemId(page, lineoff);
- HeapTupleData loctup;
- bool valid;
+ HeapTuple tup;
- if (!ItemIdIsNormal(lpp))
+ if (unlikely(!ItemIdIsNormal(lpp)))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem(page, lpp);
- loctup.t_len = ItemIdGetLength(lpp);
- loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
- ItemPointerSet(&(loctup.t_self), block, lineoff);
-
- if (all_visible)
- valid = true;
- else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ /*
+ * If the page is not all-visible or we need to check serializability,
+ * maintain enough state to be able to refind the tuple efficiently,
+ * without again first needing to fetch the item and then via that the
+ * tuple.
+ */
+ if (!all_visible || check_serializable)
+ {
+ tup = &batchmvcc.tuples[ntup];
- if (check_serializable)
- HeapCheckForSerializableConflictOut(valid, scan->rs_base.rs_rd,
- &loctup, buffer, snapshot);
+ tup->t_data = (HeapTupleHeader) PageGetItem(page, lpp);
+ tup->t_len = ItemIdGetLength(lpp);
+ tup->t_tableOid = relid;
+ ItemPointerSet(&(tup->t_self), block, lineoff);
+ }
- if (valid)
+ /*
+ * If the page is all visible, these fields otherwise won't be
+ * populated in loop below.
+ */
+ if (all_visible)
{
+ if (check_serializable)
+ {
+ batchmvcc.visible[ntup] = true;
+ }
scan->rs_vistuples[ntup] = lineoff;
- ntup++;
}
+
+ ntup++;
}
Assert(ntup <= MaxHeapTuplesPerPage);
- return ntup;
+ /*
+ * Unless the page is all visible, test visibility for all tuples one go.
+ * That is considerably more efficient than calling
+ * HeapTupleSatisfiesMVCC() one-by-one.
+ */
+ if (all_visible)
+ nvis = ntup;
+ else
+ nvis = HeapTupleSatisfiesMVCCBatch(snapshot, buffer,
+ ntup,
+ &batchmvcc,
+ scan->rs_vistuples);
+
+ /*
+ * So far we don't have batch API for testing serializabilty, so do so
+ * one-by-one.
+ */
+ if (check_serializable)
+ {
+ for (int i = 0; i < ntup; i++)
+ {
+ HeapCheckForSerializableConflictOut(batchmvcc.visible[i],
+ scan->rs_base.rs_rd,
+ &batchmvcc.tuples[i],
+ buffer, snapshot);
+ }
+ }
+
+ return nvis;
}
/*
return true;
}
+/*
+ * Perform HeaptupleSatisfiesMVCC() on each passed in tuple. This is more
+ * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
+ *
+ * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
+ * visibility is stored in batchmvcc->visible[]. In addition,
+ * ->vistuples_dense is set to contain the offsets of visible tuples.
+ *
+ * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
+ * avoids a cross-translation-unit function call for each tuple and allows the
+ * compiler to optimize across calls to HeapTupleSatisfiesMVCC. In the future
+ * it will also allow more efficient setting of hint bits.
+ *
+ * Returns the number of visible tuples.
+ */
+int
+HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
+ int ntups,
+ BatchMVCCState *batchmvcc,
+ OffsetNumber *vistuples_dense)
+{
+ int nvis = 0;
+
+ Assert(IsMVCCSnapshot(snapshot));
+
+ for (int i = 0; i < ntups; i++)
+ {
+ bool valid;
+ HeapTuple tup = &batchmvcc->tuples[i];
+
+ valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer);
+ batchmvcc->visible[i] = valid;
+
+ if (likely(valid))
+ {
+ vistuples_dense[nvis] = tup->t_self.ip_posid;
+ nvis++;
+ }
+ }
+
+ return nvis;
+}
+
/*
* HeapTupleSatisfiesVisibility
* True iff heap tuple satisfies a time qual.
extern bool HeapTupleIsSurelyDead(HeapTuple htup,
GlobalVisState *vistest);
+/*
+ * Some of the input/output to/from HeapTupleSatisfiesMVCCBatch() is passed
+ * via this struct, as otherwise the increased number of arguments to
+ * HeapTupleSatisfiesMVCCBatch() leads to on-stack argument passing on x86-64,
+ * which causes a small regression.
+ */
+typedef struct BatchMVCCState
+{
+ HeapTupleData tuples[MaxHeapTuplesPerPage];
+ bool visible[MaxHeapTuplesPerPage];
+} BatchMVCCState;
+
+extern int HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
+ int ntups,
+ BatchMVCCState *batchmvcc,
+ OffsetNumber *vistuples_dense);
+
/*
* To avoid leaking too much knowledge about reorderbuffer implementation
* details this is implemented in reorderbuffer.c not heapam_visibility.c