From 6ca96315ccf4df6de4778aa8b848b79d7a5bc0f4 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 24 Jul 2019 09:51:53 -0400 Subject: [PATCH] Yet another ugly progress commit --- Cargo.toml | 4 ++++ src/drivers.rs | 6 ++++++ src/drivers/mysql.rs | 44 ++++++++++++++++++++++++++++++++--------- src/drivers/postgres.rs | 16 +++++++-------- src/drivers/sqlite.rs | 24 +++++++++++++--------- src/fns.rs | 4 ++-- src/lib.rs | 30 +++++++++++++++++++--------- src/query_builder.rs | 42 ++++++++++++++++++++++++++++++++++----- 8 files changed, 128 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5612785..149d395 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,11 @@ package="mysql" [features] default=['postgres'] +docs-rs=['postgres', 'sqlite', 'mysql'] postgres=['pg'] sqlite=['slite'] mysql=['my'] mssql=[] + +[package.metadata.docs.rs] +features=['docs-rs'] \ No newline at end of file diff --git a/src/drivers.rs b/src/drivers.rs index 51f9cff..af465d0 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -121,6 +121,12 @@ pub trait DatabaseDriver { // ! Driver-specific SQL methods // ------------------------------------------------------------------------ + /// Return the sql to empty a table + fn truncate(&self, table: &str) -> String { + String::from("TRUNCATE TABLE ") + + &self.quote_identifier(table) + } + /// Take an existing sql query and add a limit and/or offset fn limit(&self, sql: &str, limit: Option, offset: Option) -> String { let mut sql = sql.to_string(); diff --git a/src/drivers/mysql.rs b/src/drivers/mysql.rs index 0d31474..4f7cb8e 100644 --- a/src/drivers/mysql.rs +++ b/src/drivers/mysql.rs @@ -3,16 +3,42 @@ //! Use of this driver requires enabling the `mysql` feature. //! //! Contains database-specific query data +//! Uses the [Mysql](https://crates.io/crates/mysql) crate for +//! interfacing with the database use super::*; +use std::any::Any; +use std::cell::RefCell; +use my::{Pool}; + /// The struct implementing the `DatabaseDriver` trait #[derive(Debug)] -pub struct MySQLDriver; +pub struct MySQLDriver { + connection: RefCell>, +} impl MySQLDriver { /// Create a MySQLDriver driver - pub fn new() -> Self { - MySQLDriver {} + pub fn new(dsn: &str) -> Self { + let mut driver = MySQLDriver { + connection: RefCell::new(None) + }; + + driver.connect(dsn); + + driver + } + + fn test_new() -> Self { + MySQLDriver { + connection: RefCell::new(None) + } + } + + fn connect(&mut self, dsn: &str) { + let connection = Pool::new(dsn).unwrap(); + + self.connection = RefCell::new(Some(connection)); } } @@ -23,6 +49,10 @@ impl DatabaseDriver for MySQLDriver { ('`', '`') } + fn query(&self, sql: &str) -> Result, Box> { + unimplemented!(); + } + fn limit(&self, sql: &str, limit: Option, offset: Option) -> String { if limit.is_none() { return sql.to_string(); @@ -44,10 +74,6 @@ impl DatabaseDriver for MySQLDriver { fn random(&self) -> String { String::from(" RAND() DESC") } - - fn query(&self, sql: &str) -> Result, Box> { - unimplemented!(); - } } #[cfg(test)] @@ -56,7 +82,7 @@ mod tests { #[test] fn test_quote_identifier_backtick_quote() { - let driver = MySQLDriver::new(); + let driver = MySQLDriver::test_new(); assert_eq!( driver.quote_identifier("foo, bar, baz"), @@ -70,7 +96,7 @@ mod tests { #[test] fn test_quote_identifiers_backtick_quote() { - let driver = MySQLDriver::new(); + let driver = MySQLDriver::test_new(); assert_eq!( driver.quote_identifiers(vec![ diff --git a/src/drivers/postgres.rs b/src/drivers/postgres.rs index 97d4675..8d61f6d 100644 --- a/src/drivers/postgres.rs +++ b/src/drivers/postgres.rs @@ -36,14 +36,6 @@ impl PostgresDriver { } impl DatabaseDriver for PostgresDriver { - fn explain(&self, sql: &str) -> String { - format!("EXPLAIN VERBOSE {}", sql) - } - - fn random(&self) -> String { - String::from(" RANDOM()") - } - fn query(&self, sql: &str) -> Result, Box> { if self.connection.borrow().is_none() { panic!("No database connection."); @@ -61,4 +53,12 @@ impl DatabaseDriver for PostgresDriver { Err(e) => Err(Box::new(e)), } } + + fn explain(&self, sql: &str) -> String { + format!("EXPLAIN VERBOSE {}", sql) + } + + fn random(&self) -> String { + String::from(" RANDOM()") + } } diff --git a/src/drivers/sqlite.rs b/src/drivers/sqlite.rs index a26b590..6585579 100644 --- a/src/drivers/sqlite.rs +++ b/src/drivers/sqlite.rs @@ -7,7 +7,7 @@ use super::*; use slite::NO_PARAMS; -use slite::{params, Connection, Result}; +use slite::{params, Connection, Result as SResult}; use std::cell::RefCell; /// The struct implementing the `DatabaseDriver` trait @@ -40,14 +40,6 @@ impl SQLiteDriver { } impl DatabaseDriver for SQLiteDriver { - fn explain(&self, sql: &str) -> String { - format!("EXPLAIN QUERY PLAN {}", sql) - } - - fn random(&self) -> String { - String::from(" RANDOM()") - } - fn query(&self, sql: &str) -> Result, Box> { if self.connection.borrow().is_none() { panic!("No database connection."); @@ -62,4 +54,18 @@ impl DatabaseDriver for SQLiteDriver { // TODO: map native result to generic result unimplemented!(); } + + /// Return the sql to empty a table + fn truncate(&self, table: &str) -> String { + String::from("DELETE FROM ") + + &self.quote_identifier(table) + } + + fn explain(&self, sql: &str) -> String { + format!("EXPLAIN QUERY PLAN {}", sql) + } + + fn random(&self) -> String { + String::from(" RANDOM()") + } } diff --git a/src/fns.rs b/src/fns.rs index 32ae6c8..f154b53 100644 --- a/src/fns.rs +++ b/src/fns.rs @@ -24,7 +24,7 @@ pub fn split_map_join<'a>( .join(split_join_by) } -pub fn get_typed_ref<'a, T: 'static>(val: &'a Any) -> Option<&'a T> { +pub fn get_typed_ref(val: &Any) -> Option<&T> { if ! val.is::() { return None; } @@ -32,7 +32,7 @@ pub fn get_typed_ref<'a, T: 'static>(val: &'a Any) -> Option<&'a T> { val.downcast_ref::() } -pub fn get_typed_mut<'a, T: 'static>(val: &'a mut Any) -> Option<&'a mut T> { +pub fn get_typed_mut(val: &mut Any) -> Option<&mut T> { if ! val.is::() { return None; } diff --git a/src/lib.rs b/src/lib.rs index 11dac3d..76debc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,19 +2,31 @@ //! //! A query builder using mostly strings, with methods following common SQL syntax //! -//! -//! ```no_run -//! use stringqb::prelude::*; -//! -//! // Create a QueryBuilder object, with the chosen database driver -//! let mut qb = QueryBuilder::new(PostgresDriver::new("postgresql://user@localhost")); -//! -//! ``` -//! //! Drivers include: //! * `PostgresDriver` - for PostgreSQL databases //! * `MySQLDriver` - for MySQL/MariaDB databases //! * `SQLiteDriver` - for SQLite databases +//! +//! Example: +//! ```no_run +//! use stringqb::prelude::*; +//! +//! // Create the database driver object (Postgres is enabled by default) +//! let pgDriver = PostgresDriver::new("postgres://user:pass@host:port/database"); +//! +//! // The query builder must be mutable to be useful +//! let mut qb = QueryBuilder::new(pgDriver); +//! +//! // Each builder method returns a mutable reference to itself so +//! // the methods are chainable +//! qb.select("field as f") +//! .from("table t") +//! .inner_join("table_two tt", "field2 as ff", "=", "f") +//! .wher("f >", 3); +//! +//! // Since they are references, you do not have to chain. +//! let sql = qb.get_compiled_select(); +//! ``` #![warn(missing_docs)] #[macro_use] diff --git a/src/query_builder.rs b/src/query_builder.rs index f66807c..3873cec 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -137,13 +137,16 @@ impl QueryBuilder { /// use stringqb::prelude::*; /// /// // Postgres Driver (If enabled) - /// let pgDriver = PostgresDriver::new("postgres://"); + /// let pgDriver = PostgresDriver::new("postgres://user:pass@host:port/database"); /// - /// // SQLite Driver (memory) + /// // MySQL/MariaDB Driver, requires "mysql" feature + /// let myDriver = MySQLDriver::new("mysql://user:pass@host:port/database"); + /// + /// // SQLite Driver (memory), requires "sqlite" feature /// #[cfg(feature = "sqlite")] /// let liteMemoryDriver = SQLiteDriver::new(":memory:"); /// - /// // SQLite Driver (file) + /// // SQLite Driver (file), requires "sqlite" feature /// #[cfg(feature = "sqlite")] /// let liteDriver = SQLiteDriver::new("/path/to/db.sqlite3"); /// @@ -740,6 +743,19 @@ impl QueryBuilder { self.run(&sql) } + /// Empty the table of all values + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// let query = qb.truncate("table"); + /// ``` + pub fn truncate(&mut self, table: &str) -> Result, Box> { + let sql = self.driver.truncate(table); + + self.basic_query(&sql) + } + // -------------------------------------------------------------------------- // ! SQL Returning Methods // -------------------------------------------------------------------------- @@ -757,16 +773,34 @@ impl QueryBuilder { /// Get the generated SQL for an insert query pub fn get_compiled_insert(&self, table: &str) -> String { + // The fields and values to insert must be specified + assert!( + self.state.get_set_array_keys().len() > 0, + "You must use the `set` or `set_map` method to set columns and values to insert" + ); + self.compile(QueryType::Insert, table) } /// Get the generated SQL for an update query pub fn get_compiled_update(&self, table: &str) -> String { + // Updates require fields and values + assert!( + self.state.get_set_array_keys().len() > 0, + "You must use the `set` or `set_map` method to set columns and values to update" + ); + self.compile(QueryType::Update, table) } /// Get the generated SQL for a delete query pub fn get_compiled_delete(&self, table: &str) -> String { + // Where clause required + assert!( + self.state.has_where_clause(), + "You must specify a where clause for delete. To empty a table, use the `truncate` method." + ); + self.compile(QueryType::Delete, table) } @@ -989,8 +1023,6 @@ impl QueryBuilder { .append_query_map(QueryClauseType::Where, &conj, &item); } - - fn compile(&self, query_type: QueryType, table: &str) -> String { // Get the base clause for the query let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table));