diff --git a/src/enums.rs b/src/enums.rs index 966634e..56b3860 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -50,6 +50,8 @@ pub enum QueryClauseType { GroupEnd, /// Starting a parenthetical grouping GroupStart, + /// A having clause + Having, /// A join clause Join, /// A like clause diff --git a/src/query_builder.rs b/src/query_builder.rs index fd5dfa8..45031c3 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -164,7 +164,7 @@ impl QueryBuilder { /// Generates an OR Like clause pub fn or_like(&mut self, field: &str, value: Box, position: LikeWildcard) -> &mut Self { - self._like(field, Box::new(value), position, "LIKE", "OR") + self._like(field, value, position, "LIKE", "OR") } /// Generates a NOI Like clause @@ -182,13 +182,13 @@ impl QueryBuilder { // -------------------------------------------------------------------------- /// Add a `having` clause to the query - pub fn having(&mut self, key: &str, value: Box) -> &mut Self { - unimplemented!(); + pub fn having(&mut self, key: &str, value: Vec>) -> &mut Self { + self._having(key, value, "AND") } /// Add a `having` clause to the query, prefixed with an `or` - pub fn or_having(&mut self, key: &str, value: Box) -> &mut Self { - unimplemented!(); + pub fn or_having(&mut self, key: &str, value: Vec>) -> &mut Self { + self._having(key, value, "OR") } // -------------------------------------------------------------------------- @@ -266,6 +266,13 @@ impl QueryBuilder { } /// Add a table join to the query + /// + /// ```no_run + /// # use stringqb::prelude::*; + /// # let mut qb = QueryBuilder::default(); + /// // Note that the value is not escaped, due to it often being a column + /// qb.join("table1", "column1", "<>", "foo", JoinType::Inner); + /// ``` pub fn join( &mut self, table: &str, @@ -307,16 +314,11 @@ impl QueryBuilder { /// Add an order by clause to the query pub fn order_by(&mut self, field: &str, direction: OrderDirection) -> &mut Self { - 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"), + OrderDirection::Rand => self.driver.random(), }; self.state.append_order_map(&field, &dir); @@ -327,11 +329,7 @@ impl QueryBuilder { &order_clauses.push(clause); } - let order_str = if direction != OrderDirection::Rand { - "\nORDER BY ".to_string() + &order_clauses.join(", ") - } else { - unimplemented!(); - }; + let order_str = "\nORDER BY ".to_string() + &order_clauses.join(", "); self.state.set_order_string(&order_str); @@ -358,28 +356,16 @@ impl QueryBuilder { /// Start a logical grouping in the current query pub fn group_start(&mut self) -> &mut Self { - let conj = if self.state.query_map_empty() { - " WHERE " - } else { - " " - }; - self.state - .append_query_map(QueryClauseType::GroupStart, conj, "("); + .append_query_map(QueryClauseType::GroupStart, " ", "("); self } /// Start a logical grouping, prefixed with `not` pub fn not_group_start(&mut self) -> &mut Self { - let conj = if self.state.query_map_empty() { - " WHERE " - } else { - " AND " - }; - self.state - .append_query_map(QueryClauseType::GroupStart, conj, "NOT ("); + .append_query_map(QueryClauseType::GroupStart, " AND ", "NOT ("); self } @@ -505,12 +491,6 @@ impl QueryBuilder { LikeWildcard::Both => format!("%{}%", *string_val), }; - let conj = if self.state.query_map_empty() { - " WHERE " - } else { - conj - }; - self.state .append_query_map(QueryClauseType::Like, conj, &like); self.state.append_where_values(Box::new(value)); @@ -518,10 +498,40 @@ impl QueryBuilder { self } - fn _where(&mut self, key: &str, values: Vec>) -> HashMap> { - let mut map: HashMap> = HashMap::new(); + fn _having(&mut self, key: &str, values: Vec>, conj: &str) -> &mut Self { + let keys = self._where(key, values); - unimplemented!(); + for k in keys { + self._having_key(&k, conj); + } + + self + } + + fn _having_key(&mut self, key: &str, conj:&str) -> &mut Self { + let field = key.trim().split(" ").collect::>(); + + let mut item = self.driver.quote_identifier(field[0]);; + + let item2 = if field.len() == 1 { + String::from("=?") + } else { + format!(" {} ?", &field[1]) + }; + + item += &item2; + + self.state.append_having_map(conj, &item); + + self + } + + fn _where(&mut self, key: &str, values: Vec>) -> Vec { + for x in values { + self.state.append_where_values(x); + } + + vec![String::from(key)] } fn _where_in(&mut self, key: &str, values: Vec>, in_str: &str, conj: &str) -> &mut Self { @@ -532,12 +542,6 @@ impl QueryBuilder { self.state.append_where_values(value); } - let conj = if self.state.query_map_empty() { - " WHERE " - } else { - conj - }; - let str = format!("{} {} ({}) ", key, in_str, placeholders.join(",")); self.state.append_query_map(QueryClauseType::WhereIn, conj, &str); diff --git a/src/query_builder/query_state.rs b/src/query_builder/query_state.rs index 464b975..bd97dac 100644 --- a/src/query_builder/query_state.rs +++ b/src/query_builder/query_state.rs @@ -104,11 +104,20 @@ impl QueryState { pub fn append_having_map( &mut self, - clause_type: QueryClauseType, conj: &str, s: &str, ) -> &mut Self { - self.having_map.push(QueryClause::new(clause_type, conj, s)); + let conj = if self.having_map.len() == 0 { + String::from(" HAVING ") + } else { + format!(" {} ", conj) + }; + + self.having_map.push(QueryClause::new( + QueryClauseType::Having, + &conj, + s + )); self } @@ -143,6 +152,12 @@ impl QueryState { 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 @@ -196,10 +211,6 @@ impl QueryState { &self.where_values } - pub fn having_map_empty(&self) -> bool { - self.having_map.len() == 0 - } - pub fn set_from_string(&mut self, s: &str) -> &mut Self { self.from_string = String::from(s); @@ -217,8 +228,4 @@ impl QueryState { self } - - pub fn query_map_empty(&self) -> bool { - self.query_map.len() == 0 - } }