Merging rows

sqlc supports PostgreSQL’s MERGE statement (PostgreSQL 15+). Parameters are inferred in every clause: the source subquery, the join condition, WHEN conditions, UPDATE SET assignments, and INSERT ... VALUES lists.

CREATE TABLE inventory (
  sku        text PRIMARY KEY,
  quantity   integer NOT NULL,
  updated_by text
);

CREATE TABLE inventory_updates (
  sku   text PRIMARY KEY,
  delta integer NOT NULL
);
-- name: SyncInventory :exec
MERGE INTO inventory AS t
USING inventory_updates AS u
ON t.sku = u.sku
WHEN MATCHED AND t.quantity + u.delta <= $1 THEN
    DELETE
WHEN MATCHED THEN
    UPDATE SET quantity = t.quantity + u.delta, updated_by = $2
WHEN NOT MATCHED THEN
    INSERT (sku, quantity, updated_by) VALUES (u.sku, u.delta, $2);
type SyncInventoryParams struct {
	Quantity  int32
	UpdatedBy sql.NullString
}

func (q *Queries) SyncInventory(ctx context.Context, arg SyncInventoryParams) error {
	_, err := q.db.ExecContext(ctx, syncInventory, arg.Quantity, arg.UpdatedBy)
	return err
}

Returning merged rows

On PostgreSQL 17 and later, MERGE supports a RETURNING clause, which can be used with :one or :many. The clause can reference columns from both the target and the source relations.

-- name: MergeInventory :many
MERGE INTO inventory AS t
USING inventory_updates AS u
ON t.sku = u.sku
WHEN MATCHED THEN
    UPDATE SET quantity = u.delta
WHEN NOT MATCHED THEN
    INSERT (sku, quantity) VALUES (u.sku, u.delta)
RETURNING t.sku, t.quantity;
type MergeInventoryRow struct {
	Sku      string
	Quantity int32
}

func (q *Queries) MergeInventory(ctx context.Context) ([]MergeInventoryRow, error) {
	// ...
}

Note that sqlc does not validate the server version: generating code for a MERGE ... RETURNING query succeeds, but running it against a server older than PostgreSQL 17 returns a syntax error at execution time.