use qail_core::ast::{
Action, AggregateFunc, Condition, Constraint, Expr, FrameBound, Operator, Qail, Value,
WindowFrame,
};
use qail_pg::driver::PgDriver;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("๐ฅ QAIL Complex Query Battle Test");
println!("===================================\n");
let mut driver = PgDriver::connect("127.0.0.1", 5432, "orion", "qail_test_migration").await?;
println!("๐ Setup Test Data");
println!("-------------------");
let drop_cmd = Qail {
action: Action::Drop,
table: "messages".to_string(),
..Default::default()
};
let make_cmd = Qail {
action: Action::Make,
table: "messages".to_string(),
columns: vec![
Expr::Def {
name: "id".to_string(),
data_type: "serial".to_string(),
constraints: vec![Constraint::PrimaryKey],
},
Expr::Def {
name: "phone_number".to_string(),
data_type: "text".to_string(),
constraints: vec![],
},
Expr::Def {
name: "direction".to_string(),
data_type: "text".to_string(),
constraints: vec![],
},
Expr::Def {
name: "content".to_string(),
data_type: "text".to_string(),
constraints: vec![Constraint::Nullable],
},
Expr::Def {
name: "amount".to_string(),
data_type: "int".to_string(),
constraints: vec![Constraint::Default("0".to_string())],
},
Expr::Def {
name: "created_at".to_string(),
data_type: "timestamptz".to_string(),
constraints: vec![Constraint::Default("now()".to_string())],
},
],
..Default::default()
};
let _ = driver.execute(&drop_cmd).await;
driver.execute(&make_cmd).await?;
let rows = [
("628123456789", "inbound", "Hello", 100_i64),
("628123456789", "outbound", "Hi there", 50_i64),
("628123456789", "inbound", "Thanks", 75_i64),
("628987654321", "outbound", "Welcome", 200_i64),
("628987654321", "inbound", "Got it", 150_i64),
("628111222333", "outbound", "Test", 300_i64),
];
for (phone, direction, content, amount) in rows {
let insert = Qail::add("messages")
.columns(["phone_number", "direction", "content", "amount"])
.values([
Value::String(phone.to_string()),
Value::String(direction.to_string()),
Value::String(content.to_string()),
Value::Int(amount),
]);
driver.execute(&insert).await?;
}
println!(" โ Created messages table with 6 rows");
println!("\n๐ Test 1: DISTINCT ON");
println!("-----------------------");
let mut distinct_on_query = Qail::get("messages").select_all();
distinct_on_query.distinct_on = vec![Expr::Named("phone_number".to_string())];
match driver.fetch_all(&distinct_on_query).await {
Ok(rows) => {
println!(
" โ DISTINCT ON: {} unique phone numbers (expect 3)",
rows.len()
);
assert_eq!(rows.len(), 3, "Expected 3 unique phone numbers");
}
Err(e) => println!(" โ DISTINCT ON: {}", e),
}
println!("\n๐ Test 2: Aggregate FILTER");
println!("----------------------------");
let mut filter_query = Qail::get("messages");
filter_query.columns = vec![Expr::Aggregate {
col: "*".to_string(),
func: AggregateFunc::Count,
distinct: false,
filter: Some(vec![Condition {
left: Expr::Named("direction".to_string()),
op: Operator::Eq,
value: Value::String("outbound".to_string()),
is_array_unnest: false,
}]),
alias: Some("outbound_count".to_string()),
}];
match driver.fetch_all(&filter_query).await {
Ok(rows) => {
println!(" โ COUNT FILTER: {} rows returned", rows.len());
println!(" โ Outbound messages counted (expect 3)");
}
Err(e) => println!(" โ COUNT FILTER: {}", e),
}
println!("\n๐ Test 3: Multiple FILTER Aggregates");
println!("--------------------------------------");
let mut multi_filter = Qail::get("messages");
multi_filter.columns = vec![
Expr::Aggregate {
col: "*".to_string(),
func: AggregateFunc::Count,
distinct: false,
filter: Some(vec![Condition {
left: Expr::Named("direction".to_string()),
op: Operator::Eq,
value: Value::String("inbound".to_string()),
is_array_unnest: false,
}]),
alias: Some("inbound".to_string()),
},
Expr::Aggregate {
col: "*".to_string(),
func: AggregateFunc::Count,
distinct: false,
filter: Some(vec![Condition {
left: Expr::Named("direction".to_string()),
op: Operator::Eq,
value: Value::String("outbound".to_string()),
is_array_unnest: false,
}]),
alias: Some("outbound".to_string()),
},
];
match driver.fetch_all(&multi_filter).await {
Ok(rows) => {
println!(" โ Multiple FILTER aggregates: {} rows", rows.len());
println!(" โ Inbound/Outbound counted (expect 3, 3)");
}
Err(e) => println!(" โ Multiple FILTER: {}", e),
}
println!("\n๐ Test 4: Window FRAME (Running Total)");
println!("----------------------------------------");
let mut window_frame_query = Qail::get("messages");
window_frame_query.columns = vec![
Expr::Named("id".to_string()),
Expr::Named("amount".to_string()),
Expr::Window {
name: "running_total".to_string(),
func: "SUM".to_string(),
params: vec![Expr::Named("amount".to_string())], partition: vec!["phone_number".to_string()],
order: vec![],
frame: Some(WindowFrame::Rows {
start: FrameBound::UnboundedPreceding,
end: FrameBound::CurrentRow,
}),
},
];
println!(" โ Window FRAME clause encoding verified");
match driver.fetch_all(&window_frame_query).await {
Ok(rows) => {
println!(" โ Window FRAME: {} rows with running totals", rows.len());
assert_eq!(rows.len(), 6, "Expected 6 rows");
}
Err(e) => println!(" โ Window FRAME: {}", e),
}
println!("\n๐ Test 5: DISTINCT ON Multiple Columns");
println!("----------------------------------------");
let mut multi_distinct =
Qail::get("messages").columns(["phone_number", "direction", "content"]);
multi_distinct.distinct_on = vec![
Expr::Named("phone_number".to_string()),
Expr::Named("direction".to_string()),
];
match driver.fetch_all(&multi_distinct).await {
Ok(rows) => {
println!(
" โ DISTINCT ON (phone, direction): {} unique combos",
rows.len()
);
}
Err(e) => println!(" โ DISTINCT ON multiple: {}", e),
}
println!("\n๐งน Cleanup");
println!("-----------");
driver.execute(&drop_cmd).await?;
println!(" โ Cleanup complete");
println!("\nโ
Complex query battle test complete!");
Ok(())
}