@@ -1928,7 +1928,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
19281928 * If we just deleted the PK row whose key was equal to the FK
19291929 * columns' default values, and a referencing row exists in the FK
19301930 * table, we would have updated that row to the same values it
1931- * already had --- and RI_FKey_keyequal_upd_fk would therefore
1931+ * already had --- and RI_FKey_fk_upd_check_required would hence
19321932 * believe no check is necessary. So we need to do another lookup
19331933 * now and in case a reference still exists, abort the operation.
19341934 * That is already implemented in the NO ACTION trigger, so just
@@ -2125,7 +2125,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
21252125 * If we just updated the PK row whose key was equal to the FK
21262126 * columns' default values, and a referencing row exists in the FK
21272127 * table, we would have updated that row to the same values it
2128- * already had --- and RI_FKey_keyequal_upd_fk would therefore
2128+ * already had --- and RI_FKey_fk_upd_check_required would hence
21292129 * believe no check is necessary. So we need to do another lookup
21302130 * now and in case a reference still exists, abort the operation.
21312131 * That is already implemented in the NO ACTION trigger, so just
@@ -2158,16 +2158,18 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
21582158
21592159
21602160/* ----------
2161- * RI_FKey_keyequal_upd_pk -
2161+ * RI_FKey_pk_upd_check_required -
21622162 *
2163- * Check if we have a key change on an update to a PK relation. This is
2164- * used by the AFTER trigger queue manager to see if it can skip queuing
2165- * an instance of an RI trigger.
2163+ * Check if we really need to fire the RI trigger for an update to a PK
2164+ * relation. This is called by the AFTER trigger queue manager to see if
2165+ * it can skip queuing an instance of an RI trigger. Returns TRUE if the
2166+ * trigger must be fired, FALSE if we can prove the constraint will still
2167+ * be satisfied.
21662168 * ----------
21672169 */
21682170bool
2169- RI_FKey_keyequal_upd_pk (Trigger * trigger , Relation pk_rel ,
2170- HeapTuple old_row , HeapTuple new_row )
2171+ RI_FKey_pk_upd_check_required (Trigger * trigger , Relation pk_rel ,
2172+ HeapTuple old_row , HeapTuple new_row )
21712173{
21722174 RI_ConstraintInfo riinfo ;
21732175
@@ -2177,19 +2179,32 @@ RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
21772179 ri_FetchConstraintInfo (& riinfo , trigger , pk_rel , true);
21782180
21792181 /*
2180- * Nothing to do if no column names to compare given
2182+ * Nothing to do if no columns (satisfaction of such a constraint only
2183+ * requires existence of a PK row, and this update won't change that).
21812184 */
21822185 if (riinfo .nkeys == 0 )
2183- return true ;
2186+ return false ;
21842187
21852188 switch (riinfo .confmatchtype )
21862189 {
21872190 case FKCONSTR_MATCH_SIMPLE :
21882191 case FKCONSTR_MATCH_FULL :
2189- /* Return true if keys are equal */
2190- return ri_KeysEqual (pk_rel , old_row , new_row , & riinfo , true);
21912192
2192- /* Handle MATCH PARTIAL set null delete. */
2193+ /*
2194+ * If any old key value is NULL, the row could not have been
2195+ * referenced by an FK row, so no check is needed.
2196+ */
2197+ if (ri_NullCheck (old_row , & riinfo , true) != RI_KEYS_NONE_NULL )
2198+ return false;
2199+
2200+ /* If all old and new key values are equal, no check is needed */
2201+ if (ri_KeysEqual (pk_rel , old_row , new_row , & riinfo , true))
2202+ return false;
2203+
2204+ /* Else we need to fire the trigger. */
2205+ return true;
2206+
2207+ /* Handle MATCH PARTIAL check. */
21932208 case FKCONSTR_MATCH_PARTIAL :
21942209 ereport (ERROR ,
21952210 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
@@ -2207,16 +2222,18 @@ RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
22072222}
22082223
22092224/* ----------
2210- * RI_FKey_keyequal_upd_fk -
2225+ * RI_FKey_fk_upd_check_required -
22112226 *
2212- * Check if we have a key change on an update to an FK relation. This is
2213- * used by the AFTER trigger queue manager to see if it can skip queuing
2214- * an instance of an RI trigger.
2227+ * Check if we really need to fire the RI trigger for an update to an FK
2228+ * relation. This is called by the AFTER trigger queue manager to see if
2229+ * it can skip queuing an instance of an RI trigger. Returns TRUE if the
2230+ * trigger must be fired, FALSE if we can prove the constraint will still
2231+ * be satisfied.
22152232 * ----------
22162233 */
22172234bool
2218- RI_FKey_keyequal_upd_fk (Trigger * trigger , Relation fk_rel ,
2219- HeapTuple old_row , HeapTuple new_row )
2235+ RI_FKey_fk_upd_check_required (Trigger * trigger , Relation fk_rel ,
2236+ HeapTuple old_row , HeapTuple new_row )
22202237{
22212238 RI_ConstraintInfo riinfo ;
22222239
@@ -2226,19 +2243,78 @@ RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
22262243 ri_FetchConstraintInfo (& riinfo , trigger , fk_rel , false);
22272244
22282245 /*
2229- * Nothing to do if no column names to compare given
2246+ * Nothing to do if no columns (satisfaction of such a constraint only
2247+ * requires existence of a PK row, and this update won't change that).
22302248 */
22312249 if (riinfo .nkeys == 0 )
2232- return true ;
2250+ return false ;
22332251
22342252 switch (riinfo .confmatchtype )
22352253 {
22362254 case FKCONSTR_MATCH_SIMPLE :
2255+ /*
2256+ * If any new key value is NULL, the row must satisfy the
2257+ * constraint, so no check is needed.
2258+ */
2259+ if (ri_NullCheck (new_row , & riinfo , false) != RI_KEYS_NONE_NULL )
2260+ return false;
2261+
2262+ /*
2263+ * If the original row was inserted by our own transaction, we
2264+ * must fire the trigger whether or not the keys are equal. This
2265+ * is because our UPDATE will invalidate the INSERT so that the
2266+ * INSERT RI trigger will not do anything; so we had better do the
2267+ * UPDATE check. (We could skip this if we knew the INSERT
2268+ * trigger already fired, but there is no easy way to know that.)
2269+ */
2270+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetXmin (old_row -> t_data )))
2271+ return true;
2272+
2273+ /* If all old and new key values are equal, no check is needed */
2274+ if (ri_KeysEqual (fk_rel , old_row , new_row , & riinfo , false))
2275+ return false;
2276+
2277+ /* Else we need to fire the trigger. */
2278+ return true;
2279+
22372280 case FKCONSTR_MATCH_FULL :
2238- /* Return true if keys are equal */
2239- return ri_KeysEqual (fk_rel , old_row , new_row , & riinfo , false);
2281+ /*
2282+ * If all new key values are NULL, the row must satisfy the
2283+ * constraint, so no check is needed. On the other hand, if only
2284+ * some of them are NULL, the row must fail the constraint. We
2285+ * must not throw error here, because the row might get
2286+ * invalidated before the constraint is to be checked, but we
2287+ * should queue the event to apply the check later.
2288+ */
2289+ switch (ri_NullCheck (new_row , & riinfo , false))
2290+ {
2291+ case RI_KEYS_ALL_NULL :
2292+ return false;
2293+ case RI_KEYS_SOME_NULL :
2294+ return true;
2295+ case RI_KEYS_NONE_NULL :
2296+ break ; /* continue with the check */
2297+ }
2298+
2299+ /*
2300+ * If the original row was inserted by our own transaction, we
2301+ * must fire the trigger whether or not the keys are equal. This
2302+ * is because our UPDATE will invalidate the INSERT so that the
2303+ * INSERT RI trigger will not do anything; so we had better do the
2304+ * UPDATE check. (We could skip this if we knew the INSERT
2305+ * trigger already fired, but there is no easy way to know that.)
2306+ */
2307+ if (TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetXmin (old_row -> t_data )))
2308+ return true;
22402309
2241- /* Handle MATCH PARTIAL set null delete. */
2310+ /* If all old and new key values are equal, no check is needed */
2311+ if (ri_KeysEqual (fk_rel , old_row , new_row , & riinfo , false))
2312+ return false;
2313+
2314+ /* Else we need to fire the trigger. */
2315+ return true;
2316+
2317+ /* Handle MATCH PARTIAL check. */
22422318 case FKCONSTR_MATCH_PARTIAL :
22432319 ereport (ERROR ,
22442320 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
@@ -3376,6 +3452,11 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
33763452 * ri_KeysEqual -
33773453 *
33783454 * Check if all key values in OLD and NEW are equal.
3455+ *
3456+ * Note: at some point we might wish to redefine this as checking for
3457+ * "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be
3458+ * considered equal. Currently there is no need since all callers have
3459+ * previously found at least one of the rows to contain no nulls.
33793460 * ----------
33803461 */
33813462static bool
0 commit comments