fix-get_actual_variable_range-for-HOT-chains-wip.patch
text/x-diff
Filename: fix-get_actual_variable_range-for-HOT-chains-wip.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/utils/adt/selfuncs.c | 42 | 10 |
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d7e3f09..b51cbed 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5232,8 +5232,6 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
InvalidOid, /* no reg proc for this */
(Datum) 0); /* constant */
- have_data = true;
-
/* If min is requested ... */
if (min)
{
@@ -5262,15 +5260,33 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
* get_actual_variable_range() call will not have to visit
* that heap entry. In this way we avoid repetitive work when
* this function is used a lot during planning.
+ *
+ * However, all is not perfect, because if the index was
+ * recently created it may have entries pointing to broken HOT
+ * chains that contain tuples that don't match the index entry
+ * but aren't yet known-dead either. We need to ignore such
+ * tuples, since they might not be representative of the index
+ * extremal values, indeed could even contain NULLs. We
+ * approximate this by ignoring any hot-updated tuples we see
+ * and continuing to scan.
*/
index_scan = index_beginscan(heapRel, indexRel,
&SnapshotNonVacuumable,
1, 0);
index_rescan(index_scan, scankeys, 1, NULL, 0);
- /* Fetch first tuple in sortop's direction */
- if (index_getnext_slot(index_scan, indexscandir, slot))
+ /* Fetch first/next tuple in sortop's direction */
+ while (index_getnext_slot(index_scan, indexscandir, slot))
{
+ /* Ignore hot-updated tuples, per comment above */
+ if (TTS_IS_BUFFERTUPLE(slot))
+ {
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (HeapTupleHeaderIsHotUpdated(bslot->base.tupdata.t_data))
+ continue;
+ }
+
/* Extract the index column values from the slot */
FormIndexDatum(indexInfo, slot, estate,
values, isnull);
@@ -5284,24 +5300,40 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
MemoryContextSwitchTo(oldcontext);
*min = datumCopy(values[0], typByVal, typLen);
MemoryContextSwitchTo(tmpcontext);
+ have_data = true;
+ break;
}
- else
- have_data = false;
index_endscan(index_scan);
}
+ else
+ {
+ /* If min not requested, assume index has data */
+ have_data = true;
+ }
/* If max is requested, and we didn't find the index is empty */
if (max && have_data)
{
+ have_data = false;
+
index_scan = index_beginscan(heapRel, indexRel,
&SnapshotNonVacuumable,
1, 0);
index_rescan(index_scan, scankeys, 1, NULL, 0);
- /* Fetch first tuple in reverse direction */
- if (index_getnext_slot(index_scan, -indexscandir, slot))
+ /* Fetch first/next tuple in reverse direction */
+ while (index_getnext_slot(index_scan, -indexscandir, slot))
{
+ /* Ignore hot-updated tuples, per comment above */
+ if (TTS_IS_BUFFERTUPLE(slot))
+ {
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (HeapTupleHeaderIsHotUpdated(bslot->base.tupdata.t_data))
+ continue;
+ }
+
/* Extract the index column values from the slot */
FormIndexDatum(indexInfo, slot, estate,
values, isnull);
@@ -5315,9 +5347,9 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
MemoryContextSwitchTo(oldcontext);
*max = datumCopy(values[0], typByVal, typLen);
MemoryContextSwitchTo(tmpcontext);
+ have_data = true;
+ break;
}
- else
- have_data = false;
index_endscan(index_scan);
}