diff --git a/Cargo.toml b/Cargo.toml index b60d06c..4cd096a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,14 +28,9 @@ version="15.1.0" optional=true package="mysql" -[dependencies.ms] -version="0.3.2" -optional=true -package="tiberius" - [features] default=['postgres'] postgres=['pg'] sqlite=['slite'] mysql=['my'] -mssql=['ms'] +mssql=[] diff --git a/src/drivers.rs b/src/drivers.rs index 4a29678..d035fa3 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -37,7 +37,15 @@ impl DefaultDriver { } } -impl DatabaseDriver for DefaultDriver {} +impl DatabaseDriver for DefaultDriver { + fn explain(&self, sql: &str) -> String { + return format!("EXPLAIN {}", sql) + } + + fn random(&self) -> String { + String::from(" RANDOM") + } +} /// Database Driver Trait /// @@ -96,6 +104,32 @@ pub trait DatabaseDriver: fmt::Debug { // Runs a basic sql query on the database // fn query(&self, query: &str) -> Result<(), ()>; + + + // ------------------------------------------------------------------------ + // ! Driver-specific SQL methods + // ------------------------------------------------------------------------ + + /// 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(); + + if limit.is_some() { + sql += &format!("\nLIMIT {}", limit.unwrap()); + } + + if offset.is_some() { + sql += &format!(" OFFSET {}", offset.unwrap()); + } + + sql + } + + /// Get the query plan for the existing sql + fn explain(&self, sql: &str) -> String; + + /// Get the database's keyword for sorting randomly + fn random(&self) -> String; } #[cfg(test)] diff --git a/src/drivers/mssql.rs b/src/drivers/mssql.rs index 800045a..d4e91e5 100644 --- a/src/drivers/mssql.rs +++ b/src/drivers/mssql.rs @@ -1,5 +1,8 @@ //! Database Driver for MSSQL //! +//! Note: +//! **This driver will probably never be fully implemented** +//! //! Contains database-specific query data use super::*; @@ -20,6 +23,14 @@ impl DatabaseDriver for MSSQL { fn _quotes(&self) -> (char, char) { ('[', ']') } + + fn explain(&self, sql: &str) -> String { + sql.to_string() + } + + fn random(&self) -> String { + String::from(" RANDOM") + } } #[cfg(test)] diff --git a/src/drivers/mysql.rs b/src/drivers/mysql.rs index faeb3e5..59ecb06 100644 --- a/src/drivers/mysql.rs +++ b/src/drivers/mysql.rs @@ -5,21 +5,43 @@ use super::*; /// The struct implementing the `DatabaseDriver` trait #[derive(Debug)] -pub struct MySQL; +pub struct MySQLDriver; -impl MySQL { - /// Create a MySQL driver +impl MySQLDriver { + /// Create a MySQLDriver driver pub fn new() -> Self { - MySQL {} + MySQLDriver {} } } -impl DatabaseDriver for MySQL { +impl DatabaseDriver for MySQLDriver { /// Get which characters are used to delimit identifiers /// such as tables, and columns fn _quotes(&self) -> (char, char) { ('`', '`') } + + fn limit(&self, sql: &str, limit: Option, offset: Option) -> String { + if limit.is_none() { + return sql.to_string(); + } + + // Limit and Offset are defined + if offset.is_some() { + return format!("{} LIMIT {}.{}", sql, offset.unwrap(), limit.unwrap()); + } + + // Limit is defined + format!("{} LIMIT {}", sql, limit.unwrap()) + } + + fn explain(&self, sql: &str) -> String { + return format!("EXPLAIN EXTENDED {}", sql) + } + + fn random(&self) -> String { + String::from(" RAND() DESC") + } } #[cfg(test)] @@ -28,7 +50,7 @@ mod tests { #[test] fn test_quote_identifier_backtick_quote() { - let driver = MySQL::new(); + let driver = MySQLDriver::new(); assert_eq!( driver.quote_identifier("foo, bar, baz"), @@ -42,7 +64,7 @@ mod tests { #[test] fn test_quote_identifiers_backtick_quote() { - let driver = MySQL::new(); + let driver = MySQLDriver::new(); assert_eq!( driver.quote_identifiers(vec![ diff --git a/src/drivers/postgres.rs b/src/drivers/postgres.rs index 3754430..9c73515 100644 --- a/src/drivers/postgres.rs +++ b/src/drivers/postgres.rs @@ -5,13 +5,21 @@ use super::*; /// The struct implementing the `DatabaseDriver` trait #[derive(Debug)] -pub struct Postgres; +pub struct PostgresDriver; -impl Postgres { - /// Create a Postgres driver +impl PostgresDriver { + /// Create a PostgresDriver driver pub fn new() -> Self { - Postgres {} + PostgresDriver {} } } -impl DatabaseDriver for Postgres {} +impl DatabaseDriver for PostgresDriver { + fn explain(&self, sql: &str) -> String { + return format!("EXPLAIN VERBOSE {}", sql) + } + + fn random(&self) -> String { + String::from(" RANDOM()") + } +} diff --git a/src/drivers/sqlite.rs b/src/drivers/sqlite.rs index b7e0db5..735e002 100644 --- a/src/drivers/sqlite.rs +++ b/src/drivers/sqlite.rs @@ -5,13 +5,22 @@ use super::*; /// The struct implementing the `DatabaseDriver` trait #[derive(Debug)] -pub struct SQLite; +pub struct SQLiteDriver; -impl SQLite { - /// Create an SQLite driver +impl SQLiteDriver { + /// Create an SQLiteDriver driver pub fn new() -> Self { - SQLite {} + SQLiteDriver {} + } +} + +impl DatabaseDriver for SQLiteDriver { + fn explain(&self, sql: &str) -> String { + return format!("EXPLAIN QUERY PLAN {}", sql) + } + + fn random(&self) -> String { + String::from(" RANDOM()") } } -impl DatabaseDriver for SQLite {} diff --git a/src/lib.rs b/src/lib.rs index 0f69a8f..161d541 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,21 @@ pub mod query_builder; pub mod prelude { //! Re-exports important traits and types. + //! + //! This includes enum types, traits, + //! the Query Builder, and individual database drivers. pub use crate::enums::*; - pub use crate::drivers::{DatabaseDriver, DefaultDriver}; + pub use crate::drivers::DatabaseDriver; pub use crate::query_builder::QueryBuilder; + + #[cfg(feature = "postgres")] + pub use crate::drivers::postgres::PostgresDriver; + + #[cfg(feature = "sqlite")] + pub use crate::drivers::sqlite::SQLiteDriver; + + #[cfg(feature = "mysql")] + pub use crate::drivers::mysql::MySQLDriver; + + // MSSQL is missing on purpose, as it is not a real priority to actually implement } diff --git a/src/main.rs b/src/main.rs index b77fe7d..e71c976 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,8 @@ //! 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::prelude::*; fn main() { - let mut qb = QueryBuilder::new(Postgres::new()); + let mut qb = QueryBuilder::new(PostgresDriver::new()); qb.set("foo", Box::new("bar")) .set("bar", Box::new(12)) diff --git a/src/query_builder.rs b/src/query_builder.rs index 39b9ebe..fd5dfa8 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -53,6 +53,13 @@ impl QueryBuilder { /// /// // The query builder must be mutable to be useful /// let mut qb = QueryBuilder::new(DefaultDriver::new()); + /// + /// // Each builder method returns a mutable reference to itself so + /// // the methods are chainable + /// qb.select("field f").from("table"); + /// + /// // Since they are references, you do not have to chain. + /// let sql = qb.get_compiled_select(); /// ``` pub fn new(driver: impl DatabaseDriver + 'static) -> Self { QueryBuilder { diff --git a/tests/integration_test.rs b/tests/integration_test.rs index f7dbcc9..b838866 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,4 +1,4 @@ -use stringqb::query_builder::QueryBuilder; +use stringqb::prelude::*; #[test] fn minimal_select_query() { @@ -47,6 +47,26 @@ fn select_without_from() { qb.get_compiled_select(); } +#[test] +fn select_where_in() { + let mut qb = QueryBuilder::default(); + + qb.from("test") + .where_in("foo", vec![ + Box::new(0), + Box::new(1), + Box::new(2), + Box::new(3), + Box::new(4), + Box::new(5) + ]); + + let sql = qb.get_compiled_select(); + let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) "; + + assert_eq!(sql, expected); +} + #[test] fn basic_insert_query() { let mut qb = QueryBuilder::default();