An attempt to make a less strongly-typed database abstraction layer for Rust.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
4.9KB

  1. //! Database Drivers
  2. //!
  3. //! Drivers represent a connection to a specific type of database engine
  4. use crate::fns::split_map_join;
  5. use regex::Regex;
  6. use std::any::Any;
  7. use std::error::Error;
  8. #[cfg(feature = "postgres")]
  9. pub mod postgres;
  10. #[cfg(feature = "sqlite")]
  11. pub mod sqlite;
  12. #[cfg(feature = "mysql")]
  13. pub mod mysql;
  14. #[cfg(feature = "mssql")]
  15. pub mod mssql;
  16. #[derive(Debug)]
  17. struct Connection;
  18. /// Result for a db query
  19. #[derive(Debug)]
  20. struct QueryResult;
  21. /// Empty Driver implementation
  22. ///
  23. /// Good for general testing
  24. #[derive(Debug)]
  25. pub struct DefaultDriver;
  26. impl DefaultDriver {
  27. /// Create a `DefaultDriver`
  28. pub fn new() -> Self {
  29. DefaultDriver {}
  30. }
  31. }
  32. impl DatabaseDriver for DefaultDriver {
  33. fn explain(&self, sql: &str) -> String {
  34. format!("EXPLAIN {}", sql)
  35. }
  36. fn random(&self) -> String {
  37. String::from(" RANDOM")
  38. }
  39. }
  40. /// Database Driver Trait
  41. ///
  42. /// Interface between the database connection library and the query builder
  43. pub trait DatabaseDriver {
  44. /// Get which characters are used to delimit identifiers
  45. /// such as tables, and columns
  46. fn _quotes(&self) -> (char, char) {
  47. ('"', '"')
  48. }
  49. /// Vector version of `quote_identifier`
  50. fn quote_identifiers(&self, identifiers: Vec<String>) -> Vec<String> {
  51. let mut output: Vec<String> = vec![];
  52. for identifier in identifiers {
  53. output.push(self.quote_identifier(&identifier).to_string());
  54. }
  55. output
  56. }
  57. /// Quote the identifiers passed, so the database does not
  58. /// normalize the identifiers (eg, table, column, etc.)
  59. fn quote_identifier(&self, identifier: &str) -> String {
  60. let mut identifier = &mut String::from(identifier);
  61. // If the identifier is actually a comma-separated list,
  62. // recurse to quote each identifier in the list
  63. if identifier.contains(",") {
  64. // This was the only way I could figure to get
  65. // around mutable string reference scope hell
  66. let func = |part: &str| self.quote_identifier(part.trim());
  67. identifier.replace_range(.., &split_map_join(identifier, ",", func));
  68. }
  69. let (open_char, close_char) = self._quotes();
  70. let trimmed_tiers = split_map_join(identifier, ".", |tier| {
  71. let tier = tier.trim();
  72. if tier.starts_with(open_char) && tier.ends_with(close_char) {
  73. return tier.to_string();
  74. }
  75. // Here where the quoting actually happens. Everything
  76. // else is breaking down the identifier list for this.
  77. format!("{}{}{}", &open_char, tier, &close_char)
  78. });
  79. trimmed_tiers
  80. // @TODO Fix functional calls in 'select' queries
  81. }
  82. // Runs a basic sql query on the database
  83. fn query(&self, sql: &str) -> Result<Box<dyn Any>, Box<dyn Any>> {
  84. Ok(Box::new(String::from(sql)))
  85. }
  86. /// Prepares an sql statement for the database
  87. fn prepare(&self, sql: &str) -> Result<(), ()> {
  88. Ok(())
  89. }
  90. // Runs a prepared statement on the database
  91. // fn execute(&self, sql: &str, ?) -> Result<?,?>;
  92. // Prepares and executes an sql query
  93. // fn prepare_execute(&self, sql: &str, params: &[?]) -> Result<?, ?>;
  94. // ------------------------------------------------------------------------
  95. // ! Driver-specific SQL methods
  96. // ------------------------------------------------------------------------
  97. /// Take an existing sql query and add a limit and/or offset
  98. fn limit(&self, sql: &str, limit: Option<usize>, offset: Option<usize>) -> String {
  99. let mut sql = sql.to_string();
  100. if limit.is_some() {
  101. sql += &format!("\nLIMIT {}", limit.unwrap());
  102. }
  103. if limit.is_some() && offset.is_some() {
  104. sql += &format!(" OFFSET {}", offset.unwrap());
  105. }
  106. sql
  107. }
  108. /// Get the query plan for the existing sql
  109. fn explain(&self, sql: &str) -> String;
  110. /// Get the database's keyword for sorting randomly
  111. fn random(&self) -> String;
  112. }
  113. #[cfg(test)]
  114. mod tests {
  115. use super::*;
  116. #[test]
  117. fn test_quote_identifier() {
  118. let driver = DefaultDriver::new();
  119. assert_eq!(
  120. driver.quote_identifier("foo, bar, baz"),
  121. r#""foo","bar","baz""#
  122. );
  123. assert_eq!(
  124. driver.quote_identifier("foo.bar, baz, fizz"),
  125. r#""foo"."bar","baz","fizz""#
  126. );
  127. }
  128. #[test]
  129. fn test_quote_identifiers() {
  130. let driver = DefaultDriver::new();
  131. assert_eq!(
  132. driver.quote_identifiers(vec![
  133. "\tfoo. bar".to_string(),
  134. "baz".to_string(),
  135. "fizz.\n\tbuzz.baz".to_string(),
  136. ]),
  137. vec![
  138. r#""foo"."bar""#.to_string(),
  139. r#""baz""#.to_string(),
  140. r#""fizz"."buzz"."baz""#.to_string(),
  141. ]
  142. );
  143. }
  144. }