コミットメタ情報

リビジョン3e617777c1d3c7aa208b492ee561625947deb400 (tree)
日時2018-04-05 17:13:03
作者Kyotaro Horiguchi <horiguchi.kyotaro@lab....>
コミッターKyotaro Horiguchi

ログメッセージ

Fix relcache invalidation handling

Relcache invalidation callback can be called at every chance while
planning. Planner can crash if the invalidation callblack frees
statistics cache that have been passed to planner. This patch let
pg_dbms_stats refrain from freeing statistics cache immediately by
relcache invalidation. Invalidated cache entries are removed before
and after planner runs.

変更サマリ

差分

--- a/pg_dbms_stats.c
+++ b/pg_dbms_stats.c
@@ -18,6 +18,7 @@
1818 #include "executor/spi.h"
1919 #include "funcapi.h"
2020 #include "optimizer/plancat.h"
21+#include "optimizer/planner.h"
2122 #include "parser/parse_oper.h"
2223 #include "parser/parsetree.h"
2324 #include "storage/bufmgr.h"
@@ -79,15 +80,14 @@ static bool set_acl_ok = false;
7980 typedef struct StatsRelationEntry
8081 {
8182 Oid relid; /* hash key must be at the head */
82-
8383 bool valid; /* T if the entry has valid stats */
84-
84+ bool invalidated; /* T if this relation has been
85+ * invalidated */
8586 BlockNumber relpages; /* # of pages as of last ANALYZE */
8687 double reltuples; /* # of tuples as of last ANALYZE */
8788 BlockNumber relallvisible; /* # of all-visible pages as of last
8889 * ANALYZE */
8990 BlockNumber curpages; /* # of pages as of lock/restore */
90-
9191 List *col_stats; /* list of StatsColumnEntry, each element
9292 of which is pg_statistic record of this
9393 relation. */
@@ -110,6 +110,7 @@ get_relation_info_hook_type prev_get_relation_info = NULL;
110110 get_attavgwidth_hook_type prev_get_attavgwidth = NULL;
111111 get_relation_stats_hook_type prev_get_relation_stats = NULL;
112112 get_index_stats_hook_type prev_get_index_stats = NULL;
113+planner_hook_type prev_planner_hook = NULL;
113114
114115 /* namings */
115116 #define NSPNAME "dbms_stats"
@@ -140,8 +141,10 @@ static int nested_level = 0;
140141
141142 /*
142143 * The relation_stats_effective statistic cache is stored in hash table.
144+ * rel_invalidated is set true if the hash has invalidated entries.
143145 */
144146 static HTAB *rel_stats;
147+static bool rel_invalidated = false;
145148
146149 /*
147150 * The owner of pg_dbms_stats statistic tables.
@@ -179,6 +182,7 @@ static void dbms_stats_invalidate_cache_internal(Oid relid, bool sta_col);
179182 void _PG_init(void);
180183 void _PG_fini(void);
181184
185+/* hook functions */
182186 static void dbms_stats_get_relation_info(PlannerInfo *root, Oid relid,
183187 bool inhparent, RelOptInfo *rel);
184188 static int32 dbms_stats_get_attavgwidth(Oid relid, AttrNumber attnum);
@@ -186,7 +190,10 @@ static bool dbms_stats_get_relation_stats(PlannerInfo *root, RangeTblEntry *rte,
186190 AttrNumber attnum, VariableStatData *vardata);
187191 static bool dbms_stats_get_index_stats(PlannerInfo *root, Oid indexOid,
188192 AttrNumber indexattnum, VariableStatData *vardata);
193+static PlannedStmt *dbms_stats_planner(Query *parse, int cursorOptions,
194+ ParamListInfo boundParams);
189195
196+/* internal functions */
190197 static void get_merged_relation_stats(Oid relid, BlockNumber *pages,
191198 double *tuples, double *allvisfrac, bool estimate);
192199 static int32 get_merged_avgwidth(Oid relid, AttrNumber attnum);
@@ -198,9 +205,11 @@ static HeapTuple column_cache_enter(Oid relid, int32 attnum, bool inh,
198205 HeapTuple tuple);
199206 static bool execute_plan(SPIPlanPtr *plan, const char *query, Oid relid,
200207 const AttrNumber *attnum, bool inh);
201-static void StatsCacheRelCallback(Datum arg, Oid relid);
208+static void statscache_rel_callback(Datum arg, Oid relid);
209+static void cleanup_invalidated_cache(void);
202210 static void init_rel_stats(void);
203211 static void init_rel_stats_entry(StatsRelationEntry *entry, Oid relid);
212+
204213 /* copied from PG core source tree */
205214 static void dbms_stats_estimate_rel_size(Relation rel, int32 *attr_widths,
206215 BlockNumber *pages, double *tuples, double *allvisfrac,
@@ -276,12 +285,14 @@ _PG_init(void)
276285 get_relation_stats_hook = dbms_stats_get_relation_stats;
277286 prev_get_index_stats = get_index_stats_hook;
278287 get_index_stats_hook = dbms_stats_get_index_stats;
288+ prev_planner_hook = planner_hook;
289+ planner_hook = dbms_stats_planner;
279290
280291 /* Initialize hash table for statistics caching. */
281292 init_rel_stats();
282293
283294 /* Also set up a callback for relcache SI invalidations */
284- CacheRegisterRelcacheCallback(StatsCacheRelCallback, (Datum) 0);
295+ CacheRegisterRelcacheCallback(statscache_rel_callback, (Datum) 0);
285296 }
286297
287298 /*
@@ -295,6 +306,7 @@ _PG_fini(void)
295306 get_attavgwidth_hook = prev_get_attavgwidth;
296307 get_relation_stats_hook = prev_get_relation_stats;
297308 get_index_stats_hook = prev_get_index_stats;
309+ planner_hook = prev_planner_hook;
298310
299311 /* A function to unregister callback for relcache is NOT provided. */
300312 }
@@ -1007,7 +1019,7 @@ dbms_stats_get_index_stats(PlannerInfo *root,
10071019
10081020 /*
10091021 * scan over simple_rel_array_size to find the owner relation of the
1010- * index that its oid is indexOid
1022+ * index with the oid
10111023 */
10121024 for (i = 1 ; i < root->simple_rel_array_size ; i++)
10131025 {
@@ -1048,6 +1060,29 @@ next_plugin:
10481060 return false;
10491061 }
10501062
1063+
1064+/*
1065+ * dbms_stats_planner
1066+ * Hook function for planner_hook which cleans up invalidated statistics.
1067+ */
1068+static PlannedStmt *
1069+dbms_stats_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1070+{
1071+ PlannedStmt *ret;
1072+
1073+ cleanup_invalidated_cache();
1074+
1075+ if (prev_planner_hook)
1076+ ret = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1077+ else
1078+ ret = standard_planner(parse, cursorOptions, boundParams);
1079+
1080+ cleanup_invalidated_cache();
1081+
1082+ return ret;
1083+}
1084+
1085+
10511086 /*
10521087 * Extract binary value from given column.
10531088 */
@@ -1476,46 +1511,91 @@ execute_plan(SPIPlanPtr *plan,
14761511 }
14771512
14781513 /*
1479- * StatsCacheRelCallback
1514+ * statscache_rel_callback
14801515 * Relcache inval callback function
14811516 *
1482- * Invalidate cached statistic info of the given relid, or all cached statistic
1483- * info if relid == InvalidOid. We don't complain even when we don't have such
1484- * statistics.
1517+ * Invalidates cached statistics of the given relid, or all cached statistics
1518+ * if relid == InvalidOid. The statsTuple in the hash entries are directly
1519+ * passed to planner so we cannot remove them until planner ends. Just mark
1520+ * here then cleanup after planner finishes work.
1521+ */
1522+static void
1523+statscache_rel_callback(Datum arg, Oid relid)
1524+{
1525+ StatsRelationEntry *entry;
1526+
1527+ if (relid != InvalidOid)
1528+ {
1529+ bool found;
1530+
1531+ /*
1532+ * invalidate the entry for the specfied relation. Don't mind if found.
1533+ */
1534+ entry = hash_search(rel_stats, &relid, HASH_FIND, &found);
1535+ if (found)
1536+ {
1537+ entry->invalidated = true;
1538+ rel_invalidated = true;
1539+ }
1540+ }
1541+ else
1542+ {
1543+ /* invalidate all the entries of the hash */
1544+ HASH_SEQ_STATUS status;
1545+
1546+ hash_seq_init(&status, rel_stats);
1547+ while ((entry = hash_seq_search(&status)) != NULL)
1548+ {
1549+ entry->invalidated = true;
1550+ rel_invalidated = true;
1551+ }
1552+ }
1553+}
1554+
1555+/*
1556+ * cleanup_invalidated_cache()
1557+ * Cleanup invalidated stats cache
14851558 *
1486- * Note: arg is not used.
1559+ * removes invalidated cache entries.
14871560 */
14881561 static void
1489-StatsCacheRelCallback(Datum arg, Oid relid)
1562+cleanup_invalidated_cache(void)
14901563 {
14911564 HASH_SEQ_STATUS status;
14921565 StatsRelationEntry *entry;
14931566
1567+ /* Return immediately if nothing to do */
1568+ if (!rel_invalidated)
1569+ return;
1570+
1571+ /*
1572+ * Reset rel_invalidated first so that we don't lose invalidations that
1573+ * happens during this round of cleanup.
1574+ */
1575+ rel_invalidated = false;
1576+
14941577 hash_seq_init(&status, rel_stats);
14951578 while ((entry = hash_seq_search(&status)) != NULL)
14961579 {
1497- if (relid == InvalidOid || relid == entry->relid)
1498- {
1499- ListCell *lc;
1580+ ListCell *lc;
15001581
1501- /* Mark the relation entry as INVALID */
1502- entry->valid = false;
1582+ if (!entry->invalidated)
1583+ continue;
15031584
1504- /* Discard every column statistics */
1505- foreach (lc, entry->col_stats)
1506- {
1507- StatsColumnEntry *ent = (StatsColumnEntry*) lfirst(lc);
1585+ /* Discard every column statistics */
1586+ foreach (lc, entry->col_stats)
1587+ {
1588+ StatsColumnEntry *ent = (StatsColumnEntry*) lfirst(lc);
15081589
1509- if (!ent->negative)
1510- pfree(ent->tuple);
1511- pfree(ent);
1512- }
1513- list_free(entry->col_stats);
1514- entry->col_stats = NIL;
1590+ if (!ent->negative)
1591+ pfree(ent->tuple);
1592+ pfree(ent);
15151593 }
1516- }
1594+ list_free(entry->col_stats);
15171595
1518- /* We always check throughout the list, so hash_seq_term is not necessary */
1596+ /* Finally remove the hash entry. */
1597+ hash_search(rel_stats, &entry->relid, HASH_REMOVE, NULL);
1598+ }
15191599 }
15201600
15211601 /*
@@ -1552,6 +1632,7 @@ init_rel_stats_entry(StatsRelationEntry *entry, Oid relid)
15521632 {
15531633 entry->relid = relid;
15541634 entry->valid = false;
1635+ entry->invalidated = false;
15551636 entry->relpages = InvalidBlockNumber;
15561637 entry->reltuples = 0.0;
15571638 entry->relallvisible = InvalidBlockNumber;
旧リポジトリブラウザで表示