toasty_sql/serializer/params.rs
1use crate::serializer::ExprContext;
2
3use super::{Flavor, Formatter, ToSql};
4
5use toasty_core::stmt;
6
7/// Collects query parameter values during SQL serialization.
8///
9/// Implement this trait to control how bound parameters are stored. The
10/// serializer calls [`push`](Params::push) each time it encounters a value
11/// that should be sent as a bind parameter rather than inlined into the SQL
12/// string.
13pub trait Params {
14 /// Appends a value (with an optional type hint) and returns its [`Placeholder`].
15 fn push(&mut self, param: &stmt::Value, type_hint: Option<&stmt::Type>) -> Placeholder;
16}
17
18/// A positional bind-parameter placeholder.
19///
20/// The inner `usize` is the 1-based parameter index. The serializer renders
21/// it in the target dialect's format (`$1`, `?1`, or `?`).
22///
23/// # Example
24///
25/// ```
26/// use toasty_sql::serializer::Placeholder;
27///
28/// let p = Placeholder(3);
29/// assert_eq!(p.0, 3);
30/// ```
31pub struct Placeholder(pub usize);
32
33/// A parameter value paired with an optional type hint.
34///
35/// Type hints let drivers pick the right wire format when the value alone
36/// is ambiguous (e.g. distinguishing `INTEGER` from `BIGINT`).
37///
38/// # Example
39///
40/// ```
41/// use toasty_sql::TypedValue;
42///
43/// let tv = TypedValue {
44/// value: toasty_core::stmt::Value::Null,
45/// type_hint: None,
46/// };
47/// assert!(tv.type_hint.is_none());
48/// ```
49#[derive(Debug, Clone)]
50pub struct TypedValue {
51 /// The parameter value.
52 pub value: stmt::Value,
53 /// An optional type hint for the value.
54 pub type_hint: Option<stmt::Type>,
55}
56
57impl TypedValue {
58 /// Infers the type of this value, using the type hint if available
59 pub fn infer_ty(&self) -> stmt::Type {
60 self.type_hint
61 .clone()
62 .unwrap_or_else(|| self.value.infer_ty())
63 }
64}
65
66impl Params for Vec<stmt::Value> {
67 fn push(&mut self, value: &stmt::Value, _type_hint: Option<&stmt::Type>) -> Placeholder {
68 self.push(value.clone());
69 Placeholder(self.len())
70 }
71}
72
73impl Params for Vec<TypedValue> {
74 fn push(&mut self, value: &stmt::Value, type_hint: Option<&stmt::Type>) -> Placeholder {
75 self.push(TypedValue {
76 value: value.clone(),
77 type_hint: type_hint.cloned(),
78 });
79 Placeholder(self.len())
80 }
81}
82
83impl ToSql for Placeholder {
84 fn to_sql<P: Params>(self, _cx: &ExprContext<'_>, f: &mut Formatter<'_, P>) {
85 use std::fmt::Write;
86
87 match f.serializer.flavor {
88 Flavor::Mysql => write!(&mut f.dst, "?").unwrap(),
89 Flavor::Postgresql => write!(&mut f.dst, "${}", self.0).unwrap(),
90 Flavor::Sqlite => write!(&mut f.dst, "?{}", self.0).unwrap(),
91 }
92 }
93}