Put most of the features together for actually assembling the sql

This commit is contained in:
Timothy Warren 2019-04-11 15:30:10 -04:00
parent 35c0683891
commit 2e793284dd
4 changed files with 382 additions and 197 deletions

61
src/enums.rs Normal file
View File

@ -0,0 +1,61 @@
//! Common enums
/// 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)]
pub enum QueryClauseType {
/// Ending a parenthetical grouping
GroupEnd,
/// Starting a parenthetical grouping
GroupStart,
/// A join clause
Join,
/// A like clause
Like,
/// A where clause
Where,
/// A where in clause
WhereIn,
}

View File

@ -8,6 +8,7 @@
#![allow(unused_variables)] #![allow(unused_variables)]
pub mod drivers; pub mod drivers;
pub mod enums;
pub mod query_builder; pub mod query_builder;
pub mod types; pub mod types;

View File

@ -1,64 +1,16 @@
//! Query Builder //! Query Builder
//! //!
//! The QueryBuilder creates sql queries from chained methods //! The QueryBuilder creates sql queries from chained methods
mod query_state;
use std::collections::HashMap; use std::collections::HashMap;
use crate::drivers::{DatabaseDriver, DefaultDriver}; use crate::drivers::{DatabaseDriver, DefaultDriver};
use crate::enums::*;
use crate::split_map_join; use crate::split_map_join;
use crate::types::Wild; use crate::types::Wild;
/// The position of the wildcard(s) use query_state::QueryState;
/// 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,
}
#[derive(Debug)]
enum QueryClauseType {
GroupEnd,
GroupStart,
Join,
Like,
Where,
WhereIn,
}
#[derive(Debug)] #[derive(Debug)]
enum QueryType { enum QueryType {
@ -68,116 +20,6 @@ enum QueryType {
Delete, Delete,
} }
#[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(),
}
}
}
#[derive(Debug)]
struct QueryState {
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_array: HashMap<String, String>,
// Group by clause
group_array: Vec<String>,
// Values to apply to prepared statements
values: Vec<Wild>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Wild>,
limit: Option<usize>,
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 {
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_array: 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 append_where_values(&mut self, val: Wild) -> &mut Self {
self.where_values.push(val);
self
}
pub fn append_query_map(
&mut self,
clause_type: QueryClauseType,
conj: &str,
s: &str,
) -> &mut Self {
self.query_map.push(QueryClause::new(clause_type, conj, s));
self
}
pub fn set_from_string(&mut self, s: &str) -> &mut Self {
self.from_string = s.to_owned();
self
}
}
/// The struct representing a query builder /// The struct representing a query builder
#[derive(Debug)] #[derive(Debug)]
pub struct QueryBuilder { pub struct QueryBuilder {
@ -232,20 +74,16 @@ impl QueryBuilder {
/// Adds the `distinct` keyword to a query /// Adds the `distinct` keyword to a query
pub fn distinct(&mut self) -> &mut Self { pub fn distinct(&mut self) -> &mut Self {
self.state.select_string = String::from(" DISTINCT") + &self.state.select_string; self.state.prepend_select_string(" DISTINCT");
self self
} }
/// Specify the database table to select from /// Specify the database table to select from
pub fn from(&mut self, table_name: &str) -> &mut Self { pub fn from(&mut self, table_name: &str) -> &mut Self {
let ident_vec = String::from(table_name) let from_str = split_map_join(table_name, " ", |s| self.driver.quote_identifier(s));
.split(" ")
.into_iter()
.map(|s| self.driver.quote_identifier(s))
.collect::<Vec<String>>();
self.state.from_string = ident_vec.join(" "); self.state.set_from_string(&from_str);
self self
} }
@ -295,9 +133,9 @@ impl QueryBuilder {
/// Specify a condition for the `where` clause of the query /// Specify a condition for the `where` clause of the query
pub fn r#where(&mut self, key: &str, op: &str, value: Wild) -> &mut Self { pub fn r#where(&mut self, key: &str, op: &str, value: Wild) -> &mut Self {
// @TODO actually implement setting the keys for the where // @TODO actually implement setting the keys for the where
self.state.where_values.push(value); self.state.append_where_values(value);
self unimplemented!();
} }
/// Specify a condition for a `where` clause where a column has a value /// Specify a condition for a `where` clause where a column has a value
@ -337,8 +175,7 @@ impl QueryBuilder {
/// Set a key and value for an insert or update query /// Set a key and value for an insert or update query
pub fn set(&mut self, key: &str, value: Wild) -> &mut Self { pub fn set(&mut self, key: &str, value: Wild) -> &mut Self {
// @TODO figure a way to make this easier to use // @TODO figure a way to make this easier to use
self.state.set_array_keys.push(key.to_string()); self.state.append_set_array_keys(key).append_values(value);
self.state.values.push(value);
self self
} }
@ -393,9 +230,11 @@ impl QueryBuilder {
/// Add a group by clause to the query /// Add a group by clause to the query
pub fn group_by(&mut self, field: &str) -> &mut Self { pub fn group_by(&mut self, field: &str) -> &mut Self {
self.state.group_array.push(field.to_string()); self.state.append_group_array(field);
self.state.group_string = " GROUP BY ".to_string() + &self.state.group_array.join(","); let group_string = String::from(" GROUP BY ") + &self.state.get_group_array().join(",");
self.state.set_group_string(&group_string);
self self
} }
@ -413,11 +252,11 @@ impl QueryBuilder {
OrderDirection::Desc => String::from("DESC"), OrderDirection::Desc => String::from("DESC"),
OrderDirection::Rand => String::from("RAND"), OrderDirection::Rand => String::from("RAND"),
}; };
self.state.order_array.insert(field, dir); self.state.append_order_map(&field, &dir);
let mut order_clauses: Vec<String> = vec![]; let mut order_clauses: Vec<String> = vec![];
for (f, dir) in &self.state.order_array { for (f, dir) in self.state.get_order_map() {
let clause = String::clone(f) + " " + &dir; let clause = String::clone(f) + " " + &dir;
&order_clauses.push(clause); &order_clauses.push(clause);
} }
@ -428,7 +267,7 @@ impl QueryBuilder {
unimplemented!(); unimplemented!();
}; };
self.state.order_string = order_str; self.state.set_order_string(&order_str);
self self
} }
@ -453,7 +292,7 @@ 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.query_map.len() == 0 { let conj = if self.state.query_map_empty() {
" WHERE " " WHERE "
} else { } else {
" " " "
@ -467,7 +306,7 @@ impl QueryBuilder {
/// 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.query_map.len() == 0 { let conj = if self.state.query_map_empty() {
" WHERE " " WHERE "
} else { } else {
" AND " " AND "
@ -539,23 +378,29 @@ impl QueryBuilder {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/// Get the generated SQL for a select query /// Get the generated SQL for a select query
pub fn get_compiled_select(self) -> String { pub fn get_compiled_select(&self) -> String {
unimplemented!(); // 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, "")
} }
/// Get the generated SQL for an insert query /// Get the generated SQL for an insert query
pub fn get_compiled_insert(self) -> String { pub fn get_compiled_insert(&self, table: &str) -> String {
unimplemented!(); self.compile(QueryType::Insert, table)
} }
/// Get the generated SQL for an update query /// Get the generated SQL for an update query
pub fn get_compiled_update(self) -> String { pub fn get_compiled_update(&self, table: &str) -> String {
unimplemented!(); self.compile(QueryType::Update, table)
} }
/// Get the generated SQL for a delete query /// Get the generated SQL for a delete query
pub fn get_compiled_delete(self) -> String { pub fn get_compiled_delete(&self, table: &str) -> String {
unimplemented!(); self.compile(QueryType::Delete, table)
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -563,7 +408,7 @@ impl QueryBuilder {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/// Get a new instance of the query builder /// Get a new instance of the query builder
pub fn reset_query(mut self) -> Self { pub fn reset(&mut self) -> &Self {
self.state = QueryState::new(); self.state = QueryState::new();
self self
@ -594,7 +439,7 @@ impl QueryBuilder {
LikeWildcard::Both => format!("%{}%", *string_val), LikeWildcard::Both => format!("%{}%", *string_val),
}; };
let conj = if self.state.query_map.len() == 0 { let conj = if self.state.query_map_empty() {
" WHERE " " WHERE "
} else { } else {
conj conj
@ -624,11 +469,62 @@ impl QueryBuilder {
} }
fn compile(&self, query_type: QueryType, table: &str) -> String { fn compile(&self, query_type: QueryType, table: &str) -> String {
unimplemented!(); // 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());
}
let sql = parts.join("");
// @TODO handle limit / offset
sql
} }
fn compile_type(&self, query_type: QueryType, table: &str) -> String { fn compile_type(&self, query_type: QueryType, table: &str) -> String {
unimplemented!(); 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),
}
} }
} }
@ -642,11 +538,14 @@ mod tests {
qb.set("foo", Box::new("bar")); qb.set("foo", Box::new("bar"));
assert_eq!(qb.state.set_array_keys[0], "foo"); assert_eq!(qb.state.get_set_array_keys()[0], "foo");
assert!(qb.state.values[0].is::<&str>()); assert!(qb.state.get_values()[0].is::<&str>());
// @TODO find a way to make this kind of operation much more ergonomic // @TODO find a way to make this kind of operation much more ergonomic
assert_eq!(*qb.state.values[0].downcast_ref::<&str>().unwrap(), "bar"); assert_eq!(
*qb.state.get_values()[0].downcast_ref::<&str>().unwrap(),
"bar"
);
} }
#[test] #[test]
@ -667,7 +566,7 @@ mod tests {
qb.set_map(authors); qb.set_map(authors);
// assert_eq!(qb.state.set_array_keys[0], "Chinua Achebe"); // assert_eq!(qb.state.set_array_keys[0], "Chinua Achebe");
assert_eq!(qb.state.set_array_keys.len(), 3); assert_eq!(qb.state.get_set_array_keys().len(), 3);
assert_eq!(qb.state.values.len(), 3); assert_eq!(qb.state.get_values().len(), 3);
} }
} }

View File

@ -0,0 +1,224 @@
use super::*;
#[derive(Debug)]
pub 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)]
pub struct QueryState {
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<Wild>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Wild>,
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 {
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,
clause_type: QueryClauseType,
conj: &str,
s: &str,
) -> &mut Self {
self.having_map.push(QueryClause::new(clause_type, 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: Wild) -> &mut Self {
self.values.push(val);
self
}
pub fn append_where_values(&mut self, val: Wild) -> &mut Self {
self.where_values.push(val);
self
}
pub fn append_query_map(
&mut self,
clause_type: QueryClauseType,
conj: &str,
s: &str,
) -> &mut Self {
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<Wild> {
&self.values
}
pub fn get_where_values(&self) -> &Vec<Wild> {
&self.where_values
}
pub fn having_map_empty(&self) -> bool {
self.having_map.len() == 0
}
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
}
pub fn query_map_empty(&self) -> bool {
self.query_map.len() == 0
}
}