1use std::borrow::Cow;
2
3use crate::error::Error;
4use crate::value::{ToValue, Value};
5use crate::writer::SqlWriter;
6
7use super::convert::IntoExpr;
8use super::node::Expr;
9
10#[derive(Debug, Clone)]
17pub enum RawArg {
18 Value(Value),
20 Expr(Expr),
22 Named(Cow<'static, str>),
26}
27
28impl RawArg {
29 pub fn value(v: impl ToValue) -> RawArg {
31 RawArg::Value(v.to_value())
32 }
33
34 pub fn expr(e: impl IntoExpr) -> RawArg {
36 RawArg::Expr(e.into_expr())
37 }
38
39 pub fn named(name: impl Into<Cow<'static, str>>) -> RawArg {
41 RawArg::Named(name.into())
42 }
43}
44
45pub(super) fn write_template(w: &mut SqlWriter<'_>, sql: &str, args: &[RawArg]) {
58 let mut rest = sql;
59 let mut placeholders = 0usize;
60
61 loop {
62 let Some(mark) = rest.find('?') else {
63 w.push_str(rest);
64 break;
65 };
66
67 if let Some(escape) = rest.find("\\?").filter(|escape| mark > *escape) {
71 w.push_str(&rest[..escape]);
72 w.push_str("?");
73 rest = &rest[escape + 2..];
74 continue;
75 }
76
77 w.push_str(&rest[..mark]);
78
79 match args.get(placeholders) {
80 Some(RawArg::Value(v)) => w.push_arg(v.clone()),
81 Some(RawArg::Expr(e)) => w.write_expr(e),
82 Some(RawArg::Named(name)) => w.push_named_arg(name),
83 None => w.push_arg(Value::Null),
84 }
85
86 placeholders += 1;
87 rest = &rest[mark + 1..];
88 }
89
90 if placeholders != args.len() {
91 w.record_error(Error::raw_arg_count(placeholders, args.len(), sql));
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::dialect::testing::{Numbered, TestDialect};
99 use crate::writer::build;
100
101 mod bob_raw_test {
124 use super::*;
125
126 #[test]
127 fn plain() {
128 let (sql, args) = build(
129 &TestDialect,
130 &Expr::template("SELECT a, b FROM alphabet", []),
131 )
132 .unwrap();
133 assert_eq!(sql, "SELECT a, b FROM alphabet");
134 assert!(args.is_empty());
135 }
136
137 #[test]
138 fn escaped_args() {
139 let e = Expr::template(
140 r#"SELECT a, b FROM "alphabet\?" WHERE c = ? AND d <= ?"#,
141 [RawArg::value(1i32), RawArg::value(2i32)],
142 );
143 let (sql, args) = build(&TestDialect, &e).unwrap();
144 assert_eq!(
145 sql,
146 r#"SELECT a, b FROM "alphabet?" WHERE c = ?1 AND d <= ?2"#
147 );
148 assert_eq!(args, vec![Value::I32(1), Value::I32(2)]);
149 }
150
151 #[test]
152 fn mismatched_args_and_placeholders() {
153 let e = Expr::template("SELECT a, b FROM alphabet WHERE c = ? AND d <= ?", []);
154 let mut w = SqlWriter::new(&TestDialect);
157 w.write_expr(&e);
158 assert_eq!(
159 w.sql(),
160 "SELECT a, b FROM alphabet WHERE c = ?1 AND d <= ?2"
161 );
162 let err = w.finish().unwrap_err();
163 assert_eq!(
164 err.to_string(),
165 "Bad Statement: has 2 placeholders but 0 args: \
166 SELECT a, b FROM alphabet WHERE c = ? AND d <= ?"
167 );
168 }
169
170 #[test]
171 fn numbered_args() {
172 let e = Expr::template(
173 "SELECT a, b FROM alphabet WHERE c = ? AND d <= ?",
174 [RawArg::value(1i32), RawArg::value(2i32)],
175 );
176 let (sql, args) = build(&TestDialect, &e).unwrap();
177 assert_eq!(sql, "SELECT a, b FROM alphabet WHERE c = ?1 AND d <= ?2");
178 assert_eq!(args, vec![Value::I32(1), Value::I32(2)]);
179 }
180
181 #[test]
182 fn expr_args() {
183 let e = Expr::template(
186 "SELECT a, b FROM alphabet WHERE c IN (?) AND d <= ?",
187 [RawArg::expr(Expr::args([5i32, 6, 7])), RawArg::value(2i32)],
188 );
189 let (sql, args) = build(&TestDialect, &e).unwrap();
190 assert_eq!(
191 sql,
192 "SELECT a, b FROM alphabet WHERE c IN (?1, ?2, ?3) AND d <= ?4"
193 );
194 assert_eq!(
195 args,
196 vec![Value::I32(5), Value::I32(6), Value::I32(7), Value::I32(2)]
197 );
198 }
199
200 #[test]
201 fn expr_args_group() {
202 let e = Expr::template(
204 "SELECT a, b FROM alphabet WHERE c IN ? AND d <= ?",
205 [
206 RawArg::expr(crate::expr::arg_group([5i32, 6, 7])),
207 RawArg::value(2i32),
208 ],
209 );
210 let (sql, args) = build(&TestDialect, &e).unwrap();
211 assert_eq!(
212 sql,
213 "SELECT a, b FROM alphabet WHERE c IN (?1, ?2, ?3) AND d <= ?4"
214 );
215 assert_eq!(
216 args,
217 vec![Value::I32(5), Value::I32(6), Value::I32(7), Value::I32(2)]
218 );
219 }
220
221 #[test]
222 fn expr_args_quote() {
223 let e = Expr::template(
226 "SELECT a, b FROM alphabet WHERE c = ? AND d <= ?",
227 [RawArg::expr(Expr::ident("AA")), RawArg::value(2i32)],
228 );
229 let (sql, args) = build(&TestDialect, &e).unwrap();
230 assert_eq!(
231 sql,
232 r#"SELECT a, b FROM alphabet WHERE c = "AA" AND d <= ?1"#
233 );
234 assert_eq!(args, vec![Value::I32(2)]);
235 }
236 }
237
238 #[test]
239 fn a_lone_escape_before_a_real_placeholder() {
240 let e = Expr::template(r"a\?b?c", [RawArg::value(1i32)]);
241 let (sql, _) = build(&Numbered, &e).unwrap();
242 assert_eq!(sql, "a?b$1c");
243 }
244
245 #[test]
246 fn an_escape_after_a_real_placeholder_is_still_honoured() {
247 let e = Expr::template(r"a?b\?c", [RawArg::value(1i32)]);
248 let (sql, _) = build(&Numbered, &e).unwrap();
249 assert_eq!(sql, "a$1b?c");
250 }
251
252 #[test]
253 fn consecutive_escapes_stay_literal() {
254 let e = Expr::template(r"\?\?", []);
255 let (sql, args) = build(&Numbered, &e).unwrap();
256 assert_eq!(sql, "??");
257 assert!(args.is_empty());
258 }
259
260 #[test]
261 fn a_trailing_placeholder_ends_the_scan() {
262 let e = Expr::template("a = ?", [RawArg::value(9i32)]);
263 assert_eq!(build(&Numbered, &e).unwrap().0, "a = $1");
264 }
265
266 #[test]
267 fn too_many_args_is_also_a_mismatch() {
268 let e = Expr::template("a = ?", [RawArg::value(1i32), RawArg::value(2i32)]);
269 let err = build(&Numbered, &e).unwrap_err();
270 assert_eq!(
271 err.to_string(),
272 "Bad Statement: has 1 placeholders but 2 args: a = ?"
273 );
274 }
275
276 #[test]
277 fn a_named_replacement_consumes_a_placeholder_but_no_position() {
278 let e = Expr::template(
279 "a = ? AND b = ? AND c = ?",
280 [RawArg::value(1i32), RawArg::named("b"), RawArg::value(3i32)],
281 );
282 let (sql, args) = build(&TestDialect, &e).unwrap();
283 assert_eq!(sql, "a = ?1 AND b = :b AND c = ?2");
284 assert_eq!(args, vec![Value::I32(1), Value::I32(3)]);
285 }
286
287 #[test]
288 fn a_named_replacement_fails_on_a_dialect_without_named_arguments() {
289 let e = Expr::template("a = ?", [RawArg::named("a")]);
290 assert!(matches!(build(&Numbered, &e), Err(Error::NoNamedArgs)));
291 }
292
293 #[test]
294 fn multibyte_text_around_a_placeholder_is_not_sliced_mid_character() {
295 let e = Expr::template(
296 "名前 = ? AND 年齢 > ?",
297 [RawArg::value("さくら"), RawArg::value(20i32)],
298 );
299 let (sql, _) = build(&Numbered, &e).unwrap();
300 assert_eq!(sql, "名前 = $1 AND 年齢 > $2");
301 }
302
303 #[test]
304 fn a_template_continues_the_surrounding_numbering() {
305 let outer = Expr::join([
306 Expr::arg(1i32),
307 Expr::template("f(?, ?)", [RawArg::value(2i32), RawArg::value(3i32)]),
308 Expr::arg(4i32),
309 ]);
310 let (sql, args) = build(&Numbered, &outer).unwrap();
311 assert_eq!(sql, "$1 f($2, $3) $4");
312 assert_eq!(args.len(), 4);
313 }
314}