1extern crate ordered_float;
12extern crate rusqlite;
13
14extern crate core_traits;
15extern crate mentat_core;
16extern crate sql_traits;
17
18use std::rc::Rc;
19
20use std::collections::HashMap;
21
22use ordered_float::OrderedFloat;
23
24use core_traits::TypedValue;
25
26use sql_traits::errors::{BuildQueryResult, SQLError};
27
28use mentat_core::{ToMicros, ValueRc};
29
30mod conn;
31pub use conn::{from_rusqlite, sql_error, sqlite, to_rusqlite, Param, RusqliteRow, SqliteConn};
32pub use sql_traits::conn::{Dialect, SqlConn, SqlConnExt, SqlError, SqlRow, SqlValue};
33
34pub struct SQLQuery {
39 pub sql: String,
40
41 pub args: Vec<(String, Rc<rusqlite::types::Value>)>,
43}
44
45pub trait QueryBuilder {
48 fn dialect(&self) -> Dialect {
51 Dialect::Sqlite
52 }
53 fn push_sql(&mut self, sql: &str);
54 fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult;
55 fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult;
56 fn push_bind_param(&mut self, name: &str) -> BuildQueryResult;
57 fn finish(self) -> SQLQuery;
58}
59
60pub trait QueryFragment {
61 fn push_sql(&self, out: &mut dyn QueryBuilder) -> BuildQueryResult;
62}
63
64impl QueryFragment for Box<dyn QueryFragment> {
65 fn push_sql(&self, out: &mut dyn QueryBuilder) -> BuildQueryResult {
66 QueryFragment::push_sql(&**self, out)
67 }
68}
69
70impl QueryFragment for &dyn QueryFragment {
71 fn push_sql(&self, out: &mut dyn QueryBuilder) -> BuildQueryResult {
72 QueryFragment::push_sql(&**self, out)
73 }
74}
75
76impl QueryFragment for () {
77 fn push_sql(&self, _out: &mut dyn QueryBuilder) -> BuildQueryResult {
78 Ok(())
79 }
80}
81
82#[derive(Default)]
84pub struct SQLiteQueryBuilder {
85 pub sql: String,
86
87 arg_prefix: String,
88 arg_counter: i64,
89
90 byte_args: HashMap<Vec<u8>, String>, string_args: HashMap<ValueRc<String>, String>, args: Vec<(String, Rc<rusqlite::types::Value>)>, dialect: Option<Dialect>,
99}
100
101impl SQLiteQueryBuilder {
102 pub fn new() -> Self {
103 SQLiteQueryBuilder::with_prefix("$v".to_string())
104 }
105
106 pub fn with_prefix(prefix: String) -> Self {
107 SQLiteQueryBuilder {
108 sql: String::new(),
109 arg_prefix: prefix,
110 arg_counter: 0,
111
112 byte_args: HashMap::default(),
113 string_args: HashMap::default(),
114 args: vec![],
115 dialect: None,
116 }
117 }
118
119 pub fn set_dialect(&mut self, dialect: Dialect) {
122 self.dialect = Some(dialect);
123 }
124
125 fn next_argument_name(&mut self) -> String {
126 let arg = format!("{}{}", self.arg_prefix, self.arg_counter);
127 self.arg_counter += 1;
128 arg
129 }
130
131 fn push_static_arg(&mut self, val: Rc<rusqlite::types::Value>) {
132 let arg = self.next_argument_name();
134 self.push_named_arg(arg.as_str());
135 self.args.push((arg, val));
136 }
137
138 fn push_named_arg(&mut self, arg: &str) {
139 self.push_sql(arg);
140 }
141}
142
143impl QueryBuilder for SQLiteQueryBuilder {
144 fn dialect(&self) -> Dialect {
145 self.dialect.unwrap_or(Dialect::Sqlite)
146 }
147
148 fn push_sql(&mut self, sql: &str) {
149 self.sql.push_str(sql);
150 }
151
152 fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult {
153 if self.dialect() == Dialect::DuckDb {
154 self.push_sql("\"");
156 self.push_sql(&identifier.replace('"', "\"\""));
157 self.push_sql("\"");
158 return Ok(());
159 }
160 self.push_sql("`");
161 self.push_sql(&identifier.replace("`", "``"));
162 self.push_sql("`");
163 Ok(())
164 }
165
166 fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult {
167 use crate::TypedValue::*;
168 match value {
169 Ref(entid) => self.push_sql(entid.to_string().as_str()),
170 Boolean(v) => self.push_sql(if *v { "1" } else { "0" }),
171 Long(v) => self.push_sql(v.to_string().as_str()),
172 Double(OrderedFloat(v)) => {
173 self.push_sql(format!("{:e}", v).as_str());
179 }
180 Instant(dt) => {
181 self.push_sql(format!("{}", dt.to_micros()).as_str()); }
183 Uuid(ref u) => {
184 let bytes = u.as_bytes();
185 if let Some(arg) = self.byte_args.get(bytes.as_ref()).cloned() {
186 self.push_named_arg(arg.as_str());
188 } else {
189 let arg = self.next_argument_name();
190 self.push_named_arg(arg.as_str());
191 self.byte_args.insert(bytes.clone().to_vec(), arg);
192 }
193 }
194 String(ref s) => {
198 if let Some(arg) = self.string_args.get(s).cloned() {
199 self.push_named_arg(arg.as_str());
200 } else {
201 let arg = self.next_argument_name();
202 self.push_named_arg(arg.as_str());
203 self.string_args.insert(s.clone(), arg);
204 }
205 }
206 Keyword(ref s) => {
207 let v = Rc::new(rusqlite::types::Value::Text(s.as_ref().to_string()));
209 self.push_static_arg(v);
210 }
211 Bytes(b) => {
212 let bytes = b.to_vec();
213 if let Some(arg) = self.byte_args.get(&bytes).cloned() {
214 self.push_named_arg(arg.as_str());
216 } else {
217 let arg = self.next_argument_name();
218 self.push_named_arg(arg.as_str());
219 self.byte_args.insert(bytes, arg);
220 }
221 }
222 }
223 Ok(())
224 }
225
226 fn push_bind_param(&mut self, name: &str) -> BuildQueryResult {
233 if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
236 return Err(SQLError::InvalidParameterName(name.to_string()));
237 }
238
239 if name.starts_with(self.arg_prefix.as_str())
240 && name
241 .chars()
242 .skip(self.arg_prefix.len())
243 .all(char::is_numeric)
244 {
245 return Err(SQLError::BindParamCouldBeGenerated(name.to_string()));
246 }
247
248 self.push_sql("$");
249 self.push_sql(name);
250 Ok(())
251 }
252
253 fn finish(self) -> SQLQuery {
254 let mut args = self.args;
257 let string_args = self.string_args.into_iter().map(|(val, arg)| {
258 (
259 arg,
260 Rc::new(rusqlite::types::Value::Text(val.as_ref().clone())),
261 )
262 });
263 let byte_args = self
264 .byte_args
265 .into_iter()
266 .map(|(val, arg)| (arg, Rc::new(rusqlite::types::Value::Blob(val))));
267
268 args.extend(string_args);
269 args.extend(byte_args);
270
271 args.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
273 SQLQuery {
274 sql: self.sql,
275 args,
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 fn string_arg(s: &str) -> Rc<rusqlite::types::Value> {
285 Rc::new(rusqlite::types::Value::Text(s.to_string()))
286 }
287
288 #[test]
289 fn test_sql() {
290 let mut s = SQLiteQueryBuilder::new();
291 s.push_sql("SELECT ");
292 s.push_identifier("foo").unwrap();
293 s.push_sql(" WHERE ");
294 s.push_identifier("bar").unwrap();
295 s.push_sql(" = ");
296 s.push_static_arg(string_arg("frobnicate"));
297 s.push_sql(" OR ");
298 s.push_static_arg(string_arg("swoogle"));
299 s.push_sql(" OR ");
300 s.push_identifier("bar").unwrap();
301 s.push_sql(" = ");
302 s.push_typed_value(&TypedValue::Double(1.0.into())).unwrap();
303 let q = s.finish();
304
305 assert_eq!(
306 q.sql.as_str(),
307 "SELECT `foo` WHERE `bar` = $v0 OR $v1 OR `bar` = 1e0"
308 );
309 assert_eq!(
310 q.args,
311 vec![
312 ("$v0".to_string(), string_arg("frobnicate")),
313 ("$v1".to_string(), string_arg("swoogle"))
314 ]
315 );
316 }
317}