Misc bugfixes, doc updates
This commit is contained in:
parent
aad53f0f20
commit
eb838306ec
@ -102,7 +102,9 @@ pub trait DatabaseDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Runs a basic sql query on the database
|
// Runs a basic sql query on the database
|
||||||
// fn query(&self, sql: &str) -> Result<impl Any, impl Error>;
|
fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
|
||||||
|
Ok(Box::new(String::from(sql)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepares an sql statement for the database
|
/// Prepares an sql statement for the database
|
||||||
fn prepare(&self, sql: &str) -> Result<(), ()> {
|
fn prepare(&self, sql: &str) -> Result<(), ()> {
|
||||||
|
@ -33,18 +33,6 @@ impl PostgresDriver {
|
|||||||
|
|
||||||
self.connection = RefCell::new(Some(connection));
|
self.connection = RefCell::new(Some(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(&self, sql: &str) -> Result<Vec<Row>, Error> {
|
|
||||||
if self.connection.borrow().is_none() {
|
|
||||||
panic!("No database connection.");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.connection
|
|
||||||
.borrow_mut()
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.query(sql, &[])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseDriver for PostgresDriver {
|
impl DatabaseDriver for PostgresDriver {
|
||||||
@ -55,4 +43,22 @@ impl DatabaseDriver for PostgresDriver {
|
|||||||
fn random(&self) -> String {
|
fn random(&self) -> String {
|
||||||
String::from(" RANDOM()")
|
String::from(" RANDOM()")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
|
||||||
|
if self.connection.borrow().is_none() {
|
||||||
|
panic!("No database connection.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self
|
||||||
|
.connection
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.query(sql, &[]);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(res) => Ok(Box::new(res)),
|
||||||
|
Err(e) => Err(Box::new(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,12 +35,7 @@ pub mod prelude {
|
|||||||
//! This includes enum types, traits,
|
//! This includes enum types, traits,
|
||||||
//! the Query Builder, and individual database drivers.
|
//! the Query Builder, and individual database drivers.
|
||||||
pub use crate::drivers::DatabaseDriver;
|
pub use crate::drivers::DatabaseDriver;
|
||||||
pub use crate::query_builder::{
|
pub use crate::query_builder::{JoinType, LikeWildcard, OrderDirection, QueryBuilder};
|
||||||
JoinType,
|
|
||||||
LikeWildcard,
|
|
||||||
OrderDirection,
|
|
||||||
QueryBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
/// Postgres Driver
|
/// Postgres Driver
|
||||||
|
@ -79,7 +79,36 @@ enum QueryType {
|
|||||||
Delete,
|
Delete,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The struct representing a query builder
|
/// QueryBuilder for general SQL queries
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use stringqb::prelude::*;
|
||||||
|
///
|
||||||
|
/// // You probably do not want to use the default driver, as it
|
||||||
|
/// // is basically a mock for testing
|
||||||
|
/// use stringqb::drivers::DefaultDriver;
|
||||||
|
///
|
||||||
|
/// // 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 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();
|
||||||
|
///
|
||||||
|
/// # assert_eq!(
|
||||||
|
/// # sql,
|
||||||
|
/// # r#"SELECT "field" AS "f"
|
||||||
|
/// FROM "table" "t"
|
||||||
|
/// INNER JOIN "table_two" "tt" ON "field2" AS "ff"=f
|
||||||
|
/// WHERE "f" > ?"#
|
||||||
|
/// # );
|
||||||
|
/// ```
|
||||||
pub struct QueryBuilder {
|
pub struct QueryBuilder {
|
||||||
/// The struct to store the query builder info
|
/// The struct to store the query builder info
|
||||||
state: QueryState,
|
state: QueryState,
|
||||||
@ -87,9 +116,7 @@ pub struct QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The struct representing a prepared statement
|
/// The struct representing a prepared statement
|
||||||
pub struct Prepared {
|
pub struct Prepared {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for QueryBuilder {
|
impl Default for QueryBuilder {
|
||||||
/// Creates a new QueryBuilder instance with default driver
|
/// Creates a new QueryBuilder instance with default driver
|
||||||
@ -109,19 +136,19 @@ impl QueryBuilder {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use stringqb::prelude::*;
|
/// use stringqb::prelude::*;
|
||||||
///
|
///
|
||||||
/// // You probably do not want to use the default driver, as it
|
/// // Postgres Driver (If enabled)
|
||||||
/// // is basically a mock for testing
|
/// let pgDriver = PostgresDriver::new("postgres://");
|
||||||
/// use stringqb::drivers::DefaultDriver;
|
|
||||||
///
|
///
|
||||||
/// // The query builder must be mutable to be useful
|
/// #[cfg(feature = "sqlite")]
|
||||||
/// let mut qb = QueryBuilder::new(DefaultDriver::new());
|
/// // SQLite Driver (memory)
|
||||||
|
/// let liteMemoryDriver = SQLiteDriver::new(":memory:");
|
||||||
///
|
///
|
||||||
/// // Each builder method returns a mutable reference to itself so
|
/// #[cfg(feature = "sqlite")]
|
||||||
/// // the methods are chainable
|
/// // SQLite Driver (file)
|
||||||
/// qb.select("field f").from("table");
|
/// let liteDriver = SQLiteDriver::new("/path/to/db.sqlite3");
|
||||||
///
|
///
|
||||||
/// // Since they are references, you do not have to chain.
|
/// // The variable to the query builder must be mutable
|
||||||
/// let sql = qb.get_compiled_select();
|
/// let mut queryBuilder = QueryBuilder::new(pgDriver);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
|
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
|
||||||
QueryBuilder {
|
QueryBuilder {
|
||||||
@ -137,28 +164,13 @@ impl QueryBuilder {
|
|||||||
/// Set the fields to select from the database as a string
|
/// Set the fields to select from the database as a string
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // You can also alias field names
|
/// // You can also alias field names
|
||||||
/// qb.select("foo as bar");
|
/// qb.select("foo as bar");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn select(&mut self, fields: &str) -> &mut Self {
|
pub fn select(&mut self, fields: &str) -> &mut Self {
|
||||||
lazy_static! {
|
let fields = self.quote_fields(fields);
|
||||||
static ref RE: Regex = Regex::new(r"(?i) as ").unwrap();
|
|
||||||
};
|
|
||||||
|
|
||||||
let fields = split_map_join(fields, ",", |s| {
|
|
||||||
if !RE.is_match(s) {
|
|
||||||
return self.driver.quote_identifier(s.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a operation similar to split_map_join for the
|
|
||||||
// regex matches, quoting each identifier
|
|
||||||
RE.split(s)
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| self.driver.quote_identifier(p))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(" as ")
|
|
||||||
});
|
|
||||||
|
|
||||||
self.state.append_select_string(&fields);
|
self.state.append_select_string(&fields);
|
||||||
|
|
||||||
@ -168,7 +180,8 @@ impl QueryBuilder {
|
|||||||
/// Set the fields to select from the database as a Vector
|
/// Set the fields to select from the database as a Vector
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // You can also alias via a vector of fields
|
/// // You can also alias via a vector of fields
|
||||||
/// qb.select_vec(vec!["foo as bar", "baz"]);
|
/// qb.select_vec(vec!["foo as bar", "baz"]);
|
||||||
/// ```
|
/// ```
|
||||||
@ -195,8 +208,9 @@ impl QueryBuilder {
|
|||||||
/// Specify the database table to select from
|
/// Specify the database table to select from
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # use stringqb::prelude::*;
|
||||||
/// // You can specifiy an alias for the table
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // You can specify an alias for the table
|
||||||
/// qb.from("products p");
|
/// qb.from("products p");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from(&mut self, table_name: &str) -> &mut Self {
|
pub fn from(&mut self, table_name: &str) -> &mut Self {
|
||||||
@ -215,7 +229,7 @@ impl QueryBuilder {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use stringqb::prelude::*;
|
/// # use stringqb::prelude::*;
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // Search for a value that ends with "foo"
|
/// // Search for a value that ends with "foo"
|
||||||
/// qb.like("field", String::from("foo"), LikeWildcard::Before);
|
/// qb.like("field", String::from("foo"), LikeWildcard::Before);
|
||||||
///
|
///
|
||||||
@ -233,7 +247,7 @@ impl QueryBuilder {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use stringqb::prelude::*;
|
/// # use stringqb::prelude::*;
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // Search for a value that ends with "foo"
|
/// // Search for a value that ends with "foo"
|
||||||
/// qb.or_like("field", String::from("foo"), LikeWildcard::Before);
|
/// qb.or_like("field", String::from("foo"), LikeWildcard::Before);
|
||||||
///
|
///
|
||||||
@ -251,7 +265,7 @@ impl QueryBuilder {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use stringqb::prelude::*;
|
/// # use stringqb::prelude::*;
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // Search for a value that does not end with "foo"
|
/// // Search for a value that does not end with "foo"
|
||||||
/// qb.not_like("field", String::from("foo"), LikeWildcard::Before);
|
/// qb.not_like("field", String::from("foo"), LikeWildcard::Before);
|
||||||
///
|
///
|
||||||
@ -269,7 +283,7 @@ impl QueryBuilder {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use stringqb::prelude::*;
|
/// # use stringqb::prelude::*;
|
||||||
/// # let mut qb = stringqb::query_builder::QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // Search for a value that does not end with "foo"
|
/// // Search for a value that does not end with "foo"
|
||||||
/// qb.or_not_like("field", String::from("foo"), LikeWildcard::Before);
|
/// qb.or_not_like("field", String::from("foo"), LikeWildcard::Before);
|
||||||
///
|
///
|
||||||
@ -393,16 +407,37 @@ impl QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a `where in` clause for the query, prefixed with `or`
|
/// Specify a `where in` clause for the query, prefixed with `or`
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Look for a set of rows matching the values passed
|
||||||
|
/// qb.or_where_in("key", vec![1,2,3]);
|
||||||
|
/// ```
|
||||||
pub fn or_where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
pub fn or_where_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
||||||
self._where_in(key, values, "IN", "OR")
|
self._where_in(key, values, "IN", "OR")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a `where not in` clause for the query
|
/// Specify a `where not in` clause for the query
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Look for a set of rows not matching the values passed
|
||||||
|
/// qb.where_not_in("key", vec![1,2,3]);
|
||||||
|
/// ```
|
||||||
pub fn where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
pub fn where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
||||||
self._where_in(key, values, "NOT IN", "AND")
|
self._where_in(key, values, "NOT IN", "AND")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a `where not in` clause for the query, prefixed with `or`
|
/// Specify a `where not in` clause for the query, prefixed with `or`
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Look for a set of rows not matching the values passed
|
||||||
|
/// qb.or_where_not_in("key", vec![1,2,3]);
|
||||||
|
/// ```
|
||||||
pub fn or_where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
pub fn or_where_not_in(&mut self, key: &str, values: Vec<impl Any>) -> &mut Self {
|
||||||
self._where_in(key, values, "NOT IN", "OR")
|
self._where_in(key, values, "NOT IN", "OR")
|
||||||
}
|
}
|
||||||
@ -412,6 +447,15 @@ impl QueryBuilder {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Set a key and value for an insert or update query
|
/// Set a key and value for an insert or update query
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Can be called multiple times to set multiple values
|
||||||
|
/// qb.set("foo", 3)
|
||||||
|
/// .set("bar", 4)
|
||||||
|
/// .insert("table");
|
||||||
|
/// ```
|
||||||
pub fn set(&mut self, key: &str, value: impl Any) -> &mut Self {
|
pub fn set(&mut self, key: &str, value: impl Any) -> &mut Self {
|
||||||
let key = self.driver.quote_identifier(key);
|
let key = self.driver.quote_identifier(key);
|
||||||
self.state
|
self.state
|
||||||
@ -431,11 +475,25 @@ impl QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method for a `left` join
|
/// Convenience method for a `left` join
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Note that the value is not prepared/escaped, due to it often being a column
|
||||||
|
/// qb.left_join("table1", "column1", "<>", "foo");
|
||||||
|
/// ```
|
||||||
pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
||||||
self.join(table, col, op, value, JoinType::Left)
|
self.join(table, col, op, value, JoinType::Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method for an `inner` join
|
/// Convenience method for an `inner` join
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // Note that the value is not prepared/escaped, due to it often being a column
|
||||||
|
/// qb.inner_join("table1", "column1", "<>", "foo");
|
||||||
|
/// ```
|
||||||
pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
||||||
self.join(table, col, op, value, JoinType::Inner)
|
self.join(table, col, op, value, JoinType::Inner)
|
||||||
}
|
}
|
||||||
@ -445,7 +503,7 @@ impl QueryBuilder {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use stringqb::prelude::*;
|
/// # use stringqb::prelude::*;
|
||||||
/// # let mut qb = QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // Note that the value is not escaped, due to it often being a column
|
/// // Note that the value is not prepared/escaped, due to it often being a column
|
||||||
/// qb.join("table1", "column1", "<>", "foo", JoinType::Inner);
|
/// qb.join("table1", "column1", "<>", "foo", JoinType::Inner);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn join(
|
pub fn join(
|
||||||
@ -456,9 +514,16 @@ impl QueryBuilder {
|
|||||||
value: &str,
|
value: &str,
|
||||||
join_type: JoinType,
|
join_type: JoinType,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let table = self.driver.quote_identifier(table);
|
let col = self.quote_fields(col);
|
||||||
let col = self.driver.quote_identifier(col);
|
let table = self.quote_table(table);
|
||||||
let condition = table + " ON " + &col + op + value;
|
|
||||||
|
let condition = format!(
|
||||||
|
"{} ON {}{}{}",
|
||||||
|
table,
|
||||||
|
&col,
|
||||||
|
op,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
|
||||||
let join_type = match join_type {
|
let join_type = match join_type {
|
||||||
JoinType::Cross => "CROSS ",
|
JoinType::Cross => "CROSS ",
|
||||||
@ -520,6 +585,11 @@ impl QueryBuilder {
|
|||||||
|
|
||||||
/// Add an offset to the query
|
/// Add an offset to the query
|
||||||
pub fn offset(&mut self, offset: usize) -> &mut Self {
|
pub fn offset(&mut self, offset: usize) -> &mut Self {
|
||||||
|
assert!(
|
||||||
|
self.state.offset.is_some(),
|
||||||
|
"You must set a limit to set an offset"
|
||||||
|
);
|
||||||
|
|
||||||
self.state.offset = Some(offset);
|
self.state.offset = Some(offset);
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -531,16 +601,28 @@ impl QueryBuilder {
|
|||||||
|
|
||||||
/// Start a logical grouping in the current query
|
/// Start a logical grouping in the current query
|
||||||
pub fn group_start(&mut self) -> &mut Self {
|
pub fn group_start(&mut self) -> &mut Self {
|
||||||
|
let conj = if ! self.state.has_where_clause() {
|
||||||
|
"\nWHERE "
|
||||||
|
} else {
|
||||||
|
" "
|
||||||
|
};
|
||||||
|
|
||||||
self.state
|
self.state
|
||||||
.append_query_map(QueryClauseType::GroupStart, " ", "(");
|
.append_query_map(QueryClauseType::GroupStart, conj, "(");
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a logical grouping, prefixed with `not`
|
/// Start a logical grouping, prefixed with `not`
|
||||||
pub fn not_group_start(&mut self) -> &mut Self {
|
pub fn not_group_start(&mut self) -> &mut Self {
|
||||||
|
let conj = if ! self.state.has_where_clause() {
|
||||||
|
"\nWHERE "
|
||||||
|
} else {
|
||||||
|
" AND "
|
||||||
|
};
|
||||||
|
|
||||||
self.state
|
self.state
|
||||||
.append_query_map(QueryClauseType::GroupStart, " AND ", "NOT (");
|
.append_query_map(QueryClauseType::GroupStart, conj, "NOT (");
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -580,7 +662,7 @@ impl QueryBuilder {
|
|||||||
/// # let mut qb = QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // The get() method actually calls the driver to run
|
/// // The get() method actually calls the driver to run
|
||||||
/// // the SQL query
|
/// // the SQL query
|
||||||
/// let query = db.select_vec(vec!["foo", "bar"])
|
/// let query = qb.select_vec(vec!["foo", "bar"])
|
||||||
/// .from("table t")
|
/// .from("table t")
|
||||||
/// .get();
|
/// .get();
|
||||||
/// ```
|
/// ```
|
||||||
@ -602,7 +684,7 @@ impl QueryBuilder {
|
|||||||
/// # let mut qb = QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // The insert() method actually calls the driver to run
|
/// // The insert() method actually calls the driver to run
|
||||||
/// // the SQL query
|
/// // the SQL query
|
||||||
/// let query = db.set("foo", 3)
|
/// let query = qb.set("foo", 3)
|
||||||
/// .insert("table");
|
/// .insert("table");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert(&mut self, table: &str) {
|
pub fn insert(&mut self, table: &str) {
|
||||||
@ -618,7 +700,7 @@ impl QueryBuilder {
|
|||||||
/// # let mut qb = QueryBuilder::default();
|
/// # let mut qb = QueryBuilder::default();
|
||||||
/// // The update() method actually calls the driver to run
|
/// // The update() method actually calls the driver to run
|
||||||
/// // the SQL query
|
/// // the SQL query
|
||||||
/// let query = db.set("foo", 3)
|
/// let query = qb.set("foo", 3)
|
||||||
/// .wher("foo", 4)
|
/// .wher("foo", 4)
|
||||||
/// .update("table");
|
/// .update("table");
|
||||||
/// ```
|
/// ```
|
||||||
@ -629,6 +711,15 @@ impl QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the generated delete query
|
/// Execute the generated delete query
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use stringqb::prelude::*;
|
||||||
|
/// # let mut qb = QueryBuilder::default();
|
||||||
|
/// // The delete() method actually calls the driver to run
|
||||||
|
/// // the SQL query
|
||||||
|
/// let query = qb.wher("foo", 3)
|
||||||
|
/// .delete("table");
|
||||||
|
/// ```
|
||||||
pub fn delete(&mut self, table: &str) {
|
pub fn delete(&mut self, table: &str) {
|
||||||
let sql = self.get_compiled_delete(table);
|
let sql = self.get_compiled_delete(table);
|
||||||
|
|
||||||
@ -670,14 +761,14 @@ impl QueryBuilder {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Get a new instance of the query builder
|
/// Get a new instance of the query builder
|
||||||
pub fn reset(&mut self) -> &Self {
|
pub fn reset(&mut self) -> &mut Self {
|
||||||
self.state = QueryState::new();
|
self.state = QueryState::new();
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute an SQL query with no parameters
|
/// Execute an SQL query with no parameters
|
||||||
pub fn basic_query(&mut self, sql: &str) {
|
pub fn basic_query(&mut self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
|
||||||
self.driver.query(sql)
|
self.driver.query(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,6 +782,32 @@ impl QueryBuilder {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Quotes table column(s)/field(s) accounting for 'as' aliases
|
||||||
|
fn quote_fields(&mut self, fields: &str) -> String {
|
||||||
|
lazy_static! {
|
||||||
|
static ref RE: Regex = Regex::new(r"(?i) as ").unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
split_map_join(fields, ",", |s| {
|
||||||
|
if !RE.is_match(s) {
|
||||||
|
return self.driver.quote_identifier(s.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a operation similar to split_map_join for the
|
||||||
|
// regex matches, quoting each identifier
|
||||||
|
RE.split(s)
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| self.driver.quote_identifier(p))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(" AS ")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quotes table(s), accounting for aliases
|
||||||
|
pub fn quote_table(&mut self, table: &str) -> String {
|
||||||
|
split_map_join(table, " ", |s| self.driver.quote_identifier(s))
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// ! Implementation Details
|
// ! Implementation Details
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
@ -703,6 +820,12 @@ impl QueryBuilder {
|
|||||||
like: &str,
|
like: &str,
|
||||||
conj: &str,
|
conj: &str,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
let conj = if ! self.state.has_where_clause() {
|
||||||
|
"\nWHERE "
|
||||||
|
} else {
|
||||||
|
conj
|
||||||
|
};
|
||||||
|
|
||||||
let field = self.driver.quote_identifier(field);
|
let field = self.driver.quote_identifier(field);
|
||||||
|
|
||||||
let like = format!("{} {} ?", field, like);
|
let like = format!("{} {} ?", field, like);
|
||||||
@ -775,6 +898,12 @@ impl QueryBuilder {
|
|||||||
|
|
||||||
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
|
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
|
||||||
|
|
||||||
|
let conj = if ! self.state.has_where_clause() {
|
||||||
|
"\nWHERE "
|
||||||
|
} else {
|
||||||
|
conj
|
||||||
|
};
|
||||||
|
|
||||||
self.state
|
self.state
|
||||||
.append_query_map(QueryClauseType::WhereIn, conj, &str);
|
.append_query_map(QueryClauseType::WhereIn, conj, &str);
|
||||||
|
|
||||||
@ -805,6 +934,12 @@ impl QueryBuilder {
|
|||||||
|
|
||||||
item += &item2;
|
item += &item2;
|
||||||
|
|
||||||
|
let conj = if self.state.query_map.len() == 0 || ( ! self.state.has_where_clause()) {
|
||||||
|
String::from("\nWHERE")
|
||||||
|
} else {
|
||||||
|
String::from(conj)
|
||||||
|
};
|
||||||
|
|
||||||
let conj = if last_item.is_some() {
|
let conj = if last_item.is_some() {
|
||||||
let last_item = last_item.unwrap();
|
let last_item = last_item.unwrap();
|
||||||
|
|
||||||
@ -820,6 +955,8 @@ 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));
|
||||||
@ -893,7 +1030,7 @@ impl QueryBuilder {
|
|||||||
// @TODO determine query result type
|
// @TODO determine query result type
|
||||||
// @TODO prepare/execute query, and return result
|
// @TODO prepare/execute query, and return result
|
||||||
let stmt = self.prepare(sql);
|
let stmt = self.prepare(sql);
|
||||||
self.execute(&stmt, values)
|
self.execute(&stmt, &values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1041,12 +1178,6 @@ impl QueryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_query_map(&mut self, clause_type: QueryClauseType, conj: &str, s: &str) -> &mut Self {
|
fn append_query_map(&mut self, clause_type: QueryClauseType, conj: &str, s: &str) -> &mut Self {
|
||||||
let conj = if self.query_map.len() == 0 {
|
|
||||||
" WHERE "
|
|
||||||
} else {
|
|
||||||
conj
|
|
||||||
};
|
|
||||||
|
|
||||||
self.query_map.push(QueryClause::new(clause_type, conj, s));
|
self.query_map.push(QueryClause::new(clause_type, conj, s));
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -1110,6 +1241,20 @@ impl QueryState {
|
|||||||
&mut self.where_values
|
&mut self.where_values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_where_clause(&self) -> bool {
|
||||||
|
let has_clause = false;
|
||||||
|
|
||||||
|
for clause in self.query_map.iter() {
|
||||||
|
match clause.clause_type {
|
||||||
|
QueryClauseType::Where => return true,
|
||||||
|
QueryClauseType::WhereIn => return true,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
has_clause
|
||||||
|
}
|
||||||
|
|
||||||
fn set_from_string(&mut self, s: &str) -> &mut Self {
|
fn set_from_string(&mut self, s: &str) -> &mut Self {
|
||||||
self.from_string = String::from(s);
|
self.from_string = String::from(s);
|
||||||
|
|
||||||
@ -1172,7 +1317,7 @@ mod tests {
|
|||||||
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
|
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
let sql = qb.get_compiled_select();
|
let sql = qb.get_compiled_select();
|
||||||
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) ";
|
let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\" IN (?,?,?,?,?,?) ";
|
||||||
|
|
||||||
assert_eq!(sql, expected);
|
assert_eq!(sql, expected);
|
||||||
}
|
}
|
||||||
|
@ -120,16 +120,13 @@ impl<'a, T: ?Sized> From<&'a T> for ToDriverOutput<'a>
|
|||||||
//from_value!(f64);
|
//from_value!(f64);
|
||||||
//from_value!(Vec<u8>);
|
//from_value!(Vec<u8>);
|
||||||
|
|
||||||
|
|
||||||
/// Types that can be converted to a type that the driver understands
|
/// Types that can be converted to a type that the driver understands
|
||||||
pub trait ToDriver {
|
pub trait ToDriver {
|
||||||
fn to_driver(&self) -> Result<ToDriverOutput<'_>, ()>;
|
fn to_driver(&self) -> Result<ToDriverOutput<'_>, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for types that can be created from the result of a query on the driver
|
/// A trait for types that can be created from the result of a query on the driver
|
||||||
pub trait FromDriver: Sized {
|
pub trait FromDriver: Sized {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enum struct for mapping between database and Rust types
|
/// Enum struct for mapping between database and Rust types
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||||
|
@ -15,7 +15,7 @@ fn select_keys_as_query() {
|
|||||||
|
|
||||||
let sql = qb.select("foo as bar, baz").from("a").get_compiled_select();
|
let sql = qb.select("foo as bar, baz").from("a").get_compiled_select();
|
||||||
|
|
||||||
assert_eq!(sql, "SELECT \"foo\" as \"bar\",\"baz\"\nFROM \"a\"");
|
assert_eq!(sql, "SELECT \"foo\" AS \"bar\",\"baz\"\nFROM \"a\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -54,7 +54,7 @@ fn select_where() {
|
|||||||
qb.from("test").r#where("foo", "bar");
|
qb.from("test").r#where("foo", "bar");
|
||||||
|
|
||||||
let sql = qb.get_compiled_select();
|
let sql = qb.get_compiled_select();
|
||||||
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\"=?";
|
let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\"=?";
|
||||||
|
|
||||||
assert_eq!(sql, expected);
|
assert_eq!(sql, expected);
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ fn select_where_in() {
|
|||||||
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
|
qb.from("test").where_in("foo", vec![0, 1, 2, 3, 4, 5]);
|
||||||
|
|
||||||
let sql = qb.get_compiled_select();
|
let sql = qb.get_compiled_select();
|
||||||
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) ";
|
let expected = "SELECT *\nFROM \"test\"\nWHERE \"foo\" IN (?,?,?,?,?,?) ";
|
||||||
|
|
||||||
assert_eq!(sql, expected);
|
assert_eq!(sql, expected);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user