diff options
Diffstat (limited to 'src/backend/utils/cache/typcache.c')
-rw-r--r-- | src/backend/utils/cache/typcache.c | 158 |
1 files changed, 124 insertions, 34 deletions
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index cdf6331a971..854f133f9be 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -23,11 +23,12 @@ * permanently allows caching pointers to them in long-lived places. * * We have some provisions for updating cache entries if the stored data - * becomes obsolete. Information dependent on opclasses is cleared if we - * detect updates to pg_opclass. We also support clearing the tuple - * descriptor and operator/function parts of a rowtype's cache entry, - * since those may need to change as a consequence of ALTER TABLE. - * Domain constraint changes are also tracked properly. + * becomes obsolete. Core data extracted from the pg_type row is updated + * when we detect updates to pg_type. Information dependent on opclasses is + * cleared if we detect updates to pg_opclass. We also support clearing the + * tuple descriptor and operator/function parts of a rowtype's cache entry, + * since those may need to change as a consequence of ALTER TABLE. Domain + * constraint changes are also tracked properly. * * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group @@ -80,24 +81,31 @@ static HTAB *TypeCacheHash = NULL; static TypeCacheEntry *firstDomainTypeEntry = NULL; /* Private flag bits in the TypeCacheEntry.flags field */ -#define TCFLAGS_CHECKED_BTREE_OPCLASS 0x000001 -#define TCFLAGS_CHECKED_HASH_OPCLASS 0x000002 -#define TCFLAGS_CHECKED_EQ_OPR 0x000004 -#define TCFLAGS_CHECKED_LT_OPR 0x000008 -#define TCFLAGS_CHECKED_GT_OPR 0x000010 -#define TCFLAGS_CHECKED_CMP_PROC 0x000020 -#define TCFLAGS_CHECKED_HASH_PROC 0x000040 -#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC 0x000080 -#define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x000100 -#define TCFLAGS_HAVE_ELEM_EQUALITY 0x000200 -#define TCFLAGS_HAVE_ELEM_COMPARE 0x000400 -#define TCFLAGS_HAVE_ELEM_HASHING 0x000800 -#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING 0x001000 -#define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x002000 -#define TCFLAGS_HAVE_FIELD_EQUALITY 0x004000 -#define TCFLAGS_HAVE_FIELD_COMPARE 0x008000 -#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x010000 -#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE 0x020000 +#define TCFLAGS_HAVE_PG_TYPE_DATA 0x000001 +#define TCFLAGS_CHECKED_BTREE_OPCLASS 0x000002 +#define TCFLAGS_CHECKED_HASH_OPCLASS 0x000004 +#define TCFLAGS_CHECKED_EQ_OPR 0x000008 +#define TCFLAGS_CHECKED_LT_OPR 0x000010 +#define TCFLAGS_CHECKED_GT_OPR 0x000020 +#define TCFLAGS_CHECKED_CMP_PROC 0x000040 +#define TCFLAGS_CHECKED_HASH_PROC 0x000080 +#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC 0x000100 +#define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x000200 +#define TCFLAGS_HAVE_ELEM_EQUALITY 0x000400 +#define TCFLAGS_HAVE_ELEM_COMPARE 0x000800 +#define TCFLAGS_HAVE_ELEM_HASHING 0x001000 +#define TCFLAGS_HAVE_ELEM_EXTENDED_HASHING 0x002000 +#define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x004000 +#define TCFLAGS_HAVE_FIELD_EQUALITY 0x008000 +#define TCFLAGS_HAVE_FIELD_COMPARE 0x010000 +#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x020000 +#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE 0x040000 + +/* The flags associated with equality/comparison/hashing are all but these: */ +#define TCFLAGS_OPERATOR_FLAGS \ + (~(TCFLAGS_HAVE_PG_TYPE_DATA | \ + TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS | \ + TCFLAGS_DOMAIN_BASE_IS_COMPOSITE)) /* * Data stored about a domain type's constraints. Note that we do not create @@ -295,6 +303,7 @@ static bool range_element_has_hashing(TypeCacheEntry *typentry); static bool range_element_has_extended_hashing(TypeCacheEntry *typentry); static void cache_range_element_properties(TypeCacheEntry *typentry); static void TypeCacheRelCallback(Datum arg, Oid relid); +static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue); static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue); static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue); static void load_enum_cache_data(TypeCacheEntry *tcache); @@ -337,9 +346,9 @@ lookup_type_cache(Oid type_id, int flags) /* Also set up callbacks for SI invalidations */ CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0); + CacheRegisterSyscacheCallback(TYPEOID, TypeCacheTypCallback, (Datum) 0); CacheRegisterSyscacheCallback(CLAOID, TypeCacheOpcCallback, (Datum) 0); CacheRegisterSyscacheCallback(CONSTROID, TypeCacheConstrCallback, (Datum) 0); - CacheRegisterSyscacheCallback(TYPEOID, TypeCacheConstrCallback, (Datum) 0); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) @@ -381,7 +390,13 @@ lookup_type_cache(Oid type_id, int flags) Assert(!found); /* it wasn't there a moment ago */ MemSet(typentry, 0, sizeof(TypeCacheEntry)); + + /* These fields can never change, by definition */ typentry->type_id = type_id; + typentry->type_id_hash = GetSysCacheHashValue1(TYPEOID, + ObjectIdGetDatum(type_id)); + + /* Keep this part in sync with the code below */ typentry->typlen = typtup->typlen; typentry->typbyval = typtup->typbyval; typentry->typalign = typtup->typalign; @@ -390,6 +405,7 @@ lookup_type_cache(Oid type_id, int flags) typentry->typrelid = typtup->typrelid; typentry->typelem = typtup->typelem; typentry->typcollation = typtup->typcollation; + typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA; /* If it's a domain, immediately thread it into the domain cache list */ if (typentry->typtype == TYPTYPE_DOMAIN) @@ -400,6 +416,43 @@ lookup_type_cache(Oid type_id, int flags) ReleaseSysCache(tp); } + else if (!(typentry->flags & TCFLAGS_HAVE_PG_TYPE_DATA)) + { + /* + * We have an entry, but its pg_type row got changed, so reload the + * data obtained directly from pg_type. + */ + HeapTuple tp; + Form_pg_type typtup; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id)); + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type with OID %u does not exist", type_id))); + typtup = (Form_pg_type) GETSTRUCT(tp); + if (!typtup->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" is only a shell", + NameStr(typtup->typname)))); + + /* + * Keep this part in sync with the code above. Many of these fields + * shouldn't ever change, particularly typtype, but copy 'em anyway. + */ + typentry->typlen = typtup->typlen; + typentry->typbyval = typtup->typbyval; + typentry->typalign = typtup->typalign; + typentry->typstorage = typtup->typstorage; + typentry->typtype = typtup->typtype; + typentry->typrelid = typtup->typrelid; + typentry->typelem = typtup->typelem; + typentry->typcollation = typtup->typcollation; + typentry->flags |= TCFLAGS_HAVE_PG_TYPE_DATA; + + ReleaseSysCache(tp); + } /* * Look up opclasses if we haven't already and any dependent info is @@ -750,12 +803,17 @@ lookup_type_cache(Oid type_id, int flags) /* * If requested, get information about a range type + * + * This includes making sure that the basic info about the range element + * type is up-to-date. */ if ((flags & TYPECACHE_RANGE_INFO) && - typentry->rngelemtype == NULL && typentry->typtype == TYPTYPE_RANGE) { - load_rangetype_info(typentry); + if (typentry->rngelemtype == NULL) + load_rangetype_info(typentry); + else if (!(typentry->rngelemtype->flags & TCFLAGS_HAVE_PG_TYPE_DATA)) + (void) lookup_type_cache(typentry->rngelemtype->type_id, 0); } /* @@ -2129,7 +2187,7 @@ TypeCacheRelCallback(Datum arg, Oid relid) } /* Reset equality/comparison/hashing validity information */ - typentry->flags = 0; + typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS; } else if (typentry->typtype == TYPTYPE_DOMAIN) { @@ -2140,7 +2198,39 @@ TypeCacheRelCallback(Datum arg, Oid relid) * type is composite, we don't need to reset anything. */ if (typentry->flags & TCFLAGS_DOMAIN_BASE_IS_COMPOSITE) - typentry->flags = 0; + typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS; + } + } +} + +/* + * TypeCacheTypCallback + * Syscache inval callback function + * + * This is called when a syscache invalidation event occurs for any + * pg_type row. If we have information cached about that type, mark + * it as needing to be reloaded. + */ +static void +TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue) +{ + HASH_SEQ_STATUS status; + TypeCacheEntry *typentry; + + /* TypeCacheHash must exist, else this callback wouldn't be registered */ + hash_seq_init(&status, TypeCacheHash); + while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL) + { + /* Is this the targeted type row (or it's a total cache flush)? */ + if (hashvalue == 0 || typentry->type_id_hash == hashvalue) + { + /* + * Mark the data obtained directly from pg_type as invalid. Also, + * if it's a domain, typnotnull might've changed, so we'll need to + * recalculate its constraints. + */ + typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA | + TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS); } } } @@ -2172,7 +2262,7 @@ TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue) while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL) { /* Reset equality/comparison/hashing validity information */ - typentry->flags = 0; + typentry->flags &= ~TCFLAGS_OPERATOR_FLAGS; } } @@ -2181,12 +2271,12 @@ TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue) * Syscache inval callback function * * This is called when a syscache invalidation event occurs for any - * pg_constraint or pg_type row. We flush information about domain - * constraints when this happens. + * pg_constraint row. We flush information about domain constraints + * when this happens. * - * It's slightly annoying that we can't tell whether the inval event was for a - * domain constraint/type record or not; there's usually more update traffic - * for table constraints/types than domain constraints, so we'll do a lot of + * It's slightly annoying that we can't tell whether the inval event was for + * a domain constraint record or not; there's usually more update traffic + * for table constraints than domain constraints, so we'll do a lot of * useless flushes. Still, this is better than the old no-caching-at-all * approach to domain constraints. */ |