diff --git i/src/backend/optimizer/plan/planner.c w/src/backend/optimizer/plan/planner.c index 5d515842dc..65d74be557 100644 --- i/src/backend/optimizer/plan/planner.c +++ w/src/backend/optimizer/plan/planner.c @@ -316,6 +316,10 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->transientPlan = false; glob->dependsOnRole = false; + + /* clear crappy variable range cache */ + clear_variable_range_cache(); + /* * Assess whether it's feasible to use parallel mode for this query. We * can't do this in a standalone backend, or if the command will try to diff --git i/src/backend/utils/adt/selfuncs.c w/src/backend/utils/adt/selfuncs.c index e96bfce4dc..0e0cc68307 100644 --- i/src/backend/utils/adt/selfuncs.c +++ w/src/backend/utils/adt/selfuncs.c @@ -5567,6 +5567,99 @@ get_stats_slot_range(AttStatsSlot *sslot, Oid opfuncoid, FmgrInfo *opproc, } +/* + * a lot of crappy code to emulate variable range caching + * just intended for testing purposes + */ +typedef struct ActualVariableRangeCache ActualVariableRangeCache; +struct ActualVariableRangeCache { + Oid indexoid; + int64 min_value; + int64 max_value; + bool has_min; + bool has_max; +}; + +static unsigned long range_caches_size = 0; +static ActualVariableRangeCache range_caches[1000] = {{0, 0, 0, false, false}}; + +void +clear_variable_range_cache(void) +{ + for (unsigned long i = 0; i < range_caches_size; i++) { + range_caches[i].indexoid = 0; + range_caches[i].has_min = false; + range_caches[i].has_max = false; + } + range_caches_size = 0; +} + +static bool +get_cached_actual_variable_min(Oid indexoid, Datum *min) +{ + for (unsigned long i = 0; i < range_caches_size; i++) { + if (range_caches[i].indexoid == indexoid) { + if (!range_caches[i].has_min) + return false; + + *min = range_caches[i].min_value; + return true; + } + } + return false; +} + + +static bool +get_cached_actual_variable_max(Oid indexoid, Datum *max) +{ + for (unsigned long i = 0; i < range_caches_size; i++) { + if (range_caches[i].indexoid == indexoid) { + if (!range_caches[i].has_max) + return false; + + *max = range_caches[i].max_value; + return true; + } + } + return false; +} + + +static void +add_actual_variable_min_to_cache(Oid indexoid, Datum *min) +{ + for (unsigned long i = 0; i < range_caches_size; i++) { + if (range_caches[i].indexoid == indexoid) { + range_caches[i].min_value = *min; + range_caches[i].has_min = true; + return; + } + } + range_caches[range_caches_size].indexoid = indexoid; + range_caches[range_caches_size].min_value = *min; + range_caches[range_caches_size].has_min = true; + range_caches_size++; +} + + +static void +add_actual_variable_max_to_cache(Oid indexoid, Datum *max) +{ + for (unsigned long i = 0; i < range_caches_size; i++) { + if (range_caches[i].indexoid == indexoid) { + range_caches[i].max_value = *max; + range_caches[i].has_max = true; + return; + } + } + range_caches[range_caches_size].indexoid = indexoid; + range_caches[range_caches_size].max_value = *max; + range_caches[range_caches_size].has_max = true; + range_caches_size++; +} + + /* * get_actual_variable_range * Attempt to identify the current *actual* minimum and/or maximum @@ -5691,15 +5784,23 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, /* If min is requested ... */ if (min) { - have_data = get_actual_variable_endpoint(heapRel, - indexRel, - indexscandir, - scankeys, - typLen, - typByVal, - slot, - oldcontext, - min); + /* try to reuse min from previous planner nodes */ + have_data = get_cached_actual_variable_min(index->indexoid, min); + /* or fetch from index if first time here */ + if (!have_data) + have_data = get_actual_variable_endpoint(heapRel, + indexRel, + indexscandir, + scankeys, + typLen, + typByVal, + slot, + oldcontext, + min); + /* update the min memorization */ + if (have_data) + add_actual_variable_min_to_cache(index->indexoid, + min); } else { @@ -5710,16 +5811,25 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, /* If max is requested, and we didn't find the index is empty */ if (max && have_data) { + /* try to reuse max from previous planner nodes */ + have_data = get_cached_actual_variable_max(index->indexoid, max); + + /* or fetch from index if first time here */ /* scan in the opposite direction; all else is the same */ - have_data = get_actual_variable_endpoint(heapRel, - indexRel, - -indexscandir, - scankeys, - typLen, - typByVal, - slot, - oldcontext, - max); + if (!have_data) + have_data = get_actual_variable_endpoint(heapRel, + indexRel, + -indexscandir, + scankeys, + typLen, + typByVal, + slot, + oldcontext, + max); + /* update the max memorization */ + if (have_data) + add_actual_variable_max_to_cache(index->indexoid, + max); } /* Clean everything up */ diff --git i/src/include/utils/selfuncs.h w/src/include/utils/selfuncs.h index 7ac4a06391..a46c272ac7 100644 --- i/src/include/utils/selfuncs.h +++ w/src/include/utils/selfuncs.h @@ -220,4 +220,7 @@ extern Selectivity scalararraysel_containment(PlannerInfo *root, Oid elemtype, bool isEquality, bool useOr, int varRelid); +extern void +clear_variable_range_cache(void); + #endif /* SELFUNCS_H */