diff --git a/sea-orm-sync/src/executor/paginator.rs b/sea-orm-sync/src/executor/paginator.rs index 5215ba1815..7a0bc04f04 100644 --- a/sea-orm-sync/src/executor/paginator.rs +++ b/sea-orm-sync/src/executor/paginator.rs @@ -87,11 +87,7 @@ where Some(res) => res, None => return Ok(0), }; - #[allow(clippy::match_single_binding)] - let num_items = match self.db.get_database_backend() { - _ => result.try_get::("", "num_items")? as u64, - }; - Ok(num_items) + Ok(result.try_get::("", "num_items")? as u64) } /// Get the total number of pages @@ -272,12 +268,30 @@ where /// Paginate the result of a select operation. fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>; - /// Perform a count on the paginated results + /// Count the number of rows this query selects, honoring any `limit` and + /// `offset` set on it. + /// + /// This differs from [`Paginator::num_items`], which resets `limit`/`offset` + /// to report the grand total for pagination. `count` wraps the query as-is in + /// a `SELECT COUNT(*) FROM (..)` subquery, so a query with a `limit` counts at + /// most that many rows. fn count(self, db: &'db C) -> Result where Self: Sized, { - self.paginate(db, 1).num_items() + let paginator = self.paginate(db, 1); + let query = SelectStatement::new() + .expr(Expr::cust("COUNT(*) AS num_items")) + .from_subquery( + paginator.query.clone().clear_order_by().to_owned(), + "sub_query", + ) + .to_owned(); + let result = match db.query_one(&query)? { + Some(res) => res, + None => return Ok(0), + }; + Ok(result.try_get::("", "num_items")? as u64) } } diff --git a/sea-orm-sync/src/executor/select_ext.rs b/sea-orm-sync/src/executor/select_ext.rs index 8213c52e28..fa43ef547c 100644 --- a/sea-orm-sync/src/executor/select_ext.rs +++ b/sea-orm-sync/src/executor/select_ext.rs @@ -4,7 +4,6 @@ use crate::{ }; use sea_query::{Expr, SelectStatement}; -// TODO: Move count here /// Helper trait for selectors with convenient methods pub trait SelectExt { /// This method is unstable and is only used for internal testing. diff --git a/sea-orm-sync/tests/paginator_tests.rs b/sea-orm-sync/tests/paginator_tests.rs index 1e91016adc..950268e34c 100644 --- a/sea-orm-sync/tests/paginator_tests.rs +++ b/sea-orm-sync/tests/paginator_tests.rs @@ -4,7 +4,7 @@ pub mod common; pub use common::{TestContext, features::*, setup::*}; use pretty_assertions::assert_eq; -use sea_orm::{PaginatorTrait, QueryOrder, Set, entity::prelude::*}; +use sea_orm::{PaginatorTrait, QueryOrder, QuerySelect, Set, entity::prelude::*}; #[sea_orm_macros::test] fn paginator_tests() -> Result<(), DbErr> { @@ -145,5 +145,10 @@ pub fn paginator_count(db: &DatabaseConnection) -> Result<(), DbErr> { assert_eq!(Entity::find().filter(Column::Id.gt(100)).count(db)?, 0); + // `count` honors `limit`/`offset` set on the query, unlike `num_items`. + assert_eq!(Entity::find().limit(3).count(db)?, 3); + + assert_eq!(Entity::find().limit(4).offset(8).count(db)?, 2); + Ok(()) } diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index a3e88a6572..83d7166823 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -89,11 +89,7 @@ where Some(res) => res, None => return Ok(0), }; - #[allow(clippy::match_single_binding)] - let num_items = match self.db.get_database_backend() { - _ => result.try_get::("", "num_items")? as u64, - }; - Ok(num_items) + Ok(result.try_get::("", "num_items")? as u64) } /// Get the total number of pages @@ -279,12 +275,30 @@ where /// Paginate the result of a select operation. fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>; - /// Perform a count on the paginated results + /// Count the number of rows this query selects, honoring any `limit` and + /// `offset` set on it. + /// + /// This differs from [`Paginator::num_items`], which resets `limit`/`offset` + /// to report the grand total for pagination. `count` wraps the query as-is in + /// a `SELECT COUNT(*) FROM (..)` subquery, so a query with a `limit` counts at + /// most that many rows. async fn count(self, db: &'db C) -> Result where Self: Send + Sized, { - self.paginate(db, 1).num_items().await + let paginator = self.paginate(db, 1); + let query = SelectStatement::new() + .expr(Expr::cust("COUNT(*) AS num_items")) + .from_subquery( + paginator.query.clone().clear_order_by().to_owned(), + "sub_query", + ) + .to_owned(); + let result = match db.query_one(&query).await? { + Some(res) => res, + None => return Ok(0), + }; + Ok(result.try_get::("", "num_items")? as u64) } } diff --git a/src/executor/select_ext.rs b/src/executor/select_ext.rs index 4ff81d310a..6cbbeba55b 100644 --- a/src/executor/select_ext.rs +++ b/src/executor/select_ext.rs @@ -4,7 +4,6 @@ use crate::{ }; use sea_query::{Expr, SelectStatement}; -// TODO: Move count here #[async_trait::async_trait] /// Helper trait for selectors with convenient methods pub trait SelectExt { diff --git a/tests/paginator_tests.rs b/tests/paginator_tests.rs index 14deca3cf0..c52d007138 100644 --- a/tests/paginator_tests.rs +++ b/tests/paginator_tests.rs @@ -4,7 +4,7 @@ pub mod common; pub use common::{TestContext, features::*, setup::*}; use pretty_assertions::assert_eq; -use sea_orm::{PaginatorTrait, QueryOrder, Set, entity::prelude::*}; +use sea_orm::{PaginatorTrait, QueryOrder, QuerySelect, Set, entity::prelude::*}; #[sea_orm_macros::test] async fn paginator_tests() -> Result<(), DbErr> { @@ -149,5 +149,10 @@ pub async fn paginator_count(db: &DatabaseConnection) -> Result<(), DbErr> { 0 ); + // `count` honors `limit`/`offset` set on the query, unlike `num_items`. + assert_eq!(Entity::find().limit(3).count(db).await?, 3); + + assert_eq!(Entity::find().limit(4).offset(8).count(db).await?, 2); + Ok(()) }