diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 61edde9c5d35a4ccdae954d7e25329d208ab15e8..0469522a567bb3e17752b192ded77a33d0bb442d 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -183,6 +183,7 @@ typedef struct RI_CompareHashEntry * ---------- */ static HTAB *ri_constraint_cache = NULL; +static long ri_constraint_cache_seq_count = 0; static HTAB *ri_query_cache = NULL; static HTAB *ri_compare_cache = NULL; @@ -215,6 +216,7 @@ static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup, static bool ri_AttributesEqual(Oid eq_opr, Oid typeid, Datum oldvalue, Datum newvalue); +static void ri_InitConstraintCache(void); static void ri_InitHashTables(void); static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue); static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key); @@ -2945,6 +2947,20 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) Assert(ri_constraint_cache != NULL); + /* + * Prevent an O(N^2) problem when creating large amounts of foreign + * key constraints with ALTER TABLE, like it happens at the end of + * a pg_dump with hundred-thousands of tables having references. + */ + ri_constraint_cache_seq_count += hash_get_num_entries(ri_constraint_cache); + if (ri_constraint_cache_seq_count > 1000000) + { + hash_destroy(ri_constraint_cache); + ri_InitConstraintCache(); + ri_constraint_cache_seq_count = 0; + return; + } + hash_seq_init(&status, ri_constraint_cache); while ((hentry = (RI_ConstraintInfo *) hash_seq_search(&status)) != NULL) { @@ -3364,13 +3380,15 @@ ri_NullCheck(HeapTuple tup, /* ---------- - * ri_InitHashTables - + * ri_InitConstraintCache * - * Initialize our internal hash tables. + * Initialize ri_constraint_cache when new or being rebuilt. + * + * This needs to be done from two places, so split it out to prevent drift. * ---------- */ static void -ri_InitHashTables(void) +ri_InitConstraintCache(void) { HASHCTL ctl; @@ -3380,6 +3398,20 @@ ri_InitHashTables(void) ri_constraint_cache = hash_create("RI constraint cache", RI_INIT_CONSTRAINTHASHSIZE, &ctl, HASH_ELEM | HASH_BLOBS); +} + +/* ---------- + * ri_InitHashTables - + * + * Initialize our internal hash tables. + * ---------- + */ +static void +ri_InitHashTables(void) +{ + HASHCTL ctl; + + ri_InitConstraintCache(); /* Arrange to flush cache on pg_constraint changes */ CacheRegisterSyscacheCallback(CONSTROID,