Skip to main content

Update

update changes fields on rows that match a filter. Like insert, it's a dedicated protocol verb — not an ONQL query — but the query field inside an update payload is an ONQL filter expression, so everything you know from the filters chapter applies.

Wire payload

{
"db": "mydb",
"table": "users",
"records": { "status": "active", "last_login": "2026-04-19T10:00:00Z" },
"query": "id = 42",
"protopass": "default"
}
FieldMeaning
db / tableTarget database and table
recordsObject of columns to set on matched rows
queryONQL filter expression — same grammar as inside [...] in a read query
protopassProtocol identifier

Only rows matching query are touched. An empty query is rejected to prevent accidental table-wide updates — pass a deliberately-broad filter like id > 0 if you really mean it.

Shell

The shell accepts either a raw JSON payload or a shorthand:

# Shorthand — <db>.<table> <records-json> on <filter>
onql> update mydb.users {"status":"active"} on id = 42

# Raw JSON — equivalent
onql> update {"db":"mydb","table":"users","records":{"status":"active"},"query":"id = 42"}

The shorthand splits on the first space (for db.table) and on on (for the filter). Use raw JSON if your filter itself contains the literal on substring.

Filter grammar

query uses the same expressions as read-query filters:

status = "pending" and created_at < "2026-01-01"
country in ["IN","US","DE"] or vip = true

See Language → Filters → Operators for the full operator list, and Parameters for using $1, $2 substitutions (driver-side) instead of inlining values.

From a driver

# Python
client.update(
db="mydb", table="users",
records={"status": "active"},
query="id = 42",
)
// Node
await client.update({
db: 'mydb', table: 'users',
records: { status: 'active' },
query: 'id = 42',
});

Response

Returns the updated row(s), or {"error": "..."} on failure. If the filter matches zero rows the response is an empty array — not an error.

Idempotency

update is idempotent on the field set, but not on side effects: re-running the same update will re-trigger any on_update protocol hooks, bump updated_at timestamps, etc. If you need exactly-once semantics, gate on a condition column (e.g. status = "pending") so the second run matches zero rows.