use crate as toasty;
use crate::engine::simplify::Simplify;
use crate::schema::Register;
use toasty_core::{
driver::Capability,
schema::{Builder, app, app::FieldId, app::ModelId},
stmt::{self, Association, Expr, ExprInSubquery, Path, Query, SourceModel, Value},
};
#[allow(dead_code)]
#[derive(toasty::Model)]
struct User {
#[key]
id: i64,
#[has_many(pair = author)]
posts: toasty::HasMany<Post>,
}
#[allow(dead_code)]
#[derive(toasty::Model)]
struct Post {
#[key]
id: i64,
#[index]
user_id: i64,
#[belongs_to(key = user_id, references = id)]
author: toasty::BelongsTo<User>,
}
struct UserPostSchema {
schema: toasty_core::Schema,
user_model: ModelId,
user_id: FieldId,
user_posts: FieldId,
post_model: ModelId,
post_author: FieldId,
}
impl UserPostSchema {
fn new() -> Self {
let app_schema = app::Schema::from_macro([User::schema(), Post::schema()])
.expect("schema should build from macro");
let schema = Builder::new()
.build(app_schema, &Capability::SQLITE)
.expect("schema should build");
let user_model = User::id();
let post_model = Post::id();
let user_id = schema
.app
.model(user_model)
.as_root_unwrap()
.field_by_name("id")
.unwrap()
.id;
let user_posts = schema
.app
.model(user_model)
.as_root_unwrap()
.field_by_name("posts")
.unwrap()
.id;
let post_author = schema
.app
.model(post_model)
.as_root_unwrap()
.field_by_name("author")
.unwrap()
.id;
Self {
schema,
user_model,
user_id,
user_posts,
post_model,
post_author,
}
}
}
#[test]
fn has_many_via_becomes_in_subquery() {
let s = UserPostSchema::new();
let mut simplify = Simplify::new(&s.schema);
let user_filter = Expr::eq(
Expr::ref_self_field(s.user_id),
Expr::Value(Value::from(42i64)),
);
let user_query = Query::new_select(s.user_model, user_filter);
let association = Association {
source: Box::new(user_query),
path: Path::field(s.user_model, s.user_posts.index),
};
let mut query = Query::new_select(s.post_model, Expr::Value(Value::Bool(true)));
if let stmt::ExprSet::Select(select) = &mut query.body
&& let stmt::Source::Model(model) = &mut select.source
{
model.via = Some(association);
}
simplify.simplify_via_association_for_query(&mut query);
let stmt::ExprSet::Select(select) = &query.body else {
panic!("expected Select");
};
assert!(matches!(
&select.source,
stmt::Source::Model(SourceModel { via: None, .. })
));
let filter_expr = select.filter.as_expr();
let Expr::InSubquery(ExprInSubquery {
expr,
query: subquery,
}) = filter_expr
else {
panic!("expected filter expression to be an `Expr::InSubquery`");
};
assert!(matches!(
&**expr,
Expr::Reference(stmt::ExprReference::Field { index, .. }) if *index == s.post_author.index
));
let stmt::ExprSet::Select(select) = &subquery.body else {
panic!("expected subquery body to be a `ExprSet::Select`");
};
assert!(matches!(
&select.source,
stmt::Source::Model(SourceModel { id, .. }) if *id == s.user_model
));
}