ScalarValue has interior mutability but is intentionally used as hash key
(
self,
max_target_partitions: usize,
)
| 490 | /// number of unique partition values is less than the target. |
| 491 | #[allow(clippy::allow_attributes, clippy::mutable_key_type)] // ScalarValue has interior mutability but is intentionally used as hash key |
| 492 | pub fn group_by_partition_values( |
| 493 | self, |
| 494 | max_target_partitions: usize, |
| 495 | ) -> Vec<FileGroup> { |
| 496 | if self.is_empty() || max_target_partitions == 0 { |
| 497 | return vec![]; |
| 498 | } |
| 499 | |
| 500 | let mut partition_groups: HashMap< |
| 501 | Vec<datafusion_common::ScalarValue>, |
| 502 | Vec<PartitionedFile>, |
| 503 | > = HashMap::new(); |
| 504 | |
| 505 | for file in self.files { |
| 506 | partition_groups |
| 507 | .entry(file.partition_values.clone()) |
| 508 | .or_default() |
| 509 | .push(file); |
| 510 | } |
| 511 | |
| 512 | let num_unique_partitions = partition_groups.len(); |
| 513 | |
| 514 | // Sort for deterministic bucket assignment across query executions. |
| 515 | let mut sorted_partitions: Vec<_> = partition_groups.into_iter().collect(); |
| 516 | let sort_options = |
| 517 | vec![ |
| 518 | SortOptions::default(); |
| 519 | sorted_partitions.first().map(|(k, _)| k.len()).unwrap_or(0) |
| 520 | ]; |
| 521 | sorted_partitions.sort_by(|a, b| { |
| 522 | compare_rows(&a.0, &b.0, &sort_options).unwrap_or(Ordering::Equal) |
| 523 | }); |
| 524 | |
| 525 | if num_unique_partitions <= max_target_partitions { |
| 526 | sorted_partitions |
| 527 | .into_iter() |
| 528 | .map(|(_, files)| FileGroup::new(files)) |
| 529 | .collect() |
| 530 | } else { |
| 531 | // Merge into max_target_partitions buckets using round-robin. |
| 532 | // This maintains grouping by partition value as we are merging groups which already |
| 533 | // contain all values for a partition key. |
| 534 | let mut target_groups = vec![vec![]; max_target_partitions]; |
| 535 | |
| 536 | for (idx, (_, files)) in sorted_partitions.into_iter().enumerate() { |
| 537 | let bucket = idx % max_target_partitions; |
| 538 | target_groups[bucket].extend(files); |
| 539 | } |
| 540 | |
| 541 | target_groups.into_iter().map(FileGroup::new).collect() |
| 542 | } |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | impl Index<usize> for FileGroup { |