mssql_upsert, the insert-or-update tool for Microsoft SQL Server exposed through the Agents Platform.
Authentication
Same asmssql_execute_query: SQL Authentication credentials plus TLS options (encrypt, trust_server_certificate, host_name_in_certificate) and an optional schema (default dbo), all injected by the platform from the stored integration — not entered per run.
mssql_upsert
Inserts a row into a SQL Server table, or updates it in place when a conflict key already exists, via MERGE ... WHEN MATCHED THEN UPDATE ... WHEN NOT MATCHED THEN INSERT. This is the SQL Server equivalent of postgres_upsert’s INSERT ... ON CONFLICT.
This is a mutating tool.
Inputs
Required:table: Target table name. The table must have a unique constraint onconflict_column— see the concurrency note below.conflict_column: Unique/primary key column used to detect an existing row. Must be present in every row’s values.- One of:
values: a single column → value object (or a${VAR_N}reference to one).rows: an array of column → value objects for a batch upsert (or a${VAR_N}reference to one) — the whole batch commits or rolls back together.query: a complete SQL statement (INSERT/UPDATE/DELETE/MERGE, or a SELECT/OUTPUT-returning statement) run directly, bypassing the structured path entirely.
updated_at_column: column set toSYSUTCDATETIME()when an existing row is updated.
values, rows, or query should be supplied; query takes priority and ignores the others.
MERGE semantics and the concurrency caveat
The generated statement wraps the target inWITH (HOLDLOCK):
HOLDLOCK narrows, but does not fully eliminate, SQL Server’s documented MERGE race under concurrent writers: two simultaneous MERGE statements against the same key can both evaluate WHEN NOT MATCHED and both attempt an insert, causing a duplicate-key error or a deadlock. Unlike Postgres’s ON CONFLICT (atomic at the engine level), this is a real, if narrow, residual risk. v1 does not retry on a duplicate-key error — this is a known limitation, not a bug. Mitigate it by:
- Always having a unique constraint on
conflict_column(required regardless — without it,MERGE’sWHEN NOT MATCHEDsemantics are meaningless). - Avoiding very high write concurrency against the same key from multiple agent tasks running in parallel.
^[a-zA-Z_][a-zA-Z0-9_]*$) and then bracket-quoted ([identifier]) before being placed in the generated SQL — this guards against SQL Server’s long reserved/contextual keyword list (user, key, order, identity, transaction, …) colliding with a real column name. Values are always parameterized (@p1, @p2, ...), never interpolated.
Batch upsert transaction semantics
Unlike Postgres’spgx.Batch (one network round-trip for the whole batch), the SQL Server driver has no equivalent pipelining API: each row in rows runs as its own MERGE statement, sequentially, inside one transaction — so a batch of N rows means N round-trips, not N separate all-or-nothing commits. Correctness is unchanged (the whole batch still commits or rolls back together); only latency scales with batch size.
Output
Single-row (values) mode:
inserted:trueif newly inserted,falseif an existing row was updatedrows_affected,table,conflict_column,conflict_value
rows) mode:
total,inserted,updated,unchangedcounts,table,conflict_column
query mode:
command: the statement’s leading keyword (INSERT,UPDATE,MERGE, …)rows_affected: exact for a plain write with noOUTPUTclause (from the driver’s result); for aSELECTor anOUTPUT-returning statement, a best-effort count of returned rowscolumns,rows,truncatedwhen the statement returns rows
Notes
rows/values/queryall accept a${VAR_N}reference to a prior step’s output, so a large batch doesn’t need to be re-emitted as a literal argument.- Every upsert execution emits a HIPAA audit event (
external_call, providermssql) recording the host, latency, and outcome — SQL Server, like SFTP, is a raw (non-HTTP) protocol, so this is emitted manually rather than via the generic HTTP audit transport. - For the self-signed-certificate escape hatch (
trust_server_certificate), see SQL Server Execute Query.