diff options
Diffstat (limited to 'src/backend/utils/adt/ri_triggers.c')
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 67 |
1 files changed, 63 insertions, 4 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 6e3a41062fc..09a2ad28814 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -101,7 +101,10 @@ typedef struct RI_ConstraintInfo { Oid constraint_id; /* OID of pg_constraint entry (hash key) */ bool valid; /* successfully initialized? */ - uint32 oidHashValue; /* hash value of pg_constraint OID */ + Oid constraint_root_id; /* OID of topmost ancestor constraint; + * same as constraint_id if not inherited */ + uint32 oidHashValue; /* hash value of constraint_id */ + uint32 rootHashValue; /* hash value of constraint_root_id */ NameData conname; /* name of the FK constraint */ Oid pk_relid; /* referenced relation */ Oid fk_relid; /* referencing relation */ @@ -207,6 +210,7 @@ static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, static const RI_ConstraintInfo *ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk); static const RI_ConstraintInfo *ri_LoadConstraintInfo(Oid constraintOid); +static Oid get_ri_constraint_root(Oid constrOid); static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel); static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo, @@ -1892,7 +1896,7 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation) * Construct a hashtable key for a prepared SPI plan of an FK constraint. * * key: output argument, *key is filled in based on the other arguments - * riinfo: info from pg_constraint entry + * riinfo: info derived from pg_constraint entry * constr_queryno: an internal number identifying the query type * (see RI_PLAN_XXX constants at head of file) * ---------- @@ -1902,10 +1906,27 @@ ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo, int32 constr_queryno) { /* + * Inherited constraints with a common ancestor can share ri_query_cache + * entries for all query types except RI_PLAN_CHECK_LOOKUPPK_FROM_PK. + * Except in that case, the query processes the other table involved in + * the FK constraint (i.e., not the table on which the trigger has been + * fired), and so it will be the same for all members of the inheritance + * tree. So we may use the root constraint's OID in the hash key, rather + * than the constraint's own OID. This avoids creating duplicate SPI + * plans, saving lots of work and memory when there are many partitions + * with similar FK constraints. + * + * (Note that we must still have a separate RI_ConstraintInfo for each + * constraint, because partitions can have different column orders, + * resulting in different pk_attnums[] or fk_attnums[] array contents.) + * * We assume struct RI_QueryKey contains no padding bytes, else we'd need * to use memset to clear them. */ - key->constr_id = riinfo->constraint_id; + if (constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK) + key->constr_id = riinfo->constraint_root_id; + else + key->constr_id = riinfo->constraint_id; key->constr_queryno = constr_queryno; } @@ -2051,8 +2072,15 @@ ri_LoadConstraintInfo(Oid constraintOid) /* And extract data */ Assert(riinfo->constraint_id == constraintOid); + if (OidIsValid(conForm->conparentid)) + riinfo->constraint_root_id = + get_ri_constraint_root(conForm->conparentid); + else + riinfo->constraint_root_id = constraintOid; riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID, ObjectIdGetDatum(constraintOid)); + riinfo->rootHashValue = GetSysCacheHashValue1(CONSTROID, + ObjectIdGetDatum(riinfo->constraint_root_id)); memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData)); riinfo->pk_relid = conForm->confrelid; riinfo->fk_relid = conForm->conrelid; @@ -2083,6 +2111,30 @@ ri_LoadConstraintInfo(Oid constraintOid) } /* + * get_ri_constraint_root + * Returns the OID of the constraint's root parent + */ +static Oid +get_ri_constraint_root(Oid constrOid) +{ + for (;;) + { + HeapTuple tuple; + Oid constrParentOid; + + tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for constraint %u", constrOid); + constrParentOid = ((Form_pg_constraint) GETSTRUCT(tuple))->conparentid; + ReleaseSysCache(tuple); + if (!OidIsValid(constrParentOid)) + break; /* we reached the root constraint */ + constrOid = constrParentOid; + } + return constrOid; +} + +/* * Callback for pg_constraint inval events * * While most syscache callbacks just flush all their entries, pg_constraint @@ -2117,7 +2169,14 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) RI_ConstraintInfo *riinfo = dlist_container(RI_ConstraintInfo, valid_link, iter.cur); - if (hashvalue == 0 || riinfo->oidHashValue == hashvalue) + /* + * We must invalidate not only entries directly matching the given + * hash value, but also child entries, in case the invalidation + * affects a root constraint. + */ + if (hashvalue == 0 || + riinfo->oidHashValue == hashvalue || + riinfo->rootHashValue == hashvalue) { riinfo->valid = false; /* Remove invalidated entries from the list, too */ |