@@ -101,7 +101,10 @@ typedef struct RI_ConstraintInfo
101101{
102102 Oid constraint_id ; /* OID of pg_constraint entry (hash key) */
103103 bool valid ; /* successfully initialized? */
104- uint32 oidHashValue ; /* hash value of pg_constraint OID */
104+ Oid constraint_root_id ; /* OID of topmost ancestor constraint;
105+ * same as constraint_id if not inherited */
106+ uint32 oidHashValue ; /* hash value of constraint_id */
107+ uint32 rootHashValue ; /* hash value of constraint_root_id */
105108 NameData conname ; /* name of the FK constraint */
106109 Oid pk_relid ; /* referenced relation */
107110 Oid fk_relid ; /* referencing relation */
@@ -207,6 +210,7 @@ static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
207210static const RI_ConstraintInfo * ri_FetchConstraintInfo (Trigger * trigger ,
208211 Relation trig_rel , bool rel_is_pk );
209212static const RI_ConstraintInfo * ri_LoadConstraintInfo (Oid constraintOid );
213+ static Oid get_ri_constraint_root (Oid constrOid );
210214static SPIPlanPtr ri_PlanCheck (const char * querystr , int nargs , Oid * argtypes ,
211215 RI_QueryKey * qkey , Relation fk_rel , Relation pk_rel );
212216static bool ri_PerformCheck (const RI_ConstraintInfo * riinfo ,
@@ -1892,7 +1896,7 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
18921896 * Construct a hashtable key for a prepared SPI plan of an FK constraint.
18931897 *
18941898 * key: output argument, *key is filled in based on the other arguments
1895- * riinfo: info from pg_constraint entry
1899+ * riinfo: info derived from pg_constraint entry
18961900 * constr_queryno: an internal number identifying the query type
18971901 * (see RI_PLAN_XXX constants at head of file)
18981902 * ----------
@@ -1902,10 +1906,27 @@ ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
19021906 int32 constr_queryno )
19031907{
19041908 /*
1909+ * Inherited constraints with a common ancestor can share ri_query_cache
1910+ * entries for all query types except RI_PLAN_CHECK_LOOKUPPK_FROM_PK.
1911+ * Except in that case, the query processes the other table involved in
1912+ * the FK constraint (i.e., not the table on which the trigger has been
1913+ * fired), and so it will be the same for all members of the inheritance
1914+ * tree. So we may use the root constraint's OID in the hash key, rather
1915+ * than the constraint's own OID. This avoids creating duplicate SPI
1916+ * plans, saving lots of work and memory when there are many partitions
1917+ * with similar FK constraints.
1918+ *
1919+ * (Note that we must still have a separate RI_ConstraintInfo for each
1920+ * constraint, because partitions can have different column orders,
1921+ * resulting in different pk_attnums[] or fk_attnums[] array contents.)
1922+ *
19051923 * We assume struct RI_QueryKey contains no padding bytes, else we'd need
19061924 * to use memset to clear them.
19071925 */
1908- key -> constr_id = riinfo -> constraint_id ;
1926+ if (constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK )
1927+ key -> constr_id = riinfo -> constraint_root_id ;
1928+ else
1929+ key -> constr_id = riinfo -> constraint_id ;
19091930 key -> constr_queryno = constr_queryno ;
19101931}
19111932
@@ -2051,8 +2072,15 @@ ri_LoadConstraintInfo(Oid constraintOid)
20512072
20522073 /* And extract data */
20532074 Assert (riinfo -> constraint_id == constraintOid );
2075+ if (OidIsValid (conForm -> conparentid ))
2076+ riinfo -> constraint_root_id =
2077+ get_ri_constraint_root (conForm -> conparentid );
2078+ else
2079+ riinfo -> constraint_root_id = constraintOid ;
20542080 riinfo -> oidHashValue = GetSysCacheHashValue1 (CONSTROID ,
20552081 ObjectIdGetDatum (constraintOid ));
2082+ riinfo -> rootHashValue = GetSysCacheHashValue1 (CONSTROID ,
2083+ ObjectIdGetDatum (riinfo -> constraint_root_id ));
20562084 memcpy (& riinfo -> conname , & conForm -> conname , sizeof (NameData ));
20572085 riinfo -> pk_relid = conForm -> confrelid ;
20582086 riinfo -> fk_relid = conForm -> conrelid ;
@@ -2082,6 +2110,30 @@ ri_LoadConstraintInfo(Oid constraintOid)
20822110 return riinfo ;
20832111}
20842112
2113+ /*
2114+ * get_ri_constraint_root
2115+ * Returns the OID of the constraint's root parent
2116+ */
2117+ static Oid
2118+ get_ri_constraint_root (Oid constrOid )
2119+ {
2120+ for (;;)
2121+ {
2122+ HeapTuple tuple ;
2123+ Oid constrParentOid ;
2124+
2125+ tuple = SearchSysCache1 (CONSTROID , ObjectIdGetDatum (constrOid ));
2126+ if (!HeapTupleIsValid (tuple ))
2127+ elog (ERROR , "cache lookup failed for constraint %u" , constrOid );
2128+ constrParentOid = ((Form_pg_constraint ) GETSTRUCT (tuple ))-> conparentid ;
2129+ ReleaseSysCache (tuple );
2130+ if (!OidIsValid (constrParentOid ))
2131+ break ; /* we reached the root constraint */
2132+ constrOid = constrParentOid ;
2133+ }
2134+ return constrOid ;
2135+ }
2136+
20852137/*
20862138 * Callback for pg_constraint inval events
20872139 *
@@ -2117,7 +2169,14 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
21172169 RI_ConstraintInfo * riinfo = dlist_container (RI_ConstraintInfo ,
21182170 valid_link , iter .cur );
21192171
2120- if (hashvalue == 0 || riinfo -> oidHashValue == hashvalue )
2172+ /*
2173+ * We must invalidate not only entries directly matching the given
2174+ * hash value, but also child entries, in case the invalidation
2175+ * affects a root constraint.
2176+ */
2177+ if (hashvalue == 0 ||
2178+ riinfo -> oidHashValue == hashvalue ||
2179+ riinfo -> rootHashValue == hashvalue )
21212180 {
21222181 riinfo -> valid = false;
21232182 /* Remove invalidated entries from the list, too */
0 commit comments