diff --git a/crates/db_schema/src/aggregates/comment_aggregates.rs b/crates/db_schema/src/aggregates/comment_aggregates.rs
index e081d1a1ece3c705c1a70250998de605c89bc219..110532c5ab4cb6908671627bae8ddf6e53245dee 100644
--- a/crates/db_schema/src/aggregates/comment_aggregates.rs
+++ b/crates/db_schema/src/aggregates/comment_aggregates.rs
@@ -11,7 +11,7 @@ impl CommentAggregates {
   pub async fn read(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     comment_aggregates::table
-      .filter(comment_aggregates::comment_id.eq(comment_id))
+      .find(comment_id)
       .first::<Self>(conn)
       .await
   }
@@ -22,8 +22,7 @@ impl CommentAggregates {
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
 
-    diesel::update(comment_aggregates::table)
-      .filter(comment_aggregates::comment_id.eq(comment_id))
+    diesel::update(comment_aggregates::table.find(comment_id))
       .set(comment_aggregates::hot_rank.eq(hot_rank(
         comment_aggregates::score,
         comment_aggregates::published,
diff --git a/crates/db_schema/src/aggregates/community_aggregates.rs b/crates/db_schema/src/aggregates/community_aggregates.rs
index e968fdb08b1a4be7694587d6a12b8115323585aa..f61a95c7d418f6e193e584bd4126eae579b8ca70 100644
--- a/crates/db_schema/src/aggregates/community_aggregates.rs
+++ b/crates/db_schema/src/aggregates/community_aggregates.rs
@@ -1,10 +1,7 @@
 use crate::{
   aggregates::structs::CommunityAggregates,
   newtypes::CommunityId,
-  schema::{
-    community_aggregates,
-    community_aggregates::{community_id, subscribers},
-  },
+  schema::{community_aggregates, community_aggregates::subscribers},
   utils::{get_conn, DbPool},
 };
 use diesel::{result::Error, ExpressionMethods, QueryDsl};
@@ -14,7 +11,7 @@ impl CommunityAggregates {
   pub async fn read(pool: &mut DbPool<'_>, for_community_id: CommunityId) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     community_aggregates::table
-      .filter(community_id.eq(for_community_id))
+      .find(for_community_id)
       .first::<Self>(conn)
       .await
   }
@@ -26,7 +23,7 @@ impl CommunityAggregates {
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     let new_subscribers: i64 = new_subscribers.into();
-    diesel::update(community_aggregates::table.filter(community_id.eq(for_community_id)))
+    diesel::update(community_aggregates::table.find(for_community_id))
       .set(subscribers.eq(new_subscribers))
       .get_result::<Self>(conn)
       .await
diff --git a/crates/db_schema/src/aggregates/person_aggregates.rs b/crates/db_schema/src/aggregates/person_aggregates.rs
index e6195de28560def1921f7514688da76dd1b502c1..d3065daf390921aa154ffb29a9376890f1f77d77 100644
--- a/crates/db_schema/src/aggregates/person_aggregates.rs
+++ b/crates/db_schema/src/aggregates/person_aggregates.rs
@@ -4,14 +4,14 @@ use crate::{
   schema::person_aggregates,
   utils::{get_conn, DbPool},
 };
-use diesel::{result::Error, ExpressionMethods, QueryDsl};
+use diesel::{result::Error, QueryDsl};
 use diesel_async::RunQueryDsl;
 
 impl PersonAggregates {
   pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     person_aggregates::table
-      .filter(person_aggregates::person_id.eq(person_id))
+      .find(person_id)
       .first::<Self>(conn)
       .await
   }
diff --git a/crates/db_schema/src/aggregates/person_post_aggregates.rs b/crates/db_schema/src/aggregates/person_post_aggregates.rs
index 1cbaa242215b4cd1df4c8a010171442ab0e80a95..7657dae9e001930dc2ccc1c747b7e4320d520ec1 100644
--- a/crates/db_schema/src/aggregates/person_post_aggregates.rs
+++ b/crates/db_schema/src/aggregates/person_post_aggregates.rs
@@ -1,11 +1,10 @@
 use crate::{
   aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
-  diesel::BoolExpressionMethods,
   newtypes::{PersonId, PostId},
   schema::person_post_aggregates::dsl::{person_id, person_post_aggregates, post_id},
   utils::{get_conn, DbPool},
 };
-use diesel::{insert_into, result::Error, ExpressionMethods, QueryDsl};
+use diesel::{insert_into, result::Error, QueryDsl};
 use diesel_async::RunQueryDsl;
 
 impl PersonPostAggregates {
@@ -29,7 +28,7 @@ impl PersonPostAggregates {
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     person_post_aggregates
-      .filter(post_id.eq(post_id_).and(person_id.eq(person_id_)))
+      .find((person_id_, post_id_))
       .first::<Self>(conn)
       .await
   }
diff --git a/crates/db_schema/src/aggregates/post_aggregates.rs b/crates/db_schema/src/aggregates/post_aggregates.rs
index 1816ee5f3f6535d812889d210bb2d8684cca9ba5..a8efe95811a344cc7e47a7a1371be8a62e9d8882 100644
--- a/crates/db_schema/src/aggregates/post_aggregates.rs
+++ b/crates/db_schema/src/aggregates/post_aggregates.rs
@@ -15,7 +15,7 @@ impl PostAggregates {
   pub async fn read(pool: &mut DbPool<'_>, post_id: PostId) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     post_aggregates::table
-      .filter(post_aggregates::post_id.eq(post_id))
+      .find(post_id)
       .first::<Self>(conn)
       .await
   }
@@ -33,8 +33,7 @@ impl PostAggregates {
       .first::<i64>(conn)
       .await?;
 
-    diesel::update(post_aggregates::table)
-      .filter(post_aggregates::post_id.eq(post_id))
+    diesel::update(post_aggregates::table.find(post_id))
       .set((
         post_aggregates::hot_rank.eq(hot_rank(post_aggregates::score, post_aggregates::published)),
         post_aggregates::hot_rank_active.eq(hot_rank(
diff --git a/crates/db_schema/src/aggregates/structs.rs b/crates/db_schema/src/aggregates/structs.rs
index e01e6d984323f049cbe434b47787a7077d14f768..24b2d82c54875d0b1fe5dd71ef0373399d6f908e 100644
--- a/crates/db_schema/src/aggregates/structs.rs
+++ b/crates/db_schema/src/aggregates/structs.rs
@@ -16,10 +16,10 @@ use ts_rs::TS;
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = comment_aggregates))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
+#[cfg_attr(feature = "full", diesel(primary_key(comment_id)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// Aggregate data for a comment.
 pub struct CommentAggregates {
-  pub id: i32,
   pub comment_id: CommentId,
   pub score: i64,
   pub upvotes: i64,
@@ -40,10 +40,10 @@ pub struct CommentAggregates {
   feature = "full",
   diesel(belongs_to(crate::source::community::Community))
 )]
+#[cfg_attr(feature = "full", diesel(primary_key(community_id)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// Aggregate data for a community.
 pub struct CommunityAggregates {
-  pub id: i32,
   pub community_id: CommunityId,
   pub subscribers: i64,
   pub posts: i64,
@@ -65,10 +65,10 @@ pub struct CommunityAggregates {
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = person_aggregates))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// Aggregate data for a person.
 pub struct PersonAggregates {
-  pub id: i32,
   pub person_id: PersonId,
   pub post_count: i64,
   #[serde(skip)]
@@ -82,10 +82,10 @@ pub struct PersonAggregates {
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = post_aggregates))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
+#[cfg_attr(feature = "full", diesel(primary_key(post_id)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// Aggregate data for a post.
 pub struct PostAggregates {
-  pub id: i32,
   pub post_id: PostId,
   pub comments: i64,
   pub score: i64,
@@ -124,10 +124,10 @@ pub struct PostAggregates {
 #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
 #[cfg_attr(feature = "full", diesel(table_name = person_post_aggregates))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
 /// Aggregate data for a person's post.
 pub struct PersonPostAggregates {
-  pub id: i32,
   pub person_id: PersonId,
   pub post_id: PostId,
   /// The number of comments they've read on that post.
@@ -151,10 +151,10 @@ pub struct PersonPostAggregatesForm {
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = site_aggregates))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
+#[cfg_attr(feature = "full", diesel(primary_key(site_id)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// Aggregate data for a site.
 pub struct SiteAggregates {
-  pub id: i32,
   pub site_id: SiteId,
   pub users: i64,
   pub posts: i64,
diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs
index 9b781d597149f08a8d036fb85119f9247c5645c3..fe1a521a9b7b31c15f3b5ded1e861fc0880027c2 100644
--- a/crates/db_schema/src/impls/activity.rs
+++ b/crates/db_schema/src/impls/activity.rs
@@ -39,16 +39,15 @@ impl SentActivity {
 
 impl ReceivedActivity {
   pub async fn create(pool: &mut DbPool<'_>, ap_id_: &DbUrl) -> Result<(), Error> {
-    use crate::schema::received_activity::dsl::{ap_id, id, received_activity};
+    use crate::schema::received_activity::dsl::{ap_id, received_activity};
     let conn = &mut get_conn(pool).await?;
-    let res = insert_into(received_activity)
+    let rows_affected = insert_into(received_activity)
       .values(ap_id.eq(ap_id_))
       .on_conflict_do_nothing()
-      .returning(id)
-      .get_result::<i64>(conn)
+      .execute(conn)
       .await
       .optional()?;
-    if res.is_some() {
+    if rows_affected == Some(1) {
       // new activity inserted successfully
       Ok(())
     } else {
diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs
index 313762a72f97c070229989d2f3e5711a23d3060d..5c4e252d40e47a656faaeb711b3bff5280cc9bf3 100644
--- a/crates/db_schema/src/impls/actor_language.rs
+++ b/crates/db_schema/src/impls/actor_language.rs
@@ -119,7 +119,7 @@ impl SiteLanguage {
     site::table
       .inner_join(local_site::table)
       .inner_join(site_language::table)
-      .order(site_language::id)
+      .order(site_language::language_id)
       .select(site_language::language_id)
       .load(conn)
       .await
@@ -191,14 +191,12 @@ impl CommunityLanguage {
     for_language_id: Option<LanguageId>,
     for_community_id: CommunityId,
   ) -> Result<(), LemmyError> {
-    use crate::schema::community_language::dsl::{community_id, community_language, language_id};
+    use crate::schema::community_language::dsl::community_language;
     let conn = &mut get_conn(pool).await?;
 
     if let Some(for_language_id) = for_language_id {
       let is_allowed = select(exists(
-        community_language
-          .filter(language_id.eq(for_language_id))
-          .filter(community_id.eq(for_community_id)),
+        community_language.find((for_community_id, for_language_id)),
       ))
       .get_result(conn)
       .await?;
diff --git a/crates/db_schema/src/impls/captcha_answer.rs b/crates/db_schema/src/impls/captcha_answer.rs
index 0404ce00561ae9abe89f444be5aaaf5171dcc08c..72e2d1285e21c558903920e72e412afeaaed1cdc 100644
--- a/crates/db_schema/src/impls/captcha_answer.rs
+++ b/crates/db_schema/src/impls/captcha_answer.rs
@@ -1,5 +1,5 @@
 use crate::{
-  schema::captcha_answer::dsl::{answer, captcha_answer, uuid},
+  schema::captcha_answer::dsl::{answer, captcha_answer},
   source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm, CheckCaptchaAnswer},
   utils::{functions::lower, get_conn, DbPool},
 };
@@ -31,16 +31,15 @@ impl CaptchaAnswer {
     let conn = &mut get_conn(pool).await?;
 
     // fetch requested captcha
-    let captcha_exists = select(exists(
-      captcha_answer
-        .filter((uuid).eq(to_check.uuid))
-        .filter(lower(answer).eq(to_check.answer.to_lowercase().clone())),
-    ))
-    .get_result::<bool>(conn)
-    .await?;
+    let captcha_exists =
+      select(exists(captcha_answer.find(to_check.uuid).filter(
+        lower(answer).eq(to_check.answer.to_lowercase().clone()),
+      )))
+      .get_result::<bool>(conn)
+      .await?;
 
     // delete checked captcha
-    delete(captcha_answer.filter(uuid.eq(to_check.uuid)))
+    delete(captcha_answer.find(to_check.uuid))
       .execute(conn)
       .await?;
 
diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs
index cab033df30e3ea58e85eb8d83d36d36b532daab0..35bc8314ad06601fdd9ae1fcd98e0dc0ef398432 100644
--- a/crates/db_schema/src/impls/comment.rs
+++ b/crates/db_schema/src/impls/comment.rs
@@ -192,18 +192,14 @@ impl Likeable for CommentLike {
   }
   async fn remove(
     pool: &mut DbPool<'_>,
-    person_id_: PersonId,
-    comment_id_: CommentId,
+    person_id: PersonId,
+    comment_id: CommentId,
   ) -> Result<usize, Error> {
-    use crate::schema::comment_like::dsl::{comment_id, comment_like, person_id};
+    use crate::schema::comment_like::dsl::comment_like;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      comment_like
-        .filter(comment_id.eq(comment_id_))
-        .filter(person_id.eq(person_id_)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(comment_like.find((person_id, comment_id)))
+      .execute(conn)
+      .await
   }
 }
 
@@ -228,12 +224,10 @@ impl Saveable for CommentSaved {
     pool: &mut DbPool<'_>,
     comment_saved_form: &CommentSavedForm,
   ) -> Result<usize, Error> {
-    use crate::schema::comment_saved::dsl::{comment_id, comment_saved, person_id};
+    use crate::schema::comment_saved::dsl::comment_saved;
     let conn = &mut get_conn(pool).await?;
     diesel::delete(
-      comment_saved
-        .filter(comment_id.eq(comment_saved_form.comment_id))
-        .filter(person_id.eq(comment_saved_form.person_id)),
+      comment_saved.find((comment_saved_form.person_id, comment_saved_form.comment_id)),
     )
     .execute(conn)
     .await
@@ -349,7 +343,6 @@ mod tests {
     let inserted_comment_like = CommentLike::like(pool, &comment_like_form).await.unwrap();
 
     let expected_comment_like = CommentLike {
-      id: inserted_comment_like.id,
       comment_id: inserted_comment.id,
       post_id: inserted_post.id,
       person_id: inserted_person.id,
@@ -366,7 +359,6 @@ mod tests {
     let inserted_comment_saved = CommentSaved::save(pool, &comment_saved_form).await.unwrap();
 
     let expected_comment_saved = CommentSaved {
-      id: inserted_comment_saved.id,
       comment_id: inserted_comment.id,
       person_id: inserted_person.id,
       published: inserted_comment_saved.published,
diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs
index 442239289796edabe67d6c398cdf056b5f9721b1..0d2cc88bad0057e9b125b665547f920131188a66 100644
--- a/crates/db_schema/src/impls/community.rs
+++ b/crates/db_schema/src/impls/community.rs
@@ -95,13 +95,12 @@ impl Joinable for CommunityModerator {
     pool: &mut DbPool<'_>,
     community_moderator_form: &CommunityModeratorForm,
   ) -> Result<usize, Error> {
-    use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
+    use crate::schema::community_moderator::dsl::community_moderator;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      community_moderator
-        .filter(community_id.eq(community_moderator_form.community_id))
-        .filter(person_id.eq(community_moderator_form.person_id)),
-    )
+    diesel::delete(community_moderator.find((
+      community_moderator_form.person_id,
+      community_moderator_form.community_id,
+    )))
     .execute(conn)
     .await
   }
@@ -199,13 +198,12 @@ impl Bannable for CommunityPersonBan {
     pool: &mut DbPool<'_>,
     community_person_ban_form: &CommunityPersonBanForm,
   ) -> Result<usize, Error> {
-    use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
+    use crate::schema::community_person_ban::dsl::community_person_ban;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      community_person_ban
-        .filter(community_id.eq(community_person_ban_form.community_id))
-        .filter(person_id.eq(community_person_ban_form.person_id)),
-    )
+    diesel::delete(community_person_ban.find((
+      community_person_ban_form.person_id,
+      community_person_ban_form.community_id,
+    )))
     .execute(conn)
     .await
   }
@@ -274,35 +272,22 @@ impl Followable for CommunityFollower {
   }
   async fn follow_accepted(
     pool: &mut DbPool<'_>,
-    community_id_: CommunityId,
-    person_id_: PersonId,
+    community_id: CommunityId,
+    person_id: PersonId,
   ) -> Result<Self, Error> {
-    use crate::schema::community_follower::dsl::{
-      community_follower,
-      community_id,
-      pending,
-      person_id,
-    };
+    use crate::schema::community_follower::dsl::{community_follower, pending};
     let conn = &mut get_conn(pool).await?;
-    diesel::update(
-      community_follower
-        .filter(community_id.eq(community_id_))
-        .filter(person_id.eq(person_id_)),
-    )
-    .set(pending.eq(false))
-    .get_result::<Self>(conn)
-    .await
+    diesel::update(community_follower.find((person_id, community_id)))
+      .set(pending.eq(false))
+      .get_result::<Self>(conn)
+      .await
   }
   async fn unfollow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result<usize, Error> {
-    use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
+    use crate::schema::community_follower::dsl::community_follower;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      community_follower
-        .filter(community_id.eq(&form.community_id))
-        .filter(person_id.eq(&form.person_id)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(community_follower.find((form.person_id, form.community_id)))
+      .execute(conn)
+      .await
   }
 }
 
@@ -448,7 +433,6 @@ mod tests {
       .unwrap();
 
     let expected_community_follower = CommunityFollower {
-      id: inserted_community_follower.id,
       community_id: inserted_community.id,
       person_id: inserted_person.id,
       pending: false,
@@ -465,7 +449,6 @@ mod tests {
       .unwrap();
 
     let expected_community_moderator = CommunityModerator {
-      id: inserted_community_moderator.id,
       community_id: inserted_community.id,
       person_id: inserted_person.id,
       published: inserted_community_moderator.published,
@@ -482,7 +465,6 @@ mod tests {
       .unwrap();
 
     let expected_community_person_ban = CommunityPersonBan {
-      id: inserted_community_person_ban.id,
       community_id: inserted_community.id,
       person_id: inserted_person.id,
       published: inserted_community_person_ban.published,
diff --git a/crates/db_schema/src/impls/community_block.rs b/crates/db_schema/src/impls/community_block.rs
index 997985a5bff060c659ac0805b4e5cf65436b60dc..1393f49d30c3646b94d7d92c854bb1da2468026a 100644
--- a/crates/db_schema/src/impls/community_block.rs
+++ b/crates/db_schema/src/impls/community_block.rs
@@ -9,7 +9,6 @@ use diesel::{
   dsl::{exists, insert_into},
   result::Error,
   select,
-  ExpressionMethods,
   QueryDsl,
 };
 use diesel_async::RunQueryDsl;
@@ -22,9 +21,7 @@ impl CommunityBlock {
   ) -> Result<bool, Error> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      community_block
-        .filter(community_id.eq(for_community_id))
-        .filter(person_id.eq(for_person_id)),
+      community_block.find((for_person_id, for_community_id)),
     ))
     .get_result(conn)
     .await
@@ -49,11 +46,10 @@ impl Blockable for CommunityBlock {
     community_block_form: &Self::Form,
   ) -> Result<usize, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      community_block
-        .filter(person_id.eq(community_block_form.person_id))
-        .filter(community_id.eq(community_block_form.community_id)),
-    )
+    diesel::delete(community_block.find((
+      community_block_form.person_id,
+      community_block_form.community_id,
+    )))
     .execute(conn)
     .await
   }
diff --git a/crates/db_schema/src/impls/image_upload.rs b/crates/db_schema/src/impls/image_upload.rs
index 58edbf2e38f01b7724b95f1bb06fa8a12a99bfc9..b62e5ceba6adf482714564884a356f90920d9308 100644
--- a/crates/db_schema/src/impls/image_upload.rs
+++ b/crates/db_schema/src/impls/image_upload.rs
@@ -1,6 +1,6 @@
 use crate::{
-  newtypes::{ImageUploadId, LocalUserId},
-  schema::image_upload::dsl::{image_upload, local_user_id, pictrs_alias},
+  newtypes::LocalUserId,
+  schema::image_upload::dsl::{image_upload, local_user_id},
   source::image_upload::{ImageUpload, ImageUploadForm},
   utils::{get_conn, DbPool},
 };
@@ -28,20 +28,8 @@ impl ImageUpload {
       .await
   }
 
-  pub async fn delete(
-    pool: &mut DbPool<'_>,
-    image_upload_id: ImageUploadId,
-  ) -> Result<usize, Error> {
-    let conn = &mut get_conn(pool).await?;
-    diesel::delete(image_upload.find(image_upload_id))
-      .execute(conn)
-      .await
-  }
-
   pub async fn delete_by_alias(pool: &mut DbPool<'_>, alias: &str) -> Result<usize, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(image_upload.filter(pictrs_alias.eq(alias)))
-      .execute(conn)
-      .await
+    diesel::delete(image_upload.find(alias)).execute(conn).await
   }
 }
diff --git a/crates/db_schema/src/impls/instance.rs b/crates/db_schema/src/impls/instance.rs
index b94e628d66a8de0d64542ce574ba8be4192d26b1..7da4ce354b7b511dfb1c19f47b76c23110cb22fb 100644
--- a/crates/db_schema/src/impls/instance.rs
+++ b/crates/db_schema/src/impls/instance.rs
@@ -115,7 +115,7 @@ impl Instance {
         .left_join(federation_allowlist::table)
         .select((
           Self::as_select(),
-          federation_allowlist::id.nullable().is_not_null(),
+          federation_allowlist::instance_id.nullable().is_not_null(),
           is_dead_expr,
         ))
         .order_by(instance::id)
@@ -126,7 +126,7 @@ impl Instance {
         .left_join(federation_blocklist::table)
         .select((
           Self::as_select(),
-          federation_blocklist::id.nullable().is_null(),
+          federation_blocklist::instance_id.nullable().is_null(),
           is_dead_expr,
         ))
         .order_by(instance::id)
@@ -150,8 +150,8 @@ impl Instance {
       .select((
         Self::as_select(),
         Option::<FederationQueueState>::as_select(),
-        federation_blocklist::id.nullable().is_not_null(),
-        federation_allowlist::id.nullable().is_not_null(),
+        federation_blocklist::instance_id.nullable().is_not_null(),
+        federation_allowlist::instance_id.nullable().is_not_null(),
       ))
       .get_results(conn)
       .await
diff --git a/crates/db_schema/src/impls/instance_block.rs b/crates/db_schema/src/impls/instance_block.rs
index f024f263642c5a10a82a958b5c49bbe5afe2a5dd..e326884119633a18d47221e316f9aa8fa13920d6 100644
--- a/crates/db_schema/src/impls/instance_block.rs
+++ b/crates/db_schema/src/impls/instance_block.rs
@@ -9,7 +9,6 @@ use diesel::{
   dsl::{exists, insert_into},
   result::Error,
   select,
-  ExpressionMethods,
   QueryDsl,
 };
 use diesel_async::RunQueryDsl;
@@ -22,9 +21,7 @@ impl InstanceBlock {
   ) -> Result<bool, Error> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      instance_block
-        .filter(instance_id.eq(for_instance_id))
-        .filter(person_id.eq(for_person_id)),
+      instance_block.find((for_person_id, for_instance_id)),
     ))
     .get_result(conn)
     .await
@@ -49,11 +46,10 @@ impl Blockable for InstanceBlock {
     instance_block_form: &Self::Form,
   ) -> Result<usize, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      instance_block
-        .filter(person_id.eq(instance_block_form.person_id))
-        .filter(instance_id.eq(instance_block_form.instance_id)),
-    )
+    diesel::delete(instance_block.find((
+      instance_block_form.person_id,
+      instance_block_form.instance_id,
+    )))
     .execute(conn)
     .await
   }
diff --git a/crates/db_schema/src/impls/language.rs b/crates/db_schema/src/impls/language.rs
index 53aadbac80d3b1d75e2ea5f8b2c4a7d02eeb8c0f..db72b837ffb83b5c6bf300b6aef5cee179416267 100644
--- a/crates/db_schema/src/impls/language.rs
+++ b/crates/db_schema/src/impls/language.rs
@@ -1,7 +1,7 @@
 use crate::{
   diesel::ExpressionMethods,
   newtypes::LanguageId,
-  schema::language::dsl::{code, id, language},
+  schema::language::dsl::{code, language},
   source::language::Language,
   utils::{get_conn, DbPool},
 };
@@ -16,7 +16,7 @@ impl Language {
 
   pub async fn read_from_id(pool: &mut DbPool<'_>, id_: LanguageId) -> Result<Language, Error> {
     let conn = &mut get_conn(pool).await?;
-    language.filter(id.eq(id_)).first::<Self>(conn).await
+    language.find(id_).first::<Self>(conn).await
   }
 
   /// Attempts to find the given language code and return its ID. If not found, returns none.
diff --git a/crates/db_schema/src/impls/login_token.rs b/crates/db_schema/src/impls/login_token.rs
index b1d1124d68419f83ca2a87b0bc9de38fe59ee20b..71cac6a19b18745baac57d9ee60e5db6e3fa05b5 100644
--- a/crates/db_schema/src/impls/login_token.rs
+++ b/crates/db_schema/src/impls/login_token.rs
@@ -1,7 +1,7 @@
 use crate::{
   diesel::{ExpressionMethods, QueryDsl},
   newtypes::LocalUserId,
-  schema::login_token::{dsl::login_token, token, user_id},
+  schema::login_token::{dsl::login_token, user_id},
   source::login_token::{LoginToken, LoginTokenCreateForm},
   utils::{get_conn, DbPool},
 };
@@ -25,9 +25,7 @@ impl LoginToken {
   ) -> Result<bool, Error> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      login_token
-        .filter(user_id.eq(user_id_))
-        .filter(token.eq(token_)),
+      login_token.find(token_).filter(user_id.eq(user_id_)),
     ))
     .get_result(conn)
     .await
@@ -48,9 +46,7 @@ impl LoginToken {
   /// Invalidate specific token on user logout.
   pub async fn invalidate(pool: &mut DbPool<'_>, token_: &str) -> Result<usize, Error> {
     let conn = &mut get_conn(pool).await?;
-    delete(login_token.filter(token.eq(token_)))
-      .execute(conn)
-      .await
+    delete(login_token.find(token_)).execute(conn).await
   }
 
   /// Invalidate all logins of given user on password reset/change, account deletion or site ban.
diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs
index 12ec0392fc6c5e068be1ef46eb96f048a17c4a66..e36e7b35eaae7f20fc9f18a000e944aced292553 100644
--- a/crates/db_schema/src/impls/person.rs
+++ b/crates/db_schema/src/impls/person.rs
@@ -155,15 +155,11 @@ impl Followable for PersonFollower {
     unimplemented!()
   }
   async fn unfollow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<usize, Error> {
-    use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
+    use crate::schema::person_follower::dsl::person_follower;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      person_follower
-        .filter(follower_id.eq(&form.follower_id))
-        .filter(person_id.eq(&form.person_id)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(person_follower.find((form.follower_id, form.person_id)))
+      .execute(conn)
+      .await
   }
 }
 
diff --git a/crates/db_schema/src/impls/person_block.rs b/crates/db_schema/src/impls/person_block.rs
index a616375e1ff19a8788b6826b99bc18996c8b467b..0dbf003d841d1278c701856ed441b390ab44629c 100644
--- a/crates/db_schema/src/impls/person_block.rs
+++ b/crates/db_schema/src/impls/person_block.rs
@@ -9,7 +9,6 @@ use diesel::{
   dsl::{exists, insert_into},
   result::Error,
   select,
-  ExpressionMethods,
   QueryDsl,
 };
 use diesel_async::RunQueryDsl;
@@ -21,13 +20,9 @@ impl PersonBlock {
     for_recipient_id: PersonId,
   ) -> Result<bool, Error> {
     let conn = &mut get_conn(pool).await?;
-    select(exists(
-      person_block
-        .filter(person_id.eq(for_person_id))
-        .filter(target_id.eq(for_recipient_id)),
-    ))
-    .get_result(conn)
-    .await
+    select(exists(person_block.find((for_person_id, for_recipient_id))))
+      .get_result(conn)
+      .await
   }
 }
 
@@ -49,12 +44,8 @@ impl Blockable for PersonBlock {
   }
   async fn unblock(pool: &mut DbPool<'_>, person_block_form: &Self::Form) -> Result<usize, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      person_block
-        .filter(person_id.eq(person_block_form.person_id))
-        .filter(target_id.eq(person_block_form.target_id)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(person_block.find((person_block_form.person_id, person_block_form.target_id)))
+      .execute(conn)
+      .await
   }
 }
diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs
index 4a719415ae173dba55537d9337acea6dcba5c253..f7ff633ecc530c2400eeb87dc8a22a6151232093 100644
--- a/crates/db_schema/src/impls/post.rs
+++ b/crates/db_schema/src/impls/post.rs
@@ -266,13 +266,9 @@ impl Likeable for PostLike {
   ) -> Result<usize, Error> {
     use crate::schema::post_like::dsl;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      dsl::post_like
-        .filter(dsl::post_id.eq(post_id))
-        .filter(dsl::person_id.eq(person_id)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(dsl::post_like.find((person_id, post_id)))
+      .execute(conn)
+      .await
   }
 }
 
@@ -291,15 +287,11 @@ impl Saveable for PostSaved {
       .await
   }
   async fn unsave(pool: &mut DbPool<'_>, post_saved_form: &PostSavedForm) -> Result<usize, Error> {
-    use crate::schema::post_saved::dsl::{person_id, post_id, post_saved};
+    use crate::schema::post_saved::dsl::post_saved;
     let conn = &mut get_conn(pool).await?;
-    diesel::delete(
-      post_saved
-        .filter(post_id.eq(post_saved_form.post_id))
-        .filter(person_id.eq(post_saved_form.person_id)),
-    )
-    .execute(conn)
-    .await
+    diesel::delete(post_saved.find((post_saved_form.person_id, post_saved_form.post_id)))
+      .execute(conn)
+      .await
   }
 }
 
@@ -444,7 +436,6 @@ mod tests {
     let inserted_post_like = PostLike::like(pool, &post_like_form).await.unwrap();
 
     let expected_post_like = PostLike {
-      id: inserted_post_like.id,
       post_id: inserted_post.id,
       person_id: inserted_person.id,
       published: inserted_post_like.published,
@@ -460,7 +451,6 @@ mod tests {
     let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await.unwrap();
 
     let expected_post_saved = PostSaved {
-      id: inserted_post_saved.id,
       post_id: inserted_post.id,
       person_id: inserted_person.id,
       published: inserted_post_saved.published,
diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs
index c5957767050111be0c8e215dade478ba016f9399..5e5970e91d0d10db040ffc0a13db7ecfeb094117 100644
--- a/crates/db_schema/src/newtypes.rs
+++ b/crates/db_schema/src/newtypes.rs
@@ -83,12 +83,6 @@ pub struct PersonMentionId(i32);
 /// The person block id.
 pub struct PersonBlockId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// The community block id.
-pub struct CommunityBlockId(i32);
-
 #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
 #[cfg_attr(feature = "full", derive(DieselNewType, TS))]
 #[cfg_attr(feature = "full", ts(export))]
@@ -119,30 +113,12 @@ pub struct SiteId(i32);
 /// The language id.
 pub struct LanguageId(pub i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(DieselNewType))]
-pub struct LocalUserLanguageId(pub i32);
-
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(DieselNewType))]
-pub struct SiteLanguageId(pub i32);
-
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(DieselNewType))]
-pub struct CommunityLanguageId(pub i32);
-
 #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
 #[cfg_attr(feature = "full", derive(DieselNewType, TS))]
 #[cfg_attr(feature = "full", ts(export))]
 /// The comment reply id.
 pub struct CommentReplyId(i32);
 
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// The Image Upload id.
-pub struct ImageUploadId(i32);
-
 #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
 #[cfg_attr(feature = "full", derive(DieselNewType, TS))]
 #[cfg_attr(feature = "full", ts(export))]
diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs
index fe23a6a3073f6f6c957e101405a020809716d631..88d468a6fa34545ad3c73b920e157f0fc2b88f12 100644
--- a/crates/db_schema/src/schema.rs
+++ b/crates/db_schema/src/schema.rs
@@ -65,8 +65,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    captcha_answer (id) {
-        id -> Int4,
+    captcha_answer (uuid) {
         uuid -> Uuid,
         answer -> Text,
         published -> Timestamptz,
@@ -96,8 +95,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    comment_aggregates (id) {
-        id -> Int4,
+    comment_aggregates (comment_id) {
         comment_id -> Int4,
         score -> Int8,
         upvotes -> Int8,
@@ -110,8 +108,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    comment_like (id) {
-        id -> Int4,
+    comment_like (person_id, comment_id) {
         person_id -> Int4,
         comment_id -> Int4,
         post_id -> Int4,
@@ -145,8 +142,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    comment_saved (id) {
-        id -> Int4,
+    comment_saved (person_id, comment_id) {
         comment_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -191,8 +187,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    community_aggregates (id) {
-        id -> Int4,
+    community_aggregates (community_id) {
         community_id -> Int4,
         subscribers -> Int8,
         posts -> Int8,
@@ -207,8 +202,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    community_block (id) {
-        id -> Int4,
+    community_block (person_id, community_id) {
         person_id -> Int4,
         community_id -> Int4,
         published -> Timestamptz,
@@ -216,8 +210,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    community_follower (id) {
-        id -> Int4,
+    community_follower (person_id, community_id) {
         community_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -226,16 +219,14 @@ diesel::table! {
 }
 
 diesel::table! {
-    community_language (id) {
-        id -> Int4,
+    community_language (community_id, language_id) {
         community_id -> Int4,
         language_id -> Int4,
     }
 }
 
 diesel::table! {
-    community_moderator (id) {
-        id -> Int4,
+    community_moderator (person_id, community_id) {
         community_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -243,8 +234,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    community_person_ban (id) {
-        id -> Int4,
+    community_person_ban (person_id, community_id) {
         community_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -267,8 +257,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    custom_emoji_keyword (id) {
-        id -> Int4,
+    custom_emoji_keyword (custom_emoji_id, keyword) {
         custom_emoji_id -> Int4,
         #[max_length = 128]
         keyword -> Varchar,
@@ -286,8 +275,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    federation_allowlist (id) {
-        id -> Int4,
+    federation_allowlist (instance_id) {
         instance_id -> Int4,
         published -> Timestamptz,
         updated -> Nullable<Timestamptz>,
@@ -295,8 +283,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    federation_blocklist (id) {
-        id -> Int4,
+    federation_blocklist (instance_id) {
         instance_id -> Int4,
         published -> Timestamptz,
         updated -> Nullable<Timestamptz>,
@@ -304,8 +291,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    federation_queue_state (id) {
-        id -> Int4,
+    federation_queue_state (instance_id) {
         instance_id -> Int4,
         last_successful_id -> Nullable<Int8>,
         fail_count -> Int4,
@@ -315,8 +301,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    image_upload (id) {
-        id -> Int4,
+    image_upload (pictrs_alias) {
         local_user_id -> Int4,
         pictrs_alias -> Text,
         pictrs_delete_token -> Text,
@@ -339,8 +324,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    instance_block (id) {
-        id -> Int4,
+    instance_block (person_id, instance_id) {
         person_id -> Int4,
         instance_id -> Int4,
         published -> Timestamptz,
@@ -391,8 +375,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    local_site_rate_limit (id) {
-        id -> Int4,
+    local_site_rate_limit (local_site_id) {
         local_site_id -> Int4,
         message -> Int4,
         message_per_second -> Int4,
@@ -452,16 +435,14 @@ diesel::table! {
 }
 
 diesel::table! {
-    local_user_language (id) {
-        id -> Int4,
+    local_user_language (local_user_id, language_id) {
         local_user_id -> Int4,
         language_id -> Int4,
     }
 }
 
 diesel::table! {
-    login_token (id) {
-        id -> Int4,
+    login_token (token) {
         token -> Text,
         user_id -> Int4,
         published -> Timestamptz,
@@ -632,8 +613,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    person_aggregates (id) {
-        id -> Int4,
+    person_aggregates (person_id) {
         person_id -> Int4,
         post_count -> Int8,
         post_score -> Int8,
@@ -643,16 +623,14 @@ diesel::table! {
 }
 
 diesel::table! {
-    person_ban (id) {
-        id -> Int4,
+    person_ban (person_id) {
         person_id -> Int4,
         published -> Timestamptz,
     }
 }
 
 diesel::table! {
-    person_block (id) {
-        id -> Int4,
+    person_block (person_id, target_id) {
         person_id -> Int4,
         target_id -> Int4,
         published -> Timestamptz,
@@ -660,8 +638,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    person_follower (id) {
-        id -> Int4,
+    person_follower (follower_id, person_id) {
         person_id -> Int4,
         follower_id -> Int4,
         published -> Timestamptz,
@@ -680,8 +657,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    person_post_aggregates (id) {
-        id -> Int4,
+    person_post_aggregates (person_id, post_id) {
         person_id -> Int4,
         post_id -> Int4,
         read_comments -> Int8,
@@ -719,8 +695,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    post_aggregates (id) {
-        id -> Int4,
+    post_aggregates (post_id) {
         post_id -> Int4,
         comments -> Int8,
         score -> Int8,
@@ -742,8 +717,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    post_like (id) {
-        id -> Int4,
+    post_like (person_id, post_id) {
         post_id -> Int4,
         person_id -> Int4,
         score -> Int2,
@@ -752,8 +726,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    post_read (id) {
-        id -> Int4,
+    post_read (person_id, post_id) {
         post_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -778,8 +751,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    post_saved (id) {
-        id -> Int4,
+    post_saved (person_id, post_id) {
         post_id -> Int4,
         person_id -> Int4,
         published -> Timestamptz,
@@ -817,8 +789,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    received_activity (id) {
-        id -> Int8,
+    received_activity (ap_id) {
         ap_id -> Text,
         published -> Timestamptz,
     }
@@ -884,8 +855,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    site_aggregates (id) {
-        id -> Int4,
+    site_aggregates (site_id) {
         site_id -> Int4,
         users -> Int8,
         posts -> Int8,
@@ -899,8 +869,7 @@ diesel::table! {
 }
 
 diesel::table! {
-    site_language (id) {
-        id -> Int4,
+    site_language (site_id, language_id) {
         site_id -> Int4,
         language_id -> Int4,
     }
diff --git a/crates/db_schema/src/source/actor_language.rs b/crates/db_schema/src/source/actor_language.rs
index 6780be51bcc9f2de1911eca31a4d18f0617af0df..b3da61abc0c68ec90dc84c1d81054538640f4f16 100644
--- a/crates/db_schema/src/source/actor_language.rs
+++ b/crates/db_schema/src/source/actor_language.rs
@@ -1,12 +1,4 @@
-use crate::newtypes::{
-  CommunityId,
-  CommunityLanguageId,
-  LanguageId,
-  LocalUserId,
-  LocalUserLanguageId,
-  SiteId,
-  SiteLanguageId,
-};
+use crate::newtypes::{CommunityId, LanguageId, LocalUserId, SiteId};
 #[cfg(feature = "full")]
 use crate::schema::local_user_language;
 use serde::{Deserialize, Serialize};
@@ -14,9 +6,8 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
 #[cfg_attr(feature = "full", diesel(table_name = local_user_language))]
+#[cfg_attr(feature = "full", diesel(primary_key(local_user_id, language_id)))]
 pub struct LocalUserLanguage {
-  #[serde(skip)]
-  pub id: LocalUserLanguageId,
   pub local_user_id: LocalUserId,
   pub language_id: LanguageId,
 }
@@ -35,9 +26,8 @@ use crate::schema::community_language;
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
 #[cfg_attr(feature = "full", diesel(table_name = community_language))]
+#[cfg_attr(feature = "full", diesel(primary_key(community_id, language_id)))]
 pub struct CommunityLanguage {
-  #[serde(skip)]
-  pub id: CommunityLanguageId,
   pub community_id: CommunityId,
   pub language_id: LanguageId,
 }
@@ -56,9 +46,8 @@ use crate::schema::site_language;
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
 #[cfg_attr(feature = "full", diesel(table_name = site_language))]
+#[cfg_attr(feature = "full", diesel(primary_key(site_id, language_id)))]
 pub struct SiteLanguage {
-  #[serde(skip)]
-  pub id: SiteLanguageId,
   pub site_id: SiteId,
   pub language_id: LanguageId,
 }
diff --git a/crates/db_schema/src/source/captcha_answer.rs b/crates/db_schema/src/source/captcha_answer.rs
index b7e9636c4211abfd246dc94f3aeb7c122b5f1d37..2660968ec16ebe1c177ff0f5462eb94397f0fbce 100644
--- a/crates/db_schema/src/source/captcha_answer.rs
+++ b/crates/db_schema/src/source/captcha_answer.rs
@@ -10,7 +10,6 @@ use uuid::Uuid;
 #[cfg_attr(feature = "full", derive(Queryable))]
 #[cfg_attr(feature = "full", diesel(table_name = captcha_answer))]
 pub struct CaptchaAnswer {
-  pub id: i32,
   pub uuid: Uuid,
   pub answer: String,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs
index efe3b7cdf78471be66f2bf88c60f37835027bacf..4969968bed345ecb7a117567ee5534a6d53bc6c8 100644
--- a/crates/db_schema/src/source/comment.rs
+++ b/crates/db_schema/src/source/comment.rs
@@ -86,8 +86,8 @@ pub struct CommentUpdateForm {
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
 #[cfg_attr(feature = "full", diesel(table_name = comment_like))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))]
 pub struct CommentLike {
-  pub id: i32,
   pub person_id: PersonId,
   pub comment_id: CommentId,
   pub post_id: PostId, // TODO this is redundant
@@ -109,8 +109,8 @@ pub struct CommentLikeForm {
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
 #[cfg_attr(feature = "full", diesel(table_name = comment_saved))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))]
 pub struct CommentSaved {
-  pub id: i32,
   pub comment_id: CommentId,
   pub person_id: PersonId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs
index 5da772be3b71c35a4457391702c241dab58aae21..29663c81d0d44d6b3021f5526d4221ea16947630 100644
--- a/crates/db_schema/src/source/community.rs
+++ b/crates/db_schema/src/source/community.rs
@@ -134,8 +134,8 @@ pub struct CommunityUpdateForm {
   diesel(belongs_to(crate::source::community::Community))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = community_moderator))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
 pub struct CommunityModerator {
-  pub id: i32,
   pub community_id: CommunityId,
   pub person_id: PersonId,
   pub published: DateTime<Utc>,
@@ -156,8 +156,8 @@ pub struct CommunityModeratorForm {
   diesel(belongs_to(crate::source::community::Community))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = community_person_ban))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
 pub struct CommunityPersonBan {
-  pub id: i32,
   pub community_id: CommunityId,
   pub person_id: PersonId,
   pub published: DateTime<Utc>,
@@ -180,8 +180,8 @@ pub struct CommunityPersonBanForm {
   diesel(belongs_to(crate::source::community::Community))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = community_follower))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
 pub struct CommunityFollower {
-  pub id: i32,
   pub community_id: CommunityId,
   pub person_id: PersonId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/community_block.rs b/crates/db_schema/src/source/community_block.rs
index 628e77ade90c071360ea422f0b39d57b191447c1..3154f1d77e87be843dbdd77404a40e2c61410c88 100644
--- a/crates/db_schema/src/source/community_block.rs
+++ b/crates/db_schema/src/source/community_block.rs
@@ -1,4 +1,4 @@
-use crate::newtypes::{CommunityBlockId, CommunityId, PersonId};
+use crate::newtypes::{CommunityId, PersonId};
 #[cfg(feature = "full")]
 use crate::schema::community_block;
 use chrono::{DateTime, Utc};
@@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize};
   diesel(belongs_to(crate::source::community::Community))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = community_block))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
 pub struct CommunityBlock {
-  pub id: CommunityBlockId,
   pub person_id: PersonId,
   pub community_id: CommunityId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/custom_emoji_keyword.rs b/crates/db_schema/src/source/custom_emoji_keyword.rs
index b1811cf44ef3552451fd912ea7f19c9516cc4536..679bbccea9011e335f3c4be3652727a7c7898ea0 100644
--- a/crates/db_schema/src/source/custom_emoji_keyword.rs
+++ b/crates/db_schema/src/source/custom_emoji_keyword.rs
@@ -13,10 +13,10 @@ use typed_builder::TypedBuilder;
   feature = "full",
   diesel(belongs_to(crate::source::custom_emoji::CustomEmoji))
 )]
+#[cfg_attr(feature = "full", diesel(primary_key(custom_emoji_id, keyword)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A custom keyword for an emoji.
 pub struct CustomEmojiKeyword {
-  pub id: i32,
   pub custom_emoji_id: CustomEmojiId,
   pub keyword: String,
 }
diff --git a/crates/db_schema/src/source/federation_allowlist.rs b/crates/db_schema/src/source/federation_allowlist.rs
index 534e1b02e3ae889849cd13677d4fd8f129953fdc..62ceb7d6f49428ded1cb35ff548669c8e58667dd 100644
--- a/crates/db_schema/src/source/federation_allowlist.rs
+++ b/crates/db_schema/src/source/federation_allowlist.rs
@@ -12,8 +12,8 @@ use std::fmt::Debug;
   diesel(belongs_to(crate::source::instance::Instance))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))]
+#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
 pub struct FederationAllowList {
-  pub id: i32,
   pub instance_id: InstanceId,
   pub published: DateTime<Utc>,
   pub updated: Option<DateTime<Utc>>,
diff --git a/crates/db_schema/src/source/federation_blocklist.rs b/crates/db_schema/src/source/federation_blocklist.rs
index 0cf615d7b24de81eb24842a32e66f41e945fb595..5f5bd835b948f186c00f70d2afabf60c2eeb95ef 100644
--- a/crates/db_schema/src/source/federation_blocklist.rs
+++ b/crates/db_schema/src/source/federation_blocklist.rs
@@ -12,8 +12,8 @@ use std::fmt::Debug;
   diesel(belongs_to(crate::source::instance::Instance))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))]
+#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
 pub struct FederationBlockList {
-  pub id: i32,
   pub instance_id: InstanceId,
   pub published: DateTime<Utc>,
   pub updated: Option<DateTime<Utc>>,
diff --git a/crates/db_schema/src/source/image_upload.rs b/crates/db_schema/src/source/image_upload.rs
index 0a3c4d6c4d2256ac2b0a96ce835eb35700c25e17..db1b7800ba4448c19cbde5af5833aa3f75b7f6e0 100644
--- a/crates/db_schema/src/source/image_upload.rs
+++ b/crates/db_schema/src/source/image_upload.rs
@@ -1,4 +1,4 @@
-use crate::newtypes::{ImageUploadId, LocalUserId};
+use crate::newtypes::LocalUserId;
 #[cfg(feature = "full")]
 use crate::schema::image_upload;
 use chrono::{DateTime, Utc};
@@ -13,13 +13,13 @@ use typed_builder::TypedBuilder;
 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = image_upload))]
+#[cfg_attr(feature = "full", diesel(primary_key(pictrs_alias)))]
 #[cfg_attr(feature = "full", ts(export))]
 #[cfg_attr(
   feature = "full",
   diesel(belongs_to(crate::source::local_user::LocalUser))
 )]
 pub struct ImageUpload {
-  pub id: ImageUploadId,
   pub local_user_id: LocalUserId,
   pub pictrs_alias: String,
   pub pictrs_delete_token: String,
diff --git a/crates/db_schema/src/source/instance_block.rs b/crates/db_schema/src/source/instance_block.rs
index 1aa215e455890c98acf2f5073e53a252ba987902..fe737ec6ff526f3018c58a878be8eae1e2f2ca79 100644
--- a/crates/db_schema/src/source/instance_block.rs
+++ b/crates/db_schema/src/source/instance_block.rs
@@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize};
   diesel(belongs_to(crate::source::instance::Instance))
 )]
 #[cfg_attr(feature = "full", diesel(table_name = instance_block))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, instance_id)))]
 pub struct InstanceBlock {
-  pub id: i32,
   pub person_id: PersonId,
   pub instance_id: InstanceId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/local_site_rate_limit.rs b/crates/db_schema/src/source/local_site_rate_limit.rs
index af7023f0ff26c4c2e54abf30fd4b5e999177f9f9..59da06270bea6b922d1fc29ed3b320dcb59cfdff 100644
--- a/crates/db_schema/src/source/local_site_rate_limit.rs
+++ b/crates/db_schema/src/source/local_site_rate_limit.rs
@@ -12,6 +12,7 @@ use typed_builder::TypedBuilder;
 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
+#[cfg_attr(feature = "full", diesel(primary_key(local_site_id)))]
 #[cfg_attr(
   feature = "full",
   diesel(belongs_to(crate::source::local_site::LocalSite))
@@ -19,7 +20,6 @@ use typed_builder::TypedBuilder;
 #[cfg_attr(feature = "full", ts(export))]
 /// Rate limits for your site. Given in count / length of time.
 pub struct LocalSiteRateLimit {
-  pub id: i32,
   pub local_site_id: LocalSiteId,
   pub message: i32,
   pub message_per_second: i32,
diff --git a/crates/db_schema/src/source/login_token.rs b/crates/db_schema/src/source/login_token.rs
index 45f74c41f1ba3fd5ba93340852e87de8c9d17317..97a3bba535cf63cbfcee51e772f6ec40934c4b94 100644
--- a/crates/db_schema/src/source/login_token.rs
+++ b/crates/db_schema/src/source/login_token.rs
@@ -12,9 +12,9 @@ use ts_rs::TS;
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = login_token))]
+#[cfg_attr(feature = "full", diesel(primary_key(token)))]
 #[cfg_attr(feature = "full", ts(export))]
 pub struct LoginToken {
-  pub id: i32,
   /// Jwt token for this login
   #[serde(skip)]
   pub token: String,
diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs
index 82772fefa222f978c649b8bd18c94e423911f68a..657006f201d54640e5fbe3faddabb46d29696e8d 100644
--- a/crates/db_schema/src/source/person.rs
+++ b/crates/db_schema/src/source/person.rs
@@ -115,8 +115,8 @@ pub struct PersonUpdateForm {
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
 #[cfg_attr(feature = "full", diesel(table_name = person_follower))]
+#[cfg_attr(feature = "full", diesel(primary_key(follower_id, person_id)))]
 pub struct PersonFollower {
-  pub id: i32,
   pub person_id: PersonId,
   pub follower_id: PersonId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/person_block.rs b/crates/db_schema/src/source/person_block.rs
index 3380fbfc361535071b98e4857a55420bc26708bd..3b22aec2ce515b614b0fe3fa587301bb163cb7cd 100644
--- a/crates/db_schema/src/source/person_block.rs
+++ b/crates/db_schema/src/source/person_block.rs
@@ -1,4 +1,4 @@
-use crate::newtypes::{PersonBlockId, PersonId};
+use crate::newtypes::PersonId;
 #[cfg(feature = "full")]
 use crate::schema::person_block;
 use chrono::{DateTime, Utc};
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
 #[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
 #[cfg_attr(feature = "full", diesel(table_name = person_block))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, target_id)))]
 pub struct PersonBlock {
-  pub id: PersonBlockId,
   pub person_id: PersonId,
   pub target_id: PersonId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs
index 72c32d4af6f37a9edd7b198838b83646861aa1ff..760697b0b43bdfc7a0e5acb5aa1b136adb1a1ffb 100644
--- a/crates/db_schema/src/source/post.rs
+++ b/crates/db_schema/src/source/post.rs
@@ -114,8 +114,8 @@ pub struct PostUpdateForm {
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
 #[cfg_attr(feature = "full", diesel(table_name = post_like))]
+#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
 pub struct PostLike {
-  pub id: i32,
   pub post_id: PostId,
   pub person_id: PersonId,
   pub score: i16,
@@ -135,8 +135,8 @@ pub struct PostLikeForm {
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
 #[cfg_attr(feature = "full", diesel(table_name = post_saved))]
+#[cfg_attr(feature = "full", diesel(primary_key(post_id, person_id)))]
 pub struct PostSaved {
-  pub id: i32,
   pub post_id: PostId,
   pub person_id: PersonId,
   pub published: DateTime<Utc>,
diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs
index e1b182cafb532cb3c4b0031f2216d20664451117..b883fb8fed12795d8917a5299d0d6edee4d668f5 100644
--- a/crates/db_views/src/comment_report_view.rs
+++ b/crates/db_views/src/comment_report_view.rs
@@ -62,7 +62,7 @@ fn queries<'a>() -> Queries<
     person::all_columns,
     aliases::person1.fields(person::all_columns),
     comment_aggregates::all_columns,
-    community_person_ban::id.nullable().is_not_null(),
+    community_person_ban::community_id.nullable().is_not_null(),
     comment_like::score.nullable(),
     aliases::person2.fields(person::all_columns).nullable(),
   );
@@ -425,7 +425,6 @@ mod tests {
       },
       creator_banned_from_community: false,
       counts: CommentAggregates {
-        id: agg.id,
         comment_id: inserted_comment.id,
         score: 0,
         upvotes: 0,
diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs
index 99f046cdf7e50570739886ddb6ef185e296498d8..ad01ff309f7190bdd5ec4ce91ef84e7dcb1145f3 100644
--- a/crates/db_views/src/comment_view.rs
+++ b/crates/db_views/src/comment_view.rs
@@ -110,14 +110,14 @@ fn queries<'a>() -> Queries<
     post::all_columns,
     community::all_columns,
     comment_aggregates::all_columns,
-    community_person_ban::id.nullable().is_not_null(),
+    community_person_ban::community_id.nullable().is_not_null(),
     aliases::community_moderator1
-      .field(community_moderator::id)
+      .field(community_moderator::community_id)
       .nullable()
       .is_not_null(),
     CommunityFollower::select_subscribed_type(),
-    comment_saved::id.nullable().is_not_null(),
-    person_block::id.nullable().is_not_null(),
+    comment_saved::comment_id.nullable().is_not_null(),
+    person_block::person_id.nullable().is_not_null(),
     comment_like::score.nullable(),
   );
 
@@ -509,7 +509,6 @@ mod tests {
       .unwrap();
 
     let expected_block = PersonBlock {
-      id: inserted_block.id,
       person_id: inserted_person.id,
       target_id: inserted_person_2.id,
       published: inserted_block.published,
@@ -947,7 +946,6 @@ mod tests {
         featured_url: data.inserted_community.featured_url.clone(),
       },
       counts: CommentAggregates {
-        id: agg.id,
         comment_id: data.inserted_comment_0.id,
         score: 1,
         upvotes: 1,
diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs
index 4e0d896ca8fa9654d5a6bbd228a8a3f350284dce..32d8f83924247b8bfc02e781b92d49e869a51e28 100644
--- a/crates/db_views/src/post_report_view.rs
+++ b/crates/db_views/src/post_report_view.rs
@@ -60,7 +60,7 @@ fn queries<'a>() -> Queries<
         community::all_columns,
         person::all_columns,
         aliases::person1.fields(person::all_columns),
-        community_person_ban::id.nullable().is_not_null(),
+        community_person_ban::community_id.nullable().is_not_null(),
         post_like::score.nullable(),
         post_aggregates::all_columns,
         aliases::person2.fields(person::all_columns.nullable()),
diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs
index 51213abb20c0deb1d0f31e41edd9584baed4e418..909b8079f6d1089b3c7f2570c3c9ed6845258756 100644
--- a/crates/db_views/src/post_view.rs
+++ b/crates/db_views/src/post_view.rs
@@ -995,7 +995,6 @@ mod tests {
     let inserted_post_like = PostLike::like(pool, &post_like_form).await.unwrap();
 
     let expected_post_like = PostLike {
-      id: inserted_post_like.id,
       post_id: data.inserted_post.id,
       person_id: data.local_user_view.person.id,
       published: inserted_post_like.published,
@@ -1462,7 +1461,6 @@ mod tests {
         featured_url: inserted_community.featured_url.clone(),
       },
       counts: PostAggregates {
-        id: agg.id,
         post_id: inserted_post.id,
         comments: 0,
         score: 0,
diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs
index de0ae22822559572a8cb6d3158494b2e6571f96b..54bae94827be07e3264d1768f46bfb0ece62f7dd 100644
--- a/crates/db_views/src/private_message_view.rs
+++ b/crates/db_views/src/private_message_view.rs
@@ -312,7 +312,6 @@ mod tests {
       .unwrap();
 
     let expected_block = PersonBlock {
-      id: inserted_block.id,
       person_id: timmy.id,
       target_id: sara.id,
       published: inserted_block.published,
diff --git a/crates/db_views_actor/src/comment_reply_view.rs b/crates/db_views_actor/src/comment_reply_view.rs
index e5a03eaf6d28a0c5ebf04fe5cd7d52ba1e842dc3..eed8f73ad7c8f2baf7fd081b7efd817b4e519907 100644
--- a/crates/db_views_actor/src/comment_reply_view.rs
+++ b/crates/db_views_actor/src/comment_reply_view.rs
@@ -96,11 +96,11 @@ fn queries<'a>() -> Queries<
         community::all_columns,
         aliases::person1.fields(person::all_columns),
         comment_aggregates::all_columns,
-        community_person_ban::id.nullable().is_not_null(),
-        community_moderator::id.nullable().is_not_null(),
+        community_person_ban::community_id.nullable().is_not_null(),
+        community_moderator::community_id.nullable().is_not_null(),
         CommunityFollower::select_subscribed_type(),
-        comment_saved::id.nullable().is_not_null(),
-        person_block::id.nullable().is_not_null(),
+        comment_saved::comment_id.nullable().is_not_null(),
+        person_block::person_id.nullable().is_not_null(),
         comment_like::score.nullable(),
       ))
   };
diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs
index f646e21f813d48bf16362448fafff3178338bfd5..7db6cfcf8eab5536c2214e052df141ec6a975094 100644
--- a/crates/db_views_actor/src/community_view.rs
+++ b/crates/db_views_actor/src/community_view.rs
@@ -62,7 +62,7 @@ fn queries<'a>() -> Queries<
   let selection = (
     community::all_columns,
     CommunityFollower::select_subscribed_type(),
-    community_block::id.nullable().is_not_null(),
+    community_block::community_id.nullable().is_not_null(),
     community_aggregates::all_columns,
   );
 
diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_mention_view.rs
index c44ee791ccd6bcb93e2ae0c7129423350b8607c7..4b1178fe65ed2473fddf8488e1fdd8ae0558e1a4 100644
--- a/crates/db_views_actor/src/person_mention_view.rs
+++ b/crates/db_views_actor/src/person_mention_view.rs
@@ -85,11 +85,11 @@ fn queries<'a>() -> Queries<
     community::all_columns,
     aliases::person1.fields(person::all_columns),
     comment_aggregates::all_columns,
-    community_person_ban::id.nullable().is_not_null(),
-    community_moderator::id.nullable().is_not_null(),
+    community_person_ban::community_id.nullable().is_not_null(),
+    community_moderator::community_id.nullable().is_not_null(),
     CommunityFollower::select_subscribed_type(),
-    comment_saved::id.nullable().is_not_null(),
-    person_block::id.nullable().is_not_null(),
+    comment_saved::comment_id.nullable().is_not_null(),
+    person_block::person_id.nullable().is_not_null(),
     comment_like::score.nullable(),
   );
 
diff --git a/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/down.sql b/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/down.sql
new file mode 100644
index 0000000000000000000000000000000000000000..b5078355e010d2f9ce83669936f564cbc94d704a
--- /dev/null
+++ b/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/down.sql
@@ -0,0 +1,191 @@
+ALTER TABLE captcha_answer
+    ADD UNIQUE (uuid),
+    DROP CONSTRAINT captcha_answer_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE comment_aggregates
+    ADD UNIQUE (comment_id),
+    DROP CONSTRAINT comment_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_comment_like_person ON comment_like (person_id);
+
+ALTER TABLE comment_like
+    ADD UNIQUE (comment_id, person_id),
+    DROP CONSTRAINT comment_like_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_comment_saved_person_id ON comment_saved (person_id);
+
+ALTER TABLE comment_saved
+    ADD UNIQUE (comment_id, person_id),
+    DROP CONSTRAINT comment_saved_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE community_aggregates
+    ADD UNIQUE (community_id),
+    DROP CONSTRAINT community_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_community_block_person ON community_block (person_id);
+
+ALTER TABLE community_block
+    ADD UNIQUE (person_id, community_id),
+    DROP CONSTRAINT community_block_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_community_follower_person ON community_follower (person_id);
+
+ALTER TABLE community_follower
+    ADD UNIQUE (community_id, person_id),
+    DROP CONSTRAINT community_follower_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE community_language
+    ADD UNIQUE (community_id, language_id),
+    DROP CONSTRAINT community_language_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_community_moderator_person ON community_moderator (person_id);
+
+ALTER TABLE community_moderator
+    ADD UNIQUE (community_id, person_id),
+    DROP CONSTRAINT community_moderator_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE community_person_ban
+    ADD UNIQUE (community_id, person_id),
+    DROP CONSTRAINT community_person_ban_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE custom_emoji_keyword
+    ADD UNIQUE (custom_emoji_id, keyword),
+    DROP CONSTRAINT custom_emoji_keyword_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE federation_allowlist
+    ADD UNIQUE (instance_id),
+    DROP CONSTRAINT federation_allowlist_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE federation_blocklist
+    ADD UNIQUE (instance_id),
+    DROP CONSTRAINT federation_blocklist_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE federation_queue_state
+    ADD UNIQUE (instance_id),
+    DROP CONSTRAINT federation_queue_state_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE image_upload
+    ADD UNIQUE (pictrs_alias),
+    DROP CONSTRAINT image_upload_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE instance_block
+    ADD UNIQUE (person_id, instance_id),
+    DROP CONSTRAINT instance_block_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE local_site_rate_limit
+    ADD UNIQUE (local_site_id),
+    DROP CONSTRAINT local_site_rate_limit_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE local_user_language
+    ADD UNIQUE (local_user_id, language_id),
+    DROP CONSTRAINT local_user_language_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE login_token
+    ADD UNIQUE (token),
+    DROP CONSTRAINT login_token_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE person_aggregates
+    ADD UNIQUE (person_id),
+    DROP CONSTRAINT person_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE person_ban
+    ADD UNIQUE (person_id),
+    DROP CONSTRAINT person_ban_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE person_block
+    ADD UNIQUE (person_id, target_id),
+    DROP CONSTRAINT person_block_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE person_follower
+    ADD UNIQUE (follower_id, person_id),
+    DROP CONSTRAINT person_follower_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE person_post_aggregates
+    ADD UNIQUE (person_id, post_id),
+    DROP CONSTRAINT person_post_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE post_aggregates
+    ADD UNIQUE (post_id),
+    DROP CONSTRAINT post_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_post_like_person ON post_like (person_id);
+
+ALTER TABLE post_like
+    ADD UNIQUE (post_id, person_id),
+    DROP CONSTRAINT post_like_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE post_read
+    ADD UNIQUE (post_id, person_id),
+    DROP CONSTRAINT post_read_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE received_activity
+    ADD UNIQUE (ap_id),
+    DROP CONSTRAINT received_activity_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE INDEX idx_post_saved_person_id ON post_saved (person_id);
+
+ALTER TABLE post_saved
+    ADD UNIQUE (post_id, person_id),
+    DROP CONSTRAINT post_saved_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE site_aggregates
+    DROP CONSTRAINT site_aggregates_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+ALTER TABLE site_language
+    ADD UNIQUE (site_id, language_id),
+    DROP CONSTRAINT site_language_pkey,
+    ADD COLUMN id serial PRIMARY KEY;
+
+CREATE OR REPLACE FUNCTION site_aggregates_site ()
+    RETURNS TRIGGER
+    LANGUAGE plpgsql
+    AS $$
+BEGIN
+    -- we only ever want to have a single value in site_aggregate because the site_aggregate triggers update all rows in that table.
+    -- a cleaner check would be to insert it for the local_site but that would break assumptions at least in the tests
+    IF (TG_OP = 'INSERT') AND NOT EXISTS (
+    SELECT
+        id
+    FROM
+        site_aggregates
+    LIMIT 1) THEN
+        INSERT INTO site_aggregates (site_id)
+            VALUES (NEW.id);
+    ELSIF (TG_OP = 'DELETE') THEN
+        DELETE FROM site_aggregates
+        WHERE site_id = OLD.id;
+    END IF;
+    RETURN NULL;
+END
+$$;
+
diff --git a/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/up.sql b/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9d27e02792775db2bdfd82dac84b55e20bc3187a
--- /dev/null
+++ b/migrations/2023-10-24-030352_change_primary_keys_and_remove_some_id_columns/up.sql
@@ -0,0 +1,228 @@
+ALTER TABLE captcha_answer
+    DROP COLUMN id,
+    ADD PRIMARY KEY (uuid),
+    DROP CONSTRAINT captcha_answer_uuid_key;
+
+ALTER TABLE comment_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (comment_id),
+    DROP CONSTRAINT comment_aggregates_comment_id_key;
+
+ALTER TABLE comment_like
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, comment_id),
+    DROP CONSTRAINT comment_like_comment_id_person_id_key;
+
+DROP INDEX idx_comment_like_person;
+
+ALTER TABLE comment_saved
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, comment_id),
+    DROP CONSTRAINT comment_saved_comment_id_person_id_key;
+
+DROP INDEX idx_comment_saved_person_id;
+
+ALTER TABLE community_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (community_id),
+    DROP CONSTRAINT community_aggregates_community_id_key;
+
+ALTER TABLE community_block
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, community_id),
+    DROP CONSTRAINT community_block_person_id_community_id_key;
+
+DROP INDEX idx_community_block_person;
+
+ALTER TABLE community_follower
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, community_id),
+    DROP CONSTRAINT community_follower_community_id_person_id_key;
+
+DROP INDEX idx_community_follower_person;
+
+ALTER TABLE community_language
+    DROP COLUMN id,
+    ADD PRIMARY KEY (community_id, language_id),
+    DROP CONSTRAINT community_language_community_id_language_id_key;
+
+ALTER TABLE community_moderator
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, community_id),
+    DROP CONSTRAINT community_moderator_community_id_person_id_key;
+
+DROP INDEX idx_community_moderator_person;
+
+ALTER TABLE community_person_ban
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, community_id),
+    DROP CONSTRAINT community_person_ban_community_id_person_id_key;
+
+ALTER TABLE custom_emoji_keyword
+    DROP COLUMN id,
+    ADD PRIMARY KEY (custom_emoji_id, keyword),
+    DROP CONSTRAINT custom_emoji_keyword_custom_emoji_id_keyword_key;
+
+ALTER TABLE federation_allowlist
+    DROP COLUMN id,
+    ADD PRIMARY KEY (instance_id),
+    DROP CONSTRAINT federation_allowlist_instance_id_key;
+
+ALTER TABLE federation_blocklist
+    DROP COLUMN id,
+    ADD PRIMARY KEY (instance_id),
+    DROP CONSTRAINT federation_blocklist_instance_id_key;
+
+ALTER TABLE federation_queue_state
+    DROP COLUMN id,
+    ADD PRIMARY KEY (instance_id),
+    DROP CONSTRAINT federation_queue_state_instance_id_key;
+
+ALTER TABLE image_upload
+    DROP COLUMN id,
+    ADD PRIMARY KEY (pictrs_alias),
+    DROP CONSTRAINT image_upload_pictrs_alias_key;
+
+ALTER TABLE instance_block
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, instance_id),
+    DROP CONSTRAINT instance_block_person_id_instance_id_key;
+
+ALTER TABLE local_site_rate_limit
+    DROP COLUMN id,
+    ADD PRIMARY KEY (local_site_id),
+    DROP CONSTRAINT local_site_rate_limit_local_site_id_key;
+
+ALTER TABLE local_user_language
+    DROP COLUMN id,
+    ADD PRIMARY KEY (local_user_id, language_id),
+    DROP CONSTRAINT local_user_language_local_user_id_language_id_key;
+
+ALTER TABLE login_token
+    DROP COLUMN id,
+    ADD PRIMARY KEY (token),
+    DROP CONSTRAINT login_token_token_key;
+
+-- Delete duplicates which can exist because of missing `UNIQUE` constraint
+DELETE FROM person_aggregates AS a USING (
+    SELECT
+        min(id) AS id,
+        person_id
+    FROM
+        person_aggregates
+    GROUP BY
+        person_id
+    HAVING
+        count(*) > 1) AS b
+WHERE
+    a.person_id = b.person_id
+    AND a.id != b.id;
+
+ALTER TABLE person_aggregates
+    DROP CONSTRAINT IF EXISTS person_aggregates_person_id_key;
+
+ALTER TABLE person_aggregates
+    ADD UNIQUE (person_id);
+
+ALTER TABLE person_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id),
+    DROP CONSTRAINT person_aggregates_person_id_key;
+
+ALTER TABLE person_ban
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id),
+    DROP CONSTRAINT person_ban_person_id_key;
+
+ALTER TABLE person_block
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, target_id),
+    DROP CONSTRAINT person_block_person_id_target_id_key;
+
+ALTER TABLE person_follower
+    DROP COLUMN id,
+    ADD PRIMARY KEY (follower_id, person_id),
+    DROP CONSTRAINT person_follower_follower_id_person_id_key;
+
+ALTER TABLE person_post_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, post_id),
+    DROP CONSTRAINT person_post_aggregates_person_id_post_id_key;
+
+ALTER TABLE post_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (post_id),
+    DROP CONSTRAINT post_aggregates_post_id_key;
+
+ALTER TABLE post_like
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, post_id),
+    DROP CONSTRAINT post_like_post_id_person_id_key;
+
+DROP INDEX idx_post_like_person;
+
+ALTER TABLE post_read
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, post_id),
+    DROP CONSTRAINT post_read_post_id_person_id_key;
+
+ALTER TABLE post_saved
+    DROP COLUMN id,
+    ADD PRIMARY KEY (person_id, post_id),
+    DROP CONSTRAINT post_saved_post_id_person_id_key;
+
+DROP INDEX idx_post_saved_person_id;
+
+ALTER TABLE received_activity
+    DROP COLUMN id,
+    ADD PRIMARY KEY (ap_id),
+    DROP CONSTRAINT received_activity_ap_id_key;
+
+-- Delete duplicates which can exist because of missing `UNIQUE` constraint
+DELETE FROM site_aggregates AS a USING (
+    SELECT
+        min(id) AS id,
+        site_id
+    FROM
+        site_aggregates
+    GROUP BY
+        site_id
+    HAVING
+        count(*) > 1) AS b
+WHERE
+    a.site_id = b.site_id
+    AND a.id != b.id;
+
+ALTER TABLE site_aggregates
+    DROP COLUMN id,
+    ADD PRIMARY KEY (site_id);
+
+ALTER TABLE site_language
+    DROP COLUMN id,
+    ADD PRIMARY KEY (site_id, language_id),
+    DROP CONSTRAINT site_language_site_id_language_id_key;
+
+-- Change functions to not use the removed columns
+CREATE OR REPLACE FUNCTION site_aggregates_site ()
+    RETURNS TRIGGER
+    LANGUAGE plpgsql
+    AS $$
+BEGIN
+    -- we only ever want to have a single value in site_aggregate because the site_aggregate triggers update all rows in that table.
+    -- a cleaner check would be to insert it for the local_site but that would break assumptions at least in the tests
+    IF (TG_OP = 'INSERT') AND NOT EXISTS (
+    SELECT
+        *
+    FROM
+        site_aggregates
+    LIMIT 1) THEN
+        INSERT INTO site_aggregates (site_id)
+            VALUES (NEW.id);
+    ELSIF (TG_OP = 'DELETE') THEN
+        DELETE FROM site_aggregates
+        WHERE site_id = OLD.id;
+    END IF;
+    RETURN NULL;
+END
+$$;
+
diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs
index 5c1cab3faf201f32cdf199a4fc0832513ba3fd1b..b669836e5918acd31aaff5147f28bdd26e900289 100644
--- a/src/scheduled_tasks.rs
+++ b/src/scheduled_tasks.rs
@@ -130,7 +130,7 @@ async fn update_hot_ranks(pool: &mut DbPool<'_>) {
 
       process_ranks_in_batches(
         &mut conn,
-        "comment_aggregates",
+        "comment",
         "a.hot_rank != 0",
         "SET hot_rank = hot_rank(a.score, a.published)",
       )
@@ -138,7 +138,7 @@ async fn update_hot_ranks(pool: &mut DbPool<'_>) {
 
       process_ranks_in_batches(
         &mut conn,
-        "community_aggregates",
+        "community",
         "a.hot_rank != 0",
         "SET hot_rank = hot_rank(a.subscribers, a.published)",
       )
@@ -180,16 +180,17 @@ async fn process_ranks_in_batches(
     // Raw `sql_query` is used as a performance optimization - Diesel does not support doing this
     // in a single query (neither as a CTE, nor using a subquery)
     let result = sql_query(format!(
-      r#"WITH batch AS (SELECT a.id
+      r#"WITH batch AS (SELECT a.{id_column}
                FROM {aggregates_table} a
                WHERE a.published > $1 AND ({where_clause})
                ORDER BY a.published
                LIMIT $2
                FOR UPDATE SKIP LOCKED)
          UPDATE {aggregates_table} a {set_clause}
-             FROM batch WHERE a.id = batch.id RETURNING a.published;
+             FROM batch WHERE a.{id_column} = batch.{id_column} RETURNING a.published;
     "#,
-      aggregates_table = table_name,
+      id_column = format!("{table_name}_id"),
+      aggregates_table = format!("{table_name}_aggregates"),
       set_clause = set_clause,
       where_clause = where_clause
     ))
@@ -228,7 +229,7 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection)
   let mut previous_batch_result = Some(process_start_time);
   while let Some(previous_batch_last_published) = previous_batch_result {
     let result = sql_query(
-      r#"WITH batch AS (SELECT pa.id
+      r#"WITH batch AS (SELECT pa.post_id
                FROM post_aggregates pa
                WHERE pa.published > $1
                AND (pa.hot_rank != 0 OR pa.hot_rank_active != 0)
@@ -240,7 +241,7 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection)
            hot_rank_active = hot_rank(pa.score, pa.newest_comment_time_necro),
            scaled_rank = scaled_rank(pa.score, pa.published, ca.users_active_month)
          FROM batch, community_aggregates ca
-         WHERE pa.id = batch.id and pa.community_id = ca.community_id RETURNING pa.published;
+         WHERE pa.post_id = batch.post_id and pa.community_id = ca.community_id RETURNING pa.published;
     "#,
     )
     .bind::<Timestamptz, _>(previous_batch_last_published)