qbrs_core/raw.rs
1//! The `sql!{}` escape hatch: a pressure valve for SQL constructs the typed
2//! builder doesn't cover yet.
3//!
4//! A `?` slot takes a value, which binds as a parameter, or an expression —
5//! a column, an aggregate, another fragment — which the renderer writes out
6//! itself. A column slot is quoted by the same code that quotes it anywhere
7//! else and carries its table into the fragment's `Req`, so
8//! `sql!(Numeric, "sum(?)", invoices::amount)` is checked against the
9//! query's scope like any other expression. Only the text between the slots
10//! is unchecked.
11//!
12//! `sql!` binds that text to a `const` first, so text assembled at runtime
13//! cannot reach SQL through this macro. The primitive it expands to,
14//! `expr::raw_expr`, takes a bare `&'static str` and has no such guard —
15//! `String::leak` reaches it. It is `#[doc(hidden)]` because `sql!` is the
16//! door; a caller who walks around it is writing the unchecked SQL
17//! themselves. Every `?` in the text is a slot; a literal `?` belongs in a
18//! slot's value, since MySQL and SQLite spell their own bind parameters the
19//! same way.
20
21/// `sql!(Bool, "lower(?) = ?", users::email, "dan")` -> a `Declared`
22/// expression over whatever tables its slots name — keyed as `row::Anon`,
23/// so it is selectable but not readable by name until `.label(..)`. Slots are filled positionally, left to
24/// right.
25///
26/// Every `?` in the text is a slot: there is no escape for a literal one,
27/// because two of the three dialects write their own bind parameters as `?`
28/// and a `?` left in the text would be read as one. A string containing a
29/// `?` goes in a slot, where it binds as a value; Postgres's `jsonb`
30/// operators are reached through their function spellings
31/// (`jsonb_exists(?, 'key')`).
32#[macro_export]
33macro_rules! sql {
34 // The expansion is a *call*, with the const-guard block as its first
35 // argument: a block in tail position is coerced to whatever the
36 // surrounding expression expects, which made `!sql!(..)` unify against
37 // the operand type instead of `Not::Output`.
38 ($sql_type:ty, $text:expr $(, $arg:expr)* $(,)?) => {
39 $crate::expr::raw_expr::<$sql_type, _>(
40 {
41 // Binding the text to a `const` first is what rejects a
42 // runtime string: `concat!`, `include_str!` and a `const` of
43 // your own all pass, an assembled `String` does not. The
44 // binding is named for the rule because rustc quotes the
45 // line back at whoever breaks it.
46 const SQL_TEXT_MUST_BE_A_LITERAL: &'static str = $text;
47 // Both counts are constants here, so a mismatch is a compile
48 // error rather than a panic when the expression is built.
49 const _: () = ::std::assert!(
50 $crate::expr::placeholder_count(SQL_TEXT_MUST_BE_A_LITERAL)
51 == <[&'static str]>::len(&[$(::std::stringify!($arg)),*]),
52 "`sql!` needs one argument per `?` slot",
53 );
54 SQL_TEXT_MUST_BE_A_LITERAL
55 },
56 ($($arg,)*),
57 )
58 };
59}