1010from contextlib import suppress
1111from typing import TYPE_CHECKING , Any , Optional , Tuple , Union , overload
1212
13- from sqlalchemy import func , literal_column , select
13+ from sqlalchemy import func , select
1414from sqlalchemy .orm import Query , Session , noload
1515from typing_extensions import TypeAlias
1616
@@ -51,9 +51,16 @@ def paginate_query(query: Select, params: AbstractParams) -> Select:
5151 return generic_query_apply_params (query , params .to_raw_params ().as_limit_offset ())
5252
5353
54- def count_query (query : Select ) -> Select :
55- count_subquery = query .order_by (None ).options (noload ("*" )).subquery ()
56- return select (func .count (literal_column ("*" ))).select_from (count_subquery )
54+ def count_query (query : Select , * , use_subquery : bool = True ) -> Select :
55+ query = query .order_by (None ).options (noload ("*" ))
56+
57+ if use_subquery :
58+ return select (func .count ()).select_from (query .subquery ())
59+
60+ return query .with_only_columns ( # noqa: PIE804
61+ func .count (),
62+ ** {"maintain_column_froms" : True },
63+ )
5764
5865
5966def _maybe_unique (result : Any , unique : bool ) -> Any :
@@ -66,6 +73,7 @@ def exec_pagination(
6673 conn : SyncConn ,
6774 transformer : Optional [ItemsTransformer ] = None ,
6875 additional_data : AdditionalData = None ,
76+ subquery_count : bool = True ,
6977 unique : bool = True ,
7078 async_ : bool = False ,
7179) -> AbstractPage [Any ]:
@@ -82,6 +90,8 @@ def _apply_items_transformer(*args: Any, **kwargs: Any) -> Any:
8290 if is_cursor (raw_params ):
8391 if paging is None :
8492 raise ImportError ("sqlakeyset is not installed" )
93+ if not getattr (query , "_order_by_clauses" , True ):
94+ raise ValueError ("Cursor pagination requires ordering" )
8595
8696 page = paging .select_page (
8797 conn ,
@@ -100,7 +110,7 @@ def _apply_items_transformer(*args: Any, **kwargs: Any) -> Any:
100110 ** (additional_data or {}),
101111 )
102112
103- total = conn .scalar (count_query (query ))
113+ total = conn .scalar (count_query (query , use_subquery = subquery_count ))
104114 query = paginate_query (query , params )
105115 items = _maybe_unique (conn .execute (query ), unique )
106116 items = unwrap_scalars (items )
@@ -130,6 +140,7 @@ def paginate(
130140 query : Query [Any ],
131141 params : Optional [AbstractParams ] = None ,
132142 * ,
143+ subquery_count : bool = True ,
133144 transformer : Optional [SyncItemsTransformer ] = None ,
134145 additional_data : AdditionalData = None ,
135146) -> Any :
@@ -142,6 +153,7 @@ def paginate(
142153 query : Select ,
143154 params : Optional [AbstractParams ] = None ,
144155 * ,
156+ subquery_count : bool = True ,
145157 transformer : Optional [SyncItemsTransformer ] = None ,
146158 additional_data : AdditionalData = None ,
147159 unique : bool = True ,
@@ -155,6 +167,7 @@ async def paginate(
155167 query : Select ,
156168 params : Optional [AbstractParams ] = None ,
157169 * ,
170+ subquery_count : bool = True ,
158171 transformer : Optional [AsyncItemsTransformer ] = None ,
159172 additional_data : AdditionalData = None ,
160173 unique : bool = True ,
@@ -166,9 +179,9 @@ def paginate(*args: Any, **kwargs: Any) -> Any:
166179 try :
167180 assert args
168181 assert isinstance (args [0 ], Query )
169- query , conn , params , transformer , additional_data , unique = _old_paginate_sign (* args , ** kwargs )
182+ query , conn , params , transformer , additional_data , unique , subquery_count = _old_paginate_sign (* args , ** kwargs )
170183 except (TypeError , AssertionError ):
171- query , conn , params , transformer , additional_data , unique = _new_paginate_sign (* args , ** kwargs )
184+ query , conn , params , transformer , additional_data , unique , subquery_count = _new_paginate_sign (* args , ** kwargs )
172185
173186 params , _ = verify_params (params , "limit-offset" , "cursor" )
174187
@@ -181,20 +194,22 @@ def paginate(*args: Any, **kwargs: Any) -> Any:
181194 sync_conn ,
182195 transformer ,
183196 additional_data ,
197+ subquery_count ,
184198 unique ,
185199 async_ = True ,
186200 )
187201
188- return exec_pagination (query , params , conn , transformer , additional_data , unique , async_ = False )
202+ return exec_pagination (query , params , conn , transformer , additional_data , subquery_count , unique , async_ = False )
189203
190204
191205def _old_paginate_sign (
192206 query : Query [Any ],
193207 params : Optional [AbstractParams ] = None ,
194208 * ,
209+ subquery_count : bool = True ,
195210 transformer : Optional [ItemsTransformer ] = None ,
196211 additional_data : AdditionalData = None ,
197- ) -> Tuple [Select , SyncConn , Optional [AbstractParams ], Optional [ItemsTransformer ], AdditionalData , bool ]:
212+ ) -> Tuple [Select , SyncConn , Optional [AbstractParams ], Optional [ItemsTransformer ], AdditionalData , bool , bool ]:
198213 if query .session is None :
199214 raise ValueError ("query.session is None" )
200215
@@ -205,16 +220,17 @@ def _old_paginate_sign(
205220 stacklevel = 3 ,
206221 )
207222
208- return query , query .session , params , transformer , additional_data , True # type: ignore
223+ return query , query .session , params , transformer , additional_data , True , subquery_count # type: ignore
209224
210225
211226def _new_paginate_sign (
212227 conn : SyncConn ,
213228 query : Select ,
214229 params : Optional [AbstractParams ] = None ,
215230 * ,
231+ subquery_count : bool = True ,
216232 transformer : Optional [ItemsTransformer ] = None ,
217233 additional_data : AdditionalData = None ,
218234 unique : bool = True ,
219- ) -> Tuple [Select , SyncConn , Optional [AbstractParams ], Optional [ItemsTransformer ], AdditionalData , bool ]:
220- return query , conn , params , transformer , additional_data , unique
235+ ) -> Tuple [Select , SyncConn , Optional [AbstractParams ], Optional [ItemsTransformer ], AdditionalData , bool , bool ]:
236+ return query , conn , params , transformer , additional_data , unique , subquery_count
0 commit comments