qql_core/params/mod.rs
1//! Parameter binding and prepared query substitution.
2//!
3//! Provides type-safe substitution of named (`:name`) and positional (`?`)
4//! parameter placeholders in QQL query text.
5//!
6//! ### Placeholders & Syntax Rules
7//!
8//! - **Named placeholders**: `:name` (e.g. `:category`, `:limit`).
9//! - **Positional placeholders**: `?` (sequential 1-to-1 mapping with parameters list).
10//!
11//! In QQL, `$` is a first-class identifier character (e.g. `$category`, `$1`), so
12//! parameter placeholders exclusively use `:name` and `?`. This guarantees that
13//! `$`-prefixed identifiers in queries are never accidentally or silently rewritten.
14//!
15//! Furthermore, a colon `:` is only recognized as a parameter placeholder when it occurs
16//! at a valid token boundary (preceded by whitespace, punctuation, or start of query).
17//! Colons in compact dictionary syntax (e.g. `{a:b}`, `{'a':b}`) are not placeholders
18//! and are preserved without modification. Note that unconventional spacing with whitespace
19//! before the colon (`{a :b}`) makes `:b` lexically indistinguishable from a placeholder.
20//!
21//! Literals and dictionary keys are safely formatted and escaped to prevent
22//! query injection breakouts. String literals (`'...'`, `r'...'`, `"""..."""`,
23//! and `` `...` ``) and comments (`-- ...`) in the source query are preserved
24//! verbatim and never substituted.
25//!
26//! `params` (typed `Value`) and `params_json` (JSON `serde_json::Value`) are
27//! dual contracts that must stay in lockstep: a binding-rule fix in one must
28//! be ported to the other. JSON is the host-SDK round-trip; typed `Value` is
29//! the zero-copy FFI fast path (`F32Array`, packed multivector).
30
31pub(crate) mod ast;
32pub(crate) mod filter;
33pub(crate) mod formula;
34pub(crate) mod input;
35pub(crate) mod render;
36pub(crate) mod scan;
37pub(crate) mod text;
38pub(crate) mod validate;
39pub(crate) mod validate_collect;
40pub(crate) mod value;
41
42#[cfg(test)]
43mod tests;
44
45pub use ast::{bind_page_spec, bind_query_expr, bind_query_stmt, bind_stmt};
46pub use filter::{bind_filter, bind_point_selector};
47pub use formula::bind_formula;
48pub use input::bind_query_input;
49pub use render::{escape_str_literal, truncate_vector_literals, value_to_literal};
50pub use scan::{ident_at, is_ident_continue, is_ident_start, is_placeholder_start, skip_protected};
51pub use text::{bind_named, bind_named_readable, bind_positional, bind_positional_readable};
52pub use validate::{
53 collect_statement_params, stmt_has_point_params, validate_no_unbound_params,
54 validate_no_unbound_scalar_params,
55};
56pub use value::{bind_point_id, bind_shard_key, bind_value, resolve_param_u64};