| From: | Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com> |
|---|---|
| To: | Tender Wang <tndrwang(at)gmail(dot)com> |
| Cc: | Jeff Davis <pgsql(at)j-davis(dot)com>, pgsql-bugs(at)postgresql(dot)org |
| Subject: | Re: wrong results: merge when not matched by source |
| Date: | 2026-09-18 11:03:25 |
| Message-ID: | CAEZATCU795F4OP+NvDvEUjcmND6pk0iOdePNGQNQvQkhTvhJfA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
On Fri, 18 Sept 2026 at 03:02, Tender Wang <tndrwang(at)gmail(dot)com> wrote:
>
> Jeff Davis <pgsql(at)j-davis(dot)com> 于2026年9月18日周五 03:18写道:
> >
> > -- Expected: only UPDATE of key=1; table is {(1, src)}.
> > -- Actual: also INSERT of (NULL, NULL).
>
>
> I think the problem is that the TM_Deleted case in
> ExecMergeMatched() unconditionally sets *matched to false.
>
> This is correct when the candidate row was originally a MATCHED row:
> the source row still exists after the target row is concurrently
> deleted, so it should be processed by the NOT MATCHED BY TARGET
> actions.
>
> However, it is not correct when processing an original NOT MATCHED BY
> SOURCE row. In that case, the source row was already absent, and after
> the concurrent deletion of the target row, there is no candidate row
> left to process. Setting *matched to false causes ExecMerge() to
> call ExecMergeNotMatched(), which executes the NOT MATCHED BY TARGET
> INSERT using the null source side of the original join row.
Yes, that analysis is correct. If there was no source row, and the
target row is concurrently deleted, then there's nothing left to do.
It shouldn't execute a NOT MATCHED BY TARGET action if there was never
a source row to begin with.
> A tentative fix is to set *matched to false only if the current
> action is a MATCHED action, as in the attached patch.
That fix looks good to me. Nice and simple.
> There is another TM_Deleted case below, reached while handling
> TM_Updated and following the update chain:
> ...
> case TM_Deleted:
> *matched = false;
> goto out;
> ...
>
> I think that case may need similar treatment. The existing
> was_matched variable could perhaps be used there:
> ...
> if (was_matched)
> *matched = false;
Yes, that's basically the same. It can be triggered by a slight
modification to the original test case, making Session 1 do an UPDATE
before the DELETE:
-- Session 1:
BEGIN ISOLATION LEVEL READ COMMITTED;
UPDATE target SET val = 'nms-updated' WHERE key = 2; -- holds the NMS row
DELETE FROM target WHERE key = 2;
> The attached patch is intended to show the proposed fix and does not
> yet include a regression test.
Attached is an update with the fix for the second case, and tests for both.
Regards,
Dean
| Attachment | Content-Type | Size |
|---|---|---|
| v2-fix-concurrent-delete-issue-with-merge-when-not-matched-by-source.patch | text/x-patch | 11.3 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Dean Rasheed | 2026-09-18 12:21:53 | Re: wrong results: merge when not matched by source |
| Previous Message | Andrey Rachitskiy | 2026-09-18 09:37:50 | Re: BUG #19697: HAVING-to-WHERE transfer gives wrong count when scale(numeric) distinguishes equal grouping values |