@@ -2275,6 +2275,116 @@ ExecOnConflictUpdate(ModifyTableContext *context,
2275
2275
return true;
2276
2276
}
2277
2277
2278
+ /*
2279
+ * Perform MERGE.
2280
+ */
2281
+ static TupleTableSlot *
2282
+ ExecMerge (ModifyTableContext * context , ResultRelInfo * resultRelInfo , ChunkDispatchState * cds ,
2283
+ ItemPointer tupleid , HeapTuple oldtuple , bool canSetTag )
2284
+ {
2285
+ TupleTableSlot * rslot = NULL ;
2286
+ bool matched ;
2287
+
2288
+ /*-----
2289
+ * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
2290
+ * valid, depending on whether the result relation is a table or a view.
2291
+ * We execute the first action for which the additional WHEN MATCHED AND
2292
+ * quals pass. If an action without quals is found, that action is
2293
+ * executed.
2294
+ *
2295
+ * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
2296
+ * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
2297
+ * in sequence until one passes. This is almost identical to the WHEN
2298
+ * MATCHED case, and both cases are handled by ExecMergeMatched().
2299
+ *
2300
+ * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
2301
+ * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
2302
+ * TARGET] actions in sequence until one passes.
2303
+ *
2304
+ * Things get interesting in case of concurrent update/delete of the
2305
+ * target tuple. Such concurrent update/delete is detected while we are
2306
+ * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
2307
+ *
2308
+ * A concurrent update can:
2309
+ *
2310
+ * 1. modify the target tuple so that the results from checking any
2311
+ * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
2312
+ * SOURCE actions potentially change, but the result from the join
2313
+ * quals does not change.
2314
+ *
2315
+ * In this case, we are still dealing with the same kind of match
2316
+ * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
2317
+ * actions from the start and choose the first one that satisfies the
2318
+ * new target tuple.
2319
+ *
2320
+ * 2. modify the target tuple in the WHEN MATCHED case so that the join
2321
+ * quals no longer pass and hence the source and target tuples no
2322
+ * longer match.
2323
+ *
2324
+ * In this case, we are now dealing with a NOT MATCHED case, and we
2325
+ * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
2326
+ * TARGET] actions. First ExecMergeMatched() processes the list of
2327
+ * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
2328
+ * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
2329
+ * TARGET] actions in sequence until one passes. Thus we may execute
2330
+ * two actions; one of each kind.
2331
+ *
2332
+ * Thus we support concurrent updates that turn MATCHED candidate rows
2333
+ * into NOT MATCHED rows. However, we do not attempt to support cases
2334
+ * that would turn NOT MATCHED rows into MATCHED rows, or which would
2335
+ * cause a target row to match a different source row.
2336
+ *
2337
+ * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
2338
+ * [BY TARGET].
2339
+ *
2340
+ * ExecMergeMatched() takes care of following the update chain and
2341
+ * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
2342
+ * action, as long as the target tuple still exists. If the target tuple
2343
+ * gets deleted or a concurrent update causes the join quals to fail, it
2344
+ * returns a matched status of false and we call ExecMergeNotMatched().
2345
+ * Given that ExecMergeMatched() always makes progress by following the
2346
+ * update chain and we never switch from ExecMergeNotMatched() to
2347
+ * ExecMergeMatched(), there is no risk of a livelock.
2348
+ */
2349
+ #if PG17_LT
2350
+ matched = tupleid != NULL ;
2351
+ #else
2352
+ matched = tupleid != NULL || oldtuple != NULL ;
2353
+ #endif
2354
+ if (matched )
2355
+ rslot = ExecMergeMatched (context , resultRelInfo , tupleid , oldtuple ,
2356
+ canSetTag , & matched );
2357
+
2358
+ /*
2359
+ * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
2360
+ * join, or a previously MATCHED tuple for which ExecMergeMatched() set
2361
+ * "matched" to false, indicating that it no longer matches).
2362
+ */
2363
+ if (!matched )
2364
+ {
2365
+ /*
2366
+ * If a concurrent update turned a MATCHED case into a NOT MATCHED
2367
+ * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
2368
+ * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
2369
+ * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
2370
+ * SOURCE action, and computed the row to return. If so, we cannot
2371
+ * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
2372
+ * pending (to be processed on the next call to ExecModifyTable()).
2373
+ * Otherwise, just process the action now.
2374
+ */
2375
+ #if PG17_LT
2376
+ ExecMergeNotMatched (context , resultRelInfo , cds , canSetTag );
2377
+ #else
2378
+ if (rslot == NULL )
2379
+ rslot = ExecMergeNotMatched (context , resultRelInfo , cds , canSetTag );
2380
+ else
2381
+ context -> mtstate -> mt_merge_pending_not_matched = context -> planSlot ;
2382
+ #endif
2383
+ }
2384
+
2385
+ return rslot ;
2386
+ }
2387
+
2278
2388
2279
2389
static void fireASTriggers (ModifyTableState * node );
2280
2390
static void fireBSTriggers (ModifyTableState * node );
@@ -3431,83 +3541,3 @@ ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3431
3541
return rslot ;
3432
3542
}
3433
3543
3434
- /*
3435
- * Perform MERGE.
3436
- */
3437
- TupleTableSlot *
3438
- ExecMerge (ModifyTableContext * context , ResultRelInfo * resultRelInfo , ChunkDispatchState * cds ,
3439
- ItemPointer tupleid , HeapTuple oldtuple , bool canSetTag )
3440
- {
3441
- bool matched ;
3442
- TupleTableSlot * rslot = NULL ;
3443
-
3444
- /*-----
3445
- * If we are dealing with a WHEN MATCHED case (tupleid is valid), we
3446
- * execute the first action for which the additional WHEN MATCHED AND
3447
- * quals pass. If an action without quals is found, that action is
3448
- * executed.
3449
- *
3450
- * Similarly, if we are dealing with WHEN NOT MATCHED case, we look at
3451
- * the given WHEN NOT MATCHED actions in sequence until one passes.
3452
- *
3453
- * Things get interesting in case of concurrent update/delete of the
3454
- * target tuple. Such concurrent update/delete is detected while we are
3455
- * executing a WHEN MATCHED action.
3456
- *
3457
- * A concurrent update can:
3458
- *
3459
- * 1. modify the target tuple so that it no longer satisfies the
3460
- * additional quals attached to the current WHEN MATCHED action
3461
- *
3462
- * In this case, we are still dealing with a WHEN MATCHED case.
3463
- * We recheck the list of WHEN MATCHED actions from the start and
3464
- * choose the first one that satisfies the new target tuple.
3465
- *
3466
- * 2. modify the target tuple so that the join quals no longer pass and
3467
- * hence the source tuple no longer has a match.
3468
- *
3469
- * In this case, the source tuple no longer matches the target tuple,
3470
- * so we now instead find a qualifying WHEN NOT MATCHED action to
3471
- * execute.
3472
- *
3473
- * XXX Hmmm, what if the updated tuple would now match one that was
3474
- * considered NOT MATCHED so far?
3475
- *
3476
- * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED.
3477
- *
3478
- * ExecMergeMatched takes care of following the update chain and
3479
- * re-finding the qualifying WHEN MATCHED action, as long as the updated
3480
- * target tuple still satisfies the join quals, i.e., it remains a WHEN
3481
- * MATCHED case. If the tuple gets deleted or the join quals fail, it
3482
- * returns and we try ExecMergeNotMatched. Given that ExecMergeMatched
3483
- * always make progress by following the update chain and we never switch
3484
- * from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
3485
- * livelock.
3486
- */
3487
- #if PG17_GE
3488
- matched = tupleid != NULL || oldtuple != NULL ;
3489
- #else
3490
- matched = tupleid != NULL ;
3491
- #endif
3492
- if (matched )
3493
- rslot = ExecMergeMatched (context , resultRelInfo , tupleid , oldtuple , canSetTag , & matched );
3494
-
3495
- /*
3496
- * Either we were dealing with a NOT MATCHED tuple or
3497
- * ExecMergeMatched() returned "false", indicating the previously
3498
- * MATCHED tuple no longer matches.
3499
- */
3500
- if (!matched )
3501
- {
3502
- #if PG17_GE
3503
- if (rslot == NULL )
3504
- rslot = ExecMergeNotMatched (context , resultRelInfo , cds , canSetTag );
3505
- else
3506
- context -> mtstate -> mt_merge_pending_not_matched = context -> planSlot ;
3507
- #else
3508
- (void ) ExecMergeNotMatched (context , resultRelInfo , cds , canSetTag );
3509
- #endif
3510
- }
3511
-
3512
- return rslot ;
3513
- }
0 commit comments