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}