use crate::{AkitaValue, IntoAkitaValue, SqlOperator, Wrapper};
#[derive(Debug, Clone, PartialEq)]
pub enum SubQueryType {
In,
NotIn,
Exists,
NotExists,
}
pub struct SubQuery {
wrapper: Wrapper,
query_type: SubQueryType,
column: Option<String>,
}
impl SubQuery {
pub fn in_query(column: &str) -> Self {
Self {
wrapper: Wrapper::new(),
query_type: SubQueryType::In,
column: Some(column.to_string()),
}
}
pub fn not_in_query(column: &str) -> Self {
Self {
wrapper: Wrapper::new(),
query_type: SubQueryType::NotIn,
column: Some(column.to_string()),
}
}
pub fn exists() -> Self {
Self {
wrapper: Wrapper::new(),
query_type: SubQueryType::Exists,
column: None,
}
}
pub fn not_exists() -> Self {
Self {
wrapper: Wrapper::new(),
query_type: SubQueryType::NotExists,
column: None,
}
}
pub fn select<S: Into<String>>(mut self, columns: Vec<S>) -> Self {
self.wrapper = self.wrapper.select(columns);
self
}
pub fn from<S: Into<String>>(mut self, table: S) -> Self {
self.wrapper = self.wrapper.table(table);
self
}
pub fn alias<S: Into<String>>(mut self, alias: S) -> Self {
self.wrapper = self.wrapper.alias(alias);
self
}
pub fn where_eq<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.eq(column, value);
self
}
pub fn where_ne<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.ne(column, value);
self
}
pub fn where_gt<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.gt(column, value);
self
}
pub fn where_ge<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.ge(column, value);
self
}
pub fn where_lt<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.lt(column, value);
self
}
pub fn where_le<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.le(column, value);
self
}
pub fn where_like<S: Into<String>, V: IntoAkitaValue>(mut self, column: S, value: V) -> Self {
self.wrapper = self.wrapper.like(column, value);
self
}
pub fn where_is_null<S: Into<String>>(mut self, column: S) -> Self {
self.wrapper = self.wrapper.is_null(column);
self
}
pub fn where_is_not_null<S: Into<String>>(mut self, column: S) -> Self {
self.wrapper = self.wrapper.is_not_null(column);
self
}
pub fn where_in<S, V, I>(mut self, column: S, values: I) -> Self
where
S: Into<String>,
V: IntoAkitaValue,
I: IntoIterator<Item = V>,
{
self.wrapper = self.wrapper.r#in(column, values);
self
}
pub fn where_between<S: Into<String>, V: IntoAkitaValue>(
mut self,
column: S,
start: V,
end: V,
) -> Self {
self.wrapper = self.wrapper.between(column, start, end);
self
}
pub fn build(self) -> (SubQueryType, Option<String>, String) {
let sql = self.wrapper.build_select_sql();
(self.query_type, self.column, sql)
}
pub fn query_type(&self) -> &SubQueryType {
&self.query_type
}
pub fn column(&self) -> Option<&String> {
self.column.as_ref()
}
pub fn wrapper(&self) -> &Wrapper {
&self.wrapper
}
}
impl Wrapper {
pub fn in_subquery<S: Into<String>>(self, column: S, subquery: SubQuery) -> Self {
let (query_type, _, sql) = subquery.build();
let column_str = column.into();
match query_type {
SubQueryType::In => self.apply_raw(format!("{} IN ({})", column_str, sql)),
SubQueryType::NotIn => self.apply_raw(format!("{} NOT IN ({})", column_str, sql)),
_ => self,
}
}
pub fn exists_subquery(self, subquery: SubQuery) -> Self {
let (query_type, _, sql) = subquery.build();
match query_type {
SubQueryType::Exists => self.apply_raw(format!("EXISTS ({})", sql)),
SubQueryType::NotExists => self.apply_raw(format!("NOT EXISTS ({})", sql)),
_ => self,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subquery_in() {
let sub = SubQuery::in_query("user_id")
.select(vec!["id"])
.from("users")
.where_eq("status", "active");
let (query_type, column, sql) = sub.build();
assert_eq!(query_type, SubQueryType::In);
assert_eq!(column, Some("user_id".to_string()));
assert!(sql.contains("SELECT"));
assert!(sql.contains("users"));
}
#[test]
fn test_subquery_not_in() {
let sub = SubQuery::not_in_query("id")
.select(vec!["id"])
.from("users")
.where_eq("status", "inactive");
let (query_type, column, _) = sub.build();
assert_eq!(query_type, SubQueryType::NotIn);
assert_eq!(column, Some("id".to_string()));
}
#[test]
fn test_subquery_exists() {
let sub = SubQuery::exists()
.select(vec!["1"])
.from("orders")
.where_eq("orders.user_id", "users.id");
let (query_type, column, sql) = sub.build();
assert_eq!(query_type, SubQueryType::Exists);
assert_eq!(column, None);
assert!(sql.contains("SELECT"));
}
#[test]
fn test_subquery_not_exists() {
let sub = SubQuery::not_exists()
.select(vec!["1"])
.from("orders")
.where_eq("orders.user_id", "users.id");
let (query_type, _, _) = sub.build();
assert_eq!(query_type, SubQueryType::NotExists);
}
#[test]
fn test_wrapper_in_subquery() {
let sub = SubQuery::in_query("user_id")
.select(vec!["id"])
.from("users")
.where_eq("status", "active");
let wrapper = Wrapper::new().in_subquery("id", sub);
let sql = wrapper.build_select_sql();
assert!(sql.contains("IN"), "Expected IN in SQL: {}", sql);
}
#[test]
fn test_wrapper_exists_subquery() {
let sub = SubQuery::exists()
.select(vec!["1"])
.from("orders")
.where_eq("orders.user_id", "users.id");
let wrapper = Wrapper::new().exists_subquery(sub);
let sql = wrapper.build_select_sql();
assert!(sql.contains("EXISTS"), "Expected EXISTS in SQL: {}", sql);
}
#[test]
fn test_subquery_multiple_conditions() {
let sub = SubQuery::in_query("id")
.select(vec!["id"])
.from("users")
.where_eq("status", "active")
.where_gt("age", 18);
let (_, _, sql) = sub.build();
assert!(sql.contains("AND"), "Expected AND in SQL: {}", sql);
}
#[test]
fn test_subquery_with_alias() {
let sub = SubQuery::in_query("user_id")
.select(vec!["id"])
.from("users")
.alias("u")
.where_eq("u.status", "active");
let (_, _, sql) = sub.build();
assert!(sql.contains("u"), "Expected alias in SQL: {}", sql);
}
}