Skip to content

Commit a5a9c46

Browse files
authored
Merge pull request #8053 from eth3lbert/count-subq
Re-introducing improve crates endpoint performance (#7941)
2 parents d794d02 + 4a95592 commit a5a9c46

File tree

3 files changed

+326
-161
lines changed

3 files changed

+326
-161
lines changed

src/controllers/helpers/pagination.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ pub(crate) trait Paginate: Sized {
146146
options,
147147
}
148148
}
149+
150+
fn pages_pagination_with_count_query<C>(
151+
self,
152+
options: PaginationOptions,
153+
count_query: C,
154+
) -> PaginatedQueryWithCountSubq<Self, C> {
155+
PaginatedQueryWithCountSubq {
156+
query: self,
157+
count_query,
158+
options,
159+
}
160+
}
149161
}
150162

151163
impl<T> Paginate for T {}
@@ -303,6 +315,63 @@ pub(crate) fn decode_seek<D: for<'a> Deserialize<'a>>(seek: &str) -> anyhow::Res
303315
Ok(decoded)
304316
}
305317

318+
#[derive(Debug)]
319+
pub(crate) struct PaginatedQueryWithCountSubq<T, C> {
320+
query: T,
321+
count_query: C,
322+
options: PaginationOptions,
323+
}
324+
325+
impl<T, C> QueryId for PaginatedQueryWithCountSubq<T, C> {
326+
const HAS_STATIC_QUERY_ID: bool = false;
327+
type QueryId = ();
328+
}
329+
330+
impl<
331+
T: Query,
332+
C: Query + QueryDsl + diesel::query_dsl::methods::SelectDsl<diesel::dsl::CountStar>,
333+
> Query for PaginatedQueryWithCountSubq<T, C>
334+
{
335+
type SqlType = (T::SqlType, BigInt);
336+
}
337+
338+
impl<T, C, DB> RunQueryDsl<DB> for PaginatedQueryWithCountSubq<T, C> {}
339+
340+
impl<T, C> QueryFragment<Pg> for PaginatedQueryWithCountSubq<T, C>
341+
where
342+
T: QueryFragment<Pg>,
343+
C: QueryFragment<Pg>,
344+
{
345+
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
346+
out.push_sql("SELECT *, (");
347+
self.count_query.walk_ast(out.reborrow())?;
348+
out.push_sql(") FROM (");
349+
self.query.walk_ast(out.reborrow())?;
350+
out.push_sql(") t LIMIT ");
351+
out.push_bind_param::<BigInt, _>(&self.options.per_page)?;
352+
if let Some(offset) = self.options.offset() {
353+
// Injection safety: `offset()` returns `Option<i64>`, so this interpolation is constrained to known
354+
// valid values and this is not vulnerable to user injection attacks.
355+
out.push_sql(format!(" OFFSET {offset}").as_str());
356+
}
357+
Ok(())
358+
}
359+
}
360+
361+
impl<T, C> PaginatedQueryWithCountSubq<T, C> {
362+
pub(crate) fn load<'a, U>(self, conn: &mut PgConnection) -> QueryResult<Paginated<U>>
363+
where
364+
Self: LoadQuery<'a, PgConnection, WithCount<U>>,
365+
{
366+
let options = self.options.clone();
367+
let records_and_total = self.internal_load(conn)?.collect::<QueryResult<_>>()?;
368+
Ok(Paginated {
369+
records_and_total,
370+
options,
371+
})
372+
}
373+
}
374+
306375
#[cfg(test)]
307376
mod tests {
308377
use super::*;

0 commit comments

Comments
 (0)