cake/update
A DSL to build UPDATE queries.
Aliases
import cake/update as u
import cake/where as w
import cake/join as j
Query Lifecycle
flowchart LR
A[u.new] --> B[u.table]
B --> C[u.set / u.sets]
C --> D[u.from_table / u.from_sub_query]
D --> E[u.join]
E --> F[u.where]
F --> G[u.returning]
G --> H[u.to_query]
Constructor
new() -> Update(a)
Creates an empty Update query.
to_query(update: Update(a)) -> WriteQuery(a)
Converts an Update into a WriteQuery for execution.
Table
table(update, name) -> Update(a)
Sets the target table for the UPDATE.
u.new() |> u.table("users")
// UPDATE users SET ...
SET clauses
Each UpdateSet represents one column = value assignment. You accumulate
sets with set() / sets(), then Cake renders them as a SET col = val, ...
clause.
Value setters
| Function | SQL equivalent |
|---|---|
set_bool(col, value) | col = TRUE/FALSE |
set_true(col) | col = TRUE |
set_false(col) | col = FALSE |
set_float(col, value) | col = $n (float) |
set_int(col, value) | col = $n (int) |
set_string(col, value) | col = $n (string) |
set_null(col) | col = NULL |
set_date(col, date) | col = $n (date) |
set_expression(col, expr) | col = raw_expr |
set_sub_query(col, query) | col = (SELECT ...) |
set_fragment(col, fragment) | col = <fragment> |
u.new()
|> u.table("users")
|> u.set(u.set_string("name", "Alice"))
|> u.set(u.set_int("age", 31))
|> u.set(u.set_null("deleted_at"))
// UPDATE users SET name = $1, age = $2, deleted_at = NULL
set_expression(col, expr) -> UpdateSet
Injects a raw SQL expression on the right-hand side. Useful for self-referencing updates or database functions.
u.set_expression("score", "score + 10")
// score = score + 10
u.set_expression("updated_at", "NOW()")
// updated_at = NOW()
set_fragment(col, fragment) -> UpdateSet
Binds a prepared fragment as the RHS. Preferred over set_expression when
user-controlled values are involved.
import cake/fragment as f
u.set_fragment(
"org_id",
f.prepared("?::uuid", [f.string("0000-0000-4000-a000-a00000000000")]),
)
set_sub_query(col, query) -> UpdateSet
Sets a column to the result of a sub-query.
import cake/select as s
let sub =
s.new()
|> s.from_table("profiles")
|> s.col("display_name")
|> s.where(w.eq(w.col("profiles.user_id"), w.col("users.id")))
|> s.to_query
u.new()
|> u.table("users")
|> u.set(u.set_sub_query("cached_name", sub))
Multi-column setters
| Function | Notes |
|---|---|
sets_expression(cols, expr) | Expression must return same column count |
sets_sub_query(cols, query) | Sub-query must return same column count |
Accumulating vs replacing sets
| Function | Effect |
|---|---|
set(update, set) | Append one UpdateSet |
set_replace(update, set) | Replace all with one UpdateSet |
sets(update, sets) | Append many UpdateSets |
sets_replace(update, sets) | Replace all with many UpdateSets |
FROM clause
On 🐘 PostgreSQL and 🪶 SQLite an UPDATE supports a FROM clause to join
additional tables for use in SET expressions or WHERE conditions.
🦭 MariaDB and 🐬 MySQL do not use
FROMinUPDATE; useJOINinstead.
from_table(update, name) -> Update(a)
u.new()
|> u.table("employees")
|> u.from_table("departments")
|> u.set(u.set_expression("salary", "departments.budget / 10"))
|> u.where(w.eq(w.col("employees.dept_id"), w.col("departments.id")))
// UPDATE employees SET salary = departments.budget / 10
// FROM departments WHERE employees.dept_id = departments.id
from_sub_query(update, query, alias) -> Update(a)
Use an aliased sub-query as the FROM source.
no_from(update) -> Update(a)
Remove the FROM clause.
JOIN
On 🦭 MariaDB and 🐬 MySQL JOIN is the standard way to reference other
tables in an UPDATE.
On 🐘 PostgreSQL and 🪶 SQLite,
JOINis only allowed when aFROMclause is also set.
u.new()
|> u.table("orders")
|> u.join(j.inner(
with: j.table("users"),
on: w.eq(w.col("orders.user_id"), w.col("users.id")),
alias: "users",
))
|> u.set(u.set_expression("orders.status", "'shipped'"))
|> u.where(w.eq(w.col("users.tier"), w.string("premium")))
| Function | Effect |
|---|---|
join(update, join) | Append one join |
replace_join(update, join) | Replace all joins with one |
joins(update, joins) | Append many joins |
replace_joins(update, joins) | Replace all joins |
no_join(update) | Remove all joins |
WHERE clause
See cake/where for building Where values.
where(update, where) -> Update(a)
Adds a condition with AND semantics.
u.new()
|> u.table("users")
|> u.set(u.set_bool("active", False))
|> u.where(w.lt(w.col("last_login"), w.date(cutoff_date)))
// UPDATE users SET active = $1 WHERE last_login < $2
or_where(update, where) -> Update(a)
Combines with OR semantics.
xor_where(update, where) -> Update(a)
Combines with exactly-one-true XOR semantics. Implemented via a custom
OR / AND / NOT expansion on all adapters — native XOR is not used on any
adapter, including 🦭 MariaDB / 🐬 MySQL.
For odd-parity XOR (matching 🦭 MariaDB / 🐬 MySQL native
XOR), usew.xor_parityinstead.
not_where(update, where) -> Update(a)
Negates the given condition with NOT and combines with AND semantics.
- If there is no current WHERE, the condition is set as a standalone
NOT. - If the outermost WHERE is an
AndWhere, the negated condition is appended to it. - Otherwise, the existing WHERE and the new
NOTcondition are wrapped in anAndWhere.
| Function | Effect |
|---|---|
replace_where(update, where) | Replace the entire WHERE |
no_where(update) | Remove WHERE clause |
RETURNING
Fetch column values from the updated rows.
Only supported by 🐘 PostgreSQL and 🪶 SQLite. 🦭 MariaDB and 🐬 MySQL do not support
RETURNINGinUPDATE.
u.new()
|> u.table("users")
|> u.set(u.set_int("login_count", 1))
|> u.returning(["id", "login_count"])
// UPDATE users SET login_count = $1 RETURNING id, login_count
| Function | Effect |
|---|---|
returning(update, cols) | Return the listed columns |
no_returning(update) | Remove RETURNING clause |
Epilog and Comment
u.new()
|> u.table("sessions")
|> u.set(u.set_expression("expires_at", "NOW() + INTERVAL '1 hour'"))
|> u.epilog("RETURNING id")
|> u.comment("extend active sessions")
Full Example
import cake/update as u
import cake/where as w
import cake/fragment as f
u.new()
|> u.table("products")
|> u.sets([
u.set_expression("price", "price * 0.9"),
u.set_fragment("updated_at", f.literal("NOW()")),
])
|> u.where(w.and([
w.eq(w.col("category"), w.string("electronics")),
w.gt(w.col("stock"), w.int(0)),
]))
|> u.returning(["id", "price"])
|> u.to_query
Types
pub type UpdateSets =
@internal UpdateSets
pub type UpdateTable =
@internal UpdateTable
pub type WriteQuery(a) =
@internal WriteQuery(a)
Values
pub fn comment(
update update: Update(a),
comment comment: String,
) -> Update(a)
Sets a Comment or appends into an existing Comment.
pub fn epilog(
update update: Update(a),
epilog epilog: String,
) -> Update(a)
Sets an Epilog or appends into an existing Epilog.
pub fn from_sub_query(
update update: Update(a),
query query: ReadQuery,
alias alias: String,
) -> Update(a)
Sets the FROM clause of the Update query to an aliased sub-query.
pub fn from_table(
update update: Update(a),
name table_name: String,
) -> Update(a)
Sets the FROM clause of the Update query to a table name.
pub fn get_table(update update: Update(a)) -> UpdateTable
Get the table of the Update query.
pub fn join(
update update: Update(a),
join join: Join,
) -> Update(a)
Adds a Join to the Update query.
NOTICE: On 🐘PostgreSQL and 🪶SQLite Joins are only allowed if the FROM
clause is set as well.
pub fn joins(
update update: Update(a),
joins joins: List(Join),
) -> Update(a)
Adds Joins to the Update query.
NOTICE: On 🐘PostgreSQL and 🪶SQLite Joins are only allowed if the FROM
clause is set as well.
pub fn no_returning(update update: Update(a)) -> Update(a)
NOTICE: 🦭MariaDB and 🐬MySQL do not support RETURNING in UPDATE
queries; they do support it in INSERT (and REPLACE) queries, however.
pub fn not_where(
update update: Update(a),
where where: Where,
) -> Update(a)
Sets a NotWhere or appends into an existing AndWhere.
- Wraps the given
Wherein aNotWhere, then applies it withANDsemantics: - If the query does not have a
Whereclause, the givenWhereis set as aNotWhere. - If the outermost
Whereis anAndWhere, the newNotWhereis appended to the list withinAndWhere. - If the outermost
Whereis any other kind ofWhere, this and the current outermostWhereare wrapped in anAndWhere.
pub fn or_where(
update update: Update(a),
where where: Where,
) -> Update(a)
Sets an OrWhere or appends into an existing OrWhere.
- If the outermost
Whereis anOrWhere, the newWhereis appended to the list withinOrWhere. - If the query does not have a
Whereclause, the givenWhereis set instead. - If the outermost
Whereis any other kind ofWhere, this and the current outermostWhereare wrapped in anOrWhere.
pub fn replace_join(
update update: Update(a),
join join: Join,
) -> Update(a)
Replaces any Joins of the Update query with a single Join.
NOTICE: On 🐘PostgreSQL and 🪶SQLite Joins are only allowed if the FROM
clause is set as well.
pub fn replace_joins(
update update: Update(a),
joins joins: List(Join),
) -> Update(a)
Replaces any Joins of the Update query with the given Joins.
NOTICE: On 🐘PostgreSQL and 🪶SQLite Joins are only allowed if the FROM
clause is set as well.
pub fn replace_where(
update update: Update(a),
where where: Where,
) -> Update(a)
Replaces the Where in the Update query.
pub fn returning(
update update: Update(a),
returning returning: List(String),
) -> Update(a)
NOTICE: 🦭MariaDB and 🐬MySQL do not support RETURNING in UPDATE
queries; they do support it in INSERT (and REPLACE) queries, however.
pub fn set(
update update: Update(a),
set set: UpdateSet,
) -> Update(a)
Sets or appends one column set in an Update query.
pub fn set_bool(
column column: String,
value value: Bool,
) -> UpdateSet
Sets a column to a Bool UpdateParamSet.
pub fn set_date(
column column: String,
date date: calendar.Date,
) -> UpdateSet
Sets a column to a calendar.Date UpdateParamSet.
pub fn set_expression(
column column: String,
expression expression: String,
) -> UpdateSet
Sets a column to an expression value.
pub fn set_false(column column: String) -> UpdateSet
Sets a column to a False UpdateParamSet.
pub fn set_float(
column column: String,
value value: Float,
) -> UpdateSet
Sets a column to a Float UpdateParamSet.
pub fn set_fragment(
column column: String,
value value: fragment.Fragment,
) -> UpdateSet
Sets a column to a fragment value with parameter binding.
Example
import cake/fragment as f
import cake/update as u
"org_id" |> u.set_fragment(f.prepared("$::uuid", [f.string("0000000000-0000-4000-a000-a00000000000")]))
pub fn set_int(
column column: String,
value value: Int,
) -> UpdateSet
Sets a column to a Int UpdateParamSet.
pub fn set_null(column column: String) -> UpdateSet
Sets a column to an SQL NULL UpdateParamSet.
pub fn set_replace(
update update: Update(a),
set set: UpdateSet,
) -> Update(a)
Sets or replaces one column set in an Update query.
pub fn set_string(
column column: String,
value value: String,
) -> UpdateSet
Sets a column to a string UpdateParamSet.
pub fn set_sub_query(
column column: String,
query query: ReadQuery,
) -> UpdateSet
Sets a column to a sub-query value.
pub fn set_true(column column: String) -> UpdateSet
Sets a column to a True UpdateParamSet.
pub fn sets(
update update: Update(a),
set sets: List(UpdateSet),
) -> Update(a)
Sets or appends many column sets in an Update query.
pub fn sets_expression(
columns columns: List(String),
expression expression: String,
) -> UpdateSet
Sets many columns to an expression value.
NOTICE: the expression must return an equal count of columns.
pub fn sets_replace(
update update: Update(a),
sets sets: List(UpdateSet),
) -> Update(a)
Sets or replaces many column sets in an Update query.
pub fn sets_sub_query(
columns columns: List(String),
query query: ReadQuery,
) -> UpdateSet
Sets many columns to a sub-query value.
NOTICE: the sub-query must return an equal count of columns.
pub fn table(
update update: Update(a),
name name: String,
) -> Update(a)
Sets the table of the Update query.
pub fn to_query(update update: Update(a)) -> WriteQuery(a)
Creates a WriteQuery from an Update query.
pub fn where(
update update: Update(a),
where where: Where,
) -> Update(a)
Sets an AndWhere or appends into an existing AndWhere.
- If the outermost
Whereis anAndWhere, the newWhereis appended to the list withinAndWhere. - If the query does not have a
Whereclause, the givenWhereis set instead. - If the outermost
Whereis any other kind ofWhere, this and the current outermostWhereare wrapped in anAndWhere.
pub fn xor_where(
update update: Update(a),
where where: Where,
) -> Update(a)
Sets an XorWhere or appends into an existing XorWhere.
- If the outermost
Whereis anXorWhere, the newWhereis appended to the list withinXorWhere. - If the query does not have a
Whereclause, the givenWhereis set instead. - If the outermost
Whereis any other kind ofWhere, this and the current outermostWhereare wrapped in anXorWhere.
NOTICE: Cake implements this using a custom OR / AND / NOT expansion
on all four adapters (🐘PostgreSQL, 🪶SQLite, 🦭MariaDB, 🐬MySQL) —
native XOR is not used on any adapter.
For odd-parity XOR (which on 🦭MariaDB / 🐬MySQL delegates to its native
XOR) use where.xor_parity instead.