OriginChainDB docs
examples · sql · 12 / 13 · works today

12. DELETE via /sql

← SQL examples
works today

DELETE over /sql executes. On its own it removes the matched rows in a single durable frame and answers with rows_affected. A WHERE <pk> = <literal> takes a typed single-row fast path; any other supported predicate - and a composite primary key - lowers to a scan-based delete. Inside BEGIN; ... COMMIT; the same statement buffers instead, reporting rows_buffered until commit.

supported - delete inside a transaction

Inside an open transaction, a primary-key DELETE buffers a row-removal op and the COMMIT applies it durably in one frame. Carry the same session id across all three calls so they share one transaction buffer.

POST /v1/tenants/:t/sql (BEGIN / DELETE / COMMIT)
# Supported SQL route: wrap the DELETE in an explicit transaction.
# The same session id must accompany BEGIN, the DELETE, and COMMIT so
# the buffered row-removal is applied atomically on COMMIT.
SID="sess-$(date +%s)"

# 1. open the transaction
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/sql" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "X-OC-Session-Id: $SID" \
  -H "Content-Type: application/json" \
  -d '{ "sql": "BEGIN" }'

# 2. buffer the delete (matched by primary key)
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/sql" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "X-OC-Session-Id: $SID" \
  -H "Content-Type: application/json" \
  -d '{ "sql": "DELETE FROM shop.orders WHERE id = '\''o_003'\''" }'

# 3. commit - the buffered row-removal lands durably here
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/sql" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "X-OC-Session-Id: $SID" \
  -H "Content-Type: application/json" \
  -d '{ "sql": "COMMIT" }'
responses
// the DELETE step, inside the open transaction:
{
  "kind":          "delete",
  "schema":        "shop.orders",
  "pk":            "o_003",
  "rows_buffered": 1
}

// the COMMIT step - the buffered op is now durable:
{
  "kind":          "tx",
  "op":            "commit",
  "ops_committed": 1
}

rows_buffered is 1 when the row existed (and 0 when it didn't). The removal is not visible to other readers until COMMIT returns ops_committed: 1.

also supported - typed row delete

When you already hold the primary key and don't need SQL, delete the row through the typed row endpoints. The row delete path carries the same idempotency-key plumbing as the rest of the typed /rows surface.

also supported - DELETE without a transaction

With no BEGIN around it, the same statement autocommits: the rows are removed in one frame before the response returns, and rows_affected counts them. Foreign keys are enforced on this path too. Note that /sql is the one surface that accepts a bare DELETE FROM t with no WHERE and empties the table - the typed row endpoints refuse that shape.

POST /v1/tenants/:t/sql (autocommit - removes the row)
# A bare DELETE outside a transaction does NOT reliably remove the row.
# The response echoes the translated delete (the pk it resolved) - treat
# it as a translation, not a confirmation that the row is gone.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/sql" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "DELETE FROM shop.orders WHERE id = '\''o_003'\''"
  }'
response (the row is already gone)
{
  "kind":   "delete",
  "schema": "shop.orders",
  "rows_affected": 1
}

Add RETURNING order_id, total_cents (or RETURNING *) and the response also carries a rows array holding the deleted rows' values, projected to that column list. A no-match delete answers rows_affected: 0 with an empty rows array rather than an error.