From 7fd51aa76ce34282614d1f256bd69dfabcb2945a Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 3 Apr 2019 16:29:51 -0400 Subject: [PATCH] Yet another ugly progress commit --- src/drivers/mod.rs | 15 +++++ src/drivers/postgres.rs | 6 +- src/drivers/sqlite.rs | 7 ++ src/query_builder.rs | 145 ++++++++++++++++++++++++++++++---------- 4 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index d0f6e62..bab0fb2 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -21,6 +21,21 @@ struct QueryResult; /// /// Interface between the database connection library and the query builder pub trait DatabaseDriver: fmt::Debug { + /// Vector version of `quote_identifier` + fn quote_identifiers(&self, identifiers: Vec) -> Vec { + let mut output: Vec = vec![]; + + for identifier in identifiers { + output.push(self.quote_identifier(&identifier)); + } + + output + } + + /// Quote the identifiers passed, so the database does not + /// normalize the identifiers (eg, table, column, etc.) + fn quote_identifier(&self, identifier: &str) -> String; + /// Runs a basic sql query on the database fn query(&self, query: &str) -> Result<(), ()>; } diff --git a/src/drivers/postgres.rs b/src/drivers/postgres.rs index be7473b..354d21b 100644 --- a/src/drivers/postgres.rs +++ b/src/drivers/postgres.rs @@ -1,12 +1,14 @@ use super::*; -use pg; - #[derive(Debug)] pub struct Postgres; #[cfg(feature="pg")] impl DatabaseDriver for Postgres { + fn quote_identifier(&self, identifier: &str) -> String { + String::from(identifier) + } + fn query(&self, _query: &str) -> Result<(), ()> { Ok(()) } diff --git a/src/drivers/sqlite.rs b/src/drivers/sqlite.rs index 1ef71ca..47b7a47 100644 --- a/src/drivers/sqlite.rs +++ b/src/drivers/sqlite.rs @@ -1,6 +1,13 @@ +use super::*; + +#[derive(Debug)] pub struct SQLite; impl DatabaseDriver for SQLite { + fn quote_identifier(&self, identifier: &str) -> String { + String::from(identifier) + } + fn query(&self, _query: &str) -> Result<(), ()> { Ok(()) } diff --git a/src/query_builder.rs b/src/query_builder.rs index aa58f5a..ac9742b 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -6,24 +6,40 @@ use std::collections::HashMap; use crate::drivers::DatabaseDriver; +/// 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 { Inner, Outer, Left, Right, + LeftOuter, + RightOuter, } +/// The sort direction #[derive(Debug)] pub enum OrderDirection { + /// Sort Ascending Asc, + /// Sort Descending Desc, } @@ -45,7 +61,7 @@ struct QueryClause { string: String, } -#[derive(Default, Debug)] +#[derive(Debug)] struct QueryState { select_string: String, from_string: String, @@ -68,9 +84,9 @@ struct QueryState { // Values to apply to where clauses in prepared statements where_values: Vec>, - limit: u32, + limit: Option, - offset: u32, + offset: Option, // Query components for complex selects query_map: Vec, @@ -79,6 +95,30 @@ struct QueryState { having_map: Vec, } +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: HashMap::new(), + values: vec![], + where_values: vec![], + + limit: None, + offset: None, + + query_map: vec![], + having_map: vec![], + } + } +} + impl QueryState { pub fn new() -> QueryState { QueryState::default() @@ -94,7 +134,7 @@ pub struct QueryBuilder { impl QueryBuilder { /// Creates a new QueryBuilder instance - pub fn new() -> QueryBuilder { + pub fn new() -> Self { QueryBuilder { state: QueryState::new(), driver: None, @@ -110,7 +150,7 @@ impl QueryBuilder { unimplemented!(); } - // Adds the 'distinct' keyword to a query + /// Adds the `distinct` keyword to a query pub fn distinct(mut self) -> Self { unimplemented!(); } @@ -127,22 +167,22 @@ impl QueryBuilder { // ! 'Like' methods // -------------------------------------------------------------------------- - // Creates a like clause in the sql statement + /// Creates a `like` clause in the sql statement pub fn like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } - // Generates an OR Like clause + /// Generates an OR Like clause pub fn or_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } - // Generates a NOI Like clause + /// Generates a NOI Like clause pub fn not_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } - // Generates an OR NOT Like clause + /// Generates an OR NOT Like clause pub fn or_not_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } @@ -151,10 +191,12 @@ impl QueryBuilder { // ! Having methods // -------------------------------------------------------------------------- + /// Add a `having` clause to the query pub fn having(mut self, key:&str, value: Box) -> Self { unimplemented!(); } + /// Add a `having` clause to the query, prefixed with an `or` pub fn or_having(mut self, key:&str, value: Box) -> Self { unimplemented!(); } @@ -163,27 +205,32 @@ impl QueryBuilder { // ! 'Where' methods // -------------------------------------------------------------------------- - /// 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, value: Box) -> Self { unimplemented!(); } + /// Specify a condition for the `where` clause of the query, prefixed with `or` pub fn or_where(mut self, key: &str, value: Box) -> Self { unimplemented!(); } + /// Specify a `where in` clause for the query pub fn where_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } + /// Specify a `where in` clause for the query, prefixed with `or` pub fn or_where_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } + /// Specify a `where not in` clause for the query pub fn where_not_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } + /// Specify a `where not in` clause for the query, prefixed with `or` pub fn or_where_not_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } @@ -202,7 +249,7 @@ impl QueryBuilder { } /// Set a map of data for an insert or update query - pub fn set_batch(mut self, data: HashMap>) -> Self { + pub fn set_map(mut self, data: HashMap>) -> Self { for (key, value) in data { self = self.set(&key, value); } @@ -210,43 +257,60 @@ impl QueryBuilder { self } - // Add a table join to the query + /// Add a table join to the query pub fn join(mut self, table: &str, condition: &str, join_type: JoinType) -> Self { unimplemented!(); } + /// Add a group by clause to the query pub fn group_by(mut self, field: &str) -> Self { unimplemented!(); } + /// Add an order by clause to the query pub fn order_by(mut self, field: &str, direction: OrderDirection) -> Self { unimplemented!(); } - pub fn limit(mut self, limit: u32, offset: u32) -> Self { - unimplemented!(); + /// Add a limit to the query + pub fn limit(mut self, limit: u32) -> Self { + self.state.limit = Some(limit); + + self + } + + /// Add an offset to the query + pub fn offset(mut self, offset: u32) -> Self { + self.state.offset = Some(offset); + + self } // -------------------------------------------------------------------------- // ! Query Grouping Methods // -------------------------------------------------------------------------- + /// Start a logical grouping in the current query pub fn group_start(mut self) -> Self { unimplemented!(); } + /// Start a logical grouping, prefixed with `not` pub fn not_group_start(mut self) -> Self { unimplemented!(); } + /// Start a logical grouping, prefixed with `or` pub fn or_group_start(mut self) -> Self { unimplemented!(); } + /// Start a logical grouping, prefixed with `or not` pub fn or_not_group_start(mut self) -> Self { unimplemented!(); } + /// End the current logical grouping pub fn group_end(mut self) -> Self { unimplemented!(); } @@ -260,21 +324,25 @@ impl QueryBuilder { unimplemented!(); } + /// Count all the rows in the specified database table pub fn count_all(self, table: &str) -> u32 { unimplemented!(); } + /// Execute the generated insert query pub fn insert(mut self, table: &str) { // @TODO determine query result type unimplemented!(); } + /// Execute the generated update query pub fn update(mut self, table: &str) { // @TODO determine query result type unimplemented!(); } - pub fn delete(mut self, table: &str, where_clause: &str) { + /// Execute the generated delete query + pub fn delete(mut self, table: &str) { unimplemented!(); } @@ -282,18 +350,22 @@ impl QueryBuilder { // ! SQL Returning Methods // -------------------------------------------------------------------------- + /// Get the generated SQL for a select query pub fn get_compiled_select(self) -> String { unimplemented!(); } + /// Get the generated SQL for an insert query pub fn get_compiled_insert(self) -> String { unimplemented!(); } + /// Get the generated SQL for an update query pub fn get_compiled_update(self) -> String { unimplemented!(); } + /// Get the generated SQL for a delete query pub fn get_compiled_delete(self) -> String { unimplemented!(); } @@ -302,8 +374,9 @@ impl QueryBuilder { // ! Miscellaneous Methods // -------------------------------------------------------------------------- + /// Get a new instance of the query builder pub fn reset_query(mut self) -> Self { - unimplemented!(); + QueryBuilder::new() } } @@ -317,24 +390,28 @@ mod tests { .set("foo", Box::new("bar")); assert!(builder.state.values[0].is::<&str>()); + + // @TODO find a way to make this kind of operation much more ergonomic + assert_eq!(*builder.state.values[0].downcast_ref::<&str>().unwrap(), "bar"); } -// fn set_hashmap() { -// let qb = QueryBuilder::new(); -// -// let mut authors = HashMap::new(); -// authors.insert( -// String::from("Chinua Achebe"), -// Box::new("Nigeria")); -// authors.insert( -// String::from("Rabindranath Tagore"), -// Box::new("India")); -// authors.insert( -// String::from("Anita Nair"), -// Box::new("India")); -// -// let qb = qb.set_batch(authors); -// -// assert_eq!(qb.state.values.len(), 3); -// } + #[test] + fn set_hashmap() { + let qb = QueryBuilder::new(); + + let mut authors: HashMap> = HashMap::new(); + authors.insert( + String::from("Chinua Achebe"), + Box::new(String::from("Nigeria"))); + authors.insert( + String::from("Rabindranath Tagore"), + Box::new(String::from("India"))); + authors.insert( + String::from("Anita Nair"), + Box::new(String::from("India"))); + + let qb = qb.set_map(authors); + + assert_eq!(qb.state.values.len(), 3); + } } \ No newline at end of file