use rust_ef::db_context::{DbContext, DbContextOptionsBuilder};
use rust_ef::prelude::*;
use rust_ef_sqlite::DbContextOptionsBuilderExt as _;
#[derive(Debug, Clone, EntityType)]
#[table("in_sub_blogs")]
struct SubBlog {
#[primary_key]
#[auto_increment]
blog_id: i32,
#[required]
url: String,
#[navigation]
posts: HasMany<SubPost>,
}
#[derive(Debug, Clone, EntityType)]
#[table("in_sub_posts")]
struct SubPost {
#[primary_key]
#[auto_increment]
post_id: i32,
#[required]
title: String,
#[foreign_key(SubBlog)]
blog_id: i32,
#[navigation]
blog: BelongsTo<SubBlog>,
}
fn build_ctx() -> DbContext {
let mut builder = DbContextOptionsBuilder::new();
builder.use_sqlite_in_memory();
let options = builder.build();
DbContext::from_options(&options).expect("DbContext")
}
async fn seed(ctx: &mut DbContext) {
ctx.set::<SubBlog>();
ctx.set::<SubPost>();
ctx.ensure_created().await.unwrap();
ctx.set::<SubBlog>().add(SubBlog {
blog_id: 0,
url: "https://a.example".into(),
posts: HasMany::new(),
});
ctx.set::<SubBlog>().add(SubBlog {
blog_id: 0,
url: "https://b.example".into(),
posts: HasMany::new(),
});
ctx.save_changes().await.unwrap();
let blogs = ctx.set::<SubBlog>().query().to_list().await.unwrap();
let blog_a = blogs[0].blog_id;
ctx.set::<SubPost>().add(SubPost {
post_id: 0,
title: "A1".into(),
blog_id: blog_a,
blog: BelongsTo::new(),
});
ctx.set::<SubPost>().add(SubPost {
post_id: 0,
title: "A2".into(),
blog_id: blog_a,
blog: BelongsTo::new(),
});
ctx.save_changes().await.unwrap();
}
#[tokio::test]
async fn test_in_subquery_form_a() {
let mut ctx = build_ctx();
seed(&mut ctx).await;
let blogs = linq!(ctx.set::<SubBlog>(), |b: SubBlog| b
.blog_id
.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert_eq!(blogs.len(), 1);
assert_eq!(blogs[0].url, "https://a.example");
}
#[tokio::test]
async fn test_not_in_subquery_form_a() {
let mut ctx = build_ctx();
seed(&mut ctx).await;
let blogs = linq!(ctx.set::<SubBlog>(), |b: SubBlog| !b
.blog_id
.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert_eq!(blogs.len(), 1);
assert_eq!(blogs[0].url, "https://b.example");
}
#[tokio::test]
async fn test_in_subquery_combined_with_other_filter() {
let mut ctx = build_ctx();
seed(&mut ctx).await;
let blogs = linq!(ctx.set::<SubBlog>(), |b: SubBlog| b.url
== "https://a.example"
&& b.blog_id.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert_eq!(blogs.len(), 1);
assert_eq!(blogs[0].url, "https://a.example");
let blogs2 = linq!(ctx.set::<SubBlog>(), |b: SubBlog| b.url
== "https://b.example"
&& b.blog_id.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert!(blogs2.is_empty());
}
#[tokio::test]
async fn test_in_subquery_form_c_filter() {
let mut ctx = build_ctx();
seed(&mut ctx).await;
ctx.set::<SubBlog>()
.set_query_filter(linq!(filter |b: SubBlog| b.blog_id.in_subquery(|p: SubPost| p.blog_id)));
let blogs = ctx.set::<SubBlog>().query().to_list().await.unwrap();
assert_eq!(blogs.len(), 1);
assert_eq!(blogs[0].url, "https://a.example");
}
#[tokio::test]
async fn test_in_subquery_empty_subquery_result() {
let mut ctx = build_ctx();
ctx.set::<SubBlog>();
ctx.set::<SubPost>();
ctx.ensure_created().await.unwrap();
ctx.set::<SubBlog>().add(SubBlog {
blog_id: 0,
url: "https://empty.example".into(),
posts: HasMany::new(),
});
ctx.save_changes().await.unwrap();
let blogs = linq!(ctx.set::<SubBlog>(), |b: SubBlog| b
.blog_id
.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert!(blogs.is_empty());
}
#[tokio::test]
async fn test_not_in_subquery_empty_subquery_result() {
let mut ctx = build_ctx();
ctx.set::<SubBlog>();
ctx.set::<SubPost>();
ctx.ensure_created().await.unwrap();
ctx.set::<SubBlog>().add(SubBlog {
blog_id: 0,
url: "https://empty.example".into(),
posts: HasMany::new(),
});
ctx.save_changes().await.unwrap();
let blogs = linq!(ctx.set::<SubBlog>(), |b: SubBlog| !b
.blog_id
.in_subquery(|p: SubPost| p.blog_id))
.to_list()
.await
.unwrap();
assert_eq!(blogs.len(), 1);
assert_eq!(blogs[0].url, "https://empty.example");
}