Misc bugfixes, doc updates

This commit is contained in:
Timothy Warren 2019-07-19 12:45:18 -04:00
parent aad53f0f20
commit eb838306ec
6 changed files with 233 additions and 88 deletions

View File

@ -102,7 +102,9 @@ pub trait DatabaseDriver {
} }
// Runs a basic sql query on the database // Runs a basic sql query on the database
// fn query(&self, sql: &str) -> Result<impl Any, impl Error>; fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
Ok(Box::new(String::from(sql)))
}
/// Prepares an sql statement for the database /// Prepares an sql statement for the database
fn prepare(&self, sql: &str) -> Result<(), ()> { fn prepare(&self, sql: &str) -> Result<(), ()> {

View File

@ -33,18 +33,6 @@ impl PostgresDriver {
self.connection = RefCell::new(Some(connection)); self.connection = RefCell::new(Some(connection));
} }
pub fn query(&self, sql: &str) -> Result<Vec<Row>, Error> {
if self.connection.borrow().is_none() {
panic!("No database connection.");
}
self.connection
.borrow_mut()
.as_mut()
.unwrap()
.query(sql, &[])
}
} }
impl DatabaseDriver for PostgresDriver { impl DatabaseDriver for PostgresDriver {
@ -55,4 +43,22 @@ impl DatabaseDriver for PostgresDriver {
fn random(&self) -> String { fn random(&self) -> String {
String::from(" RANDOM()") String::from(" RANDOM()")
} }
fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
if self.connection.borrow().is_none() {
panic!("No database connection.");
}
let result = self
.connection
.borrow_mut()
.as_mut()
.unwrap()
.query(sql, &[]);
match result {
Ok(res) => Ok(Box::new(res)),
Err(e) => Err(Box::new(e)),
}
}
} }

View File

