aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeMemoize.c
diff options
context:
space:
mode:
authorDavid Rowley <drowley@postgresql.org>2021-11-24 14:56:18 +1300
committerDavid Rowley <drowley@postgresql.org>2021-11-24 14:56:18 +1300
commit1050048a315790a505465bfcceb26eaf8dbc7e2e (patch)
tree86093353545d96b85123fb067415854eaae36549 /src/backend/executor/nodeMemoize.c
parente502150f7d0be41e3c8784be007fa871a32d8a7f (diff)
downloadpostgresql-1050048a315790a505465bfcceb26eaf8dbc7e2e.tar.gz
postgresql-1050048a315790a505465bfcceb26eaf8dbc7e2e.zip
Flush Memoize cache when non-key parameters change
It's possible that a subplan below a Memoize node contains a parameter from above the Memoize node. If this parameter changes then cache entries may become out-dated due to the new parameter value. Previously Memoize was mistakenly not aware of this. We fix this here by flushing the cache whenever a parameter that's not part of the cache key changes. Bug: #17213 Reported by: Elvis Pranskevichus Author: David Rowley Discussion: https://postgr.es/m/17213-988ed34b225a2862@postgresql.org Backpatch-through: 14, where Memoize was added
Diffstat (limited to 'src/backend/executor/nodeMemoize.c')
-rw-r--r--src/backend/executor/nodeMemoize.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index 683502dd90e..31e39722239 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -368,6 +368,37 @@ remove_cache_entry(MemoizeState *mstate, MemoizeEntry *entry)
}
/*
+ * cache_purge_all
+ * Remove all items from the cache
+ */
+static void
+cache_purge_all(MemoizeState *mstate)
+{
+ uint64 evictions = mstate->hashtable->members;
+ PlanState *pstate = (PlanState *) mstate;
+
+ /*
+ * Likely the most efficient way to remove all items is to just reset the
+ * memory context for the cache and then rebuild a fresh hash table. This
+ * saves having to remove each item one by one and pfree each cached tuple
+ */
+ MemoryContextReset(mstate->tableContext);
+
+ /* Make the hash table the same size as the original size */
+ build_hash_table(mstate, ((Memoize *) pstate->plan)->est_entries);
+
+ /* reset the LRU list */
+ dlist_init(&mstate->lru_list);
+ mstate->last_tuple = NULL;
+ mstate->entry = NULL;
+
+ mstate->mem_used = 0;
+
+ /* XXX should we add something new to track these purges? */
+ mstate->stats.cache_evictions += evictions; /* Update Stats */
+}
+
+/*
* cache_reduce_memory
* Evict older and less recently used items from the cache in order to
* reduce the memory consumption back to something below the
@@ -979,6 +1010,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
* getting the first tuple. This allows us to mark it as so.
*/
mstate->singlerow = node->singlerow;
+ mstate->keyparamids = node->keyparamids;
/*
* Record if the cache keys should be compared bit by bit, or logically
@@ -1082,6 +1114,12 @@ ExecReScanMemoize(MemoizeState *node)
if (outerPlan->chgParam == NULL)
ExecReScan(outerPlan);
+ /*
+ * Purge the entire cache if a parameter changed that is not part of the
+ * cache key.
+ */
+ if (bms_nonempty_difference(outerPlan->chgParam, node->keyparamids))
+ cache_purge_all(node);
}
/*