7.9.8. SQL-08 — _where Predicates: the Full Surface
Inside _sql(...), _where(predicate) accumulates a WHERE
clause via a recursive AST walk. Multiple _where calls compose
with AND. This tutorial walks every predicate shape the chunk-3
analyzer recognizes; anything outside this surface raises a
compile-time macro_error pointing at the offending node.
7.9.8.1. Recognized predicate shapes
Source shape |
Lowered SQL |
|---|---|
|
column reference (quoted identifier) |
|
bind parameter ( |
|
|
|
same operators |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7.9.8.2. Captured-variable equality
The simplest pattern — a free variable on one side, a column ref on the other:
let target = 3
let one <- _sql(db |> select_from(type<Car>)
|> _where(_.Id == target))
// emits: ... WHERE "Id" = ? binds: [3]
7.9.8.3. Composition with multiple _where
Each _where adds an AND-clause; chain freely:
let cheap_F <- _sql(db |> select_from(type<Car>)
|> _where(_.Price < 1000)
|> _where(_.Name |> starts_with("F")))
// emits: ... WHERE "Price" < ? AND "Name" LIKE ? || '%'
// binds: [1000, "F"]
7.9.8.4. Boolean operators
&&, ||, ! lower to SQL AND / OR / NOT. Use
parentheses to disambiguate precedence:
let cheap_or_F <- _sql(db |> select_from(type<Car>)
|> _where(_.Price < 100
|| (_.Name |> starts_with("F"))))
7.9.8.5. String-tests via LIKE
starts_with / ends_with / contains lower to a LIKE
pattern:
let ends_da <- _sql(db |> select_from(type<Car>)
|> _where(_.Name |> ends_with("da")))
let contains_o <- _sql(db |> select_from(type<Car>)
|> _where(_.Name |> contains("o")))
7.9.8.6. Case folding
to_lower / to_upper lower to LOWER / UPPER:
let case_match <- _sql(db |> select_from(type<Car>)
|> _where(_.Name |> to_lower() == "ford"))
7.9.8.7. String length and numeric scalar
length(s) and x |> abs() lower to LENGTH(...) and
ABS(...):
let long_names <- _sql(db |> select_from(type<Car>)
|> _where(length(_.Name) > 5))
let dear <- _sql(db |> select_from(type<Car>)
|> _where((_.Price |> abs()) > 5000))
7.9.8.8. Inspecting the emitted SQL
_sql_text returns the SQL the macro would emit instead of running
it — useful when the chain grows complex:
let sql = _sql_text(db |> select_from(type<Car>)
|> _where((_.Price |> abs()) > 5000))
// sql == 'SELECT "Id", "Name", "Price" FROM "Cars" WHERE ABS("Price") > ?'
7.9.8.9. Untranslatable predicates
If you write a shape the analyzer doesn’t recognize (a user-defined
function, a math op without a builtin lowering, a regex), the macro
raises macro_error at compile time pointing at the offending
node. For those cases, drop down to the raw-SQL escape hatch
(query_one, query_scalar, exec) covered in
SQL-05 — Parameter Binding.
See also
Full source: tutorials/sql/08-where.das
Previous tutorial: SQL-07 — Anatomy of _sql
Next tutorial: SQL-09 — _select Projections