Skip to main content

drizzle_core/
placeholder.rs

1use crate::bind::{BindValue, NullableBindValue};
2use crate::expr::{Expr, NonNull, Null, Nullability, Scalar};
3use crate::param::ParamBind;
4use crate::traits::{SQLParam, ToSQL};
5use crate::types::DataType;
6use crate::{Param, SQL};
7use core::fmt;
8use core::marker::PhantomData;
9
10/// A parameter placeholder whose value is supplied later, by name.
11///
12/// Use placeholders in prepared statements: build the query once, then bind
13/// values each time it runs. The SQL text depends on the dialect: `:name`
14/// for a named placeholder on SQLite, `$1, $2, ...` on PostgreSQL, `?` on
15/// MySQL. Prefer [`Placeholder::typed`], which also checks the bound value's
16/// type.
17///
18/// # Examples
19///
20/// ```
21/// use drizzle_core::{ParamBind, Placeholder, SQL, ToSQL};
22/// # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
23/// # use std::borrow::Cow;
24/// # #[derive(Debug, Clone, PartialEq)]
25/// # struct Value(i64);
26/// # impl SQLParam for Value {
27/// #     const DIALECT: Dialect = Dialect::SQLite;
28/// #     type DialectMarker = SQLiteDialect;
29/// # }
30/// # impl From<Value> for Cow<'_, Value> {
31/// #     fn from(value: Value) -> Self { Cow::Owned(value) }
32/// # }
33///
34/// let sql: SQL<'_, Value> = SQL::raw("SELECT * FROM users WHERE id =")
35///     .append(Placeholder::named("id").to_sql());
36/// assert_eq!(sql.sql(), "SELECT * FROM users WHERE id = :id");
37///
38/// let bound = sql.bind([ParamBind::new("id", Value(1))]);
39/// assert_eq!(bound.params().count(), 1);
40/// ```
41#[derive(Default, Debug, Clone, Hash, Copy, PartialEq, Eq)]
42pub struct Placeholder {
43    /// The name values are bound by; `None` for an anonymous placeholder.
44    pub name: Option<&'static str>,
45}
46
47/// A named placeholder that knows its SQL type `T` and nullability `N`.
48///
49/// Create one with [`Placeholder::typed`] or [`Placeholder::typed_nullable`].
50/// It works as a typed expression, and [`bind`](Self::bind) only accepts
51/// values that can be stored in `T`.
52///
53/// # Examples
54///
55/// ```
56/// use drizzle_core::{ParamBind, Placeholder, SQL, ToSQL};
57/// use drizzle_types::sqlite::types::Integer;
58/// # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
59/// # use std::borrow::Cow;
60/// # #[derive(Debug, Clone, PartialEq)]
61/// # struct Value(i64);
62/// # impl SQLParam for Value {
63/// #     const DIALECT: Dialect = Dialect::SQLite;
64/// #     type DialectMarker = SQLiteDialect;
65/// # }
66/// # impl From<Value> for Cow<'_, Value> {
67/// #     fn from(value: Value) -> Self { Cow::Owned(value) }
68/// # }
69/// # impl From<i64> for Value {
70/// #     fn from(value: i64) -> Self { Value(value) }
71/// # }
72/// # impl From<&str> for Value {
73/// #     fn from(_: &str) -> Self { Value(0) }
74/// # }
75///
76/// let id = Placeholder::typed::<Integer>("id");
77/// let sql: SQL<'_, Value> = SQL::raw("SELECT * FROM users WHERE id =").append(id.to_sql());
78///
79/// let binding: ParamBind<'_, Value> = id.bind(7_i64);
80/// let bound = sql.bind([binding]);
81/// assert_eq!(bound.params().collect::<Vec<_>>(), [&Value(7)]);
82/// ```
83///
84/// # Compile-time checks
85///
86/// Binding a value of the wrong SQL type does not compile:
87///
88/// ```compile_fail
89/// use drizzle_core::{ParamBind, Placeholder};
90/// use drizzle_types::sqlite::types::Integer;
91/// # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
92/// # use std::borrow::Cow;
93/// # #[derive(Debug, Clone, PartialEq)]
94/// # struct Value(i64);
95/// # impl SQLParam for Value {
96/// #     const DIALECT: Dialect = Dialect::SQLite;
97/// #     type DialectMarker = SQLiteDialect;
98/// # }
99/// # impl From<Value> for Cow<'_, Value> {
100/// #     fn from(value: Value) -> Self { Cow::Owned(value) }
101/// # }
102/// # impl From<i64> for Value {
103/// #     fn from(value: i64) -> Self { Value(value) }
104/// # }
105/// # impl From<&str> for Value {
106/// #     fn from(_: &str) -> Self { Value(0) }
107/// # }
108///
109/// let id = Placeholder::typed::<Integer>("id");
110/// let _: ParamBind<'_, Value> = id.bind("seven"); // TEXT into INTEGER
111/// ```
112#[derive(Default, Debug, Clone, Hash, Copy, PartialEq, Eq)]
113pub struct TypedPlaceholder<T: DataType, N: Nullability = NonNull> {
114    inner: Placeholder,
115    _marker: PhantomData<fn() -> (T, N)>,
116}
117
118impl Placeholder {
119    /// Creates a named placeholder.
120    ///
121    /// Values are bound by `name`. The SQL text depends on the dialect:
122    /// - PostgreSQL: `$1`, `$2`, ... (the name does not appear);
123    /// - SQLite: `:name`;
124    /// - MySQL: `?` (the name does not appear).
125    #[must_use]
126    pub const fn named(name: &'static str) -> Self {
127        Self { name: Some(name) }
128    }
129
130    /// Creates an anonymous placeholder. It cannot be bound by name.
131    #[must_use]
132    pub const fn anonymous() -> Self {
133        Self { name: None }
134    }
135
136    /// Creates a named placeholder for a non-null value of SQL type `T`.
137    #[must_use]
138    pub const fn typed<T: DataType>(name: &'static str) -> TypedPlaceholder<T, NonNull> {
139        TypedPlaceholder {
140            inner: Self::named(name),
141            _marker: PhantomData,
142        }
143    }
144
145    /// Creates a named placeholder for a nullable value of SQL type `T`.
146    /// Bind it with [`TypedPlaceholder::bind_opt`].
147    #[must_use]
148    pub const fn typed_nullable<T: DataType>(name: &'static str) -> TypedPlaceholder<T, Null> {
149        TypedPlaceholder {
150            inner: Self::named(name),
151            _marker: PhantomData,
152        }
153    }
154}
155
156impl<T: DataType, N: Nullability> TypedPlaceholder<T, N> {
157    /// Creates a named placeholder of this type.
158    #[must_use]
159    pub const fn named(name: &'static str) -> Self {
160        Self {
161            inner: Placeholder::named(name),
162            _marker: PhantomData,
163        }
164    }
165
166    /// Pairs this placeholder's name with `value`, for
167    /// [`SQL::bind`](crate::SQL::bind) or a prepared statement.
168    ///
169    /// Only values whose SQL type can be stored in `T` are accepted.
170    pub fn bind<'a, V, R>(self, value: R) -> ParamBind<'a, V>
171    where
172        V: SQLParam,
173        R: BindValue<'a, V, T>,
174    {
175        ParamBind {
176            name: self.inner.name.unwrap_or(""),
177            value: value.into_bind_value(),
178        }
179    }
180
181    /// Returns the placeholder name.
182    #[must_use]
183    pub const fn name(self) -> Option<&'static str> {
184        self.inner.name
185    }
186
187    /// Drops the type, returning a plain [`Placeholder`].
188    #[must_use]
189    pub const fn into_placeholder(self) -> Placeholder {
190        self.inner
191    }
192}
193
194impl<T: DataType> TypedPlaceholder<T, Null> {
195    /// Pairs this placeholder's name with an optional value; `None` binds
196    /// NULL.
197    pub fn bind_opt<'a, V, R>(self, value: Option<R>) -> ParamBind<'a, V>
198    where
199        V: SQLParam,
200        Option<R>: NullableBindValue<'a, V, T>,
201    {
202        ParamBind {
203            name: self.inner.name.unwrap_or(""),
204            value: value.into_nullable_bind_value(),
205        }
206    }
207}
208
209impl<T: DataType, N: Nullability> From<TypedPlaceholder<T, N>> for Placeholder {
210    fn from(value: TypedPlaceholder<T, N>) -> Self {
211        value.inner
212    }
213}
214
215impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Placeholder {
216    fn to_sql(&self) -> SQL<'a, V> {
217        SQL {
218            chunks: smallvec::smallvec![crate::SQLChunk::Param(Param {
219                value: None,
220                placeholder: *self,
221            })],
222        }
223    }
224}
225
226impl crate::expr::ExprSources for Placeholder {
227    type Sources = ();
228}
229
230impl<T: DataType, N: Nullability> crate::expr::ExprSources for TypedPlaceholder<T, N> {
231    type Sources = ();
232}
233
234impl<'a, V: SQLParam + 'a> Expr<'a, V> for Placeholder {
235    type SQLType = crate::types::Placeholder;
236    type Nullable = NonNull;
237    type Aggregate = Scalar;
238}
239
240impl<'a, V: SQLParam + 'a, T: DataType, N: Nullability> ToSQL<'a, V> for TypedPlaceholder<T, N> {
241    fn to_sql(&self) -> SQL<'a, V> {
242        self.inner.to_sql()
243    }
244}
245
246impl<'a, V: SQLParam + 'a, T: DataType, N: Nullability> Expr<'a, V> for TypedPlaceholder<T, N> {
247    type SQLType = T;
248    type Nullable = N;
249    type Aggregate = Scalar;
250}
251
252impl fmt::Display for Placeholder {
253    /// Shows `:name`, or `?` when anonymous. SQL rendering uses the
254    /// dialect's syntax instead (see [`SQL::write_to`](crate::SQL::write_to)).
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match self.name {
257            Some(name) => write!(f, ":{name}"),
258            None => write!(f, "?"),
259        }
260    }
261}