Implement more
This commit is contained in:
parent
f9979e9470
commit
0e1c6755b0
@ -4,16 +4,16 @@
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
mod postgres;
|
||||
pub mod postgres;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
mod sqlite;
|
||||
pub mod sqlite;
|
||||
|
||||
#[cfg(feature = "mysql")]
|
||||
mod mysql;
|
||||
pub mod mysql;
|
||||
|
||||
#[cfg(feature = "mssql")]
|
||||
mod mssql;
|
||||
pub mod mssql;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Connection;
|
||||
@ -22,12 +22,20 @@ struct Connection;
|
||||
#[derive(Debug)]
|
||||
struct QueryResult;
|
||||
|
||||
struct DriverBase {
|
||||
escape_char_open: char,
|
||||
escape_char_close: char,
|
||||
has_truncate: bool,
|
||||
/// Empty Driver implementation
|
||||
///
|
||||
/// Good for general testing
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultDriver;
|
||||
|
||||
impl DefaultDriver {
|
||||
pub fn new() -> Self {
|
||||
DefaultDriver {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseDriver for DefaultDriver {}
|
||||
|
||||
/// Database Driver Trait
|
||||
///
|
||||
/// Interface between the database connection library and the query builder
|
||||
@ -39,7 +47,7 @@ pub trait DatabaseDriver: fmt::Debug {
|
||||
}
|
||||
|
||||
/// Vector version of `quote_identifier`
|
||||
fn quote_identifiers<'a>(&self, identifiers: Vec<&'a str>) -> Vec<String> {
|
||||
fn quote_identifiers(&self, identifiers: Vec<&str>) -> Vec<String> {
|
||||
let mut output: Vec<String> = vec![];
|
||||
|
||||
for identifier in identifiers {
|
||||
@ -81,7 +89,7 @@ pub trait DatabaseDriver: fmt::Debug {
|
||||
if tier.starts_with(open_char) && tier.ends_with(close_char) {
|
||||
trimmed_tiers.push(tier.to_string());
|
||||
} else {
|
||||
let mut tier = format!("{}{}{}", open_char, tier, close_char);
|
||||
let tier = format!("{}{}{}", open_char, tier, close_char);
|
||||
trimmed_tiers.push(tier.to_string());
|
||||
}
|
||||
}
|
||||
@ -90,26 +98,17 @@ pub trait DatabaseDriver: fmt::Debug {
|
||||
// @TODO Fix functional calls in 'select' queries
|
||||
}
|
||||
|
||||
/// Runs a basic sql query on the database
|
||||
fn query(&self, query: &str) -> Result<(), ()>;
|
||||
// Runs a basic sql query on the database
|
||||
// fn query(&self, query: &str) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestDriver;
|
||||
|
||||
impl DatabaseDriver for TestDriver {
|
||||
fn query(&self, _query: &str) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifier() {
|
||||
let driver = TestDriver {};
|
||||
let driver = DefaultDriver::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifier("foo, bar, baz"),
|
||||
@ -123,7 +122,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifiers() {
|
||||
let driver = TestDriver {};
|
||||
let driver = DefaultDriver::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifiers(vec!["\tfoo. bar", "baz", "fizz.\n\tbuzz.baz",]),
|
||||
|
@ -3,16 +3,18 @@ use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct MSSQL;
|
||||
|
||||
impl MSSQL {
|
||||
pub fn new() -> Self {
|
||||
MSSQL {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseDriver for MSSQL {
|
||||
/// Get which characters are used to delimit identifiers
|
||||
/// such as tables, and columns
|
||||
fn _quotes(&self) -> (char, char) {
|
||||
('[', ']')
|
||||
}
|
||||
|
||||
fn query(&self, _query: &str) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -21,7 +23,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifier_bracket_quote() {
|
||||
let driver = MSSQL {};
|
||||
let driver = MSSQL::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifier("foo, bar, baz"),
|
||||
@ -35,7 +37,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifiers_bracket_quote() {
|
||||
let driver = MSSQL {};
|
||||
let driver = MSSQL::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifiers(vec!["\tfoo. bar", "baz", "fizz.\n\tbuzz.baz",]),
|
||||
|
@ -3,16 +3,18 @@ use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct MySQL;
|
||||
|
||||
impl MySQL {
|
||||
pub fn new() -> Self {
|
||||
MySQL {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseDriver for MySQL {
|
||||
/// Get which characters are used to delimit identifiers
|
||||
/// such as tables, and columns
|
||||
fn _quotes(&self) -> (char, char) {
|
||||
('`', '`')
|
||||
}
|
||||
|
||||
fn query(&self, _query: &str) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -21,7 +23,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifier_backtick_quote() {
|
||||
let driver = MySQL {};
|
||||
let driver = MySQL::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifier("foo, bar, baz"),
|
||||
@ -35,7 +37,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_quote_identifiers_backtick_quote() {
|
||||
let driver = MySQL {};
|
||||
let driver = MySQL::new();
|
||||
|
||||
assert_eq!(
|
||||
driver.quote_identifiers(vec!["\tfoo. bar", "baz", "fizz.\n\tbuzz.baz",]),
|
||||
|
@ -3,8 +3,10 @@ use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct Postgres;
|
||||
|
||||
impl DatabaseDriver for Postgres {
|
||||
fn query(&self, _query: &str) -> Result<(), ()> {
|
||||
Ok(())
|
||||
impl Postgres {
|
||||
pub fn new() -> Self {
|
||||
Postgres {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseDriver for Postgres { }
|
||||
|
@ -3,12 +3,10 @@ use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct SQLite;
|
||||
|
||||
impl DatabaseDriver for SQLite {
|
||||
fn quote_identifier(&self, identifier: &str) -> String {
|
||||
String::from(identifier)
|
||||
}
|
||||
|
||||
fn query(&self, _query: &str) -> Result<(), ()> {
|
||||
Ok(())
|
||||
impl SQLite {
|
||||
pub fn new() -> Self {
|
||||
SQLite {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseDriver for SQLite { }
|
||||
|
@ -1,9 +1,10 @@
|
||||
//! This main file is just for temparary testing
|
||||
use stringqb::query_builder::QueryBuilder;
|
||||
use stringqb::types::{SQLType, Type};
|
||||
use stringqb::drivers::postgres::Postgres;
|
||||
|
||||
fn main() {
|
||||
let mut qb = QueryBuilder::new();
|
||||
let mut qb = QueryBuilder::new(Postgres::new());
|
||||
|
||||
qb.set("foo", Box::new("bar"))
|
||||
.set("bar", Box::new(12))
|
||||
|
@ -4,7 +4,7 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::drivers::DatabaseDriver;
|
||||
use crate::drivers::{ DatabaseDriver, DefaultDriver };
|
||||
|
||||
/// The position of the wildcard(s)
|
||||
/// for a `like` clause
|
||||
@ -35,21 +35,22 @@ pub enum JoinType {
|
||||
}
|
||||
|
||||
/// The sort direction
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum OrderDirection {
|
||||
/// Sort Ascending
|
||||
Asc,
|
||||
/// Sort Descending
|
||||
Desc,
|
||||
/// Random Sort
|
||||
Rand,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum QueryClauseType {
|
||||
AndGroupStart,
|
||||
GroupEnd,
|
||||
GroupStart,
|
||||
Join,
|
||||
Like,
|
||||
OrGroupStart,
|
||||
Where,
|
||||
WhereIn,
|
||||
}
|
||||
@ -62,11 +63,11 @@ struct QueryClause {
|
||||
}
|
||||
|
||||
impl QueryClause {
|
||||
pub fn new(clause_type: QueryClauseType, conjunction: String, string: String) -> Self {
|
||||
pub fn new(clause_type: QueryClauseType, conjunction: &str, string: &str) -> Self {
|
||||
QueryClause {
|
||||
clause_type,
|
||||
conjunction,
|
||||
string,
|
||||
conjunction: conjunction.to_string(),
|
||||
string: string.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,7 +87,7 @@ struct QueryState {
|
||||
order_array: HashMap<String, String>,
|
||||
|
||||
// Group by clause
|
||||
group_array: HashMap<String, String>,
|
||||
group_array: Vec<String>,
|
||||
|
||||
// Values to apply to prepared statements
|
||||
values: Vec<Box<dyn Any>>,
|
||||
@ -116,7 +117,7 @@ impl Default for QueryState {
|
||||
|
||||
set_array_keys: vec![],
|
||||
order_array: HashMap::new(),
|
||||
group_array: HashMap::new(),
|
||||
group_array: vec![],
|
||||
values: vec![],
|
||||
where_values: vec![],
|
||||
|
||||
@ -133,21 +134,43 @@ impl QueryState {
|
||||
pub fn new() -> Self {
|
||||
QueryState::default()
|
||||
}
|
||||
|
||||
pub fn append_select_string(&mut self, s:&str) -> &mut Self {
|
||||
self.select_string += s;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_from_string(&mut self, s:&str) -> &mut Self {
|
||||
self.from_string = s.to_owned();
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The struct representing a query builder
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct QueryBuilder {
|
||||
state: QueryState,
|
||||
driver: Option<Box<dyn DatabaseDriver>>,
|
||||
driver: Box<dyn DatabaseDriver>,
|
||||
}
|
||||
|
||||
impl Default for QueryBuilder {
|
||||
/// Creates a new QueryBuilder instance with default driver
|
||||
fn default() -> Self {
|
||||
QueryBuilder {
|
||||
state: QueryState::new(),
|
||||
driver: Box::new(DefaultDriver::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryBuilder {
|
||||
/// Creates a new QueryBuilder instance
|
||||
pub fn new() -> Self {
|
||||
/// Create a new QueryBuilder instance with a driver
|
||||
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
|
||||
QueryBuilder {
|
||||
state: QueryState::new(),
|
||||
driver: None,
|
||||
driver: Box::new(driver),
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,19 +319,82 @@ impl QueryBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Convenience method for a `left` join
|
||||
pub fn left_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
||||
self.join(table, col, op, value, JoinType::Left)
|
||||
}
|
||||
|
||||
/// Convenience method for an `inner` join
|
||||
pub fn inner_join(&mut self, table: &str, col: &str, op: &str, value: &str) -> &mut Self {
|
||||
self.join(table, col, op, value, JoinType::Inner)
|
||||
}
|
||||
|
||||
/// Add a table join to the query
|
||||
pub fn join(&mut self, table: &str, condition: &str, join_type: JoinType) -> &mut Self {
|
||||
unimplemented!();
|
||||
pub fn join(&mut self, table: &str, col: &str, op: &str, value: &str, join_type: JoinType) -> &mut Self {
|
||||
let table = self.driver.quote_identifier(table);
|
||||
let col = self.driver.quote_identifier(col);
|
||||
let condition = table + " ON " + &col + op + value;
|
||||
|
||||
let join_type = match join_type {
|
||||
JoinType::Left => "LEFT ",
|
||||
JoinType::Inner => "INNER ",
|
||||
JoinType::LeftOuter => "LEFT OUTER ",
|
||||
JoinType::Outer => "OUTER ",
|
||||
JoinType::Right => "RIGHT ",
|
||||
JoinType::RightOuter => "RIGHT OUTER",
|
||||
};
|
||||
|
||||
let conjunction = "\n".to_string() + join_type + "JOIN ";
|
||||
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::Join,
|
||||
&conjunction,
|
||||
&condition
|
||||
));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a group by clause to the query
|
||||
pub fn group_by(&mut self, field: &str) -> &mut Self {
|
||||
unimplemented!();
|
||||
self.state.group_array.push(field.to_string());
|
||||
|
||||
self.state.group_string = " GROUP BY ".to_string() + &self.state.group_array.join(",");
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an order by clause to the query
|
||||
pub fn order_by(&mut self, field: &str, direction: OrderDirection) -> &mut Self {
|
||||
unimplemented!();
|
||||
if direction == OrderDirection::Rand {
|
||||
// @TODO handle random sorting
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
let field = self.driver.quote_identifier(field);
|
||||
let dir = match direction {
|
||||
OrderDirection::Asc => String::from("ASC"),
|
||||
OrderDirection::Desc => String::from("DESC"),
|
||||
OrderDirection::Rand => String::from("RAND"),
|
||||
};
|
||||
self.state.order_array.insert(field, dir);
|
||||
|
||||
let mut order_clauses: Vec<String> = vec![];
|
||||
|
||||
for (f, dir) in &self.state.order_array {
|
||||
let clause = String::clone(f) + " " + &dir;
|
||||
&order_clauses.push(clause);
|
||||
}
|
||||
|
||||
let order_str = if direction != OrderDirection::Rand {
|
||||
"\nORDER BY ".to_string() + &order_clauses.join(", ")
|
||||
} else {
|
||||
unimplemented!();
|
||||
};
|
||||
|
||||
self.state.order_string = order_str;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a limit to the query
|
||||
@ -331,27 +417,73 @@ impl QueryBuilder {
|
||||
|
||||
/// Start a logical grouping in the current query
|
||||
pub fn group_start(&mut self) -> &mut Self {
|
||||
unimplemented!();
|
||||
if self.state.query_map.len() == 0 {
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
" ",
|
||||
"("
|
||||
));
|
||||
} else {
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
" WHERE ",
|
||||
"("
|
||||
));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Start a logical grouping, prefixed with `not`
|
||||
pub fn not_group_start(&mut self) -> &mut Self {
|
||||
unimplemented!();
|
||||
if self.state.query_map.len() == 0 {
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
" WHERE ",
|
||||
"("
|
||||
));
|
||||
} else {
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
" AND ",
|
||||
"("
|
||||
));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Start a logical grouping, prefixed with `or`
|
||||
pub fn or_group_start(&mut self) -> &mut Self {
|
||||
unimplemented!();
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
"",
|
||||
" OR ("
|
||||
));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Start a logical grouping, prefixed with `or not`
|
||||
pub fn or_not_group_start(&mut self) -> &mut Self {
|
||||
unimplemented!();
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupStart,
|
||||
"",
|
||||
" OR NOT ("
|
||||
));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// End the current logical grouping
|
||||
pub fn group_end(&mut self) -> &mut Self {
|
||||
unimplemented!();
|
||||
self.state.query_map.push(QueryClause::new(
|
||||
QueryClauseType::GroupEnd,
|
||||
"",
|
||||
")"
|
||||
));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -414,8 +546,10 @@ impl QueryBuilder {
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/// Get a new instance of the query builder
|
||||
pub fn reset_query(&mut self) -> Self {
|
||||
QueryBuilder::new()
|
||||
pub fn reset_query(mut self) -> Self {
|
||||
self.state = QueryState::new();
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,7 +559,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn set_key_value() {
|
||||
let mut qb = QueryBuilder::new();
|
||||
let mut qb = QueryBuilder::default();
|
||||
|
||||
qb.set("foo", Box::new("bar"));
|
||||
|
||||
@ -438,7 +572,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn set_hashmap() {
|
||||
let mut qb = QueryBuilder::new();
|
||||
let mut qb = QueryBuilder::default();
|
||||
|
||||
let mut authors: HashMap<String, Box<dyn Any>> = HashMap::new();
|
||||
authors.insert(
|
||||
|
Loading…
Reference in New Issue
Block a user