2121//! internal contracts used to describe relative aggregate subpartitions passed
2222//! from repartition to final hash aggregation.
2323//!
24- //! Entry points: [`append_subpartition_column`], [`reorder_by_subpartition `],
24+ //! Entry points: [`append_subpartition_column`], [`partition_runs_from_column `],
2525//! [`set_partition_runs_metadata`] and [`partition_runs`].
2626
2727use std:: sync:: Arc ;
2828
29- use arrow:: array:: {
30- Array , ArrayRef , MutableArrayData , RecordBatch , RecordBatchOptions , UInt32Array ,
31- make_array,
32- } ;
29+ use arrow:: array:: { Array , ArrayRef , RecordBatch , RecordBatchOptions , UInt32Array } ;
3330use arrow:: datatypes:: { DataType , Field , Schema , SchemaRef } ;
3431use datafusion_common:: cast:: as_uint32_array;
3532use datafusion_common:: { Result , internal_err} ;
@@ -109,41 +106,11 @@ pub(crate) fn append_subpartition_column(
109106 RecordBatch :: try_new_with_options ( schema, columns, & options) . map_err ( Into :: into)
110107}
111108
112- /// Reusable scratch space for [`reorder_by_subpartition`].
113- #[ derive( Default ) ]
114- pub ( crate ) struct SubpartitionReorderBuffer {
115- counts : Vec < usize > ,
116- offsets : Vec < usize > ,
117- source_runs : Vec < SourcePartitionRun > ,
118- runs : Vec < PartitionRun > ,
119- }
120-
121- impl SubpartitionReorderBuffer {
122- /// Create an empty reorder scratch buffer.
123- pub ( crate ) fn new ( ) -> Self {
124- Self :: default ( )
125- }
126-
127- /// Return partition runs produced by the latest reorder.
128- pub ( crate ) fn runs ( & self ) -> & [ PartitionRun ] {
129- & self . runs
130- }
131-
132- fn clear ( & mut self ) {
133- self . counts . clear ( ) ;
134- self . offsets . clear ( ) ;
135- self . source_runs . clear ( ) ;
136- self . runs . clear ( ) ;
137- }
138- }
139-
140- /// Reorder `batch` by its internal subpartition column and strip that column.
141- pub ( crate ) fn reorder_by_subpartition (
109+ /// Read contiguous partition runs from the internal subpartition column and
110+ /// return a batch that omits the internal column without reordering any rows.
111+ pub ( crate ) fn partition_runs_from_column (
142112 batch : & RecordBatch ,
143- buffer : & mut SubpartitionReorderBuffer ,
144- ) -> Result < Option < RecordBatch > > {
145- buffer. clear ( ) ;
146-
113+ ) -> Result < Option < ( RecordBatch , Vec < PartitionRun > ) > > {
147114 let Some ( subpartition_idx) = subpartition_column_index ( batch. schema_ref ( ) ) else {
148115 return Ok ( None ) ;
149116 } ;
@@ -155,129 +122,29 @@ pub(crate) fn reorder_by_subpartition(
155122 ) ;
156123 }
157124
158- let num_rows = batch. num_rows ( ) ;
159- if num_rows > u32:: MAX as usize {
160- return internal_err ! (
161- "Hash aggregate batch row count {num_rows} does not fit UInt32"
162- ) ;
163- }
164-
165- for relative_partition in subpartitions. values ( ) . iter ( ) . copied ( ) {
166- let relative_partition = relative_partition as usize ;
167- if relative_partition >= buffer. counts . len ( ) {
168- buffer. counts . resize ( relative_partition + 1 , 0 ) ;
169- }
170- buffer. counts [ relative_partition] += 1 ;
171- }
172-
173- buffer. offsets . resize ( buffer. counts . len ( ) , 0 ) ;
174- let mut output_offset = 0 ;
175- for ( relative_partition, len) in buffer. counts . iter ( ) . copied ( ) . enumerate ( ) {
176- buffer. offsets [ relative_partition] = output_offset;
177- if len != 0 {
178- buffer
179- . runs
180- . push ( PartitionRun :: new ( relative_partition, len) ?) ;
181- }
182- output_offset += len;
183- }
184- debug_assert_eq ! ( output_offset, num_rows) ;
185-
186- let mut is_identity = true ;
187- let mut values = subpartitions. values ( ) . iter ( ) . copied ( ) . enumerate ( ) ;
188- if let Some ( ( run_start, first_partition) ) = values. next ( ) {
125+ let mut runs = Vec :: new ( ) ;
126+ let mut values = subpartitions. values ( ) . iter ( ) . copied ( ) ;
127+ if let Some ( first_partition) = values. next ( ) {
189128 let mut current_partition = first_partition as usize ;
190- let mut current_start = run_start;
191129 let mut current_len = 1 ;
192- for ( row_idx , relative_partition) in values {
130+ for relative_partition in values {
193131 let relative_partition = relative_partition as usize ;
194132 if relative_partition == current_partition {
195133 current_len += 1 ;
196134 } else {
197- push_source_run (
198- buffer,
199- current_partition,
200- current_start,
201- current_len,
202- & mut is_identity,
203- ) ;
135+ runs. push ( PartitionRun :: new ( current_partition, current_len) ?) ;
204136 current_partition = relative_partition;
205- current_start = row_idx;
206137 current_len = 1 ;
207138 }
208139 }
209- push_source_run (
210- buffer,
211- current_partition,
212- current_start,
213- current_len,
214- & mut is_identity,
215- ) ;
140+ runs. push ( PartitionRun :: new ( current_partition, current_len) ?) ;
216141 }
217142
218143 let schema = strip_subpartition_schema ( batch. schema_ref ( ) , subpartition_idx) ;
219144 let columns = strip_subpartition_columns ( batch, subpartition_idx) ;
220- let output = if is_identity {
221- RecordBatch :: try_new ( schema, columns) ?
222- } else {
223- buffer
224- . source_runs
225- . sort_unstable_by_key ( |run| run. output_start ) ;
226- let columns =
227- reorder_columns_by_source_runs ( & columns, & buffer. source_runs , num_rows) ;
228- let options = RecordBatchOptions :: new ( ) . with_row_count ( Some ( num_rows) ) ;
229- RecordBatch :: try_new_with_options ( schema, columns, & options) ?
230- } ;
231-
232- Ok ( Some ( output) )
233- }
234-
235- #[ derive( Debug , Clone , Copy ) ]
236- struct SourcePartitionRun {
237- start : usize ,
238- len : usize ,
239- output_start : usize ,
240- }
241-
242- fn push_source_run (
243- buffer : & mut SubpartitionReorderBuffer ,
244- relative_partition : usize ,
245- start : usize ,
246- len : usize ,
247- is_identity : & mut bool ,
248- ) {
249- let output_start = buffer. offsets [ relative_partition] ;
250- buffer. offsets [ relative_partition] += len;
251- * is_identity &= output_start == start;
252- buffer. source_runs . push ( SourcePartitionRun {
253- start,
254- len,
255- output_start,
256- } ) ;
257- }
258-
259- fn reorder_columns_by_source_runs (
260- columns : & [ ArrayRef ] ,
261- source_runs : & [ SourcePartitionRun ] ,
262- num_rows : usize ,
263- ) -> Vec < ArrayRef > {
264- columns
265- . iter ( )
266- . map ( |column| reorder_array_by_source_runs ( column, source_runs, num_rows) )
267- . collect ( )
268- }
269-
270- fn reorder_array_by_source_runs (
271- array : & ArrayRef ,
272- source_runs : & [ SourcePartitionRun ] ,
273- num_rows : usize ,
274- ) -> ArrayRef {
275- let array_data = array. to_data ( ) ;
276- let mut mutable = MutableArrayData :: new ( vec ! [ & array_data] , false , num_rows) ;
277- for run in source_runs {
278- mutable. extend ( 0 , run. start , run. start + run. len ) ;
279- }
280- make_array ( mutable. freeze ( ) )
145+ let options = RecordBatchOptions :: new ( ) . with_row_count ( Some ( batch. num_rows ( ) ) ) ;
146+ let batch = RecordBatch :: try_new_with_options ( schema, columns, & options) ?;
147+ Ok ( Some ( ( batch, runs) ) )
281148}
282149
283150/// Return partition runs encoded on `schema`, if present.
@@ -436,7 +303,7 @@ mod tests {
436303 }
437304
438305 #[ test]
439- fn test_reorder_by_subpartition_groups_rows_and_strips_column ( ) -> Result < ( ) > {
306+ fn test_partition_runs_from_column_preserves_row_order ( ) -> Result < ( ) > {
440307 let schema =
441308 Arc :: new ( Schema :: new ( vec ! [ Field :: new( "a" , DataType :: UInt32 , false ) ] ) ) ;
442309 let batch = RecordBatch :: try_new (
@@ -452,14 +319,17 @@ mod tests {
452319 ] ,
453320 ) ?;
454321
455- let mut buffer = SubpartitionReorderBuffer :: new ( ) ;
456- let batch = reorder_by_subpartition ( & batch, & mut buffer) ?. unwrap ( ) ;
322+ let ( batch, runs) = partition_runs_from_column ( & batch) ?. unwrap ( ) ;
457323 let values = as_uint32_array ( batch. column ( 0 ) . as_ref ( ) ) ?;
458- assert_eq ! ( values. values( ) , & [ 11 , 12 , 10 , 13 ] ) ;
324+ assert_eq ! ( values. values( ) , & [ 10 , 11 , 12 , 13 ] ) ;
459325 assert_eq ! ( batch. num_columns( ) , 1 ) ;
460326 assert_eq ! (
461- buffer. runs( ) ,
462- & [ PartitionRun :: new( 0 , 2 ) ?, PartitionRun :: new( 1 , 2 ) ?]
327+ runs,
328+ [
329+ PartitionRun :: new( 1 , 1 ) ?,
330+ PartitionRun :: new( 0 , 2 ) ?,
331+ PartitionRun :: new( 1 , 1 ) ?,
332+ ]
463333 ) ;
464334 Ok ( ( ) )
465335 }
0 commit comments