1use crate::*;
4pub use std::fmt::Write;
5
6pub trait SqlWriter: Write + ToString {
7 fn push_param(&mut self, value: Value, query_builder: &dyn QueryBuilder);
8
9 fn as_writer(&mut self) -> &mut dyn Write;
10}
11
12impl SqlWriter for String {
13 fn push_param(&mut self, value: Value, query_builder: &dyn QueryBuilder) {
14 self.push_str(&query_builder.value_to_string(&value))
15 }
16
17 fn as_writer(&mut self) -> &mut dyn Write {
18 self as _
19 }
20}
21
22#[derive(Debug, Clone)]
23pub struct SqlWriterValues {
24 counter: usize,
25 placeholder: String,
26 numbered: bool,
27 string: String,
28 values: Vec<Value>,
29}
30
31impl SqlWriterValues {
32 pub fn new<T>(placeholder: T, numbered: bool) -> Self
33 where
34 T: Into<String>,
35 {
36 Self {
37 counter: 0,
38 placeholder: placeholder.into(),
39 numbered,
40 string: String::with_capacity(256),
41 values: Vec::new(),
42 }
43 }
44
45 pub fn into_parts(self) -> (String, Values) {
46 (self.string, Values(self.values))
47 }
48}
49
50impl Write for SqlWriterValues {
51 fn write_str(&mut self, s: &str) -> std::fmt::Result {
52 write!(self.string, "{s}")
53 }
54}
55
56impl std::fmt::Display for SqlWriterValues {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(f, "{}", self.string)
59 }
60}
61
62impl SqlWriter for SqlWriterValues {
63 fn push_param(&mut self, value: Value, _: &dyn QueryBuilder) {
64 self.counter += 1;
65 if self.numbered {
66 let counter = self.counter;
67 write!(self.string, "{}{}", self.placeholder, counter).unwrap();
68 } else {
69 write!(self.string, "{}", self.placeholder).unwrap();
70 }
71 self.values.push(value)
72 }
73
74 fn as_writer(&mut self) -> &mut dyn Write {
75 self as _
76 }
77}
78
79pub fn inject_parameters<I>(sql: &str, params: I, query_builder: &dyn QueryBuilder) -> String
80where
81 I: IntoIterator<Item = Value>,
82{
83 let params: Vec<Value> = params.into_iter().collect();
84 let tokenizer = Tokenizer::new(sql);
85 let tokens: Vec<Token> = tokenizer.iter().collect();
86 let mut counter = 0;
87 let mut output = Vec::new();
88 let mut i = 0;
89 while i < tokens.len() {
90 let token = &tokens[i];
91 match token {
92 Token::Punctuation(mark) => {
93 if (mark.as_ref(), false) == query_builder.placeholder() {
94 output.push(query_builder.value_to_string(¶ms[counter]));
95 counter += 1;
96 i += 1;
97 continue;
98 } else if (mark.as_ref(), true) == query_builder.placeholder()
99 && i + 1 < tokens.len()
100 {
101 if let Token::Unquoted(next) = &tokens[i + 1] {
102 if let Ok(num) = next.parse::<usize>() {
103 output.push(query_builder.value_to_string(¶ms[num - 1]));
104 i += 2;
105 continue;
106 }
107 }
108 }
109 output.push(mark.to_string())
110 }
111 _ => output.push(token.to_string()),
112 }
113 i += 1;
114 }
115 output.into_iter().collect()
116}
117
118#[cfg(test)]
119#[cfg(feature = "backend-mysql")]
120mod tests_mysql {
121 use super::*;
122 use pretty_assertions::assert_eq;
123
124 #[test]
125 fn inject_parameters_1() {
126 assert_eq!(
127 inject_parameters("WHERE A = ?", ["B".into()], &MysqlQueryBuilder),
128 "WHERE A = 'B'"
129 );
130 }
131
132 #[test]
133 fn inject_parameters_2() {
134 assert_eq!(
135 inject_parameters("WHERE A = '?' AND B = ?", ["C".into()], &MysqlQueryBuilder),
136 "WHERE A = '?' AND B = 'C'"
137 );
138 }
139
140 #[test]
141 fn inject_parameters_3() {
142 assert_eq!(
143 inject_parameters(
144 "WHERE A = ? AND C = ?",
145 ["B".into(), "D".into()],
146 &MysqlQueryBuilder
147 ),
148 "WHERE A = 'B' AND C = 'D'"
149 );
150 }
151
152 #[test]
153 fn inject_parameters_4() {
154 assert_eq!(
155 inject_parameters("?", [vec![0xABu8, 0xCD, 0xEF].into()], &MysqlQueryBuilder),
156 "x'ABCDEF'"
157 );
158 }
159}
160
161#[cfg(test)]
162#[cfg(feature = "backend-postgres")]
163mod tests_postgres {
164 use super::*;
165 use pretty_assertions::assert_eq;
166
167 #[test]
168 fn inject_parameters_5() {
169 assert_eq!(
170 inject_parameters(
171 "WHERE A = $1 AND C = $2",
172 ["B".into(), "D".into()],
173 &PostgresQueryBuilder
174 ),
175 "WHERE A = 'B' AND C = 'D'"
176 );
177 }
178
179 #[test]
180 fn inject_parameters_6() {
181 assert_eq!(
182 inject_parameters(
183 "WHERE A = $2 AND C = $1",
184 ["B".into(), "D".into()],
185 &PostgresQueryBuilder
186 ),
187 "WHERE A = 'D' AND C = 'B'"
188 );
189 }
190
191 #[test]
192 fn inject_parameters_7() {
193 assert_eq!(
194 inject_parameters("WHERE A = $1", ["B'C".into()], &PostgresQueryBuilder),
195 "WHERE A = E'B\\'C'"
196 );
197 }
198}