@@ -2,6 +2,7 @@ use super::SqlExecutionError;
22use super :: aggregates:: AggregateDataset ;
33use super :: batch:: { ColumnData , ColumnarBatch , ColumnarPage } ;
44use super :: expressions:: { evaluate_expression_on_batch, evaluate_scalar_expression} ;
5+ use super :: helpers:: column_name_from_expr;
56use super :: values:: { ScalarValue , compare_scalar_values, compare_strs, format_float} ;
67use crate :: metadata_store:: TableCatalog ;
78use sqlparser:: ast:: Expr ;
@@ -28,6 +29,13 @@ pub(crate) struct OrderKey {
2829 pub ( crate ) values : Vec < ScalarValue > ,
2930}
3031
32+ #[ derive( Clone ) ]
33+ struct OrderColumn {
34+ ordinal : usize ,
35+ descending : bool ,
36+ nulls : NullsPlacement ,
37+ }
38+
3139// Legacy rowwise ordering helpers removed after pipeline refactor.
3240
3341pub ( crate ) fn compare_order_keys (
@@ -177,8 +185,32 @@ pub(crate) fn sort_batch_in_memory_with_limit(
177185 return Ok ( batch. clone ( ) ) ;
178186 }
179187
180- let order_keys = build_order_keys_on_batch ( clauses, batch, catalog) ?;
181188 let mut indices: Vec < usize > = ( 0 ..batch. num_rows ) . collect ( ) ;
189+ if let Some ( order_columns) = extract_order_columns ( clauses, batch, catalog) {
190+ let compare = |left : & usize , right : & usize | {
191+ let ordering = compare_rows_by_columns ( batch, & order_columns, * left, * right) ;
192+ if ordering == Ordering :: Equal {
193+ let left_id = batch. row_ids . get ( * left) . copied ( ) . unwrap_or ( * left as u64 ) ;
194+ let right_id = batch. row_ids . get ( * right) . copied ( ) . unwrap_or ( * right as u64 ) ;
195+ left_id. cmp ( & right_id)
196+ } else {
197+ ordering
198+ }
199+ } ;
200+
201+ if let Some ( limit) = limit
202+ && limit < indices. len ( )
203+ {
204+ indices. select_nth_unstable_by ( limit, compare) ;
205+ indices[ ..limit] . sort_unstable_by ( compare) ;
206+ return Ok ( batch. gather ( & indices[ ..limit] ) ) ;
207+ }
208+
209+ indices. sort_unstable_by ( compare) ;
210+ return Ok ( batch. gather ( & indices) ) ;
211+ }
212+
213+ let order_keys = build_order_keys_on_batch ( clauses, batch, catalog) ?;
182214 let compare = |left : & usize , right : & usize | {
183215 let ordering = compare_order_keys ( & order_keys[ * left] , & order_keys[ * right] , clauses) ;
184216 if ordering == Ordering :: Equal {
@@ -202,6 +234,108 @@ pub(crate) fn sort_batch_in_memory_with_limit(
202234 Ok ( batch. gather ( & indices) )
203235}
204236
237+ fn extract_order_columns (
238+ clauses : & [ OrderClause ] ,
239+ batch : & ColumnarBatch ,
240+ catalog : & TableCatalog ,
241+ ) -> Option < Vec < OrderColumn > > {
242+ let mut columns = Vec :: with_capacity ( clauses. len ( ) ) ;
243+ for clause in clauses {
244+ let name = column_name_from_expr ( & clause. expr ) ?;
245+ let column = catalog. column ( & name) ?;
246+ if !batch. columns . contains_key ( & column. ordinal ) {
247+ return None ;
248+ }
249+ columns. push ( OrderColumn {
250+ ordinal : column. ordinal ,
251+ descending : clause. descending ,
252+ nulls : clause. nulls ,
253+ } ) ;
254+ }
255+ Some ( columns)
256+ }
257+
258+ fn compare_rows_by_columns (
259+ batch : & ColumnarBatch ,
260+ order_columns : & [ OrderColumn ] ,
261+ left_idx : usize ,
262+ right_idx : usize ,
263+ ) -> Ordering {
264+ for column in order_columns {
265+ let Some ( page) = batch. columns . get ( & column. ordinal ) else {
266+ continue ;
267+ } ;
268+ let left_null = page. null_bitmap . is_set ( left_idx) ;
269+ let right_null = page. null_bitmap . is_set ( right_idx) ;
270+ if let Some ( null_order) = compare_nulls ( left_null, right_null, column) {
271+ if null_order != Ordering :: Equal {
272+ return null_order;
273+ }
274+ continue ;
275+ }
276+
277+ let mut ord = match & page. data {
278+ ColumnData :: Int64 ( values) => values[ left_idx] . cmp ( & values[ right_idx] ) ,
279+ ColumnData :: Timestamp ( values) => values[ left_idx] . cmp ( & values[ right_idx] ) ,
280+ ColumnData :: Float64 ( values) => values[ left_idx]
281+ . partial_cmp ( & values[ right_idx] )
282+ . unwrap_or ( Ordering :: Equal ) ,
283+ ColumnData :: Boolean ( values) => values[ left_idx] . cmp ( & values[ right_idx] ) ,
284+ ColumnData :: Text ( col) => col. get_bytes ( left_idx) . cmp ( col. get_bytes ( right_idx) ) ,
285+ ColumnData :: Dictionary ( dict) => {
286+ dict. get_bytes ( left_idx) . cmp ( dict. get_bytes ( right_idx) )
287+ }
288+ } ;
289+ if column. descending {
290+ ord = ord. reverse ( ) ;
291+ }
292+ if ord != Ordering :: Equal {
293+ return ord;
294+ }
295+ }
296+ Ordering :: Equal
297+ }
298+
299+ fn compare_nulls ( left_null : bool , right_null : bool , clause : & OrderColumn ) -> Option < Ordering > {
300+ if !left_null && !right_null {
301+ return None ;
302+ }
303+ if left_null && right_null {
304+ return Some ( Ordering :: Equal ) ;
305+ }
306+
307+ let ordering = match clause. nulls {
308+ NullsPlacement :: First => {
309+ if left_null {
310+ Ordering :: Less
311+ } else {
312+ Ordering :: Greater
313+ }
314+ }
315+ NullsPlacement :: Last => {
316+ if left_null {
317+ Ordering :: Greater
318+ } else {
319+ Ordering :: Less
320+ }
321+ }
322+ NullsPlacement :: Default => {
323+ if clause. descending {
324+ if left_null {
325+ Ordering :: Less
326+ } else {
327+ Ordering :: Greater
328+ }
329+ } else if left_null {
330+ Ordering :: Greater
331+ } else {
332+ Ordering :: Less
333+ }
334+ }
335+ } ;
336+ Some ( ordering)
337+ }
338+
205339fn column_scalar_value ( page : & ColumnarPage , idx : usize ) -> ScalarValue {
206340 if page. null_bitmap . is_set ( idx) {
207341 return ScalarValue :: Null ;
0 commit comments