Skip to content

refactor(psql/mm): replace WHEN clause mods with fluent chain API#660

Merged
stephenafamo merged 1 commit intostephenafamo:mainfrom
atzedus:refactor/merge-when-chain-api
Apr 25, 2026
Merged

refactor(psql/mm): replace WHEN clause mods with fluent chain API#660
stephenafamo merged 1 commit intostephenafamo:mainfrom
atzedus:refactor/merge-when-chain-api

Conversation

@atzedus
Copy link
Copy Markdown
Contributor

@atzedus atzedus commented Apr 25, 2026

Sorry for the back-and-forth on this - after the initial merge implementation I went back and re-read the MERGE statement syntax more carefully:

WHEN MATCHED [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING }
WHEN NOT MATCHED BY SOURCE [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING }
WHEN NOT MATCHED [ BY TARGET ] [ AND condition ] THEN { merge_insert | DO NOTHING }

It became clear that each WHEN MATCHED / WHEN NOT MATCHED clause is a self-contained, standalone expression — the condition (AND ...) and the action (THEN ...) are integral parts of that single clause, not independent modifiers applied to some external builder. The previous functional-options approach (WhenMatched(And(...), ThenUpdate(...))) obscured this structure by treating the condition and action as separate, unrelated mods passed into a bag.

Switching to a fluent chain makes the code mirror the SQL grammar directly:

// Before
mm.WhenMatched(
    mm.And(psql.Quote("s", "quantity").GT(psql.Arg(0))),
    mm.ThenUpdate(
        mm.SetCol("quantity").ToExpr(psql.Quote("s", "quantity")),
    ),
)

// After
mm.WhenMatched().
    And(psql.Quote("s", "quantity").GT(psql.Arg(0))).
    ThenUpdate(
        mm.SetCol("quantity").ToExpr(psql.Quote("s", "quantity")),
    )

Changes

  • WhenMatched(), WhenNotMatched(), WhenNotMatchedByTarget(), WhenNotMatchedBySource() now take no arguments and return a chain type
  • WhenMatchedChain (for MATCHED / NOT MATCHED BY SOURCE) exposes .And(), .ThenUpdate(), .ThenDelete(), .ThenDoNothing()
  • WhenNotMatchedChain (for NOT MATCHED [BY TARGET]) exposes .And(), .ThenInsert(), .ThenInsertDefaultValues(), .ThenDoNothing()
  • Removed standalone And, ThenDoNothing, ThenDelete, ThenUpdate, ThenInsert, ThenInsertDefaultValues functions and the WhenClause type
  • All existing tests updated and passing

Change WhenMatched/WhenNotMatched/WhenNotMatchedByTarget/WhenNotMatchedBySource
from functional options pattern to method chaining.

Before:
  mm.WhenMatched(
      mm.And(condition),
      mm.ThenUpdate(mm.SetCol("x").ToExpr(e)),
  )

After:
  mm.WhenMatched().And(condition).ThenUpdate(
      mm.SetCol("x").ToExpr(e),
  )

Introduce WhenMatchedChain (for MATCHED / NOT MATCHED BY SOURCE)
and WhenNotMatchedChain (for NOT MATCHED [BY TARGET]) with
type-safe terminal methods: ThenUpdate, ThenDelete, ThenDoNothing,
ThenInsert, ThenInsertDefaultValues.

Remove standalone And, ThenDoNothing, ThenDelete, ThenUpdate,
ThenInsert, ThenInsertDefaultValues functions and WhenClause type.

Co-authored-by: Copilot <copilot@github.com>
@stephenafamo stephenafamo merged commit febd197 into stephenafamo:main Apr 25, 2026
8 checks passed
@atzedus atzedus deleted the refactor/merge-when-chain-api branch April 26, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants