From 1776233a93689c86ed9131ff323b2837720af63f Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 10 Apr 2019 14:03:28 -0400 Subject: [PATCH] Ugly progress commit --- src/drivers.rs | 8 ++--- src/drivers/mssql.rs | 1 + src/drivers/mysql.rs | 1 + src/drivers/postgres.rs | 1 + src/drivers/sqlite.rs | 1 + src/lib.rs | 4 +-- src/main.rs | 6 ++-- src/query_builder.rs | 73 +++++++++++++++++++++++++++-------------- 8 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/drivers.rs b/src/drivers.rs index 933413b..bad0221 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -30,6 +30,7 @@ struct QueryResult; pub struct DefaultDriver; impl DefaultDriver { + /// Create a `DefaultDriver` pub fn new() -> Self { DefaultDriver {} } @@ -61,7 +62,7 @@ pub trait DatabaseDriver: fmt::Debug { /// Quote the identifiers passed, so the database does not /// normalize the identifiers (eg, table, column, etc.) fn quote_identifier(&self, identifier: &str) -> String { - let mut identifier = &mut String::from(identifier); + let identifier = &mut String::from(identifier); // If the identifier is actually a comma-separated list, // recurse to quote each identifier in the list @@ -78,13 +79,12 @@ pub trait DatabaseDriver: fmt::Debug { let trimmed_tiers = split_map_join(identifier, ".", |tier| { let tier = tier.trim(); - - // Here where the quoting actually happens. Everything - // else is breaking down the identifier list for this. if tier.starts_with(open_char) && tier.ends_with(close_char) { return tier.to_string(); } + // Here where the quoting actually happens. Everything + // else is breaking down the identifier list for this. format!("{}{}{}", &open_char, tier, &close_char) }); diff --git a/src/drivers/mssql.rs b/src/drivers/mssql.rs index 4ec1ce1..c7d4ab0 100644 --- a/src/drivers/mssql.rs +++ b/src/drivers/mssql.rs @@ -8,6 +8,7 @@ use super::*; pub struct MSSQL; impl MSSQL { + /// Create a MSSQL Driver pub fn new() -> Self { MSSQL {} } diff --git a/src/drivers/mysql.rs b/src/drivers/mysql.rs index 0263d2c..c10a880 100644 --- a/src/drivers/mysql.rs +++ b/src/drivers/mysql.rs @@ -8,6 +8,7 @@ use super::*; pub struct MySQL; impl MySQL { + /// Create a MySQL driver pub fn new() -> Self { MySQL {} } diff --git a/src/drivers/postgres.rs b/src/drivers/postgres.rs index 2d1dd9a..3754430 100644 --- a/src/drivers/postgres.rs +++ b/src/drivers/postgres.rs @@ -8,6 +8,7 @@ use super::*; pub struct Postgres; impl Postgres { + /// Create a Postgres driver pub fn new() -> Self { Postgres {} } diff --git a/src/drivers/sqlite.rs b/src/drivers/sqlite.rs index 3804ab9..b7e0db5 100644 --- a/src/drivers/sqlite.rs +++ b/src/drivers/sqlite.rs @@ -8,6 +8,7 @@ use super::*; pub struct SQLite; impl SQLite { + /// Create an SQLite driver pub fn new() -> Self { SQLite {} } diff --git a/src/lib.rs b/src/lib.rs index 9ea6d9c..17408aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ //! # StringQB //! //! A query builder using mostly strings, with methods following common SQL syntax -// #![warn(missing_docs)] +#![warn(missing_docs)] +#![allow(dead_code)] pub mod drivers; pub mod query_builder; -pub mod types; /// Split a string, apply a closure to each substring, /// then join the string back together diff --git a/src/main.rs b/src/main.rs index b4bb1ed..b77fe7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ //! This main file is just for temparary testing use stringqb::drivers::postgres::Postgres; use stringqb::query_builder::QueryBuilder; -use stringqb::types::{SQLType, Type}; +// use stringqb::types::{SQLType, Type}; fn main() { let mut qb = QueryBuilder::new(Postgres::new()); @@ -17,6 +17,6 @@ fn main() { println!("QueryBuilder object: {:#?}", &qb); - println!("SQLType mapping: {:#?}", SQLType::SmallInt(32)); - println!("Type: {:#?}", Type(Box::new(1234567890))); + // println!("SQLType mapping: {:#?}", SQLType::SmallInt(32)); + // println!("Type: {:#?}", Type(Box::new(1234567890))); } diff --git a/src/query_builder.rs b/src/query_builder.rs index e7752bb..d7e7e18 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -5,6 +5,7 @@ use std::any::Any; use std::collections::HashMap; use crate::drivers::{DatabaseDriver, DefaultDriver}; +use crate::split_map_join; /// The position of the wildcard(s) /// for a `like` clause @@ -26,18 +27,16 @@ pub enum LikeWildcard { /// The type of SQL join #[derive(Debug)] pub enum JoinType { + /// A `CROSS` join + Cross, /// An `INNER` join Inner, /// An `OUTER` join Outer, - /// A `LEFT` join + /// A `LEFT (OUTER)` join Left, - /// A `RIGHT` join + /// A `RIGHT (OUTER)` join Right, - /// A `LEFT OUTER` join - LeftOuter, - /// A `RIGHT OUTER` join - RightOuter, } /// The sort direction @@ -47,7 +46,7 @@ pub enum OrderDirection { Asc, /// Sort Descending Desc, - /// Random Sort + /// Random Sort (Not yet implemented!) Rand, } @@ -61,6 +60,14 @@ enum QueryClauseType { WhereIn, } +#[derive(Debug)] +enum QueryType { + Select, + Insert, + Update, + Delete, +} + #[derive(Debug)] struct QueryClause { clause_type: QueryClauseType, @@ -197,7 +204,18 @@ impl QueryBuilder { /// Set the fields to select from the database as a string pub fn select(&mut self, fields: &str) -> &mut Self { - unimplemented!(); + let fields = split_map_join(fields, ",", |s| s.trim().to_string()); + + // Split identifiers on `As` keyword so they can be quoted properly + // @TODO split identifiers on `as` keyword (needs to be case-insensitive) + + // Quote the identifiers (where there was an `as` keyword) + + // Rejoin those identifiers + + self.state.append_select_string(&fields); + + self } /// Set the fields to select from the database as a Vector @@ -284,7 +302,7 @@ impl QueryBuilder { self } - // 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 pub fn where_eq(&mut self, key: &str, value: Box) -> &mut Self { self.r#where(key, "=", value) } @@ -360,12 +378,11 @@ impl QueryBuilder { let condition = table + " ON " + &col + op + value; let join_type = match join_type { + JoinType::Cross => "CROSS ", JoinType::Left => "LEFT ", JoinType::Inner => "INNER ", - JoinType::LeftOuter => "LEFT OUTER ", JoinType::Outer => "OUTER ", JoinType::Right => "RIGHT ", - JoinType::RightOuter => "RIGHT OUTER", }; let conjunction = "\n".to_string() + join_type + "JOIN "; @@ -438,26 +455,28 @@ impl QueryBuilder { /// Start a logical grouping in the current query pub fn group_start(&mut self) -> &mut Self { - if self.state.query_map.len() == 0 { - self.state - .append_query_map(QueryClauseType::GroupStart, " ", "("); + let conj = if self.state.query_map.len() == 0 { + " WHERE " } else { - self.state - .append_query_map(QueryClauseType::GroupStart, " WHERE ", "("); - } + " " + }; + + self.state + .append_query_map(QueryClauseType::GroupStart, conj, "("); self } /// Start a logical grouping, prefixed with `not` pub fn not_group_start(&mut self) -> &mut Self { - if self.state.query_map.len() == 0 { - self.state - .append_query_map(QueryClauseType::GroupStart, " WHERE NOT ", "("); + let conj = if self.state.query_map.len() == 0 { + " WHERE " } else { - self.state - .append_query_map(QueryClauseType::GroupStart, " AND NOT ", "("); - } + " AND " + }; + + self.state + .append_query_map(QueryClauseType::GroupStart, conj, "NOT ("); self } @@ -491,7 +510,7 @@ impl QueryBuilder { // -------------------------------------------------------------------------- /// Execute the built query - pub fn get(self) -> Box { + pub fn get(self) { unimplemented!(); } @@ -556,7 +575,11 @@ impl QueryBuilder { // ! Implementation Details // -------------------------------------------------------------------------- - fn compile() -> String { + fn compile(&self, query_type: QueryType, table: &str) -> String { + unimplemented!(); + } + + fn compile_type(&self, query_type: QueryType, table: &str) -> String { unimplemented!(); } }