Part 2: take a dependency on rusqlite for query arguments.

This commit is contained in:
Richard Newman 2017-04-26 17:25:40 -07:00
parent 044635e8bc
commit 19a1856253
3 changed files with 33 additions and 15 deletions

View file

@ -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]

View file

@ -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"

View file

@ -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"))]);
} }
} }