stringqb/src/drivers.rs

182 lines
4.9 KiB
Rust
Raw Normal View History

2019-04-09 18:55:53 -04:00
//! Database Drivers
2019-04-05 20:46:07 -04:00
//!
//! Drivers represent a connection to a specific type of database engine
2019-04-12 15:25:59 -04:00
use crate::fns::split_map_join;
use regex::Regex;
2019-04-22 08:59:40 -04:00
use std::any::Any;
use std::error::Error;
2019-04-05 20:46:07 -04:00
#[cfg(feature = "postgres")]
2019-04-09 14:13:37 -04:00
pub mod postgres;
2019-04-05 20:46:07 -04:00
#[cfg(feature = "sqlite")]
2019-04-09 14:13:37 -04:00
pub mod sqlite;
2019-04-05 20:46:07 -04:00
#[cfg(feature = "mysql")]
2019-04-09 14:13:37 -04:00
pub mod mysql;
2019-04-05 20:46:07 -04:00
#[cfg(feature = "mssql")]
2019-04-09 14:13:37 -04:00
pub mod mssql;
2019-04-05 20:46:07 -04:00
#[derive(Debug)]
struct Connection;
/// Result for a db query
#[derive(Debug)]
struct QueryResult;
2019-04-09 14:13:37 -04:00
/// Empty Driver implementation
///
/// Good for general testing
#[derive(Debug)]
pub struct DefaultDriver;
impl DefaultDriver {
2019-04-10 14:03:28 -04:00
/// Create a `DefaultDriver`
2019-04-09 14:13:37 -04:00
pub fn new() -> Self {
DefaultDriver {}
}
2019-04-05 20:46:07 -04:00
}
2019-04-12 17:09:59 -04:00
impl DatabaseDriver for DefaultDriver {
fn explain(&self, sql: &str) -> String {
2019-04-17 09:09:31 -04:00
format!("EXPLAIN {}", sql)
2019-04-12 17:09:59 -04:00
}
fn random(&self) -> String {
String::from(" RANDOM")
}
}
2019-04-09 14:13:37 -04:00
2019-04-05 20:46:07 -04:00
/// Database Driver Trait
///
/// Interface between the database connection library and the query builder
2019-04-22 08:59:40 -04:00
pub trait DatabaseDriver {
2019-04-05 20:46:07 -04:00
/// Get which characters are used to delimit identifiers
/// such as tables, and columns
fn _quotes(&self) -> (char, char) {
('"', '"')
}
/// Vector version of `quote_identifier`
2019-04-11 11:44:06 -04:00
fn quote_identifiers(&self, identifiers: Vec<String>) -> Vec<String> {
2019-04-05 20:46:07 -04:00
let mut output: Vec<String> = vec![];
for identifier in identifiers {
2019-04-11 11:44:06 -04:00
output.push(self.quote_identifier(&identifier).to_string());
2019-04-05 20:46:07 -04:00
}
output
}
/// Quote the identifiers passed, so the database does not
/// normalize the identifiers (eg, table, column, etc.)
fn quote_identifier(&self, identifier: &str) -> String {
2019-07-18 09:36:59 -04:00
let mut identifier = &mut String::from(identifier);
2019-04-05 20:46:07 -04:00
// If the identifier is actually a comma-separated list,
2019-04-05 20:46:07 -04:00
// recurse to quote each identifier in the list
if identifier.contains(",") {
// This was the only way I could figure to get
// around mutable string reference scope hell
2019-04-26 16:38:34 -04:00
let func = |part: &str| self.quote_identifier(part.trim());
2019-04-25 13:17:42 -04:00
identifier.replace_range(.., &split_map_join(identifier, ",", func));
2019-04-05 20:46:07 -04:00
}
let (open_char, close_char) = self._quotes();
let trimmed_tiers = split_map_join(identifier, ".", |tier| {
let tier = tier.trim();
if tier.starts_with(open_char) && tier.ends_with(close_char) {
return tier.to_string();
2019-04-05 20:46:07 -04:00
}
2019-04-10 14:03:28 -04:00
// Here where the quoting actually happens. Everything
// else is breaking down the identifier list for this.
format!("{}{}{}", &open_char, tier, &close_char)
});
trimmed_tiers
// @TODO Fix functional calls in 'select' queries
2019-04-05 20:46:07 -04:00
}
2019-04-09 14:13:37 -04:00
// Runs a basic sql query on the database
2019-07-19 12:45:18 -04:00
fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
Ok(Box::new(String::from(sql)))
}
2019-04-22 08:59:40 -04:00
2019-07-18 09:36:59 -04:00
/// Prepares an sql statement for the database
2019-04-24 16:12:07 -04:00
fn prepare(&self, sql: &str) -> Result<(), ()> {
Ok(())
}
2019-04-22 08:59:40 -04:00
// Runs a prepared statement on the database
2019-04-26 16:38:34 -04:00
// fn execute(&self, sql: &str, ?) -> Result<?,?>;
// Prepares and executes an sql query
// fn prepare_execute(&self, sql: &str, params: &[?]) -> Result<?, ?>;
2019-04-12 17:09:59 -04:00
// ------------------------------------------------------------------------
// ! Driver-specific SQL methods
// ------------------------------------------------------------------------
/// Take an existing sql query and add a limit and/or offset
fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String {
let mut sql = sql.to_string();
if limit.is_some() {
sql += &format!("\nLIMIT {}", limit.unwrap());
}
2019-04-26 16:38:34 -04:00
if limit.is_some() && offset.is_some() {
2019-04-12 17:09:59 -04:00
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;
2019-04-05 20:46:07 -04:00
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quote_identifier() {
2019-04-09 14:13:37 -04:00
let driver = DefaultDriver::new();
2019-04-05 20:46:07 -04:00
assert_eq!(
driver.quote_identifier("foo, bar, baz"),
r#""foo","bar","baz""#
);
assert_eq!(
driver.quote_identifier("foo.bar, baz, fizz"),
r#""foo"."bar","baz","fizz""#
);
}
#[test]
fn test_quote_identifiers() {
2019-04-09 14:13:37 -04:00
let driver = DefaultDriver::new();
2019-04-05 20:46:07 -04:00
assert_eq!(
2019-04-11 11:44:06 -04:00
driver.quote_identifiers(vec![
"\tfoo. bar".to_string(),
"baz".to_string(),
"fizz.\n\tbuzz.baz".to_string(),
]),
2019-04-05 20:46:07 -04:00
vec![
r#""foo"."bar""#.to_string(),
r#""baz""#.to_string(),
r#""fizz"."buzz"."baz""#.to_string(),
]
);
}
}