2019-04-09 18:55:53 -04:00
|
|
|
//! Query Builder
|
2019-04-02 16:35:52 -04:00
|
|
|
//!
|
|
|
|
//! The QueryBuilder creates sql queries from chained methods
|
2019-04-12 15:25:59 -04:00
|
|
|
use std::any::Any;
|
2019-04-02 14:36:11 -04:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2019-04-09 17:33:26 -04:00
|
|
|
use crate::drivers::{DatabaseDriver, DefaultDriver};
|
2019-04-12 15:25:59 -04:00
|
|
|
use crate::fns::split_map_join;
|
2019-04-10 19:49:06 -04:00
|
|
|
|
2019-04-17 09:09:31 -04:00
|
|
|
use regex::Regex;
|
2019-04-02 14:36:11 -04:00
|
|
|
|
2019-04-17 12:01:46 -04:00
|
|
|
/// The position of the wildcard(s)
|
|
|
|
/// for a `like` clause
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum LikeWildcard {
|
|
|
|
/// Wildcard before search term
|
|
|
|
/// eg. `%foo`
|
|
|
|
Before,
|
|
|
|
|
|
|
|
/// Wildcard after the search term
|
|
|
|
/// eg. `foo%`
|
|
|
|
After,
|
|
|
|
|
|
|
|
/// Wildcards surrounding the search term
|
|
|
|
/// eg. `%foo%`
|
|
|
|
Both,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The type of SQL join
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum JoinType {
|
|
|
|
/// A `CROSS` join
|
|
|
|
Cross,
|
|
|
|
/// An `INNER` join
|
|
|
|
Inner,
|
|
|
|
/// An `OUTER` join
|
|
|
|
Outer,
|
|
|
|
/// A `LEFT (OUTER)` join
|
|
|
|
Left,
|
|
|
|
/// A `RIGHT (OUTER)` join
|
|
|
|
Right,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The sort direction
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum OrderDirection {
|
|
|
|
/// Sort Ascending
|
|
|
|
Asc,
|
|
|
|
/// Sort Descending
|
|
|
|
Desc,
|
|
|
|
/// Random Sort (Not yet implemented!)
|
|
|
|
Rand,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The type of Query Clause
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
enum QueryClauseType {
|
|
|
|
/// Ending a parenthetical grouping
|
|
|
|
GroupEnd,
|
|
|
|
/// Starting a parenthetical grouping
|
|
|
|
GroupStart,
|
|
|
|
/// A having clause
|
|
|
|
Having,
|
|
|
|
/// A join clause
|
|
|
|
Join,
|
|
|
|
/// A like clause
|
|
|
|
Like,
|
|
|
|
/// A where clause
|
|
|
|
Where,
|
|
|
|
/// A where in clause
|
|
|
|
WhereIn,
|
|
|
|
}
|
|
|
|
|
2019-04-10 14:03:28 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
enum QueryType {
|
|
|
|
Select,
|
|
|
|
Insert,
|
|
|
|
Update,
|
|
|
|
Delete,
|
|
|
|
}
|
|
|
|
|
2019-04-02 16:35:52 -04:00
|
|
|
/// The struct representing a query builder
|
2019-04-09 14:13:37 -04:00
|
|
|
#[derive(Debug)]
|
2019-04-02 14:36:11 -04:00
|
|
|
pub struct QueryBuilder {
|
2019-04-11 20:57:02 -04:00
|
|
|
/// The struct to store the query builder info
|
2019-04-17 11:33:25 -04:00
|
|
|
state: QueryState,
|
2019-04-09 14:13:37 -04:00
|
|
|
driver: Box<dyn DatabaseDriver>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for QueryBuilder {
|
|
|
|
/// Creates a new QueryBuilder instance with default driver
|
2019-04-12 15:25:59 -04:00
|
|
|
///
|
|
|
|
/// This is **not** useful for a real database.
|
2019-04-09 14:13:37 -04:00
|
|
|
fn default() -> Self {
|
|
|
|
QueryBuilder {
|
|
|
|
state: QueryState::new(),
|
|
|
|
driver: Box::new(DefaultDriver::new()),
|
|
|
|
}
|
|
|
|
}
|
2019-04-02 14:36:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl QueryBuilder {
|
2019-04-09 14:13:37 -04:00
|
|
|
/// Create a new QueryBuilder instance with a driver
|
2019-04-12 15:25:59 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// 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());
|
2019-04-12 17:09:59 -04:00
|
|
|
///
|
|
|
|
/// // Each builder method returns a mutable reference to itself so
|
|
|
|
/// // the methods are chainable
|
|
|
|
/// qb.select("field f").from("table");
|
|
|
|
///
|
|
|
|
/// // Since they are references, you do not have to chain.
|
|
|
|
/// let sql = qb.get_compiled_select();
|
2019-04-12 15:25:59 -04:00
|
|
|
/// ```
|
2019-04-09 14:13:37 -04:00
|
|
|
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
|
2019-04-02 14:36:11 -04:00
|
|
|
QueryBuilder {
|
|
|
|
state: QueryState::new(),
|
2019-04-09 14:13:37 -04:00
|
|
|
driver: Box::new(driver),
|
2019-04-02 14:36:11 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 13:29:08 -04:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Select Queries
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-05 14:51:31 -04:00
|
|
|
/// Set the fields to select from the database as a string
|
2019-04-11 17:27:05 -04:00
|
|
|
///
|
2019-04-17 11:36:16 -04:00
|
|
|
/// ```no_run
|
2019-04-11 20:57:02 -04:00
|
|
|
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
2019-04-11 17:27:05 -04:00
|
|
|
/// // You can also alias field names
|
2019-04-11 20:57:02 -04:00
|
|
|
/// qb.select("foo as bar");
|
2019-04-11 17:27:05 -04:00
|
|
|
/// ```
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn select(&mut self, fields: &str) -> &mut Self {
|
2019-04-11 17:16:35 -04:00
|
|
|
lazy_static! {
|
|
|
|
static ref RE: Regex = Regex::new(r"(?i) as ").unwrap();
|
|
|
|
};
|
2019-04-10 14:03:28 -04:00
|
|
|
|
2019-04-11 17:16:35 -04:00
|
|
|
let fields = split_map_join(fields, ",", |s| {
|
2019-04-17 09:09:31 -04:00
|
|
|
if !RE.is_match(s) {
|
2019-04-11 17:16:35 -04:00
|
|
|
return self.driver.quote_identifier(s.trim());
|
|
|
|
}
|
2019-04-10 14:03:28 -04:00
|
|
|
|
2019-04-11 17:16:35 -04:00
|
|
|
// 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 ")
|
|
|
|
});
|
2019-04-10 14:03:28 -04:00
|
|
|
|
|
|
|
self.state.append_select_string(&fields);
|
|
|
|
|
|
|
|
self
|
2019-04-02 17:23:52 -04:00
|
|
|
}
|
2019-04-05 20:46:07 -04:00
|
|
|
|
2019-04-05 14:51:31 -04:00
|
|
|
/// Set the fields to select from the database as a Vector
|
2019-04-11 17:27:05 -04:00
|
|
|
///
|
2019-04-17 11:36:16 -04:00
|
|
|
/// ```no_run
|
2019-04-11 20:57:02 -04:00
|
|
|
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
2019-04-11 17:27:05 -04:00
|
|
|
/// // You can also alias via a vector of fields
|
|
|
|
/// qb.select_vec(vec!["foo as bar", "baz"]);
|
|
|
|
/// ```
|
2019-04-05 14:51:31 -04:00
|
|
|
pub fn select_vec(&mut self, fields: Vec<&str>) -> &mut Self {
|
|
|
|
let fields = fields.join(",");
|
|
|
|
self.select(&fields)
|
|
|
|
}
|
2019-04-02 17:23:52 -04:00
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Adds the `distinct` keyword to a query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn distinct(&mut self) -> &mut Self {
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.prepend_select_string(" DISTINCT");
|
2019-04-11 11:44:06 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-16 15:51:27 -04:00
|
|
|
/// Tell the database to give you query plain info, rather
|
|
|
|
/// than a result set
|
|
|
|
pub fn explain(&mut self) -> &mut Self {
|
|
|
|
self.state.explain = true;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-04-03 13:29:08 -04:00
|
|
|
/// Specify the database table to select from
|
2019-04-11 17:27:05 -04:00
|
|
|
///
|
2019-04-17 11:36:16 -04:00
|
|
|
/// ```no_run
|
2019-04-11 20:57:02 -04:00
|
|
|
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
|
|
|
/// // You can specifiy an alias for the table
|
2019-04-11 17:27:05 -04:00
|
|
|
/// qb.from("products p");
|
|
|
|
/// ```
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn from(&mut self, table_name: &str) -> &mut Self {
|
2019-04-11 15:30:10 -04:00
|
|
|
let from_str = split_map_join(table_name, " ", |s| self.driver.quote_identifier(s));
|
2019-04-11 11:44:06 -04:00
|
|
|
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.set_from_string(&from_str);
|
2019-04-03 13:29:08 -04:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! 'Like' methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Creates a `like` clause in the sql statement
|
2019-04-12 15:25:59 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use stringqb::prelude::*;
|
|
|
|
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
|
|
|
/// // Search for a value that ends with "foo"
|
2019-04-17 11:33:25 -04:00
|
|
|
/// qb.like("field", String::from("foo"), LikeWildcard::Before);
|
2019-04-12 15:25:59 -04:00
|
|
|
///
|
|
|
|
/// // Search for a value that starts with "foo"
|
2019-04-17 11:33:25 -04:00
|
|
|
/// qb.like("field", String::from("foo"), LikeWildcard::After);
|
2019-04-12 15:25:59 -04:00
|
|
|
///
|
|
|
|
/// // Search for a value that has "foo" in it
|
2019-04-17 11:33:25 -04:00
|
|
|
/// qb.like("field", String::from("foo"), LikeWildcard::Both);
|
2019-04-12 15:25:59 -04:00
|
|
|
/// ```
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn like(&mut self, field: &str, value: impl Any, position: LikeWildcard) -> &mut Self {
|
2019-04-10 19:37:02 -04:00
|
|
|
self._like(field, value, position, "LIKE", "AND")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Generates an OR Like clause
|
2019-04-17 12:58:05 -04:00
|
|
|
pub fn or_like(&mut self, field: &str, value: impl Any, position: LikeWildcard) -> &mut Self {
|
2019-04-16 15:18:32 -04:00
|
|
|
self._like(field, value, position, "LIKE", "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Generates a NOI Like clause
|
2019-04-17 12:58:05 -04:00
|
|
|
pub fn not_like(&mut self, field: &str, value: impl Any, position: LikeWildcard) -> &mut Self {
|
2019-04-10 19:37:02 -04:00
|
|
|
self._like(field, value, position, "NOT LIKE", "AND")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Generates an OR NOT Like clause
|
2019-04-17 09:09:31 -04:00
|
|
|
pub fn or_not_like(
|
|
|
|
&mut self,
|
|
|
|
field: &str,
|
2019-04-17 11:39:54 -04:00
|
|
|
value: impl Any,
|
2019-04-17 09:09:31 -04:00
|
|
|
position: LikeWildcard,
|
|
|
|
) -> &mut Self {
|
2019-04-10 19:37:02 -04:00
|
|
|
self._like(field, value, position, "NOT LIKE", "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Having methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add a `having` clause to the query
|
2019-04-16 16:26:55 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use stringqb::prelude::*;
|
|
|
|
/// # let mut qb = QueryBuilder::default();
|
|
|
|
/// // By default, key = value
|
|
|
|
/// qb.having("key", vec![Box::new("value")]);
|
|
|
|
///
|
|
|
|
/// // Other operators can be used with a separating space
|
|
|
|
/// qb.having("clues >=", vec![Box::new(4)]);
|
|
|
|
/// ```
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn having(&mut self, key: &str, value: Vec<impl Any>) -> &mut Self {
|
2019-04-16 15:18:32 -04:00
|
|
|
self._having(key, value, "AND")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add a `having` clause to the query, prefixed with an `or`
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn or_having(&mut self, key: &str, value: Vec<impl Any>) -> &mut Self {
|
2019-04-16 15:18:32 -04:00
|
|
|
self._having(key, value, "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! 'Where' methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-16 15:51:27 -04:00
|
|
|
/// Specify a condition for the `where` clause of the query. Can be called
|
|
|
|
/// multiple times, which will then add additional where conditions, prefixed
|
|
|
|
/// with 'AND'.
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use stringqb::prelude::*;
|
|
|
|
/// # let mut qb = QueryBuilder::default();
|
|
|
|
/// // By default, key = value
|
|
|
|
/// qb.r#where("key", Box::new("value"));
|
|
|
|
///
|
|
|
|
/// // Other operators can be used with a separating space
|
|
|
|
/// qb.r#where("key >", Box::new(4));
|
|
|
|
/// ```
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn r#where(&mut self, key: &str, value: impl Any) -> &mut Self {
|
2019-04-16 15:51:27 -04:00
|
|
|
self._where_string(key, value, "AND")
|
2019-04-05 14:51:31 -04:00
|
|
|
}
|
2019-04-03 13:29:08 -04:00
|
|
|
|
2019-04-17 11:39:54 -04:00
|
|
|
/// Alias method for `where`, as using the `where` method requires
|
|
|
|
/// using the raw identifier `r#where`.
|
|
|
|
pub fn filter(&mut self, key: &str, value: impl Any) -> &mut Self {
|
|
|
|
self.r#where(key, value)
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Specify a condition for the `where` clause of the query, prefixed with `or`
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn or_where(&mut self, key: &str, value: impl Any) -> &mut Self {
|
2019-04-16 15:51:27 -04:00
|
|
|
self._where_string(key, value, "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Specify a `where in` clause for the query
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
2019-04-17 09:09:31 -04:00
|
|
|
self._where_in(key, values, "IN", "AND")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Specify a `where in` clause for the query, prefixed with `or`
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn or_where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
2019-04-12 15:25:59 -04:00
|
|
|
self._where_in(key, values, "IN", "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Specify a `where not in` clause for the query
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
2019-04-12 15:25:59 -04:00
|
|
|
self._where_in(key, values, "NOT IN", "AND")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Specify a `where not in` clause for the query, prefixed with `or`
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn or_where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
2019-04-12 15:25:59 -04:00
|
|
|
self._where_in(key, values, "NOT IN", "OR")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Other Query Modifier methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-02 16:35:52 -04:00
|
|
|
/// Set a key and value for an insert or update query
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn set(&mut self, key: &str, value: impl Any) -> &mut Self {
|
2019-04-02 17:23:52 -04:00
|
|
|
// @TODO figure a way to make this easier to use
|
2019-04-11 17:16:35 -04:00
|
|
|
let key = self.driver.quote_identifier(key);
|
2019-04-17 12:58:05 -04:00
|
|
|
self.state
|
|
|
|
.append_set_array_keys(&key)
|
|
|
|
.append_values(Box::new(value));
|
2019-04-02 14:36:11 -04:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
2019-04-02 17:23:52 -04:00
|
|
|
|
|
|
|
/// Set a map of data for an insert or update query
|
2019-04-17 11:39:54 -04:00
|
|
|
pub fn set_map(&mut self, data: HashMap<String, impl Any>) -> &mut Self {
|
2019-04-02 17:23:52 -04:00
|
|
|
for (key, value) in data {
|
2019-04-04 16:39:05 -04:00
|
|
|
self.set(&key, value);
|
2019-04-02 17:23:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-04-09 14:13:37 -04:00
|
|
|
/// Convenience method for a `left` join
|
|
|
|
pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
|
|
|
self.join(table, col, op, value, JoinType::Left)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convenience method for an `inner` join
|
|
|
|
pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
|
|
|
self.join(table, col, op, value, JoinType::Inner)
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add a table join to the query
|
2019-04-16 15:18:32 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use stringqb::prelude::*;
|
|
|
|
/// # let mut qb = QueryBuilder::default();
|
|
|
|
/// // Note that the value is not escaped, due to it often being a column
|
|
|
|
/// qb.join("table1", "column1", "<>", "foo", JoinType::Inner);
|
|
|
|
/// ```
|
2019-04-09 17:33:26 -04:00
|
|
|
pub fn join(
|
|
|
|
&mut self,
|
|
|
|
table: &str,
|
|
|
|
col: &str,
|
|
|
|
op: &str,
|
|
|
|
value: &str,
|
|
|
|
join_type: JoinType,
|
|
|
|
) -> &mut Self {
|
2019-04-09 14:13:37 -04:00
|
|
|
let table = self.driver.quote_identifier(table);
|
|
|
|
let col = self.driver.quote_identifier(col);
|
|
|
|
let condition = table + " ON " + &col + op + value;
|
|
|
|
|
|
|
|
let join_type = match join_type {
|
2019-04-10 14:03:28 -04:00
|
|
|
JoinType::Cross => "CROSS ",
|
2019-04-09 14:13:37 -04:00
|
|
|
JoinType::Left => "LEFT ",
|
|
|
|
JoinType::Inner => "INNER ",
|
|
|
|
JoinType::Outer => "OUTER ",
|
|
|
|
JoinType::Right => "RIGHT ",
|
|
|
|
};
|
|
|
|
|
|
|
|
let conjunction = "\n".to_string() + join_type + "JOIN ";
|
|
|
|
|
2019-04-09 18:55:53 -04:00
|
|
|
self.state
|
|
|
|
.append_query_map(QueryClauseType::Join, &conjunction, &condition);
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
2019-04-02 17:23:52 -04:00
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add a group by clause to the query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn group_by(&mut self, field: &str) -> &mut Self {
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.append_group_array(field);
|
|
|
|
|
|
|
|
let group_string = String::from(" GROUP BY ") + &self.state.get_group_array().join(",");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.set_group_string(&group_string);
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add an order by clause to the query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn order_by(&mut self, field: &str, direction: OrderDirection) -> &mut Self {
|
2019-04-09 14:13:37 -04:00
|
|
|
let field = self.driver.quote_identifier(field);
|
|
|
|
let dir = match direction {
|
|
|
|
OrderDirection::Asc => String::from("ASC"),
|
|
|
|
OrderDirection::Desc => String::from("DESC"),
|
2019-04-16 15:18:32 -04:00
|
|
|
OrderDirection::Rand => self.driver.random(),
|
2019-04-09 14:13:37 -04:00
|
|
|
};
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.append_order_map(&field, &dir);
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
let mut order_clauses: Vec<String> = vec![];
|
|
|
|
|
2019-04-11 15:30:10 -04:00
|
|
|
for (f, dir) in self.state.get_order_map() {
|
2019-04-09 14:13:37 -04:00
|
|
|
let clause = String::clone(f) + " " + &dir;
|
|
|
|
&order_clauses.push(clause);
|
|
|
|
}
|
|
|
|
|
2019-04-16 15:18:32 -04:00
|
|
|
let order_str = "\nORDER BY ".to_string() + &order_clauses.join(", ");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
2019-04-11 15:30:10 -04:00
|
|
|
self.state.set_order_string(&order_str);
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Add a limit to the query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn limit(&mut self, limit: usize) -> &mut Self {
|
2019-04-03 16:29:51 -04:00
|
|
|
self.state.limit = Some(limit);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add an offset to the query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn offset(&mut self, offset: usize) -> &mut Self {
|
2019-04-03 16:29:51 -04:00
|
|
|
self.state.offset = Some(offset);
|
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Query Grouping Methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Start a logical grouping in the current query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn group_start(&mut self) -> &mut Self {
|
2019-04-10 14:03:28 -04:00
|
|
|
self.state
|
2019-04-16 15:18:32 -04:00
|
|
|
.append_query_map(QueryClauseType::GroupStart, " ", "(");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Start a logical grouping, prefixed with `not`
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn not_group_start(&mut self) -> &mut Self {
|
2019-04-10 14:03:28 -04:00
|
|
|
self.state
|
2019-04-16 15:18:32 -04:00
|
|
|
.append_query_map(QueryClauseType::GroupStart, " AND ", "NOT (");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Start a logical grouping, prefixed with `or`
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn or_group_start(&mut self) -> &mut Self {
|
2019-04-09 17:33:26 -04:00
|
|
|
self.state
|
2019-04-09 18:55:53 -04:00
|
|
|
.append_query_map(QueryClauseType::GroupStart, "", " OR (");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Start a logical grouping, prefixed with `or not`
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn or_not_group_start(&mut self) -> &mut Self {
|
2019-04-09 18:55:53 -04:00
|
|
|
self.state
|
|
|
|
.append_query_map(QueryClauseType::GroupStart, "", " OR NOT (");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// End the current logical grouping
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn group_end(&mut self) -> &mut Self {
|
2019-04-09 17:33:26 -04:00
|
|
|
self.state
|
2019-04-09 18:55:53 -04:00
|
|
|
.append_query_map(QueryClauseType::GroupEnd, "", ")");
|
2019-04-09 14:13:37 -04:00
|
|
|
|
|
|
|
self
|
2019-04-02 17:23:52 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 13:29:08 -04:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Query execution methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-02 17:23:52 -04:00
|
|
|
/// Execute the built query
|
2019-04-10 14:03:28 -04:00
|
|
|
pub fn get(self) {
|
2019-04-02 17:23:52 -04:00
|
|
|
unimplemented!();
|
|
|
|
}
|
2019-04-03 13:29:08 -04:00
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Count all the rows in the specified database table
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn count_all(self, table: &str) -> usize {
|
2019-04-03 13:29:08 -04:00
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Execute the generated insert query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn insert(&mut self, table: &str) {
|
2019-04-03 13:29:08 -04:00
|
|
|
// @TODO determine query result type
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Execute the generated update query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn update(&mut self, table: &str) {
|
2019-04-03 13:29:08 -04:00
|
|
|
// @TODO determine query result type
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Execute the generated delete query
|
2019-04-04 16:39:05 -04:00
|
|
|
pub fn delete(&mut self, table: &str) {
|
2019-04-03 13:29:08 -04:00
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! SQL Returning Methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Get the generated SQL for a select query
|
2019-04-11 15:30:10 -04:00
|
|
|
pub fn get_compiled_select(&self) -> String {
|
|
|
|
// The table name should already be set from the `from` method
|
|
|
|
assert!(
|
|
|
|
self.state.get_from_string().len() > 0,
|
|
|
|
"You must use the `from` method to set the table name for a select query"
|
|
|
|
);
|
|
|
|
|
|
|
|
self.compile(QueryType::Select, "")
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Get the generated SQL for an insert query
|
2019-04-11 15:30:10 -04:00
|
|
|
pub fn get_compiled_insert(&self, table: &str) -> String {
|
|
|
|
self.compile(QueryType::Insert, table)
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Get the generated SQL for an update query
|
2019-04-11 15:30:10 -04:00
|
|
|
pub fn get_compiled_update(&self, table: &str) -> String {
|
|
|
|
self.compile(QueryType::Update, table)
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Get the generated SQL for a delete query
|
2019-04-11 15:30:10 -04:00
|
|
|
pub fn get_compiled_delete(&self, table: &str) -> String {
|
|
|
|
self.compile(QueryType::Delete, table)
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Miscellaneous Methods
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
/// Get a new instance of the query builder
|
2019-04-11 15:30:10 -04:00
|
|
|
pub fn reset(&mut self) -> &Self {
|
2019-04-09 14:13:37 -04:00
|
|
|
self.state = QueryState::new();
|
|
|
|
|
|
|
|
self
|
2019-04-03 13:29:08 -04:00
|
|
|
}
|
2019-04-09 20:45:57 -04:00
|
|
|
|
2019-04-16 16:26:55 -04:00
|
|
|
/// Execute an SQL query
|
|
|
|
pub fn query(&mut self, sql: &str) {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
2019-04-09 20:45:57 -04:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ! Implementation Details
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2019-04-10 19:49:06 -04:00
|
|
|
fn _like(
|
|
|
|
&mut self,
|
|
|
|
field: &str,
|
2019-04-17 11:39:54 -04:00
|
|
|
value: impl Any,
|
2019-04-10 19:49:06 -04:00
|
|
|
position: LikeWildcard,
|
|
|
|
like: &str,
|
|
|
|
conj: &str,
|
|
|
|
) -> &mut Self {
|
2019-04-10 19:37:02 -04:00
|
|
|
let field = self.driver.quote_identifier(field);
|
|
|
|
|
|
|
|
let like = format!("{} {} ?", field, like);
|
|
|
|
|
2019-04-17 11:33:25 -04:00
|
|
|
let value: Box<dyn Any + 'static> = Box::new(value);
|
|
|
|
let string_val = value.downcast_ref::<String>().unwrap();
|
2019-04-10 19:49:06 -04:00
|
|
|
|
2019-04-10 19:37:02 -04:00
|
|
|
// @TODO Properly parse types of `value` for string formatting
|
|
|
|
let value = match position {
|
2019-04-17 11:33:25 -04:00
|
|
|
LikeWildcard::Before => format!("%{}", string_val),
|
|
|
|
LikeWildcard::After => format!("{}%s", string_val),
|
|
|
|
LikeWildcard::Both => format!("%{}%", string_val),
|
2019-04-10 19:37:02 -04:00
|
|
|
};
|
|
|
|
|
2019-04-10 19:49:06 -04:00
|
|
|
self.state
|
|
|
|
.append_query_map(QueryClauseType::Like, conj, &like);
|
2019-04-10 19:37:02 -04:00
|
|
|
self.state.append_where_values(Box::new(value));
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-04-17 11:39:54 -04:00
|
|
|
fn _having(&mut self, key: &str, values: Vec<impl Any>, conj: &str) -> &mut Self {
|
2019-04-16 15:18:32 -04:00
|
|
|
let keys = self._where(key, values);
|
2019-04-10 19:37:02 -04:00
|
|
|
|
2019-04-16 15:18:32 -04:00
|
|
|
for k in keys {
|
|
|
|
self._having_key(&k, conj);
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-04-17 09:09:31 -04:00
|
|
|
fn _having_key(&mut self, key: &str, conj: &str) -> &mut Self {
|
2019-04-16 15:18:32 -04:00
|
|
|
let field = key.trim().split(" ").collect::<Vec<&str>>();
|
|
|
|
|
|
|
|
let mut item = self.driver.quote_identifier(field[0]);;
|
|
|
|
|
|
|
|
let item2 = if field.len() == 1 {
|
|
|
|
String::from("=?")
|
|
|
|
} else {
|
|
|
|
format!(" {} ?", &field[1])
|
|
|
|
};
|
|
|
|
|
|
|
|
item += &item2;
|
|
|
|
|
|
|
|
self.state.append_having_map(conj, &item);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-04-17 11:39:54 -04:00
|
|
|
fn _where(&mut self, key: &str, values: Vec<impl Any>) -> Vec<String> {
|
2019-04-16 15:18:32 -04:00
|
|
|
for x in values {
|
2019-04-17 11:33:25 -04:00
|
|
|
self.state.append_where_values(Box::new(x));
|
2019-04-16 15:18:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
vec![String::from(key)]
|
2019-04-10 19:37:02 -04:00
|
|
|
}
|
|
|
|
|
2019-04-17 09:09:31 -04:00
|
|
|
fn _where_in(
|
|
|
|
&mut self,
|
|
|
|
key: &str,
|
2019-04-17 11:39:54 -04:00
|
|
|
values: Vec<impl Any>,
|
2019-04-17 09:09:31 -04:00
|
|
|
in_str: &str,
|
|
|
|
conj: &str,
|
|
|
|
) -> &mut Self {
|
2019-04-12 15:25:59 -04:00
|
|
|
let key = self.driver.quote_identifier(key);
|
|
|
|
let placeholders = vec!["?"; values.len()];
|
|
|
|
|
|
|
|
for value in values {
|
2019-04-17 11:33:25 -04:00
|
|
|
self.state.append_where_values(Box::new(value));
|
2019-04-12 15:25:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
|
|
|
|
|
2019-04-17 09:09:31 -04:00
|
|
|
self.state
|
|
|
|
.append_query_map(QueryClauseType::WhereIn, conj, &str);
|
2019-04-12 15:25:59 -04:00
|
|
|
|
|
|
|
self
|
2019-04-10 19:37:02 -04:00
|
|
|
}
|
|
|
|
|
2019-04-17 11:33:25 -04:00
|
|
|
fn _where_string(&mut self, key: &str, value: impl Any, conj: &str) -> &mut Self {
|
2019-04-16 15:51:27 -04:00
|
|
|
let keys = self._where(key, vec![value]);
|
|
|
|
|
|
|
|
for k in keys {
|
|
|
|
self._where_string_key(key, conj);
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _where_string_key(&mut self, key: &str, conj: &str) {
|
|
|
|
let field = key.trim().split(" ").collect::<Vec<&str>>();
|
|
|
|
let query_map = self.state.get_query_map();
|
|
|
|
|
|
|
|
let last_item = &query_map[query_map.len() - 1];
|
|
|
|
let mut item = self.driver.quote_identifier(field[0]);
|
|
|
|
|
|
|
|
let item2 = if field.len() == 1 {
|
|
|
|
String::from("=?")
|
|
|
|
} else {
|
|
|
|
format!(" {} ?", &field[1])
|
|
|
|
};
|
|
|
|
|
|
|
|
item += &item2;
|
|
|
|
|
|
|
|
let conj = if last_item.clause_type == QueryClauseType::GroupStart {
|
|
|
|
String::from("")
|
|
|
|
} else {
|
|
|
|
format!(" {} ", conj)
|
|
|
|
};
|
|
|
|
|
2019-04-17 09:09:31 -04:00
|
|
|
self.state
|
|
|
|
.append_query_map(QueryClauseType::Where, &conj, &item);
|
2019-04-10 19:37:02 -04:00
|
|
|
}
|
|
|
|
|
2019-04-10 14:03:28 -04:00
|
|
|
fn compile(&self, query_type: QueryType, table: &str) -> String {
|
2019-04-11 15:30:10 -04:00
|
|
|
// Get the base clause for the query
|
|
|
|
let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table));
|
|
|
|
|
|
|
|
let mut parts = vec![base_sql];
|
|
|
|
|
|
|
|
for clause in self.state.get_query_map() {
|
|
|
|
&parts.push(clause.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
&parts.push(self.state.get_group_string().to_string());
|
|
|
|
&parts.push(self.state.get_order_string().to_string());
|
|
|
|
|
|
|
|
for clause in self.state.get_having_map() {
|
|
|
|
&parts.push(clause.to_string());
|
|
|
|
}
|
|
|
|
|
2019-04-16 15:51:27 -04:00
|
|
|
let mut sql = parts.join("");
|
2019-04-11 15:30:10 -04:00
|
|
|
|
2019-04-16 15:51:27 -04:00
|
|
|
// Add limit/ offset
|
|
|
|
sql = self.driver.limit(&sql, self.state.limit, self.state.offset);
|
2019-04-11 15:30:10 -04:00
|
|
|
|
2019-04-16 15:51:27 -04:00
|
|
|
// Handle explain
|
|
|
|
if self.state.explain == true {
|
|
|
|
self.driver.explain(&sql)
|
|
|
|
} else {
|
|
|
|
sql
|
|
|
|
}
|
2019-04-10 14:03:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn compile_type(&self, query_type: QueryType, table: &str) -> String {
|
2019-04-11 15:30:10 -04:00
|
|
|
match query_type {
|
|
|
|
QueryType::Select => {
|
|
|
|
let from = self.state.get_from_string();
|
|
|
|
let select = self.state.get_select_string();
|
|
|
|
|
|
|
|
let sql = format!("SELECT *\nFROM {}", from);
|
|
|
|
|
|
|
|
if select.len() > 0 {
|
|
|
|
sql.replace("*", select)
|
|
|
|
} else {
|
|
|
|
sql
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QueryType::Insert => {
|
|
|
|
let set_array_keys = self.state.get_set_array_keys();
|
|
|
|
let param_count = set_array_keys.len();
|
|
|
|
let params = vec!["?"; param_count];
|
|
|
|
|
|
|
|
format!(
|
|
|
|
"INSERT INTO {} ({})\nVALUES({})",
|
|
|
|
table,
|
|
|
|
set_array_keys.join(","),
|
|
|
|
params.join(",")
|
|
|
|
)
|
|
|
|
}
|
|
|
|
QueryType::Update => {
|
|
|
|
let set_string = self.state.get_set_string();
|
|
|
|
|
|
|
|
format!("UPDATE {}\nSET {}", table, set_string)
|
|
|
|
}
|
|
|
|
QueryType::Delete => format!("DELETE FROM {}", table),
|
|
|
|
}
|
2019-04-09 20:45:57 -04:00
|
|
|
}
|
2019-04-02 14:36:11 -04:00
|
|
|
}
|
|
|
|
|
2019-04-17 11:33:25 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct QueryClause {
|
|
|
|
clause_type: QueryClauseType,
|
|
|
|
conjunction: String,
|
|
|
|
string: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl QueryClause {
|
|
|
|
pub fn new(clause_type: QueryClauseType, conjunction: &str, string: &str) -> Self {
|
|
|
|
QueryClause {
|
|
|
|
clause_type,
|
|
|
|
conjunction: conjunction.to_string(),
|
|
|
|
string: string.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_string(&self) -> String {
|
|
|
|
format!("{}{}", self.conjunction, self.string)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct QueryState {
|
|
|
|
pub explain: bool,
|
|
|
|
|
|
|
|
select_string: String,
|
|
|
|
from_string: String,
|
|
|
|
set_string: String,
|
|
|
|
order_string: String,
|
|
|
|
group_string: String,
|
|
|
|
|
|
|
|
// Keys for insert/update statement
|
|
|
|
set_array_keys: Vec<String>,
|
|
|
|
|
|
|
|
// Order by clause
|
|
|
|
order_map: HashMap<String, String>,
|
|
|
|
|
|
|
|
// Group by clause
|
|
|
|
group_array: Vec<String>,
|
|
|
|
|
|
|
|
// Values to apply to prepared statements
|
|
|
|
values: Vec<Box<Any>>,
|
|
|
|
|
|
|
|
// Values to apply to where clauses in prepared statements
|
|
|
|
where_values: Vec<Box<Any>>,
|
|
|
|
|
|
|
|
pub limit: Option<usize>,
|
|
|
|
|
|
|
|
pub offset: Option<usize>,
|
|
|
|
|
|
|
|
// Query components for complex selects
|
|
|
|
query_map: Vec<QueryClause>,
|
|
|
|
|
|
|
|
// Query components for having clauses
|
|
|
|
having_map: Vec<QueryClause>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for QueryState {
|
|
|
|
fn default() -> Self {
|
|
|
|
QueryState {
|
|
|
|
explain: false,
|
|
|
|
|
|
|
|
select_string: String::from(""),
|
|
|
|
from_string: String::from(""),
|
|
|
|
set_string: String::from(""),
|
|
|
|
order_string: String::from(""),
|
|
|
|
group_string: String::from(""),
|
|
|
|
|
|
|
|
set_array_keys: vec![],
|
|
|
|
order_map: HashMap::new(),
|
|
|
|
group_array: vec![],
|
|
|
|
values: vec![],
|
|
|
|
where_values: vec![],
|
|
|
|
|
|
|
|
limit: None,
|
|
|
|
offset: None,
|
|
|
|
|
|
|
|
query_map: vec![],
|
|
|
|
having_map: vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl QueryState {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
QueryState::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_select_string(&mut self, s: &str) -> &mut Self {
|
|
|
|
self.select_string += s;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn prepend_select_string(&mut self, s: &str) -> &mut Self {
|
|
|
|
self.select_string = String::from(s) + &self.select_string;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_group_array(&mut self, field: &str) -> &mut Self {
|
|
|
|
self.group_array.push(String::from(field));
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_having_map(&mut self, conj: &str, s: &str) -> &mut Self {
|
|
|
|
let conj = if self.having_map.len() == 0 {
|
|
|
|
String::from(" HAVING ")
|
|
|
|
} else {
|
|
|
|
format!(" {} ", conj)
|
|
|
|
};
|
|
|
|
|
|
|
|
self.having_map
|
|
|
|
.push(QueryClause::new(QueryClauseType::Having, &conj, s));
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_order_map(&mut self, key: &str, dir: &str) -> &mut Self {
|
|
|
|
self.order_map.insert(String::from(key), String::from(dir));
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_set_array_keys(&mut self, key: &str) -> &mut Self {
|
|
|
|
self.set_array_keys.push(key.to_string());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_values(&mut self, val: Box<Any>) -> &mut Self {
|
|
|
|
self.values.push(val);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append_where_values(&mut self, val: Box<Any>) -> &mut Self {
|
|
|
|
self.where_values.push(val);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub 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
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_from_string(&self) -> &str {
|
|
|
|
&self.from_string
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_group_array(&self) -> &Vec<String> {
|
|
|
|
&self.group_array
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_group_string(&self) -> &str {
|
|
|
|
&self.group_string
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_having_map(&self) -> &Vec<QueryClause> {
|
|
|
|
&self.having_map
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_query_map(&self) -> &Vec<QueryClause> {
|
|
|
|
&self.query_map
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_select_string(&self) -> &str {
|
|
|
|
&self.select_string
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_set_array_keys(&self) -> &Vec<String> {
|
|
|
|
&self.set_array_keys
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_set_string(&self) -> &str {
|
|
|
|
&self.set_string
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_order_map(&self) -> &HashMap<String, String> {
|
|
|
|
&self.order_map
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_order_string(&self) -> &str {
|
|
|
|
&self.order_string
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_values(&self) -> &Vec<Box<Any>> {
|
|
|
|
&self.values
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_where_values(&self) -> &Vec<Box<Any>> {
|
|
|
|
&self.where_values
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_from_string(&mut self, s: &str) -> &mut Self {
|
|
|
|
self.from_string = String::from(s);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_group_string(&mut self, s: &str) -> &mut Self {
|
|
|
|
self.group_string = String::from(s);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_order_string(&mut self, order_string: &str) -> &mut Self {
|
|
|
|
self.order_string = String::from(order_string);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 14:36:11 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn set_key_value() {
|
2019-04-09 14:13:37 -04:00
|
|
|
let mut qb = QueryBuilder::default();
|
2019-04-04 16:39:05 -04:00
|
|
|
|
2019-04-17 11:33:25 -04:00
|
|
|
qb.set("foo", "bar");
|
2019-04-02 14:36:11 -04:00
|
|
|
|
2019-04-11 17:16:35 -04:00
|
|
|
assert_eq!(qb.state.get_set_array_keys()[0], "\"foo\"");
|
2019-04-11 15:30:10 -04:00
|
|
|
assert!(qb.state.get_values()[0].is::<&str>());
|
2019-04-03 16:29:51 -04:00
|
|
|
|
|
|
|
// @TODO find a way to make this kind of operation much more ergonomic
|
2019-04-11 15:30:10 -04:00
|
|
|
assert_eq!(
|
|
|
|
*qb.state.get_values()[0].downcast_ref::<&str>().unwrap(),
|
|
|
|
"bar"
|
|
|
|
);
|
2019-04-02 14:36:11 -04:00
|
|
|
}
|
2019-04-02 17:23:52 -04:00
|
|
|
|
2019-04-03 16:29:51 -04:00
|
|
|
#[test]
|
|
|
|
fn set_hashmap() {
|
2019-04-09 14:13:37 -04:00
|
|
|
let mut qb = QueryBuilder::default();
|
2019-04-03 16:29:51 -04:00
|
|
|
|
2019-04-17 11:33:25 -04:00
|
|
|
let mut authors = HashMap::new();
|
2019-04-17 12:58:05 -04:00
|
|
|
authors.insert(String::from("Chinua Achebe"), String::from("Nigeria"));
|
|
|
|
authors.insert(String::from("Rabindranath Tagore"), String::from("India"));
|
2019-04-17 11:33:25 -04:00
|
|
|
authors.insert(String::from("Anita Nair"), String::from("India"));
|
2019-04-03 16:29:51 -04:00
|
|
|
|
2019-04-04 16:39:05 -04:00
|
|
|
qb.set_map(authors);
|
2019-04-03 16:29:51 -04:00
|
|
|
|
2019-04-03 16:41:51 -04:00
|
|
|
// assert_eq!(qb.state.set_array_keys[0], "Chinua Achebe");
|
2019-04-11 15:30:10 -04:00
|
|
|
assert_eq!(qb.state.get_set_array_keys().len(), 3);
|
|
|
|
assert_eq!(qb.state.get_values().len(), 3);
|
2019-04-03 16:29:51 -04:00
|
|
|
}
|
2019-04-12 15:25:59 -04:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn set_where_in() {
|
|
|
|
let mut qb = QueryBuilder::default();
|
|
|
|
|
2019-04-17 12:58:05 -04:00
|
|
|
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
|
2019-04-12 15:25:59 -04:00
|
|
|
|
|
|
|
let sql = qb.get_compiled_select();
|
|
|
|
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) ";
|
|
|
|
|
|
|
|
assert_eq!(sql, expected);
|
|
|
|
}
|
2019-04-03 20:58:22 -04:00
|
|
|
}
|