1use super::{InProgressArray, generic::GenericInProgressArray};
6use crate::dictionary::{KeyMapping, child_values_ptr_eq, merge_dictionary_values_with_masks};
7use ahash::RandomState;
8use arrow_array::cast::AsArray;
9use arrow_array::types::ArrowDictionaryKeyType;
10use arrow_array::{
11 Array, ArrayRef, DictionaryArray, PrimitiveArray, downcast_integer, new_empty_array,
12};
13use arrow_buffer::{
14 ArrowNativeType, BooleanBufferBuilder, NullBuffer, NullBufferBuilder, ScalarBuffer,
15};
16use arrow_schema::{ArrowError, DataType};
17use std::collections::HashMap;
18use std::mem::size_of;
19use std::sync::Arc;
20
21struct KeyChunk<K: ArrowDictionaryKeyType> {
22 keys: ScalarBuffer<K::Native>,
24 nulls: Option<NullBuffer>,
26 source: usize,
28}
29
30pub(crate) fn create_in_progress_dictionary(
31 key_type: &DataType,
32 value_type: &DataType,
33) -> Box<dyn InProgressArray> {
34 macro_rules! create {
35 ($key:ty, $value_type:ident) => {
36 Box::new(InProgressDictionaryArray::<$key>::new($value_type))
37 };
38 }
39
40 match value_type {
41 DataType::Utf8 | DataType::LargeUtf8 | DataType::Binary | DataType::LargeBinary => {
42 downcast_integer! {
43 key_type => (create, value_type),
44 _ => unreachable!("illegal dictionary key type {key_type}"),
45 }
46 }
47 _ => Box::new(GenericInProgressArray::new()),
48 }
49}
50
51struct InProgressDictionaryArray<K: ArrowDictionaryKeyType> {
52 source: Option<ArrayRef>,
54 len: usize,
56 empty_values: ArrayRef,
58 sources: Vec<ArrayRef>,
60 source_ids: HashMap<usize, usize, RandomState>,
62 chunks: Vec<KeyChunk<K>>,
64}
65
66impl<K: ArrowDictionaryKeyType> std::fmt::Debug for InProgressDictionaryArray<K> {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.debug_struct("InProgressDictionaryArray").finish()
69 }
70}
71
72impl<K: ArrowDictionaryKeyType> InProgressDictionaryArray<K> {
73 fn new(value_type: &DataType) -> Self {
74 Self {
75 source: None,
76 len: 0,
77 empty_values: new_empty_array(value_type),
78 sources: Vec::new(),
79 source_ids: HashMap::with_hasher(RandomState::with_seeds(0, 0, 0, 0)),
80 chunks: Vec::new(),
81 }
82 }
83
84 fn source_id(&mut self, values: ArrayRef) -> usize {
87 if let Some(last) = self.chunks.last()
88 && child_values_ptr_eq(&self.sources[last.source], &values)
89 {
90 return last.source;
91 }
92 let values_id = Arc::as_ptr(&values).cast::<()>() as usize;
93 if let Some(&source) = self.source_ids.get(&values_id) {
94 return source;
95 }
96 if let Some(source) = self
97 .sources
98 .iter()
99 .position(|existing| child_values_ptr_eq(existing, &values))
100 {
101 return source;
102 }
103 let source = self.sources.len();
104 self.sources.push(values);
105 self.source_ids.insert(values_id, source);
106 source
107 }
108
109 fn merge_sources(
113 sources: &[ArrayRef],
114 chunks: &[KeyChunk<K>],
115 len: usize,
116 ) -> Result<(Vec<K::Native>, ArrayRef), ArrowError> {
117 let mut referenced = sources
118 .iter()
119 .map(|source| {
120 let mut mask = BooleanBufferBuilder::new(source.len());
121 mask.advance(source.len());
122 mask
123 })
124 .collect::<Vec<_>>();
125
126 for chunk in chunks {
127 let mask = &mut referenced[chunk.source];
128 match &chunk.nulls {
129 Some(nulls) => nulls
130 .inner()
131 .set_indices()
132 .for_each(|offset| mask.set_bit(chunk.keys[offset].as_usize(), true)),
133 None => chunk
134 .keys
135 .iter()
136 .for_each(|key| mask.set_bit(key.as_usize(), true)),
137 }
138 }
139
140 let referenced = referenced
141 .into_iter()
142 .map(BooleanBufferBuilder::build)
143 .collect::<Vec<_>>();
144 let source_refs = sources
145 .iter()
146 .map(|source| source.as_ref() as &dyn Array)
147 .collect::<Vec<_>>();
148 let merged = merge_dictionary_values_with_masks::<K>(&source_refs, &referenced)?;
149
150 let mut keys = Vec::with_capacity(len);
151 for chunk in chunks {
152 let KeyMapping::Dense(mapping) = &merged.key_mappings[chunk.source] else {
153 unreachable!("merge_dictionary_values_with_masks returns dense mappings");
154 };
155 match &chunk.nulls {
156 Some(nulls) => keys.extend(chunk.keys.iter().enumerate().map(|(offset, key)| {
157 if nulls.is_valid(offset) {
158 mapping[key.as_usize()]
159 } else {
160 K::Native::default()
161 }
162 })),
163 None => keys.extend(chunk.keys.iter().map(|key| mapping[key.as_usize()])),
164 }
165 }
166 Ok((keys, merged.values))
167 }
168
169 fn build_nulls(chunks: &[KeyChunk<K>], len: usize) -> Option<NullBuffer> {
171 let mut nulls = NullBufferBuilder::new(len);
172 for chunk in chunks {
173 match &chunk.nulls {
174 Some(chunk_nulls) => nulls.append_buffer(chunk_nulls),
175 None => nulls.append_n_non_nulls(chunk.keys.len()),
176 }
177 }
178 nulls.finish()
179 }
180}
181
182impl<K: ArrowDictionaryKeyType> InProgressArray for InProgressDictionaryArray<K> {
183 fn set_source(&mut self, source: Option<ArrayRef>) {
184 self.source = source;
185 }
186
187 fn copy_rows(&mut self, offset: usize, len: usize) -> Result<(), ArrowError> {
188 if len == 0 {
189 return Ok(());
190 }
191 let source = Arc::clone(self.source.as_ref().ok_or_else(|| {
192 ArrowError::InvalidArgumentError(
193 "Internal Error: InProgressDictionaryArray: source not set".to_string(),
194 )
195 })?);
196 let dictionary = source.as_dictionary::<K>();
197 let keys = dictionary.keys();
198 let source = self.source_id(Arc::clone(dictionary.values()));
199 self.chunks.push(KeyChunk {
200 keys: keys.values().slice(offset, len),
201 nulls: keys.nulls().map(|nulls| nulls.slice(offset, len)),
202 source,
203 });
204 self.len += len;
205 Ok(())
206 }
207
208 fn finish(&mut self) -> Result<ArrayRef, ArrowError> {
209 let sources = std::mem::take(&mut self.sources);
210 let chunks = std::mem::take(&mut self.chunks);
211 let len = std::mem::take(&mut self.len);
212 self.source_ids.clear();
213
214 let (keys, values) = match sources.len() {
215 0 => (Vec::new(), Arc::clone(&self.empty_values)),
216 1 => (
217 chunks
218 .iter()
219 .flat_map(|chunk| chunk.keys.iter().copied())
220 .collect(),
221 Arc::clone(&sources[0]),
222 ),
223 _ => Self::merge_sources(&sources, &chunks, len)?,
224 };
225 let nulls = Self::build_nulls(&chunks, len);
226 let keys = PrimitiveArray::<K>::try_new(ScalarBuffer::from(keys), nulls)?;
227 Ok(Arc::new(DictionaryArray::try_new(keys, values)?))
228 }
229
230 fn size(&self) -> usize {
232 self.source
233 .as_ref()
234 .map_or(0, |source| source.get_array_memory_size())
235 + self
236 .sources
237 .iter()
238 .map(|source| source.get_array_memory_size())
239 .sum::<usize>()
240 + self.sources.capacity() * size_of::<ArrayRef>()
241 + self.source_ids.capacity() * size_of::<(usize, usize)>()
242 + self.chunks.capacity() * size_of::<KeyChunk<K>>()
243 + self
244 .chunks
245 .iter()
246 .map(|chunk| {
247 chunk.keys.inner().capacity()
248 + chunk
249 .nulls
250 .as_ref()
251 .map_or(0, |nulls| nulls.inner().inner().capacity())
252 })
253 .sum::<usize>()
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::coalesce::BatchCoalescer;
261 use arrow_array::types::{Int8Type, Int32Type};
262 use arrow_array::{Int8Array, Int32Array, RecordBatch, StringArray};
263
264 fn batch(values: &ArrayRef, keys: Vec<Option<i32>>) -> RecordBatch {
265 let dictionary = Arc::new(DictionaryArray::<Int32Type>::new(
266 Int32Array::from(keys),
267 Arc::clone(values),
268 )) as ArrayRef;
269 RecordBatch::try_from_iter([("dict", dictionary)]).unwrap()
270 }
271
272 #[test]
273 fn coalesces_repeated_and_distinct_values() {
274 let first_values = Arc::new(StringArray::from(vec!["a", "b"])) as ArrayRef;
275 let second_values = Arc::new(StringArray::from(vec!["b", "c"])) as ArrayRef;
276 let batches = [
277 batch(&first_values, vec![Some(0), None]),
278 batch(&second_values, vec![Some(0), Some(1)]),
279 batch(&first_values, vec![Some(1), Some(0)]),
280 ];
281 let mut coalescer = BatchCoalescer::new(batches[0].schema(), 6);
282 for batch in batches {
283 coalescer.push_batch(batch).unwrap();
284 }
285
286 let output = coalescer.next_completed_batch().unwrap();
287 let output = output.column(0).as_dictionary::<Int32Type>();
288 let values = output.values().as_string::<i32>();
289 let decoded = output
290 .keys_iter()
291 .map(|key| key.map(|key| values.value(key)))
292 .collect::<Vec<_>>();
293 assert_eq!(
294 decoded,
295 vec![Some("a"), None, Some("b"), Some("c"), Some("b"), Some("a")]
296 );
297 assert_eq!(values.len(), 4);
298 }
299
300 #[test]
301 fn reuses_one_values_array() {
302 let values = Arc::new(StringArray::from(vec!["a", "b"])) as ArrayRef;
303 let mut coalescer = BatchCoalescer::new(batch(&values, vec![Some(0)]).schema(), 2);
304 coalescer.push_batch(batch(&values, vec![Some(0)])).unwrap();
305 coalescer.push_batch(batch(&values, vec![Some(1)])).unwrap();
306
307 let output = coalescer.next_completed_batch().unwrap();
308 let output = output.column(0).as_dictionary::<Int32Type>();
309 assert!(Arc::ptr_eq(output.values(), &values));
310 }
311
312 #[test]
313 fn reuses_values_with_shared_buffers() {
314 let values = StringArray::from_iter_values((0..128).map(|i| format!("value-{i}")));
315 let first_values = Arc::new(values.clone()) as ArrayRef;
316 let second_values = Arc::new(values) as ArrayRef;
317 let make_batch = |values| {
318 let dictionary =
319 DictionaryArray::<Int8Type>::new(Int8Array::from_iter_values(0..=127), values);
320 RecordBatch::try_from_iter([("dict", Arc::new(dictionary) as ArrayRef)]).unwrap()
321 };
322
323 let first = make_batch(Arc::clone(&first_values));
324 let mut coalescer = BatchCoalescer::new(first.schema(), 256);
325 coalescer.push_batch(first).unwrap();
326 coalescer.push_batch(make_batch(second_values)).unwrap();
327
328 let output = coalescer.next_completed_batch().unwrap();
329 let output = output.column(0).as_dictionary::<Int8Type>();
330 assert!(Arc::ptr_eq(output.values(), &first_values));
331 assert_eq!(
332 output.keys().values(),
333 &(0..=127).chain(0..=127).collect::<Vec<_>>()
334 );
335 }
336
337 #[test]
338 fn remaps_sparse_values_across_sources() {
339 let first = Arc::new(DictionaryArray::<Int32Type>::new(
340 Int32Array::from_iter_values([1, 2, 0, 2, 1]),
341 Arc::new(StringArray::from(vec!["a", "unused-a", "b"])),
342 )) as ArrayRef;
343 let second = Arc::new(DictionaryArray::<Int32Type>::new(
344 Int32Array::from_iter_values([2, 1, 0, 1, 2]),
345 Arc::new(StringArray::from(vec!["b", "c", "unused-b"])),
346 )) as ArrayRef;
347
348 let mut in_progress = InProgressDictionaryArray::<Int32Type>::new(&DataType::Utf8);
349 in_progress.set_source(Some(first));
350 in_progress.copy_rows(1, 3).unwrap();
351 in_progress.set_source(Some(second));
352 in_progress.copy_rows(1, 3).unwrap();
353
354 let output = in_progress.finish().unwrap();
355 let output = output.as_dictionary::<Int32Type>();
356 assert_eq!(output.keys().values(), &[1, 0, 1, 3, 2, 3]);
357 assert_eq!(
358 output
359 .values()
360 .as_string::<i32>()
361 .iter()
362 .collect::<Vec<_>>(),
363 vec![Some("a"), Some("b"), Some("b"), Some("c")]
364 );
365 }
366
367 #[test]
368 fn remaps_keys_when_deduplication_is_required() {
369 let make_values = || {
370 Arc::new(StringArray::from_iter_values(
371 (0..70).map(|idx| format!("value-{idx}")),
372 )) as ArrayRef
373 };
374 let first_values = make_values();
375 let second_values = make_values();
376 let make_dictionary = |values| {
377 Arc::new(DictionaryArray::<Int8Type>::new(
378 Int8Array::from_iter_values(0..70),
379 values,
380 )) as ArrayRef
381 };
382
383 let mut in_progress = InProgressDictionaryArray::<Int8Type>::new(&DataType::Utf8);
384 in_progress.set_source(Some(make_dictionary(first_values)));
385 in_progress.copy_rows(0, 70).unwrap();
386 in_progress.set_source(Some(make_dictionary(second_values)));
387 in_progress.copy_rows(0, 70).unwrap();
388
389 let output = in_progress.finish().unwrap();
390 let output = output.as_dictionary::<Int8Type>();
391 let values = output.values().as_string::<i32>();
392 for (idx, key) in output.keys().values().iter().enumerate() {
393 assert_eq!(values.value(*key as usize), format!("value-{}", idx % 70));
394 }
395 assert!(values.len() <= i8::MAX as usize + 1);
396 }
397}