use vibesql_ast::SelectStmt;
mod expression;
mod hints;
mod patterns;
mod query;
pub mod strategy;
use hints::extract_query_hint;
use patterns::has_analytical_pattern;
pub use strategy::{choose_execution_strategy, ExecutionStrategy, StrategyContext};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionModel {
RowOriented,
Columnar,
}
pub fn choose_execution_model(query: &SelectStmt) -> ExecutionModel {
if let Some(hint) = extract_query_hint(query) {
return hint;
}
if has_analytical_pattern(query) {
ExecutionModel::Columnar
} else {
ExecutionModel::RowOriented
}
}
#[cfg(test)]
mod tests {
use vibesql_ast::{
BinaryOperator, Expression, FromClause, GroupByClause, JoinType, SelectItem, SelectStmt,
};
use vibesql_types::SqlValue;
use super::*;
#[test]
fn test_row_oriented_for_point_lookup() {
let query = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Wildcard { alias: None }],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "users".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: Some(Expression::BinaryOp {
left: Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"id", false,
))),
op: BinaryOperator::Equal,
right: Box::new(Expression::Literal(SqlValue::Integer(123))),
}),
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert_eq!(choose_execution_model(&query), ExecutionModel::RowOriented);
}
#[test]
fn test_columnar_for_group_by() {
let query = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![
SelectItem::Expression {
expr: Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"region", false,
)),
alias: None,
source_text: None,
},
SelectItem::Expression {
expr: Expression::AggregateFunction {
name: vibesql_ast::FunctionIdentifier::new("SUM"),
distinct: false,
args: vec![Expression::BinaryOp {
left: Box::new(Expression::ColumnRef(
vibesql_ast::ColumnIdentifier::simple("price", false),
)),
op: BinaryOperator::Multiply,
right: Box::new(Expression::ColumnRef(
vibesql_ast::ColumnIdentifier::simple("quantity", false),
)),
}],
order_by: None,
filter: None,
},
alias: None,
source_text: None,
},
],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "orders".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: Some(GroupByClause::Simple(vec![Expression::ColumnRef(
vibesql_ast::ColumnIdentifier::simple("region", false),
)])),
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert_eq!(choose_execution_model(&query), ExecutionModel::Columnar);
}
#[test]
fn test_columnar_for_aggregation_without_group_by() {
let query = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Expression {
expr: Expression::AggregateFunction {
name: vibesql_ast::FunctionIdentifier::new("SUM"),
distinct: false,
args: vec![Expression::BinaryOp {
left: Box::new(Expression::ColumnRef(
vibesql_ast::ColumnIdentifier::simple("price", false),
)),
op: BinaryOperator::Multiply,
right: Box::new(Expression::ColumnRef(
vibesql_ast::ColumnIdentifier::simple("quantity", false),
)),
}],
order_by: None,
filter: None,
},
alias: Some("total".to_string()),
source_text: None,
}],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "orders".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert_eq!(choose_execution_model(&query), ExecutionModel::Columnar);
}
#[test]
fn test_row_oriented_for_many_joins() {
let query = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Wildcard { alias: None }],
into_table: None,
into_variables: None,
from: Some(FromClause::Join {
left: Box::new(FromClause::Join {
left: Box::new(FromClause::Join {
left: Box::new(FromClause::Table {
index_hint: None,
name: "t1".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
right: Box::new(FromClause::Table {
index_hint: None,
name: "t2".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
join_type: JoinType::Inner,
condition: None,
using_columns: None,
natural: false,
alias: None,
}),
right: Box::new(FromClause::Table {
index_hint: None,
name: "t3".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
join_type: JoinType::Inner,
condition: None,
using_columns: None,
natural: false,
alias: None,
}),
right: Box::new(FromClause::Table {
index_hint: None,
name: "t4".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
join_type: JoinType::Inner,
condition: None,
using_columns: None,
natural: false,
alias: None,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert_eq!(choose_execution_model(&query), ExecutionModel::RowOriented);
}
#[test]
fn test_has_aggregate_functions() {
let query_with_count = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Expression {
expr: Expression::AggregateFunction {
name: vibesql_ast::FunctionIdentifier::new("COUNT"),
distinct: false,
args: vec![Expression::Wildcard],
order_by: None,
filter: None,
},
alias: None,
source_text: None,
}],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "orders".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert!(query::has_aggregate_functions(&query_with_count));
}
#[test]
fn test_has_arithmetic_expressions() {
let query_with_arithmetic = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Expression {
expr: Expression::BinaryOp {
left: Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"price", false,
))),
op: BinaryOperator::Multiply,
right: Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"quantity", false,
))),
},
alias: Some("total".to_string()),
source_text: None,
}],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "orders".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert!(query::has_arithmetic_expressions(&query_with_arithmetic));
}
#[test]
fn test_selective_projection() {
let selective = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![
SelectItem::Expression {
expr: Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple("id", false)),
alias: None,
source_text: None,
},
SelectItem::Expression {
expr: Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"name", false,
)),
alias: None,
source_text: None,
},
],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "users".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert!(query::has_selective_projection(&selective));
let non_selective = SelectStmt {
with_clause: None,
distinct: false,
select_list: vec![SelectItem::Wildcard { alias: None }],
into_table: None,
into_variables: None,
from: Some(FromClause::Table {
index_hint: None,
name: "users".to_string(),
alias: None,
column_aliases: None,
quoted: false,
}),
where_clause: None,
group_by: None,
having: None,
window_definitions: None,
order_by: None,
limit: None,
offset: None,
set_operation: None,
values: None,
};
assert!(!query::has_selective_projection(&non_selective));
}
}