7.9.24. SQL-23 — Foreign keys
DDL-only foreign-key support: a child column names its parent struct,
optional on_delete / on_update actions pick the integrity
behavior. dasSQLITE deliberately ships no navigation properties
— to query across a relationship use _join (SQL-15 — _join (inner equi-join)),
_left_join (SQL-16 — _left_join with Option<TB>), or _any / _in
correlated subqueries (SQL-17 — Subqueries).
7.9.24.1. Annotation shape
Annotation |
Meaning |
|---|---|
|
parent struct (must have |
|
CASCADE / SET NULL / SET DEFAULT / RESTRICT / NO ACTION |
|
same value set |
Each decorator is a separate @name = value line — daslang field
annotations are scalar key/value pairs, not function-call shapes.
@sql_references resolves at [sql_table] expansion time: it
finds the named struct in the same module, reads its [sql_table(name
= ...)], and finds the @sql_primary_key field. Defaults when
omitted: on_delete = "no_action", on_update = "no_action".
7.9.24.2. PRAGMA foreign_keys = ON
with_sqlite enables the pragma on every connection. SQLite ships
it OFF for back-compat with pre-3.6.19 databases; without it FK
clauses are parsed and stored but never enforced — orphans silently
appear and CASCADE silently no-ops. dasSQLITE turns it on always.
7.9.24.3. CASCADE delete
[sql_table(name = "Users")]
struct User {
@sql_primary_key Id : int
Name : string
}
[sql_table(name = "Orders")]
struct Order {
@sql_primary_key Id : int
@sql_references = "User"
@sql_on_delete = "cascade"
UserId : int
Total : float
}
db |> _sql_delete(type<User>, _.Id == 1)
// DELETE FROM "Users" WHERE "Id" = ?
// (SQLite cascades internally to DELETE FROM "Orders"
// WHERE "UserId" = 1)
7.9.24.4. SET NULL requires Option<T>
The FK column must be Option<T> so SQLite can write NULL when
the parent goes away. [sql_table] rejects the combination at
compile time if the column is plain T.
[sql_table(name = "Comments")]
struct Comment {
@sql_primary_key Id : int
@sql_references = "User"
@sql_on_delete = "set_null"
@safe_when_uninitialized AuthorId : Option<int>
Body : string
}
7.9.24.5. Action reference
Annotation value |
|
|---|---|
|
CASCADE — propagate the change to children |
|
SET NULL — requires |
|
SET DEFAULT — requires field-init or @sql_default_fn |
|
RESTRICT — reject the change immediately |
|
NO ACTION — the SQL standard default |
7.9.24.6. FK violations through try_insert
Constraint violations surface as SQLite errors at insert/update
time. try_insert wraps them as Result.
let res = db |> try_insert(Order(Id = 999, UserId = 9999, Total = 1.0f))
if (res |> is_err) {
// "FOREIGN KEY constraint failed"
}
7.9.24.7. Drop / create order
Parent before child on create; child before parent on drop. The FK clause names the parent table by name, so the parent must exist when the child is created.
See also
Full source: tutorials/sql/23-foreign_keys.das
Previous tutorial: SQL-22 — Transactions
Next tutorial: SQL-24 — Indexes