@ -35,12 +35,7 @@ pub mod prelude {
//! This includes enum types, traits, //! This includes enum types, traits,
//! the Query Builder, and individual database drivers. //! the Query Builder, and individual database drivers.
pub use crate::drivers::DatabaseDriver; pub use crate::drivers::DatabaseDriver;
pub use crate::query_builder::{ pub use crate::query_builder::{JoinType, LikeWildcard, OrderDirection, QueryBuilder};
JoinType,
LikeWildcard,
OrderDirection,
QueryBuilder,
};
#[cfg(feature = "postgres")] #[cfg(feature = "postgres")]
/// Postgres Driver /// Postgres Driver

View File

@ -79,7 +79,36 @@ enum QueryType {
Delete, Delete,
} }
/// The struct representing a query builder /// QueryBuilder for general SQL queries
///
/// ```
/// use stringqb::prelude::*;
///
/// // You probably do not want to use the default driver, as it
/// // is basically a mock for testing
/// use stringqb::drivers::DefaultDriver;
///
/// // The query builder must be mutable to be useful
/// let mut qb = QueryBuilder::new(DefaultDriver::new());
///
/// // Each builder method returns a mutable reference to itself so
/// // the methods are chainable
/// qb.select("field as f")
/// .from("table t")
/// .inner_join("table_two tt", "field2 as ff", "=", "f")
/// .wher("f >", 3);
///
/// // Since they are references, you do not have to chain.
/// let sql = qb.get_compiled_select();
///
/// # assert_eq!(
/// # sql,
/// # r#"SELECT "field" AS "f"
/// FROM "table" "t"
/// INNER JOIN "table_two" "tt" ON "field2" AS "ff"=f
/// WHERE "f" > ?"#
/// # );
/// ```
pub struct QueryBuilder { pub struct QueryBuilder {
/// The struct to store the query builder info /// The struct to store the query builder info
state: QueryState, state: QueryState,
@ -87,9 +116,7 @@ pub struct QueryBuilder {
} }
/// The struct representing a prepared statement /// The struct representing a prepared statement
pub struct Prepared { pub struct Prepared {}
}
impl Default for QueryBuilder { impl Default for QueryBuilder {
/// Creates a new QueryBuilder instance with default driver /// Creates a new QueryBuilder instance with default driver
@ -109,19 +136,19 @@ impl QueryBuilder {
/// ```no_run /// ```no_run
/// use stringqb::prelude::*; /// use stringqb::prelude::*;
/// ///
/// // You probably do not want to use the default driver, as it /// // Postgres Driver (If enabled)
/// // is basically a mock for testing /// let pgDriver = PostgresDriver::new("postgres://");
/// use stringqb::drivers::DefaultDriver;
/// ///
/// // The query builder must be mutable to be useful /// #[cfg(feature = "sqlite")]
/// let mut qb = QueryBuilder::new(DefaultDriver::new()); /// // SQLite Driver (memory)
/// let liteMemoryDriver = SQLiteDriver::new(":memory:");
/// ///
/// // Each builder method returns a mutable reference to itself so /// #[cfg(feature = "sqlite")]
/// // the methods are chainable /// // SQLite Driver (file)
/// qb.select("field f").from("table"); /// let liteDriver = SQLiteDriver::new("/path/to/db.sqlite3");
/// ///
/// // Since they are references, you do not have to chain. /// // The variable to the query builder must be mutable
/// let sql = qb.get_compiled_select(); /// let mut queryBuilder = QueryBuilder::new(pgDriver);
/// ``` /// ```
pub fn new(driver: impl DatabaseDriver + 'static) -> Self { pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
QueryBuilder { QueryBuilder {
@ -137,28 +164,13 @@ impl QueryBuilder {
/// Set the fields to select from the database as a string /// Set the fields to select from the database as a string
/// ///
/// ```no_run /// ```no_run
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // You can also alias field names /// // You can also alias field names
/// qb.select("foo as bar"); /// qb.select("foo as bar");
/// ``` /// ```
pub fn select(&mut self, fields: &str) -> &mut Self { pub fn select(&mut self, fields: &str) -> &mut Self {
lazy_static! { let fields = self.quote_fields(fields);
static ref RE: Regex = Regex::new(r"(?i) as ").unwrap();
};
let fields = split_map_join(fields, ",", |s| {
if !RE.is_match(s) {
return self.driver.quote_identifier(s.trim());
}
// Do a operation similar to split_map_join for the
// regex matches, quoting each identifier
RE.split(s)
.into_iter()
.map(|p| self.driver.quote_identifier(p))
.collect::<Vec<String>>()
.join(" as ")
});
self.state.append_select_string(&fields); self.state.append_select_string(&fields);
@ -168,7 +180,8 @@ impl QueryBuilder {
/// Set the fields to select from the database as a Vector /// Set the fields to select from the database as a Vector
/// ///
/// ```no_run /// ```no_run
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // You can also alias via a vector of fields /// // You can also alias via a vector of fields
/// qb.select_vec(vec!["foo as bar", "baz"]); /// qb.select_vec(vec!["foo as bar", "baz"]);
/// ``` /// ```
@ -195,8 +208,9 @@ impl QueryBuilder {
/// Specify the database table to select from /// Specify the database table to select from
/// ///
/// ```no_run /// ```no_run
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # use stringqb::prelude::*;
/// // You can specifiy an alias for the table /// # let mut qb = QueryBuilder::default();
/// // You can specify an alias for the table
/// qb.from("products p"); /// qb.from("products p");
/// ``` /// ```
pub fn from(&mut self, table_name: &str) -> &mut Self { pub fn from(&mut self, table_name: &str) -> &mut Self {
@ -215,7 +229,7 @@ impl QueryBuilder {
/// ///
/// ```no_run /// ```no_run
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // Search for a value that ends with "foo" /// // Search for a value that ends with "foo"
/// qb.like("field", String::from("foo"), LikeWildcard::Before); /// qb.like("field", String::from("foo"), LikeWildcard::Before);
/// ///
@ -233,7 +247,7 @@ impl QueryBuilder {
/// ///
/// ```no_run /// ```no_run
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // Search for a value that ends with "foo" /// // Search for a value that ends with "foo"
/// qb.or_like("field", String::from("foo"), LikeWildcard::Before); /// qb.or_like("field", String::from("foo"), LikeWildcard::Before);
/// ///
@ -251,7 +265,7 @@ impl QueryBuilder {
/// ///
/// ```no_run /// ```no_run
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // Search for a value that does not end with "foo" /// // Search for a value that does not end with "foo"
/// qb.not_like("field", String::from("foo"), LikeWildcard::Before); /// qb.not_like("field", String::from("foo"), LikeWildcard::Before);
/// ///
@ -269,7 +283,7 @@ impl QueryBuilder {
/// ///
/// ```no_run /// ```no_run
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // Search for a value that does not end with "foo" /// // Search for a value that does not end with "foo"
/// qb.or_not_like("field", String::from("foo"), LikeWildcard::Before); /// qb.or_not_like("field", String::from("foo"), LikeWildcard::Before);
/// ///
@ -393,16 +407,37 @@ impl QueryBuilder {
} }
/// Specify a `where in` clause for the query, prefixed with `or` /// Specify a `where in` clause for the query, prefixed with `or`
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Look for a set of rows matching the values passed
/// qb.or_where_in("key", vec![1,2,3]);
/// ```
pub fn or_where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self { pub fn or_where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
self._where_in(key, values, "IN", "OR") self._where_in(key, values, "IN", "OR")
} }
/// Specify a `where not in` clause for the query /// Specify a `where not in` clause for the query
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Look for a set of rows not matching the values passed
/// qb.where_not_in("key", vec![1,2,3]);
/// ```
pub fn where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self { pub fn where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
self._where_in(key, values, "NOT IN", "AND") self._where_in(key, values, "NOT IN", "AND")
} }
/// Specify a `where not in` clause for the query, prefixed with `or` /// Specify a `where not in` clause for the query, prefixed with `or`
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Look for a set of rows not matching the values passed
/// qb.or_where_not_in("key", vec![1,2,3]);
/// ```
pub fn or_where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self { pub fn or_where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
self._where_in(key, values, "NOT IN", "OR") self._where_in(key, values, "NOT IN", "OR")
} }
@ -412,6 +447,15 @@ impl QueryBuilder {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/// Set a key and value for an insert or update query /// Set a key and value for an insert or update query
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Can be called multiple times to set multiple values
/// qb.set("foo", 3)
/// .set("bar", 4)
/// .insert("table");
/// ```
pub fn set(&mut self, key: &str, value: impl Any) -> &mut Self { pub fn set(&mut self, key: &str, value: impl Any) -> &mut Self {
let key = self.driver.quote_identifier(key); let key = self.driver.quote_identifier(key);
self.state self.state
@ -431,11 +475,25 @@ impl QueryBuilder {
} }
/// Convenience method for a `left` join /// Convenience method for a `left` join
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Note that the value is not prepared/escaped, due to it often being a column
/// qb.left_join("table1", "column1", "<>", "foo");
/// ```
pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self { pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
self.join(table, col, op, value, JoinType::Left) self.join(table, col, op, value, JoinType::Left)
} }
/// Convenience method for an `inner` join /// Convenience method for an `inner` join
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // Note that the value is not prepared/escaped, due to it often being a column
/// qb.inner_join("table1", "column1", "<>", "foo");
/// ```
pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self { pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
self.join(table, col, op, value, JoinType::Inner) self.join(table, col, op, value, JoinType::Inner)
} }
@ -445,7 +503,7 @@ impl QueryBuilder {
/// ```no_run /// ```no_run
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // Note that the value is not escaped, due to it often being a column /// // Note that the value is not prepared/escaped, due to it often being a column
/// qb.join("table1", "column1", "<>", "foo", JoinType::Inner); /// qb.join("table1", "column1", "<>", "foo", JoinType::Inner);
/// ``` /// ```
pub fn join( pub fn join(
@ -456,9 +514,16 @@ impl QueryBuilder {
value: &str, value: &str,
join_type: JoinType, join_type: JoinType,
) -> &mut Self { ) -> &mut Self {
let table = self.driver.quote_identifier(table); let col = self.quote_fields(col);
let col = self.driver.quote_identifier(col); let table = self.quote_table(table);
let condition = table + " ON " + &col + op + value;
let condition = format!(
"{} ON {}{}{}",
table,
&col,
op,
value
);
let join_type = match join_type { let join_type = match join_type {
JoinType::Cross => "CROSS ", JoinType::Cross => "CROSS ",
@ -520,6 +585,11 @@ impl QueryBuilder {
/// Add an offset to the query /// Add an offset to the query
pub fn offset(&mut self, offset: usize) -> &mut Self { pub fn offset(&mut self, offset: usize) -> &mut Self {
assert!(
self.state.offset.is_some(),
"You must set a limit to set an offset"
);
self.state.offset = Some(offset); self.state.offset = Some(offset);
self self
@ -531,16 +601,28 @@ impl QueryBuilder {
/// Start a logical grouping in the current query /// Start a logical grouping in the current query
pub fn group_start(&mut self) -> &mut Self { pub fn group_start(&mut self) -> &mut Self {
let conj = if ! self.state.has_where_clause() {
"\nWHERE "
} else {
" "
};
self.state self.state
.append_query_map(QueryClauseType::GroupStart, " ", "("); .append_query_map(QueryClauseType::GroupStart, conj, "(");
self self
} }
/// Start a logical grouping, prefixed with `not` /// Start a logical grouping, prefixed with `not`
pub fn not_group_start(&mut self) -> &mut Self { pub fn not_group_start(&mut self) -> &mut Self {
let conj = if ! self.state.has_where_clause() {
"\nWHERE "
} else {
" AND "
};
self.state self.state
.append_query_map(QueryClauseType::GroupStart, " AND ", "NOT ("); .append_query_map(QueryClauseType::GroupStart, conj, "NOT (");
self self
} }
@ -580,7 +662,7 @@ impl QueryBuilder {
/// # let mut qb = QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // The get() method actually calls the driver to run /// // The get() method actually calls the driver to run
/// // the SQL query /// // the SQL query
/// let query = db.select_vec(vec!["foo", "bar"]) /// let query = qb.select_vec(vec!["foo", "bar"])
/// .from("table t") /// .from("table t")
/// .get(); /// .get();
/// ``` /// ```
@ -602,7 +684,7 @@ impl QueryBuilder {
/// # let mut qb = QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // The insert() method actually calls the driver to run /// // The insert() method actually calls the driver to run
/// // the SQL query /// // the SQL query
/// let query = db.set("foo", 3) /// let query = qb.set("foo", 3)
/// .insert("table"); /// .insert("table");
/// ``` /// ```
pub fn insert(&mut self, table: &str) { pub fn insert(&mut self, table: &str) {
@ -618,7 +700,7 @@ impl QueryBuilder {
/// # let mut qb = QueryBuilder::default(); /// # let mut qb = QueryBuilder::default();
/// // The update() method actually calls the driver to run /// // The update() method actually calls the driver to run
/// // the SQL query /// // the SQL query
/// let query = db.set("foo", 3) /// let query = qb.set("foo", 3)
/// .wher("foo", 4) /// .wher("foo", 4)
/// .update("table"); /// .update("table");
/// ``` /// ```
@ -629,6 +711,15 @@ impl QueryBuilder {
} }
/// Execute the generated delete query /// Execute the generated delete query
///
/// ```no_run
/// # use stringqb::prelude::*;
/// # let mut qb = QueryBuilder::default();
/// // The delete() method actually calls the driver to run
/// // the SQL query
/// let query = qb.wher("foo", 3)
/// .delete("table");
/// ```
pub fn delete(&mut self, table: &str) { pub fn delete(&mut self, table: &str) {
let sql = self.get_compiled_delete(table); let sql = self.get_compiled_delete(table);
@ -670,14 +761,14 @@ impl QueryBuilder {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/// Get a new instance of the query builder /// Get a new instance of the query builder
pub fn reset(&mut self) -> &Self { pub fn reset(&mut self) -> &mut Self {
self.state = QueryState::new(); self.state = QueryState::new();
self self
} }
/// Execute an SQL query with no parameters /// Execute an SQL query with no parameters
pub fn basic_query(&mut self, sql: &str) { pub fn basic_query(&mut self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
self.driver.query(sql) self.driver.query(sql)
} }
@ -691,6 +782,32 @@ impl QueryBuilder {
unimplemented!(); unimplemented!();
} }
/// Quotes table column(s)/field(s) accounting for 'as' aliases
fn quote_fields(&mut self, fields: &str) -> String {
lazy_static! {
static ref RE: Regex = Regex::new(r"(?i) as ").unwrap();
};
split_map_join(fields, ",", |s| {
if !RE.is_match(s) {
return self.driver.quote_identifier(s.trim());
}
// Do a operation similar to split_map_join for the
// regex matches, quoting each identifier
RE.split(s)
.into_iter()
.map(|p| self.driver.quote_identifier(p))
.collect::<Vec<String>>()
.join(" AS ")
})
}
/// Quotes table(s), accounting for aliases
pub fn quote_table(&mut self, table: &str) -> String {
split_map_join(table, " ", |s| self.driver.quote_identifier(s))
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// ! Implementation Details // ! Implementation Details
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -703,6 +820,12 @@ impl QueryBuilder {
like: &str, like: &str,
conj: &str, conj: &str,
) -> &mut Self { ) -> &mut Self {
let conj = if ! self.state.has_where_clause() {
"\nWHERE "
} else {
conj
};
let field = self.driver.quote_identifier(field); let field = self.driver.quote_identifier(field);
let like = format!("{} {} ?", field, like); let like = format!("{} {} ?", field, like);
@ -775,6 +898,12 @@ impl QueryBuilder {
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(",")); let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
let conj = if ! self.state.has_where_clause() {
"\nWHERE "
} else {
conj
};
self.state self.state
.append_query_map(QueryClauseType::WhereIn, conj, &str); .append_query_map(QueryClauseType::WhereIn, conj, &str);
@ -805,21 +934,29 @@ impl QueryBuilder {
item += &item2; item += &item2;
let conj = if self.state.query_map.len() == 0 || ( ! self.state.has_where_clause()) {
String::from("\nWHERE")
} else {
String::from(conj)
};
let conj = if last_item.is_some() { let conj = if last_item.is_some() {
let last_item = last_item.unwrap(); let last_item = last_item.unwrap();
match last_item.clause_type { match last_item.clause_type {
QueryClauseType::GroupStart => String::from(""), QueryClauseType::GroupStart => String::from(""),
_ => format!(" {} ", conj), _ => format!("{} ", conj),
} }
} else { } else {
format!(" {} ", conj) format!("{} ", conj)
}; };
self.state self.state
.append_query_map(QueryClauseType::Where, &conj, &item); .append_query_map(QueryClauseType::Where, &conj, &item);
} }
fn compile(&self, query_type: QueryType, table: &str) -> String { fn compile(&self, query_type: QueryType, table: &str) -> String {
// Get the base clause for the query // Get the base clause for the query
let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table)); let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table));
@ -893,7 +1030,7 @@ impl QueryBuilder {
// @TODO determine query result type // @TODO determine query result type
// @TODO prepare/execute query, and return result // @TODO prepare/execute query, and return result
let stmt = self.prepare(sql); let stmt = self.prepare(sql);
self.execute(&stmt, values) self.execute(&stmt, &values)
} }
} }
@ -1041,12 +1178,6 @@ impl QueryState {
} }
fn append_query_map(&mut self, clause_type: QueryClauseType, conj: &str, s: &str) -> &mut Self { fn append_query_map(&mut self, clause_type: QueryClauseType, conj: &str, s: &str) -> &mut Self {
let conj = if self.query_map.len() == 0 {
" WHERE "
} else {
conj
};
self.query_map.push(QueryClause::new(clause_type, conj, s)); self.query_map.push(QueryClause::new(clause_type, conj, s));
self self
@ -1110,6 +1241,20 @@ impl QueryState {
&mut self.where_values &mut self.where_values
} }
fn has_where_clause(&self) -> bool {
let has_clause = false;
for clause in self.query_map.iter() {
match clause.clause_type {
QueryClauseType::Where => return true,
QueryClauseType::WhereIn => return true,
_ => (),
}
}
has_clause
}
fn set_from_string(&mut self, s: &str) -> &mut Self { fn set_from_string(&mut self, s: &str) -> &mut Self {
self.from_string = String::from(s); self.from_string = String::from(s);
@ -1172,7 +1317,7 @@ mod tests {
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]); qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
let sql = qb.get_compiled_select(); let sql = qb.get_compiled_select();
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) "; let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\" IN (?,?,?,?,?,?) ";
assert_eq!(sql, expected); assert_eq!(sql, expected);
} }

View File

@ -86,8 +86,8 @@ pub enum ToDriverOutput<'a> {
// Generically allow any type that can be converted into a ValueRef // Generically allow any type that can be converted into a ValueRef
// to be converted into a ToSqlOutput as well. // to be converted into a ToSqlOutput as well.
impl<'a, T: ?Sized> From<&'a T> for ToDriverOutput<'a> impl<'a, T: ?Sized> From<&'a T> for ToDriverOutput<'a>
where where
&'a T: Into<ValueRef<'a>>, &'a T: Into<ValueRef<'a>>,
{ {
fn from(t: &'a T) -> Self { fn from(t: &'a T) -> Self {
ToDriverOutput::Borrowed(t.into()) ToDriverOutput::Borrowed(t.into())
@ -120,16 +120,13 @@ impl<'a, T: ?Sized> From<&'a T> for ToDriverOutput<'a>
//from_value!(f64); //from_value!(f64);
//from_value!(Vec<u8>); //from_value!(Vec<u8>);
/// Types that can be converted to a type that the driver understands /// Types that can be converted to a type that the driver understands
pub trait ToDriver { pub trait ToDriver {
fn to_driver(&self) -> Result<ToDriverOutput<'_>, ()>; fn to_driver(&self) -> Result<ToDriverOutput<'_>, ()>;
} }
/// A trait for types that can be created from the result of a query on the driver /// A trait for types that can be created from the result of a query on the driver
pub trait FromDriver: Sized { pub trait FromDriver: Sized {}
}
/// Enum struct for mapping between database and Rust types /// Enum struct for mapping between database and Rust types
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]

View File

@ -15,7 +15,7 @@ fn select_keys_as_query() {
let sql = qb.select("foo as bar, baz").from("a").get_compiled_select(); let sql = qb.select("foo as bar, baz").from("a").get_compiled_select();
assert_eq!(sql, "SELECT \"foo\" as \"bar\",\"baz\"\nFROM \"a\""); assert_eq!(sql, "SELECT \"foo\" AS \"bar\",\"baz\"\nFROM \"a\"");
} }
#[test] #[test]
@ -54,7 +54,7 @@ fn select_where() {
qb.from("test").r#where("foo", "bar"); qb.from("test").r#where("foo", "bar");
let sql = qb.get_compiled_select(); let sql = qb.get_compiled_select();
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\"=?"; let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\"=?";
assert_eq!(sql, expected); assert_eq!(sql, expected);
} }
@ -66,7 +66,7 @@ fn select_where_in() {
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]); qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
let sql = qb.get_compiled_select(); let sql = qb.get_compiled_select();
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) "; let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\" IN (?,?,?,?,?,?) ";
assert_eq!(sql, expected); assert_eq!(sql, expected);
} }