Use a new separate crate for uplete (#5848)

* replace lemmy_db_schema::utils::uplete with in-progress crate

* use published diesel-uplete crate

* remove unused tuplex dependency

* move diesel-uplete to dev-dependencies for lemmy_db_views_post

* import table modules in table_impls.rs

* update to new diesel-uplete with renamed stuff
This commit is contained in:
dullbananas 2025-07-14 13:37:11 -07:00 committed by GitHub
parent fe30f30c4e
commit 21c1784a01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 181 additions and 557 deletions

45
Cargo.lock generated
View file

@ -756,7 +756,7 @@ dependencies = [
"cap-primitives",
"cap-std",
"io-lifetimes",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -773,7 +773,7 @@ dependencies = [
"maybe-owned",
"rustix 1.0.7",
"rustix-linux-procfs",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
"winx",
]
@ -1592,6 +1592,16 @@ dependencies = [
"syn 2.0.102",
]
[[package]]
name = "diesel-uplete"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4712bc03fc34bda37e1e46a7028cc444e2488a63cffdd2fe79f3db227e6900d8"
dependencies = [
"diesel",
"tuplex",
]
[[package]]
name = "diesel_derives"
version = "2.2.5"
@ -1894,7 +1904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2016,7 +2026,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78"
dependencies = [
"cfg-if",
"rustix 1.0.7",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2079,7 +2089,7 @@ checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a"
dependencies = [
"io-lifetimes",
"rustix 1.0.7",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2926,7 +2936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65"
dependencies = [
"io-lifetimes",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -3332,6 +3342,7 @@ dependencies = [
"diesel",
"diesel-async",
"diesel-derive-newtype",
"diesel-uplete",
"diesel_ltree",
"futures-util",
"i-love-jesus",
@ -3352,7 +3363,6 @@ dependencies = [
"tokio-postgres-rustls",
"tracing",
"ts-rs",
"tuplex",
"url",
"uuid",
]
@ -3363,6 +3373,7 @@ version = "1.0.0-alpha.5"
dependencies = [
"diesel",
"diesel-derive-enum",
"diesel-uplete",
"diesel_ltree",
"serde",
"strum",
@ -3649,6 +3660,7 @@ dependencies = [
"chrono",
"diesel",
"diesel-async",
"diesel-uplete",
"i-love-jesus",
"lemmy_db_schema",
"lemmy_db_schema_file",
@ -3875,6 +3887,7 @@ dependencies = [
"clokwerk",
"diesel",
"diesel-async",
"diesel-uplete",
"futures",
"futures-util",
"http 1.3.1",
@ -4015,7 +4028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]
@ -5213,7 +5226,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -5663,7 +5676,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -5676,7 +5689,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.9.4",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -6142,7 +6155,7 @@ dependencies = [
"cfg-if",
"libc",
"psm",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -6336,7 +6349,7 @@ dependencies = [
"fd-lock",
"io-lifetimes",
"rustix 0.38.44",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
"winx",
]
@ -6362,7 +6375,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix 1.0.7",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -7730,7 +7743,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@ -8042,7 +8055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
dependencies = [
"bitflags 2.9.1",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]

View file

@ -215,7 +215,6 @@ i-love-jesus = { version = "0.2.0" }
clap = { version = "4.5.39", features = ["derive", "env"] }
pretty_assertions = "1.4.1"
derive-new = "0.7.0"
tuplex = "0.1.2"
html2text = "0.15.1"
async-trait = "0.1.88"
either = { version = "1.15.0", features = ["serde"] }
@ -226,6 +225,7 @@ extism = { git = "https://github.com/extism/extism.git", branch = "main", defaul
] }
extism-convert = { git = "https://github.com/extism/extism.git", branch = "main" }
unified-diff = "0.2.1"
diesel-uplete = { version = "0.2.0" }
[dependencies]
lemmy_api = { workspace = true }

View file

@ -29,13 +29,13 @@ full = [
"serde_json",
"diesel_ltree",
"diesel-async",
"diesel-uplete",
"deadpool",
"tokio",
"tokio-postgres",
"tokio-postgres-rustls",
"rustls",
"i-love-jesus",
"tuplex",
"moka",
"lemmy_db_schema_file/full",
"lemmy_db_schema_setup",
@ -60,6 +60,7 @@ diesel-async = { workspace = true, features = [
"deadpool",
"postgres",
], optional = true }
diesel-uplete = { workspace = true, optional = true }
regex = { workspace = true, optional = true }
diesel_ltree = { workspace = true, optional = true }
tracing = { workspace = true }
@ -73,7 +74,6 @@ rustls = { workspace = true, optional = true }
uuid.workspace = true
i-love-jesus = { workspace = true, optional = true }
derive-new.workspace = true
tuplex = { workspace = true, optional = true }
moka = { workspace = true, optional = true }

View file

@ -13,7 +13,6 @@ use crate::{
utils::{
functions::{coalesce, hot_rank},
get_conn,
uplete,
validate_like,
DbPool,
DELETED_REPLACEMENT_TEXT,
@ -30,6 +29,7 @@ use diesel::{
};
use diesel_async::RunQueryDsl;
use diesel_ltree::Ltree;
use diesel_uplete::{uplete, UpleteCount};
use lemmy_db_schema_file::schema::{comment, comment_actions, community, post};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
@ -287,9 +287,9 @@ impl Likeable for CommentActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
comment_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(comment_actions::table.find((person_id, comment_id)))
uplete(comment_actions::table.find((person_id, comment_id)))
.set_null(comment_actions::like_score)
.set_null(comment_actions::liked_at)
.get_result(conn)
@ -300,10 +300,10 @@ impl Likeable for CommentActions {
async fn remove_all_likes(
pool: &mut DbPool<'_>,
creator_id: PersonId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(comment_actions::table.filter(comment_actions::person_id.eq(creator_id)))
uplete(comment_actions::table.filter(comment_actions::person_id.eq(creator_id)))
.set_null(comment_actions::like_score)
.set_null(comment_actions::liked_at)
.get_result(conn)
@ -315,20 +315,18 @@ impl Likeable for CommentActions {
pool: &mut DbPool<'_>,
creator_id: PersonId,
community_id: CommunityId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let comment_ids =
Comment::creator_comment_ids_in_community(pool, creator_id, community_id).await?;
let conn = &mut get_conn(pool).await?;
uplete::new(
comment_actions::table.filter(comment_actions::comment_id.eq_any(comment_ids.clone())),
)
.set_null(comment_actions::like_score)
.set_null(comment_actions::liked_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)
uplete(comment_actions::table.filter(comment_actions::comment_id.eq_any(comment_ids.clone())))
.set_null(comment_actions::like_score)
.set_null(comment_actions::liked_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)
}
}
@ -346,9 +344,9 @@ impl Saveable for CommentActions {
.await
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)
}
async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(comment_actions::table.find((form.person_id, form.comment_id)))
uplete(comment_actions::table.find((form.person_id, form.comment_id)))
.set_null(comment_actions::saved_at)
.get_result(conn)
.await
@ -385,7 +383,7 @@ mod tests {
post::{Post, PostInsertForm},
},
traits::{Crud, Likeable, Saveable},
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
utils::{build_db_pool_for_tests, RANK_DEFAULT},
};
use diesel_ltree::Ltree;
use lemmy_utils::error::LemmyResult;
@ -499,8 +497,8 @@ mod tests {
format!("0.{}.{}", expected_comment.id, inserted_child_comment.id),
inserted_child_comment.path.0,
);
assert_eq!(uplete::Count::only_updated(1), like_removed);
assert_eq!(uplete::Count::only_deleted(1), saved_removed);
assert_eq!(UpleteCount::only_updated(1), like_removed);
assert_eq!(UpleteCount::only_deleted(1), saved_removed);
assert_eq!(1, num_deleted);
Ok(())

View file

@ -20,7 +20,6 @@ use crate::{
format_actor_url,
functions::{coalesce, coalesce_2_nullable, lower, random_smallint},
get_conn,
uplete,
DbPool,
},
};
@ -36,6 +35,7 @@ use diesel::{
QueryDsl,
};
use diesel_async::RunQueryDsl;
use diesel_uplete::{uplete, UpleteCount};
use lemmy_db_schema_file::{
enums::{CommunityFollowerState, CommunityVisibility, ListingType},
schema::{comment, community, community_actions, instance, post},
@ -101,9 +101,9 @@ impl Joinable for CommunityActions {
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)
}
async fn leave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn leave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.find((form.person_id, form.community_id)))
uplete(community_actions::table.find((form.person_id, form.community_id)))
.set_null(community_actions::became_moderator_at)
.get_result(conn)
.await
@ -311,24 +311,22 @@ impl CommunityActions {
pub async fn delete_mods_for_community(
pool: &mut DbPool<'_>,
for_community_id: CommunityId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(
community_actions::table.filter(community_actions::community_id.eq(for_community_id)),
)
.set_null(community_actions::became_moderator_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
uplete(community_actions::table.filter(community_actions::community_id.eq(for_community_id)))
.set_null(community_actions::became_moderator_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn leave_mod_team_for_all_communities(
pool: &mut DbPool<'_>,
for_person_id: PersonId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.filter(community_actions::person_id.eq(for_person_id)))
uplete(community_actions::table.filter(community_actions::person_id.eq(for_person_id)))
.set_null(community_actions::became_moderator_at)
.get_result(conn)
.await
@ -475,9 +473,9 @@ impl Bannable for CommunityActions {
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)
}
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.find((form.person_id, form.community_id)))
uplete(community_actions::table.find((form.person_id, form.community_id)))
.set_null(community_actions::received_ban_at)
.set_null(community_actions::ban_expires_at)
.get_result(conn)
@ -526,9 +524,9 @@ impl Followable for CommunityActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
community_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.find((person_id, community_id)))
uplete(community_actions::table.find((person_id, community_id)))
.set_null(community_actions::followed_at)
.set_null(community_actions::follow_state)
.set_null(community_actions::follow_approver_id)
@ -561,9 +559,9 @@ impl Blockable for CommunityActions {
async fn unblock(
pool: &mut DbPool<'_>,
community_block_form: &Self::Form,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.find((
uplete(community_actions::table.find((
community_block_form.person_id,
community_block_form.community_id,
)))
@ -696,7 +694,7 @@ mod tests {
post::{Post, PostInsertForm},
},
traits::{Bannable, Crud, Followable, Joinable},
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
utils::{build_db_pool_for_tests, RANK_DEFAULT},
};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
@ -854,9 +852,9 @@ mod tests {
assert_eq!(expected_community, read_community);
assert_eq!(expected_community, updated_community);
assert_eq!(uplete::Count::only_updated(1), ignored_community);
assert_eq!(uplete::Count::only_updated(1), left_community);
assert_eq!(uplete::Count::only_deleted(1), unban);
assert_eq!(UpleteCount::only_updated(1), ignored_community);
assert_eq!(UpleteCount::only_updated(1), left_community);
assert_eq!(UpleteCount::only_deleted(1), unban);
// assert_eq!(2, loaded_count);
assert_eq!(1, num_deleted);

View file

@ -10,7 +10,6 @@ use crate::{
functions::{coalesce, lower},
get_conn,
now,
uplete,
DbPool,
},
};
@ -24,6 +23,7 @@ use diesel::{
SelectableHelper,
};
use diesel_async::RunQueryDsl;
use diesel_uplete::{uplete, UpleteCount};
use lemmy_db_schema_file::schema::{
federation_allowlist,
federation_blocklist,
@ -224,9 +224,9 @@ impl Blockable for InstanceActions {
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)
}
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(instance_actions::table.find((form.person_id, form.instance_id)))
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::blocked_at)
.get_result(conn)
.await
@ -304,10 +304,10 @@ impl Bannable for InstanceActions {
.await?,
)
}
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
Ok(
uplete::new(instance_actions::table.find((form.person_id, form.instance_id)))
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::received_ban_at)
.set_null(instance_actions::ban_expires_at)
.get_result(conn)

View file

@ -11,7 +11,7 @@ use crate::{
PersonUpdateForm,
},
traits::{ApubActor, Blockable, Crud, Followable},
utils::{format_actor_url, functions::lower, get_conn, uplete, DbPool},
utils::{format_actor_url, functions::lower, get_conn, DbPool},
};
use chrono::Utc;
use diesel::{
@ -22,6 +22,7 @@ use diesel::{
QueryDsl,
};
use diesel_async::RunQueryDsl;
use diesel_uplete::{uplete, UpleteCount};
use lemmy_db_schema_file::schema::{
instance,
instance_actions,
@ -249,9 +250,9 @@ impl Followable for PersonActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
target_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(person_actions::table.find((person_id, target_id)))
uplete(person_actions::table.find((person_id, target_id)))
.set_null(person_actions::followed_at)
.set_null(person_actions::follow_pending)
.get_result(conn)
@ -278,9 +279,9 @@ impl Blockable for PersonActions {
.with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)
}
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(person_actions::table.find((form.person_id, form.target_id)))
uplete(person_actions::table.find((form.person_id, form.target_id)))
.set_null(person_actions::blocked_at)
.get_result(conn)
.await
@ -361,9 +362,9 @@ impl PersonActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
target_id: PersonId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(person_actions::table.find((person_id, target_id)))
uplete(person_actions::table.find((person_id, target_id)))
.set_null(person_actions::note)
.set_null(person_actions::noted_at)
.get_result(conn)
@ -462,8 +463,9 @@ mod tests {
post::{Post, PostActions, PostInsertForm, PostLikeForm},
},
traits::{Crud, Followable, Likeable},
utils::{build_db_pool_for_tests, uplete},
utils::build_db_pool_for_tests,
};
use diesel_uplete::UpleteCount;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
@ -547,7 +549,7 @@ mod tests {
let unfollow =
PersonActions::unfollow(pool, follow_form.person_id, follow_form.target_id).await?;
assert_eq!(uplete::Count::only_deleted(1), unfollow);
assert_eq!(UpleteCount::only_deleted(1), unfollow);
Ok(())
}

View file

@ -16,7 +16,6 @@ use crate::{
functions::{coalesce, hot_rank, scaled_rank},
get_conn,
now,
uplete,
validate_like,
DbPool,
DELETED_REPLACEMENT_TEXT,
@ -39,6 +38,7 @@ use diesel::{
QueryDsl,
};
use diesel_async::RunQueryDsl;
use diesel_uplete::{uplete, UpleteCount};
use lemmy_db_schema_file::schema::{community, person, post, post_actions};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
@ -364,9 +364,9 @@ impl Likeable for PostActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
post_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(post_actions::table.find((person_id, post_id)))
uplete(post_actions::table.find((person_id, post_id)))
.set_null(post_actions::like_score)
.set_null(post_actions::liked_at)
.get_result(conn)
@ -377,10 +377,10 @@ impl Likeable for PostActions {
async fn remove_all_likes(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(post_actions::table.filter(post_actions::person_id.eq(person_id)))
uplete(post_actions::table.filter(post_actions::person_id.eq(person_id)))
.set_null(post_actions::like_score)
.set_null(post_actions::liked_at)
.get_result(conn)
@ -392,12 +392,12 @@ impl Likeable for PostActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
community_id: CommunityId,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let post_ids = Post::creator_post_ids_in_community(pool, person_id, community_id).await?;
let conn = &mut get_conn(pool).await?;
uplete::new(post_actions::table.filter(post_actions::post_id.eq_any(post_ids.clone())))
uplete(post_actions::table.filter(post_actions::post_id.eq_any(post_ids.clone())))
.set_null(post_actions::like_score)
.set_null(post_actions::liked_at)
.get_result(conn)
@ -420,9 +420,9 @@ impl Saveable for PostActions {
.await
.with_lemmy_type(LemmyErrorType::CouldntSavePost)
}
async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(post_actions::table.find((form.person_id, form.post_id)))
uplete(post_actions::table.find((form.person_id, form.post_id)))
.set_null(post_actions::saved_at)
.get_result(conn)
.await
@ -437,10 +437,10 @@ impl Readable for PostActions {
Self::mark_many_as_read(pool, std::slice::from_ref(form)).await
}
async fn mark_as_unread(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn mark_as_unread(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(
uplete(
post_actions::table
.filter(post_actions::post_id.eq(form.post_id))
.filter(post_actions::person_id.eq(form.person_id)),
@ -480,10 +480,10 @@ impl Hideable for PostActions {
.with_lemmy_type(LemmyErrorType::CouldntHidePost)
}
async fn unhide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
async fn unhide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(
uplete(
post_actions::table
.filter(post_actions::post_id.eq(form.post_id))
.filter(post_actions::person_id.eq(form.person_id)),
@ -516,10 +516,10 @@ impl ReadComments for PostActions {
pool: &mut DbPool<'_>,
person_id: PersonId,
post_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete::new(
uplete(
post_actions::table
.filter(post_actions::post_id.eq(post_id))
.filter(post_actions::person_id.eq(person_id)),
@ -588,9 +588,10 @@ mod tests {
},
},
traits::{Crud, Likeable, Readable, Saveable},
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
utils::{build_db_pool_for_tests, RANK_DEFAULT},
};
use chrono::DateTime;
use diesel_uplete::UpleteCount;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
@ -708,17 +709,17 @@ mod tests {
assert_eq!(1, scheduled_post_count);
let like_removed = PostActions::remove_like(pool, inserted_person.id, inserted_post.id).await?;
assert_eq!(uplete::Count::only_updated(1), like_removed);
assert_eq!(UpleteCount::only_updated(1), like_removed);
let saved_removed = PostActions::unsave(pool, &post_saved_form).await?;
assert_eq!(uplete::Count::only_updated(1), saved_removed);
assert_eq!(UpleteCount::only_updated(1), saved_removed);
let read_remove_form_1 = PostReadForm::new(inserted_post.id, inserted_person.id);
let read_removed_1 = PostActions::mark_as_unread(pool, &read_remove_form_1).await?;
assert_eq!(uplete::Count::only_deleted(1), read_removed_1);
assert_eq!(UpleteCount::only_deleted(1), read_removed_1);
let read_remove_form_2 = PostReadForm::new(inserted_post2.id, inserted_person.id);
let read_removed_2 = PostActions::mark_as_unread(pool, &read_remove_form_2).await?;
assert_eq!(uplete::Count::only_deleted(1), read_removed_2);
assert_eq!(UpleteCount::only_deleted(1), read_removed_2);
let num_deleted = Post::delete(pool, inserted_post.id).await?
+ Post::delete(pool, inserted_post2.id).await?

View file

@ -1,6 +1,6 @@
use crate::{
newtypes::{CommunityId, DbUrl, PaginationCursor, PersonId},
utils::{get_conn, uplete, DbPool},
utils::{get_conn, DbPool},
};
use diesel::{
associations::HasTable,
@ -14,6 +14,7 @@ use diesel_async::{
AsyncPgConnection,
RunQueryDsl,
};
use diesel_uplete::UpleteCount;
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
settings::structs::Settings,
@ -106,7 +107,7 @@ pub trait Followable {
pool: &mut DbPool<'_>,
person_id: PersonId,
item_id: Self::IdType,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -122,7 +123,7 @@ pub trait Joinable {
fn leave(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -140,14 +141,14 @@ pub trait Likeable {
pool: &mut DbPool<'_>,
person_id: PersonId,
item_id: Self::IdType,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
fn remove_all_likes(
pool: &mut DbPool<'_>,
creator_id: PersonId,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
@ -155,7 +156,7 @@ pub trait Likeable {
pool: &mut DbPool<'_>,
creator_id: PersonId,
community_id: CommunityId,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -171,7 +172,7 @@ pub trait Bannable {
fn unban(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -187,7 +188,7 @@ pub trait Saveable {
fn unsave(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -209,7 +210,7 @@ pub trait Readable {
fn mark_as_unread(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -227,7 +228,7 @@ pub trait ReadComments {
pool: &mut DbPool<'_>,
person_id: PersonId,
item_id: Self::IdType,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -243,7 +244,7 @@ pub trait Hideable {
fn unhide(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
}
@ -261,7 +262,7 @@ pub trait Blockable {
fn unblock(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
) -> impl Future<Output = LemmyResult<UpleteCount>> + Send
where
Self: Sized;
fn read_block(

View file

@ -1,5 +1,4 @@
pub mod queries;
pub mod uplete;
use crate::newtypes::DbUrl;
use chrono::TimeDelta;

View file

@ -1,426 +0,0 @@
use diesel::{
associations::HasTable,
dsl,
expression::{is_aggregate, ValidGrouping},
pg::Pg,
query_builder::{AsQuery, AstPass, Query, QueryFragment, QueryId},
query_dsl::methods::{FilterDsl, SelectDsl},
result::Error,
sql_types,
Column,
Expression,
Table,
};
use std::any::TypeId;
use tuplex::IntoArray;
/// Set columns (each specified with `UpleteBuilder::set_null`) to null in the rows found by
/// `query`, and delete rows that have no remaining non-null values outside of the primary key
pub fn new<Q>(query: Q) -> UpleteBuilder<dsl::Select<Q::Query, <Q::Table as Table>::PrimaryKey>>
where
Q: AsQuery + HasTable,
Q::Table: Default,
Q::Query: SelectDsl<<Q::Table as Table>::PrimaryKey>,
// For better error messages
UpleteBuilder<Q>: AsQuery,
{
UpleteBuilder {
query: query.as_query().select(Q::Table::default().primary_key()),
set_null_columns: Vec::new(),
}
}
pub struct UpleteBuilder<Q> {
query: Q,
set_null_columns: Vec<DynColumn>,
}
impl<Q: HasTable> UpleteBuilder<Q> {
pub fn set_null<C: Column<Table = Q::Table> + Into<DynColumn>>(mut self, column: C) -> Self {
self.set_null_columns.push(column.into());
self
}
}
impl<Q> AsQuery for UpleteBuilder<Q>
where
Q: HasTable,
Q::Table: Default + QueryFragment<Pg> + Send + 'static,
<Q::Table as Table>::PrimaryKey: IntoArray<DynColumn> + QueryFragment<Pg> + Send + 'static,
<Q::Table as Table>::AllColumns: IntoArray<DynColumn>,
<<Q::Table as Table>::PrimaryKey as IntoArray<DynColumn>>::Output: IntoIterator<Item = DynColumn>,
<<Q::Table as Table>::AllColumns as IntoArray<DynColumn>>::Output: IntoIterator<Item = DynColumn>,
Q: Clone + FilterDsl<AllNull> + FilterDsl<dsl::not<AllNull>>,
dsl::Filter<Q, AllNull>: QueryFragment<Pg> + Send + 'static,
dsl::Filter<Q, dsl::not<AllNull>>: QueryFragment<Pg> + Send + 'static,
{
type Query = UpleteQuery;
type SqlType = (sql_types::BigInt, sql_types::BigInt);
fn as_query(self) -> Self::Query {
let table = Q::Table::default;
let deletion_condition = AllNull(
Q::Table::all_columns()
.into_array()
.into_iter()
.filter(|c: &DynColumn| {
table()
.primary_key()
.into_array()
.into_iter()
.chain(self.set_null_columns.iter().cloned())
.all(|excluded_column| excluded_column.type_id != c.type_id)
})
.collect::<Vec<_>>(),
);
UpleteQuery {
// Updated rows and deleted rows must not overlap, so updating all rows and using the returned
// new rows to determine which ones to delete is not an option.
//
// https://www.postgresql.org/docs/16/queries-with.html#QUERIES-WITH-MODIFYING
//
// "Trying to update the same row twice in a single statement is not supported. Only one of
// the modifications takes place, but it is not easy (and sometimes not possible) to reliably
// predict which one. This also applies to deleting a row that was already updated in the same
// statement: only the update is performed."
update_subquery: Box::new(
self
.query
.clone()
.filter(dsl::not(deletion_condition.clone())),
),
delete_subquery: Box::new(self.query.filter(deletion_condition)),
table: Box::new(table()),
primary_key: Box::new(table().primary_key()),
set_null_columns: self.set_null_columns,
}
}
}
pub struct UpleteQuery {
update_subquery: Box<dyn QueryFragment<Pg> + Send + 'static>,
delete_subquery: Box<dyn QueryFragment<Pg> + Send + 'static>,
table: Box<dyn QueryFragment<Pg> + Send + 'static>,
primary_key: Box<dyn QueryFragment<Pg> + Send + 'static>,
set_null_columns: Vec<DynColumn>,
}
impl QueryId for UpleteQuery {
type QueryId = ();
const HAS_STATIC_QUERY_ID: bool = false;
}
impl Query for UpleteQuery {
type SqlType = (sql_types::BigInt, sql_types::BigInt);
}
impl QueryFragment<Pg> for UpleteQuery {
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> Result<(), Error> {
assert_ne!(self.set_null_columns.len(), 0, "`set_null` was not called");
// This is checked by require_uplete triggers
out.push_sql("/**/");
// Declare `update_keys` and `delete_keys` CTEs, which select primary keys
for (prefix, subquery) in [
("WITH update_keys", &self.update_subquery),
(", delete_keys", &self.delete_subquery),
] {
out.push_sql(prefix);
out.push_sql(" AS (");
subquery.walk_ast(out.reborrow())?;
out.push_sql(" FOR UPDATE)");
}
// Update rows that are referenced in `update_keys`
out.push_sql(", update_result AS (UPDATE ");
self.table.walk_ast(out.reborrow())?;
let mut item_prefix = " SET ";
for column in &self.set_null_columns {
out.push_sql(item_prefix);
out.push_identifier(column.name)?;
out.push_sql(" = NULL");
item_prefix = ",";
}
out.push_sql(" WHERE (");
self.primary_key.walk_ast(out.reborrow())?;
out.push_sql(") = ANY (SELECT * FROM update_keys) RETURNING 1)");
// Delete rows that are referenced in `delete_keys`
out.push_sql(", delete_result AS (DELETE FROM ");
self.table.walk_ast(out.reborrow())?;
out.push_sql(" WHERE (");
self.primary_key.walk_ast(out.reborrow())?;
out.push_sql(") = ANY (SELECT * FROM delete_keys) RETURNING 1)");
// Count updated rows and deleted rows (`RETURNING 1` makes this possible)
out.push_sql(" SELECT (SELECT count(*) FROM update_result)");
out.push_sql(", (SELECT count(*) FROM delete_result)");
Ok(())
}
}
// Types other than `DynColumn` are only used in tests
#[derive(Clone)]
pub struct AllNull<T = DynColumn>(Vec<T>);
impl<T> Expression for AllNull<T> {
type SqlType = sql_types::Bool;
}
impl<T> ValidGrouping<()> for AllNull<T> {
type IsAggregate = is_aggregate::No;
}
impl<T: QueryFragment<Pg>> QueryFragment<Pg> for AllNull<T> {
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> Result<(), Error> {
// Must produce a valid expression even if `self.0` is empty
out.push_sql("(TRUE");
for item in &self.0 {
out.push_sql(" AND (");
item.walk_ast(out.reborrow())?;
out.push_sql(" IS NULL)");
}
out.push_sql(")");
Ok(())
}
}
#[derive(Clone)]
pub struct DynColumn {
type_id: TypeId,
name: &'static str,
}
impl<T: Column + 'static> From<T> for DynColumn {
fn from(_value: T) -> Self {
DynColumn {
type_id: TypeId::of::<T>(),
name: T::NAME,
}
}
}
impl QueryFragment<Pg> for DynColumn {
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> Result<(), Error> {
out.push_identifier(self.name)
}
}
#[derive(Queryable, PartialEq, Eq, Debug)]
pub struct Count {
pub updated: i64,
pub deleted: i64,
}
impl Count {
pub fn only_updated(n: i64) -> Self {
Count {
updated: n,
deleted: 0,
}
}
pub fn only_deleted(n: i64) -> Self {
Count {
updated: 0,
deleted: n,
}
}
}
#[cfg(test)]
mod tests {
use super::AllNull;
use crate::utils::{build_db_pool_for_tests, get_conn, DbConn};
use diesel::{
debug_query,
insert_into,
pg::Pg,
query_builder::{AsQuery, QueryId},
select,
sql_types,
AppearsOnTable,
ExpressionMethods,
IntoSql,
QueryDsl,
SelectableExpression,
};
use diesel_async::{RunQueryDsl, SimpleAsyncConnection};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
impl<T, QS> AppearsOnTable<QS> for AllNull<T> {}
impl<T, QS> SelectableExpression<QS> for AllNull<T> {}
impl<T> QueryId for AllNull<T> {
type QueryId = ();
const HAS_STATIC_QUERY_ID: bool = false;
}
diesel::table! {
t (id1, id2) {
// uplete doesn't work for non-tuple primary key
id1 -> Int4,
id2 -> Int4,
a -> Nullable<Int4>,
b -> Nullable<Int4>,
}
}
async fn expect_rows(
conn: &mut DbConn<'_>,
expected: &[(Option<i32>, Option<i32>)],
) -> LemmyResult<()> {
let rows: Vec<(Option<i32>, Option<i32>)> = t::table
.select((t::a, t::b))
.order_by(t::id1)
.load(conn)
.await?;
assert_eq!(expected, &rows);
Ok(())
}
// Main purpose of this test is to check accuracy of the returned `Count`, which other modules'
// tests rely on
#[tokio::test]
#[serial]
async fn test_count() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests();
let pool = &mut pool.into();
let mut conn = get_conn(pool).await?;
conn
.batch_execute("CREATE TABLE t (id1 serial, id2 int NOT NULL DEFAULT 1, a int, b int, PRIMARY KEY (id1, id2));")
.await?;
expect_rows(&mut conn, &[]).await?;
insert_into(t::table)
.values(&[
(t::a.eq(Some(1)), t::b.eq(Some(2))),
(t::a.eq(Some(3)), t::b.eq(None)),
(t::a.eq(Some(4)), t::b.eq(Some(5))),
])
.execute(&mut conn)
.await?;
expect_rows(
&mut conn,
&[(Some(1), Some(2)), (Some(3), None), (Some(4), Some(5))],
)
.await?;
let count1 = super::new(t::table)
.set_null(t::a)
.get_result(&mut conn)
.await?;
assert_eq!(
super::Count {
updated: 2,
deleted: 1
},
count1
);
expect_rows(&mut conn, &[(None, Some(2)), (None, Some(5))]).await?;
let count2 = super::new(t::table)
.set_null(t::b)
.get_result(&mut conn)
.await?;
assert_eq!(super::Count::only_deleted(2), count2);
expect_rows(&mut conn, &[]).await?;
conn.batch_execute("DROP TABLE t;").await?;
Ok(())
}
fn expected_sql(check_null: &str, set_null: &str) -> String {
let with_queries = {
let key = r#""t"."id1", "t"."id2""#;
let t = r#""t""#;
let update_keys = format!("SELECT {key} FROM {t} WHERE NOT (({check_null})) FOR UPDATE");
let delete_keys = format!("SELECT {key} FROM {t} WHERE ({check_null}) FOR UPDATE");
let update_result = format!(
"UPDATE {t} SET {set_null} WHERE ({key}) = ANY (SELECT * FROM update_keys) RETURNING 1"
);
let delete_result =
format!("DELETE FROM {t} WHERE ({key}) = ANY (SELECT * FROM delete_keys) RETURNING 1");
format!("update_keys AS ({update_keys}), delete_keys AS ({delete_keys}), update_result AS ({update_result}), delete_result AS ({delete_result})")
};
let update_count = "SELECT count(*) FROM update_result";
let delete_count = "SELECT count(*) FROM delete_result";
format!(r#"/**/WITH {with_queries} SELECT ({update_count}), ({delete_count}) -- binds: []"#)
}
#[test]
fn test_generated_sql() {
// Unlike the `get_result` method, `debug_query` does not automatically call `as_query`
assert_eq!(
debug_query::<Pg, _>(&super::new(t::table).set_null(t::b).as_query()).to_string(),
expected_sql(r#"TRUE AND ("a" IS NULL)"#, r#""b" = NULL"#)
);
assert_eq!(
debug_query::<Pg, _>(
&super::new(t::table)
.set_null(t::a)
.set_null(t::b)
.as_query()
)
.to_string(),
expected_sql(r#"TRUE"#, r#""a" = NULL,"b" = NULL"#)
);
}
#[test]
fn test_count_methods() {
assert_eq!(
super::Count::only_updated(1),
super::Count {
updated: 1,
deleted: 0
}
);
assert_eq!(
super::Count::only_deleted(1),
super::Count {
updated: 0,
deleted: 1
}
);
}
#[tokio::test]
#[serial]
async fn test_all_null() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests();
let pool = &mut pool.into();
let mut conn = get_conn(pool).await?;
let some = Some(1).into_sql::<sql_types::Nullable<sql_types::Integer>>();
let none = None::<i32>.into_sql::<sql_types::Nullable<sql_types::Integer>>();
// Allows type inference for `vec![]`
let mut all_null = |items| select(AllNull(items)).get_result::<bool>(&mut conn);
assert!(all_null(vec![]).await?);
assert!(all_null(vec![none]).await?);
assert!(all_null(vec![none, none]).await?);
assert!(all_null(vec![none, none, none]).await?);
assert!(!all_null(vec![some]).await?);
assert!(!all_null(vec![some, none]).await?);
assert!(!all_null(vec![none, some, none]).await?);
Ok(())
}
}

View file

@ -18,7 +18,7 @@ doctest = false
workspace = true
[features]
full = ["diesel", "diesel_ltree", "diesel-derive-enum"]
full = ["diesel", "diesel_ltree", "diesel-derive-enum", "diesel-uplete"]
ts-rs = ["dep:ts-rs"]
[dependencies]
@ -28,3 +28,4 @@ diesel = { workspace = true, optional = true }
diesel_ltree = { workspace = true, optional = true }
ts-rs = { workspace = true, optional = true }
diesel-derive-enum = { workspace = true, optional = true }
diesel-uplete = { workspace = true, optional = true }

View file

@ -1,3 +1,5 @@
pub mod enums;
#[cfg(feature = "full")]
pub mod schema;
#[cfg(feature = "full")]
pub mod table_impls;

View file

@ -0,0 +1,35 @@
use crate::schema::{
comment_actions,
community_actions,
instance_actions,
person_actions,
post_actions,
};
impl diesel_uplete::SupportedTable for comment_actions::table {
type Key = (comment_actions::person_id, comment_actions::comment_id);
type AdditionalIgnoredColumns = ();
}
impl diesel_uplete::SupportedTable for community_actions::table {
type Key = (
community_actions::person_id,
community_actions::community_id,
);
type AdditionalIgnoredColumns = ();
}
impl diesel_uplete::SupportedTable for instance_actions::table {
type Key = (instance_actions::person_id, instance_actions::instance_id);
type AdditionalIgnoredColumns = ();
}
impl diesel_uplete::SupportedTable for person_actions::table {
type Key = (person_actions::person_id, person_actions::target_id);
type AdditionalIgnoredColumns = ();
}
impl diesel_uplete::SupportedTable for post_actions::table {
type Key = (post_actions::person_id, post_actions::post_id);
type AdditionalIgnoredColumns = ();
}

View file

@ -56,3 +56,4 @@ tokio = { workspace = true }
pretty_assertions = { workspace = true }
url = { workspace = true }
test-context = "0.4.1"
diesel-uplete.workspace = true

View file

@ -566,6 +566,7 @@ mod tests {
};
use chrono::Utc;
use diesel_async::SimpleAsyncConnection;
use diesel_uplete::UpleteCount;
use lemmy_db_schema::{
impls::actor_language::UNDETERMINED_ID,
newtypes::LanguageId,
@ -604,7 +605,7 @@ mod tests {
},
test_data::TestData,
traits::{Bannable, Blockable, Crud, Followable, Hideable, Joinable, Likeable, Readable},
utils::{build_db_pool, get_conn, uplete, ActualDbPool, DbPool},
utils::{build_db_pool, get_conn, ActualDbPool, DbPool},
};
use lemmy_db_schema_file::enums::{CommunityFollowerState, CommunityVisibility, ListingType};
use lemmy_db_views_local_user::LocalUserView;
@ -1012,7 +1013,7 @@ mod tests {
);
let like_removed = PostActions::remove_like(pool, data.tegan.person.id, data.post.id).await?;
assert_eq!(uplete::Count::only_deleted(1), like_removed);
assert_eq!(UpleteCount::only_deleted(1), like_removed);
Ok(())
}
@ -1058,7 +1059,7 @@ mod tests {
)
.await?;
assert_eq!(uplete::Count::only_deleted(1), note_removed);
assert_eq!(UpleteCount::only_deleted(1), note_removed);
assert!(post_listing.person_actions.is_none());
Ok(())
@ -1151,7 +1152,7 @@ mod tests {
// Remove the like
let like_removed =
PostActions::remove_like(pool, data.tegan.person.id, data.bot_post.id).await?;
assert_eq!(uplete::Count::only_deleted(1), like_removed);
assert_eq!(UpleteCount::only_deleted(1), like_removed);
let person_like_removed =
PersonActions::remove_like(pool, data.tegan.person.id, data.bot.person.id, 1).await?;
@ -1211,7 +1212,7 @@ mod tests {
let like_removed =
PostActions::remove_like(pool, data.tegan.person.id, data.bot_post.id).await?;
assert_eq!(uplete::Count::only_deleted(1), like_removed);
assert_eq!(UpleteCount::only_deleted(1), like_removed);
Ok(())
}

View file

@ -57,6 +57,7 @@ actix-web-prom = "0.10.0"
actix-cors = "0.7.1"
rand = "0.9.1"
percent-encoding = "2.3.1"
diesel-uplete.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true

View file

@ -14,6 +14,7 @@ use diesel::{
QueryableByName,
};
use diesel_async::{AsyncPgConnection, RunQueryDsl};
use diesel_uplete::uplete;
use lemmy_api_utils::{
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
@ -27,7 +28,7 @@ use lemmy_db_schema::{
post::{Post, PostUpdateForm},
},
traits::Crud,
utils::{functions::coalesce, get_conn, now, uplete, DbPool, DELETED_REPLACEMENT_TEXT},
utils::{functions::coalesce, get_conn, now, DbPool, DELETED_REPLACEMENT_TEXT},
};
use lemmy_db_schema_file::schema::{
captcha_answer,
@ -394,23 +395,19 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Updating banned column if it expires ...");
let mut conn = get_conn(pool).await?;
uplete::new(
community_actions::table.filter(community_actions::ban_expires_at.lt(now().nullable())),
)
.set_null(community_actions::received_ban_at)
.set_null(community_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.await?;
uplete(community_actions::table.filter(community_actions::ban_expires_at.lt(now().nullable())))
.set_null(community_actions::received_ban_at)
.set_null(community_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.await?;
uplete::new(
instance_actions::table.filter(instance_actions::ban_expires_at.lt(now().nullable())),
)
.set_null(instance_actions::received_ban_at)
.set_null(instance_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.await?;
uplete(instance_actions::table.filter(instance_actions::ban_expires_at.lt(now().nullable())))
.set_null(instance_actions::received_ban_at)
.set_null(instance_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.await?;
Ok(())
}