@@ -101,7 +101,10 @@ typedef struct RI_ConstraintInfo
101
101
{
102
102
Oid constraint_id ; /* OID of pg_constraint entry (hash key) */
103
103
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 */
105
108
NameData conname ; /* name of the FK constraint */
106
109
Oid pk_relid ; /* referenced relation */
107
110
Oid fk_relid ; /* referencing relation */
@@ -207,6 +210,7 @@ static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
207
210
static const RI_ConstraintInfo * ri_FetchConstraintInfo (Trigger * trigger ,
208
211
Relation trig_rel , bool rel_is_pk );
209
212
static const RI_ConstraintInfo * ri_LoadConstraintInfo (Oid constraintOid );
213
+ static Oid get_ri_constraint_root (Oid constrOid );
210
214
static SPIPlanPtr ri_PlanCheck (const char * querystr , int nargs , Oid * argtypes ,
211
215
RI_QueryKey * qkey , Relation fk_rel , Relation pk_rel );
212
216
static bool ri_PerformCheck (const RI_ConstraintInfo * riinfo ,
@@ -1892,7 +1896,7 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
1892
1896
* Construct a hashtable key for a prepared SPI plan of an FK constraint.
1893
1897
*
1894
1898
* 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
1896
1900
* constr_queryno: an internal number identifying the query type
1897
1901
* (see RI_PLAN_XXX constants at head of file)
1898
1902
* ----------
@@ -1902,10 +1906,27 @@ ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
1902
1906
int32 constr_queryno )
1903
1907
{
1904
1908
/*
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
+ *
1905
1923
* We assume struct RI_QueryKey contains no padding bytes, else we'd need
1906
1924
* to use memset to clear them.
1907
1925
*/
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 ;
1909
1930
key -> constr_queryno = constr_queryno ;
1910
1931
}
1911
1932
@@ -2051,8 +2072,15 @@ ri_LoadConstraintInfo(Oid constraintOid)
2051
2072
2052
2073
/* And extract data */
2053
2074
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 ;
2054
2080
riinfo -> oidHashValue = GetSysCacheHashValue1 (CONSTROID ,
2055
2081
ObjectIdGetDatum (constraintOid ));
2082
+ riinfo -> rootHashValue = GetSysCacheHashValue1 (CONSTROID ,
2083
+ ObjectIdGetDatum (riinfo -> constraint_root_id ));
2056
2084
memcpy (& riinfo -> conname , & conForm -> conname , sizeof (NameData ));
2057
2085
riinfo -> pk_relid = conForm -> confrelid ;
2058
2086
riinfo -> fk_relid = conForm -> conrelid ;
@@ -2082,6 +2110,30 @@ ri_LoadConstraintInfo(Oid constraintOid)
2082
2110
return riinfo ;
2083
2111
}
2084
2112
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
+
2085
2137
/*
2086
2138
* Callback for pg_constraint inval events
2087
2139
*
@@ -2117,7 +2169,14 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
2117
2169
RI_ConstraintInfo * riinfo = dlist_container (RI_ConstraintInfo ,
2118
2170
valid_link , iter .cur );
2119
2171
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 )
2121
2180
{
2122
2181
riinfo -> valid = false;
2123
2182
/* Remove invalidated entries from the list, too */
0 commit comments