use crate::{
condition::ConditionKind,
group::{Conjunction, Node},
query::Query,
value::Value,
};
pub fn build_conditions(query: &Query, start: usize) -> (String, Vec<Value>) {
let mut values: Vec<Value> = vec![];
let mut n = start;
let parts: Vec<String> = query.nodes.iter()
.map(|node| build_node(node, &mut n, &mut values))
.collect();
(parts.join(" AND "), values)
}
fn build_node(node: &Node, n: &mut usize, values: &mut Vec<Value>) -> String {
match node {
Node::Cond(c) => build_cond(&c.kind, &c.values, n, values),
Node::Group(g) => {
let sep = match g.conjunction {
Conjunction::And => " AND ",
Conjunction::Or => " OR ",
};
let parts: Vec<String> = g.nodes.iter()
.map(|node| build_node(node, n, values))
.collect();
format!("({})", parts.join(sep))
}
}
}
fn build_cond(kind: &ConditionKind, vals: &[Value], n: &mut usize, values: &mut Vec<Value>) -> String {
match kind {
ConditionKind::Eq(col) => { let s = format!("{} = ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Ne(col) => { let s = format!("{} != ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Gt(col) => { let s = format!("{} > ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Gte(col) => { let s = format!("{} >= ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Lt(col) => { let s = format!("{} < ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Lte(col) => { let s = format!("{} <= ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::IsNull(col) => format!("{} IS NULL", col),
ConditionKind::IsNotNull(col) => format!("{} IS NOT NULL", col),
ConditionKind::FilterOptional(col) => {
let s = format!("(?{0} IS NULL OR {1} = ?{0})", n, col);
values.push(vals[0].clone()); *n += 1; s
}
ConditionKind::FilterOptionalGte(col) => {
let s = format!("(?{0} IS NULL OR {1} >= ?{0})", n, col);
values.push(vals[0].clone()); *n += 1; s
}
ConditionKind::FilterOptionalLte(col) => {
let s = format!("(?{0} IS NULL OR {1} <= ?{0})", n, col);
values.push(vals[0].clone()); *n += 1; s
}
ConditionKind::In(col) => {
let phs: Vec<String> = (0..vals.len()).map(|i| format!("?{}", *n + i)).collect();
let s = format!("{} IN ({})", col, phs.join(", "));
values.extend(vals.iter().cloned());
*n += vals.len();
s
}
ConditionKind::NotIn(col) => {
let phs: Vec<String> = (0..vals.len()).map(|i| format!("?{}", *n + i)).collect();
let s = format!("{} NOT IN ({})", col, phs.join(", "));
values.extend(vals.iter().cloned());
*n += vals.len();
s
}
ConditionKind::Like(col) => { let s = format!("{} LIKE ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::NotLike(col) => { let s = format!("{} NOT LIKE ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
ConditionKind::Between(col) => {
let s = format!("{} BETWEEN ?{} AND ?{}", col, *n, *n + 1);
values.push(vals[0].clone());
values.push(vals[1].clone());
*n += 2;
s
}
}
}
pub fn build_tail(query: &Query) -> String {
let mut sql = String::new();
if let Some((col, dir)) = &query.order {
sql.push_str(&format!(" ORDER BY {} {}", col, dir.as_sql()));
}
if let Some(l) = query.limit {
sql.push_str(&format!(" LIMIT {}", l));
}
if let Some(o) = query.offset {
sql.push_str(&format!(" OFFSET {}", o));
}
sql
}