use sea_orm::sea_query::{Condition, Order};
use sea_orm::{DatabaseBackend, EntityTrait, QueryTrait, Select};
use super::{Query, WhereNode};
pub struct SQLCompiler<E: EntityTrait> {
query: Query,
select: Select<E>,
}
impl<E: EntityTrait> SQLCompiler<E> {
#[must_use]
pub fn new(select: Select<E>) -> Self {
Self {
query: Query::new(),
select,
}
}
pub fn apply_where(&mut self, condition: Condition) -> &mut Self {
self.query.where_clause = Some(WhereNode::from_condition(condition));
self
}
pub fn set_ordering(&mut self, orderings: Vec<(String, Order)>) -> &mut Self {
self.query.orderings = orderings;
self
}
pub fn set_limit(&mut self, limit: u64) -> &mut Self {
self.query.limit = Some(limit);
self
}
pub fn set_offset(&mut self, offset: u64) -> &mut Self {
self.query.offset = Some(offset);
self
}
#[must_use]
pub fn as_sql(&self, backend: DatabaseBackend) -> String {
self.select.clone().build(backend).to_string()
}
}
#[cfg(test)]
mod tests {
use sea_orm::sea_query::Order;
use sea_orm::{DatabaseBackend, EntityTrait, QuerySelect};
use super::SQLCompiler;
mod mock_entity {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "widgets")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
#[test]
fn new_compiler_starts_with_empty_query_state() {
let compiler = SQLCompiler::new(mock_entity::Entity::find());
assert!(compiler.query.is_empty());
assert!(compiler.query.orderings.is_empty());
assert_eq!(compiler.query.limit, None);
assert_eq!(compiler.query.offset, None);
}
#[test]
fn apply_where_stores_placeholder_where_node() {
let mut compiler = SQLCompiler::new(mock_entity::Entity::find());
compiler.apply_where(sea_orm::Condition::all());
assert!(compiler.query.where_clause.is_some());
assert!(
compiler
.query
.where_clause
.as_ref()
.is_some_and(crate::db::models::sql::where_node::WhereNode::is_empty)
);
}
#[test]
fn set_limit_updates_internal_query_limit() {
let mut compiler = SQLCompiler::new(mock_entity::Entity::find());
compiler.set_limit(25);
assert_eq!(compiler.query.limit, Some(25));
}
#[test]
fn set_offset_updates_internal_query_offset() {
let mut compiler = SQLCompiler::new(mock_entity::Entity::find());
compiler.set_offset(50);
assert_eq!(compiler.query.offset, Some(50));
}
#[test]
fn set_ordering_replaces_internal_orderings() {
let mut compiler = SQLCompiler::new(mock_entity::Entity::find());
compiler.set_ordering(vec![
("name".to_string(), Order::Asc),
("id".to_string(), Order::Desc),
]);
assert_eq!(compiler.query.orderings.len(), 2);
assert!(matches!(compiler.query.orderings[0], (ref field, Order::Asc) if field == "name"));
assert!(matches!(compiler.query.orderings[1], (ref field, Order::Desc) if field == "id"));
}
#[test]
fn as_sql_returns_compiled_select_sql() {
let compiler = SQLCompiler::new(mock_entity::Entity::find().limit(1));
let sql = compiler.as_sql(DatabaseBackend::Sqlite);
assert!(
sql.contains("SELECT"),
"expected SELECT statement, got: {sql}"
);
assert!(
sql.contains("widgets"),
"expected widgets table, got: {sql}"
);
assert!(sql.contains("LIMIT 1"), "expected LIMIT clause, got: {sql}");
}
#[test]
fn compiler_setters_can_be_chained() {
let mut compiler = SQLCompiler::new(mock_entity::Entity::find());
compiler
.set_limit(10)
.set_offset(20)
.set_ordering(vec![("name".to_string(), Order::Asc)]);
assert_eq!(compiler.query.limit, Some(10));
assert_eq!(compiler.query.offset, Some(20));
assert_eq!(compiler.query.orderings.len(), 1);
}
}