From aad53f0f2016b1770f088f9bc5343f5e5b70cfff Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Thu, 18 Jul 2019 09:36:59 -0400 Subject: [PATCH] Ugly progress commit --- src/drivers.rs | 4 +- src/drivers/sqlite.rs | 15 ++++- src/lib.rs | 11 +++- src/query_builder.rs | 140 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 12 deletions(-) diff --git a/src/drivers.rs b/src/drivers.rs index f88eefe..4729b93 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -72,7 +72,7 @@ pub trait DatabaseDriver { /// Quote the identifiers passed, so the database does not /// normalize the identifiers (eg, table, column, etc.) fn quote_identifier(&self, identifier: &str) -> String { - let identifier = &mut String::from(identifier); + let mut identifier = &mut String::from(identifier); // If the identifier is actually a comma-separated list, // recurse to quote each identifier in the list @@ -104,7 +104,7 @@ pub trait DatabaseDriver { // Runs a basic sql query on the database // fn query(&self, sql: &str) -> Result; - // Prepares an sql statement for the database + /// Prepares an sql statement for the database fn prepare(&self, sql: &str) -> Result<(), ()> { Ok(()) } diff --git a/src/drivers/sqlite.rs b/src/drivers/sqlite.rs index 5150be4..a5819ef 100644 --- a/src/drivers/sqlite.rs +++ b/src/drivers/sqlite.rs @@ -2,7 +2,8 @@ //! //! Use of this driver requires enabling the `sqlite` feature. //! -//! Contains database-specific query data +//! Uses the [rusqlite](https://crates.io/crates/rusqlite) crate for +//! interfacing with the database use super::*; use slite::NO_PARAMS; @@ -36,6 +37,18 @@ impl SQLiteDriver { self.connection = RefCell::new(Some(connection)); } + + pub fn query(&self, sql: &str) -> Result { + if self.connection.borrow().is_none() { + panic!("No database connection."); + } + + self.connection + .borrow_mut() + .as_mut() + .unwrap() + .execute(sql, NO_PARAMS) + } } impl DatabaseDriver for SQLiteDriver { diff --git a/src/lib.rs b/src/lib.rs index fe6f954..536ec39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,10 @@ #[macro_use] extern crate lazy_static; +#[cfg(feature = "sqlite")] +#[macro_use] +extern crate slite; + pub mod drivers; pub mod fns; pub mod query_builder; @@ -31,7 +35,12 @@ pub mod prelude { //! This includes enum types, traits, //! the Query Builder, and individual database drivers. pub use crate::drivers::DatabaseDriver; - pub use crate::query_builder::{JoinType, LikeWildcard, OrderDirection, QueryBuilder}; + pub use crate::query_builder::{ + JoinType, + LikeWildcard, + OrderDirection, + QueryBuilder, + }; #[cfg(feature = "postgres")] /// Postgres Driver diff --git a/src/query_builder.rs b/src/query_builder.rs index af6284a..72468cb 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -86,6 +86,11 @@ pub struct QueryBuilder { driver: Box, } +/// The struct representing a prepared statement +pub struct Prepared { + +} + impl Default for QueryBuilder { /// Creates a new QueryBuilder instance with default driver /// @@ -179,7 +184,7 @@ impl QueryBuilder { self } - /// Tell the database to give you query plain info, rather + /// Tell the database to give you query plan info, rather /// than a result set pub fn explain(&mut self) -> &mut Self { self.state.explain = true; @@ -225,16 +230,55 @@ impl QueryBuilder { } /// Generates an `or like` clause + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = stringqb::query_builder::QueryBuilder::default(); + /// // Search for a value that ends with "foo" + /// qb.or_like("field", String::from("foo"), LikeWildcard::Before); + /// + /// // Search for a value that starts with "foo" + /// qb.or_like("field", String::from("foo"), LikeWildcard::After); + /// + /// // Search for a value that has "foo" in it + /// qb.or_like("field", String::from("foo"), LikeWildcard::Both); + /// ``` pub fn or_like(&mut self, field: &str, value: impl Any, position: LikeWildcard) -> &mut Self { self._like(field, value, position, "LIKE", "OR") } /// Generates a `not like` clause + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = stringqb::query_builder::QueryBuilder::default(); + /// // Search for a value that does not end with "foo" + /// qb.not_like("field", String::from("foo"), LikeWildcard::Before); + /// + /// // Search for a value that does not start with "foo" + /// qb.not_like("field", String::from("foo"), LikeWildcard::After); + /// + /// // Search for a value that does not have "foo" in it + /// qb.not_like("field", String::from("foo"), LikeWildcard::Both); + /// ``` pub fn not_like(&mut self, field: &str, value: impl Any, position: LikeWildcard) -> &mut Self { self._like(field, value, position, "NOT LIKE", "AND") } /// Generates an OR NOT Like clause + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = stringqb::query_builder::QueryBuilder::default(); + /// // Search for a value that does not end with "foo" + /// qb.or_not_like("field", String::from("foo"), LikeWildcard::Before); + /// + /// // Search for a value that does not start with "foo" + /// qb.or_not_like("field", String::from("foo"), LikeWildcard::After); + /// + /// // Search for a value that does not have "foo" in it + /// qb.or_not_like("field", String::from("foo"), LikeWildcard::Both); + /// ``` pub fn or_not_like( &mut self, field: &str, @@ -264,6 +308,16 @@ impl QueryBuilder { } /// Add a `having` clause to the query, prefixed with an `or` + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // By default, key = value + /// qb.or_having("key", vec!["value"]); + /// + /// // Other operators can be used with a separating space + /// qb.or_having("clues <", vec![88]); + /// ``` pub fn or_having(&mut self, key: &str, value: Vec) -> &mut Self { self._having(key, value, "OR") } @@ -294,16 +348,46 @@ impl QueryBuilder { /// Alias method for `where`, as using the `where` method requires /// using the raw identifier `r#where`. + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // By default, key = value + /// qb.wher("key", "value"); + /// + /// // Other operators can be used with a separating space + /// qb.wher("key >", 4); + /// ``` pub fn wher(&mut self, key: &str, value: impl Any) -> &mut Self { - self.r#where(key, value) + self._where_string(key, value, "AND") } /// Specify a condition for the `where` clause of the query, prefixed with `or` + /// + /// By default does not have any query grouping. + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // By default, key = value + /// qb.or_where("key", "value"); + /// + /// // Other operators can be used with a separating space + /// qb.or_where("key !=", "foo"); + /// ``` pub fn or_where(&mut self, key: &str, value: impl Any) -> &mut Self { self._where_string(key, value, "OR") } - /// Specify a `where in` clause for the query + /// Specify a `where in` clause for the query. If called multiple times, + /// will prefix the clause with `AND` + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // Look for a set of rows matching the values passed + /// qb.where_in("key", vec![1,2,3]); + /// ``` pub fn where_in(&mut self, key: &str, values: Vec) -> &mut Self { self._where_in(key, values, "IN", "AND") } @@ -489,7 +573,17 @@ impl QueryBuilder { // ! Query execution methods // -------------------------------------------------------------------------- - /// Execute the built query + /// Execute the generated select query + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // The get() method actually calls the driver to run + /// // the SQL query + /// let query = db.select_vec(vec!["foo", "bar"]) + /// .from("table t") + /// .get(); + /// ``` pub fn get(&mut self) { let sql = self.get_compiled_select(); @@ -502,6 +596,15 @@ impl QueryBuilder { } /// Execute the generated insert query + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // The insert() method actually calls the driver to run + /// // the SQL query + /// let query = db.set("foo", 3) + /// .insert("table"); + /// ``` pub fn insert(&mut self, table: &str) { let sql = self.get_compiled_insert(table); @@ -509,6 +612,16 @@ impl QueryBuilder { } /// Execute the generated update query + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // The update() method actually calls the driver to run + /// // the SQL query + /// let query = db.set("foo", 3) + /// .wher("foo", 4) + /// .update("table"); + /// ``` pub fn update(&mut self, table: &str) { let sql = self.get_compiled_update(table); @@ -563,8 +676,18 @@ impl QueryBuilder { self } - /// Execute an SQL query - pub fn query(&mut self, sql: &str) { + /// Execute an SQL query with no parameters + pub fn basic_query(&mut self, sql: &str) { + self.driver.query(sql) + } + + /// Prepare an SQL query + pub fn prepare(&mut self, sql: &str) -> Prepared { + unimplemented!(); + } + + /// Execute a prepared statement + pub fn execute(&mut self, stmt: &Prepared, params: &[Box]) { unimplemented!(); } @@ -762,14 +885,15 @@ impl QueryBuilder { } } - fn run(&mut self, _sql: &str) { + fn run(&mut self, sql: &str) { let mut values: Vec> = vec![]; values.append(self.state.get_values()); values.append(self.state.get_where_values()); // @TODO determine query result type // @TODO prepare/execute query, and return result - unimplemented!(); + let stmt = self.prepare(sql); + self.execute(&stmt, values) } }