Yet another ugly progress commit

This commit is contained in:
Timothy Warren 2019-07-24 09:51:53 -04:00
parent 25da0ae57e
commit 6ca96315cc
8 changed files with 128 additions and 42 deletions

View File

@ -29,7 +29,11 @@ package="mysql"
[features] [features]
default=['postgres'] default=['postgres']
docs-rs=['postgres', 'sqlite', 'mysql']
postgres=['pg'] postgres=['pg']
sqlite=['slite'] sqlite=['slite']
mysql=['my'] mysql=['my']
mssql=[] mssql=[]
[package.metadata.docs.rs]
features=['docs-rs']

View File

@ -121,6 +121,12 @@ pub trait DatabaseDriver {
// ! Driver-specific SQL methods // ! 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 /// Take an existing sql query and add a limit and/or offset
fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String { fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String {
let mut sql = sql.to_string(); let mut sql = sql.to_string();

View File

@ -3,16 +3,42 @@
//! Use of this driver requires enabling the `mysql` feature. //! Use of this driver requires enabling the `mysql` feature.
//! //!
//! Contains database-specific query data //! Contains database-specific query data
//! Uses the [Mysql](https://crates.io/crates/mysql) crate for
//! interfacing with the database
use super::*; use super::*;
use std::any::Any;
use std::cell::RefCell;
use my::{Pool};
/// The struct implementing the `DatabaseDriver` trait /// The struct implementing the `DatabaseDriver` trait
#[derive(Debug)] #[derive(Debug)]
pub struct MySQLDriver; pub struct MySQLDriver {
connection: RefCell<Option<Pool>>,
}
impl MySQLDriver { impl MySQLDriver {
/// Create a MySQLDriver driver /// Create a MySQLDriver driver
pub fn new() -> Self { pub fn new(dsn: &str) -> Self {
MySQLDriver {} 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<dyn Any>, Box<dyn Any>> {
unimplemented!();
}
fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String { fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String {
if limit.is_none() { if limit.is_none() {
return sql.to_string(); return sql.to_string();
@ -44,10 +74,6 @@ impl DatabaseDriver for MySQLDriver {
fn random(&self) -> String { fn random(&self) -> String {
String::from(" RAND() DESC") String::from(" RAND() DESC")
} }
fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
unimplemented!();
}
} }
#[cfg(test)] #[cfg(test)]
@ -56,7 +82,7 @@ mod tests {
#[test] #[test]
fn test_quote_identifier_backtick_quote() { fn test_quote_identifier_backtick_quote() {
let driver = MySQLDriver::new(); let driver = MySQLDriver::test_new();
assert_eq!( assert_eq!(
driver.quote_identifier("foo, bar, baz"), driver.quote_identifier("foo, bar, baz"),
@ -70,7 +96,7 @@ mod tests {
#[test] #[test]
fn test_quote_identifiers_backtick_quote() { fn test_quote_identifiers_backtick_quote() {
let driver = MySQLDriver::new(); let driver = MySQLDriver::test_new();
assert_eq!( assert_eq!(
driver.quote_identifiers(vec![ driver.quote_identifiers(vec![

View File

@ -36,14 +36,6 @@ impl PostgresDriver {
} }
impl DatabaseDriver for 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<dyn Any>, Box<dyn Any>> { fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
if self.connection.borrow().is_none() { if self.connection.borrow().is_none() {
panic!("No database connection."); panic!("No database connection.");
@ -61,4 +53,12 @@ impl DatabaseDriver for PostgresDriver {
Err(e) => Err(Box::new(e)), Err(e) => Err(Box::new(e)),
} }
} }
fn explain(&self, sql: &str) -> String {
format!("EXPLAIN VERBOSE {}", sql)
}
fn random(&self) -> String {
String::from(" RANDOM()")
}
} }

View File

@ -7,7 +7,7 @@
use super::*; use super::*;
use slite::NO_PARAMS; use slite::NO_PARAMS;
use slite::{params, Connection, Result}; use slite::{params, Connection, Result as SResult};
use std::cell::RefCell; use std::cell::RefCell;
/// The struct implementing the `DatabaseDriver` trait /// The struct implementing the `DatabaseDriver` trait
@ -40,14 +40,6 @@ impl SQLiteDriver {
} }
impl DatabaseDriver for 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<dyn Any>, Box<dyn Any>> { fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
if self.connection.borrow().is_none() { if self.connection.borrow().is_none() {
panic!("No database connection."); panic!("No database connection.");
@ -62,4 +54,18 @@ impl DatabaseDriver for SQLiteDriver {
// TODO: map native result to generic result // TODO: map native result to generic result
unimplemented!(); 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()")
}
} }

View File

@ -24,7 +24,7 @@ pub fn split_map_join<'a>(
.join(split_join_by) .join(split_join_by)
} }
pub fn get_typed_ref<'a, T: 'static>(val: &'a Any) -> Option<&'a T> { pub fn get_typed_ref<T: 'static>(val: &Any) -> Option<&T> {
if ! val.is::<T>() { if ! val.is::<T>() {
return None; return None;
} }
@ -32,7 +32,7 @@ pub fn get_typed_ref<'a, T: 'static>(val: &'a Any) -> Option<&'a T> {
val.downcast_ref::<T>() val.downcast_ref::<T>()
} }
pub fn get_typed_mut<'a, T: 'static>(val: &'a mut Any) -> Option<&'a mut T> { pub fn get_typed_mut<T: 'static>(val: &mut Any) -> Option<&mut T> {
if ! val.is::<T>() { if ! val.is::<T>() {
return None; return None;
} }

View File

@ -2,19 +2,31 @@
//! //!
//! A query builder using mostly strings, with methods following common SQL syntax //! 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: //! Drivers include:
//! * `PostgresDriver` - for PostgreSQL databases //! * `PostgresDriver` - for PostgreSQL databases
//! * `MySQLDriver` - for MySQL/MariaDB databases //! * `MySQLDriver` - for MySQL/MariaDB databases
//! * `SQLiteDriver` - for SQLite 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)] #![warn(missing_docs)]
#[macro_use] #[macro_use]

View File

@ -137,13 +137,16 @@ impl QueryBuilder {
/// use stringqb::prelude::*; /// use stringqb::prelude::*;
/// ///
/// // Postgres Driver (If enabled) /// // 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")] /// #[cfg(feature = "sqlite")]
/// let liteMemoryDriver = SQLiteDriver::new(":memory:"); /// let liteMemoryDriver = SQLiteDriver::new(":memory:");
/// ///
/// // SQLite Driver (file) /// // SQLite Driver (file), requires "sqlite" feature
/// #[cfg(feature = "sqlite")] /// #[cfg(feature = "sqlite")]
/// let liteDriver = SQLiteDriver::new("/path/to/db.sqlite3"); /// let liteDriver = SQLiteDriver::new("/path/to/db.sqlite3");
/// ///
@ -740,6 +743,19 @@ impl QueryBuilder {
self.run(&sql) 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<dyn Any>, Box<dyn Any>> {
let sql = self.driver.truncate(table);
self.basic_query(&sql)
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// ! SQL Returning Methods // ! SQL Returning Methods
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -757,16 +773,34 @@ impl QueryBuilder {
/// Get the generated SQL for an insert query /// Get the generated SQL for an insert query
pub fn get_compiled_insert(&self, table: &str) -> String { 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) 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, table: &str) -> String { 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) 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, table: &str) -> String { 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) self.compile(QueryType::Delete, table)
} }
@ -989,8 +1023,6 @@ impl QueryBuilder {
.append_query_map(QueryClauseType::Where, &conj, &item); .append_query_map(QueryClauseType::Where, &conj, &item);
} }
fn compile(&self, query_type: QueryType, table: &str) -> String { fn compile(&self, query_type: QueryType, table: &str) -> String {
// Get the base clause for the query // Get the base clause for the query
let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table)); let base_sql = self.compile_type(query_type, &self.driver.quote_identifier(table));