SELECT -> MERGE Conversion
QueryBuilder.buildMergeQuery lets rawsql-ts synthesize conditional upserts while still keeping the instrumentation read-oriented. The helper accepts a single SimpleSelectQuery plus conversion options and emits a MERGE statement whose source derives from the projection and whose target is the alias you already operate against.
How the helper decides actions
- Normalize the options to fix the
target,sourceAlias,primaryKeys, and optional column lists. - Ensure the select clause exposes every primary key and any columns that will participate in updates or inserts.
- Determine update-worthy columns by removing primary keys and optionally honoring an
updateColumnswhitelist. - Build a
when matchedclause containing either anUPDATE,DELETE, orDO NOTHINGaction depending onmatchedAction. For updates, each column becomes aSetClauseItemwired to the source alias. - Build a
when not matchedclause that either inserts the source values or does nothing, and optionally addwhen not matched by sourceactions such asDELETEto handle orphaned rows. - Emit
MERGEwith the normalizedWITHclause, the correlation predicate returned bybuildEqualityPredicate, and the assembledwhenClausesarray.
By keeping all of the logic inside a single conversion helper, the testkit can reason about upserts purely in terms of the SELECT that would produce the same row-set, and the capture-and-replay path stays deterministic.
const mergeQuery = QueryBuilder.buildMergeQuery(simpleSelectQuery, {
target: 'customers',
primaryKeys: ['id'],
sourceAlias: 'src',
});You can omit the alias — the helper will infer it from the table name. You can still provide an explicit alias (e.g.,
customers c) when needed.
Conversion knobs
matchedAction:'update','delete', or'doNothing'controls whether a match updates target columns, deletes the row, or skips it altogether.notMatchedAction:'insert'or'doNothing'permits optionally skipping inserts when fixtures should leave missing rows untouched.notMatchedBySourceAction: includes'delete'to remove rows that no longer match the projection.updateColumns/insertColumns: enforce ordering and let you exclude columns from updates or inserts even when the select projection contains them.
The helper rebuilds the select clause to reflect the chosen column order and guards against duplicate/missing columns before constructing the MergeQuery.
Learn More
- QueryBuilder API for the
buildMergeQuerysignature and action builders such asMergeUpdateAction. - SQLite Testkit Guide showing how merge scenarios remain compatible with the select-driven pipeline.
- Why SQL Unit Testing Is Hard because it explains why the library locks to reads and rewrites writes instead of running them directly.