Part 2: take a dependency on rusqlite for query arguments.
This commit is contained in:
parent
044635e8bc
commit
19a1856253
3 changed files with 33 additions and 15 deletions
|
@ -76,8 +76,8 @@ fn prepopulated_schema() -> Schema {
|
||||||
prepopulated_typed_schema(ValueType::String)
|
prepopulated_typed_schema(ValueType::String)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_arg(name: &'static str, value: &'static str) -> (String, Rc<String>) {
|
fn make_arg(name: &'static str, value: &'static str) -> (String, Rc<mentat_sql::Value>) {
|
||||||
(name.to_string(), Rc::new(value.to_string()))
|
(name.to_string(), Rc::new(mentat_sql::Value::Text(value.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -7,5 +7,10 @@ workspace = ".."
|
||||||
error-chain = "0.8.1"
|
error-chain = "0.8.1"
|
||||||
ordered-float = "0.4.0"
|
ordered-float = "0.4.0"
|
||||||
|
|
||||||
|
[dependencies.rusqlite]
|
||||||
|
version = "0.10.1"
|
||||||
|
# System sqlite might be very old.
|
||||||
|
features = ["bundled", "limits"]
|
||||||
|
|
||||||
[dependencies.mentat_core]
|
[dependencies.mentat_core]
|
||||||
path = "../core"
|
path = "../core"
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
|
extern crate rusqlite;
|
||||||
|
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -19,6 +21,8 @@ use ordered_float::OrderedFloat;
|
||||||
|
|
||||||
use mentat_core::TypedValue;
|
use mentat_core::TypedValue;
|
||||||
|
|
||||||
|
pub use rusqlite::types::Value;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
types {
|
types {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
Error, ErrorKind, ResultExt, Result;
|
||||||
|
@ -47,7 +51,7 @@ pub struct SQLQuery {
|
||||||
pub sql: String,
|
pub sql: String,
|
||||||
|
|
||||||
/// These will eventually perhaps be rusqlite `ToSql` instances.
|
/// These will eventually perhaps be rusqlite `ToSql` instances.
|
||||||
pub args: Vec<(String, Rc<String>)>,
|
pub args: Vec<(String, Rc<rusqlite::types::Value>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gratefully based on Diesel's QueryBuilder trait:
|
/// Gratefully based on Diesel's QueryBuilder trait:
|
||||||
|
@ -88,7 +92,7 @@ pub struct SQLiteQueryBuilder {
|
||||||
|
|
||||||
arg_prefix: String,
|
arg_prefix: String,
|
||||||
arg_counter: i64,
|
arg_counter: i64,
|
||||||
args: Vec<(String, Rc<String>)>,
|
args: Vec<(String, Rc<rusqlite::types::Value>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SQLiteQueryBuilder {
|
impl SQLiteQueryBuilder {
|
||||||
|
@ -105,7 +109,7 @@ impl SQLiteQueryBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_static_arg(&mut self, val: Rc<String>) {
|
fn push_static_arg(&mut self, val: Rc<rusqlite::types::Value>) {
|
||||||
let arg = format!("{}{}", self.arg_prefix, self.arg_counter);
|
let arg = format!("{}{}", self.arg_prefix, self.arg_counter);
|
||||||
self.arg_counter = self.arg_counter + 1;
|
self.arg_counter = self.arg_counter + 1;
|
||||||
self.push_named_arg(arg.as_str());
|
self.push_named_arg(arg.as_str());
|
||||||
|
@ -136,11 +140,16 @@ impl QueryBuilder for SQLiteQueryBuilder {
|
||||||
&Boolean(v) => self.push_sql(if v { "1" } else { "0" }),
|
&Boolean(v) => self.push_sql(if v { "1" } else { "0" }),
|
||||||
&Long(v) => self.push_sql(v.to_string().as_str()),
|
&Long(v) => self.push_sql(v.to_string().as_str()),
|
||||||
&Double(OrderedFloat(v)) => self.push_sql(v.to_string().as_str()),
|
&Double(OrderedFloat(v)) => self.push_sql(v.to_string().as_str()),
|
||||||
|
// These are both `Rc`. Unfortunately, we can't use that fact when
|
||||||
// These are both `Rc`. We can just clone an `Rc<String>`, but we
|
// turning these into rusqlite Values.
|
||||||
// must make a new single `String`, wrapped in an `Rc`, for keywords.
|
&String(ref s) => {
|
||||||
&String(ref s) => self.push_static_arg(s.clone()),
|
let v = Rc::new(rusqlite::types::Value::Text(s.as_ref().clone()));
|
||||||
&Keyword(ref s) => self.push_static_arg(Rc::new(s.as_ref().to_string())),
|
self.push_static_arg(v);
|
||||||
|
},
|
||||||
|
&Keyword(ref s) => {
|
||||||
|
let v = Rc::new(rusqlite::types::Value::Text(s.as_ref().to_string()));
|
||||||
|
self.push_static_arg(v);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -180,6 +189,10 @@ impl QueryBuilder for SQLiteQueryBuilder {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn string_arg(s: &str) -> Rc<rusqlite::types::Value> {
|
||||||
|
Rc::new(rusqlite::types::Value::Text(s.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sql() {
|
fn test_sql() {
|
||||||
let mut s = SQLiteQueryBuilder::new();
|
let mut s = SQLiteQueryBuilder::new();
|
||||||
|
@ -188,14 +201,14 @@ mod tests {
|
||||||
s.push_sql(" WHERE ");
|
s.push_sql(" WHERE ");
|
||||||
s.push_identifier("bar").unwrap();
|
s.push_identifier("bar").unwrap();
|
||||||
s.push_sql(" = ");
|
s.push_sql(" = ");
|
||||||
s.push_static_arg(Rc::new("frobnicate".to_string()));
|
s.push_static_arg(string_arg("frobnicate"));
|
||||||
s.push_sql(" OR ");
|
s.push_sql(" OR ");
|
||||||
s.push_static_arg(Rc::new("swoogle".to_string()));
|
s.push_static_arg(string_arg("swoogle"));
|
||||||
let q = s.finish();
|
let q = s.finish();
|
||||||
|
|
||||||
assert_eq!(q.sql.as_str(), "SELECT `foo` WHERE `bar` = $v0 OR $v1");
|
assert_eq!(q.sql.as_str(), "SELECT `foo` WHERE `bar` = $v0 OR $v1");
|
||||||
assert_eq!(q.args,
|
assert_eq!(q.args,
|
||||||
vec![("$v0".to_string(), Rc::new("frobnicate".to_string())),
|
vec![("$v0".to_string(), string_arg("frobnicate")),
|
||||||
("$v1".to_string(), Rc::new("swoogle".to_string()))]);
|
("$v1".to_string(), string_arg("swoogle"))]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue