#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi.h"
+#include "lib/ilist.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "miscadmin.h"
* PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK =
* FK) */
+ dlist_node valid_link; /* Link in list of valid entries */
} RI_ConstraintInfo;
static HTAB *ri_constraint_cache = NULL;
static HTAB *ri_query_cache = NULL;
static HTAB *ri_compare_cache = NULL;
+static dlist_head ri_constraint_cache_valid_list;
+static int ri_constraint_cache_valid_count = 0;
/* ----------
ReleaseSysCache(tup);
+ /*
+ * For efficient processing of invalidation messages below, we keep a
+ * doubly-linked list, and a count, of all currently valid entries.
+ */
+ dlist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link);
+ ri_constraint_cache_valid_count++;
+
riinfo->valid = true;
return riinfo;
* gets enough update traffic that it's probably worth being smarter.
* Invalidate any ri_constraint_cache entry associated with the syscache
* entry with the specified hash value, or all entries if hashvalue == 0.
+ *
+ * Note: at the time a cache invalidation message is processed there may be
+ * active references to the cache. Because of this we never remove entries
+ * from the cache, but only mark them invalid, which is harmless to active
+ * uses. (Any query using an entry should hold a lock sufficient to keep that
+ * data from changing under it --- but we may get cache flushes anyway.)
*/
static void
InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
{
- HASH_SEQ_STATUS status;
- RI_ConstraintInfo *hentry;
+ dlist_mutable_iter iter;
Assert(ri_constraint_cache != NULL);
- hash_seq_init(&status, ri_constraint_cache);
- while ((hentry = (RI_ConstraintInfo *) hash_seq_search(&status)) != NULL)
+ /*
+ * If the list of currently valid entries gets excessively large, we mark
+ * them all invalid so we can empty the list. This arrangement avoids
+ * O(N^2) behavior in situations where a session touches many foreign keys
+ * and also does many ALTER TABLEs, such as a restore from pg_dump.
+ */
+ if (ri_constraint_cache_valid_count > 1000)
+ hashvalue = 0; /* pretend it's a cache reset */
+
+ dlist_foreach_modify(iter, &ri_constraint_cache_valid_list)
{
- if (hentry->valid &&
- (hashvalue == 0 || hentry->oidHashValue == hashvalue))
- hentry->valid = false;
+ RI_ConstraintInfo *riinfo = dlist_container(RI_ConstraintInfo,
+ valid_link, iter.cur);
+
+ if (hashvalue == 0 || riinfo->oidHashValue == hashvalue)
+ {
+ riinfo->valid = false;
+ /* Remove invalidated entries from the list, too */
+ dlist_delete(iter.cur);
+ ri_constraint_cache_valid_count--;
+ }
}
}