1use super::*;
19use crate::spec::VariantType;
20
21struct PruneColumn {
22 selected: HashSet<i32>,
23 select_full_types: bool,
24}
25
26pub fn prune_columns(
28 schema: &Schema,
29 selected: impl IntoIterator<Item = i32>,
30 select_full_types: bool,
31) -> Result<Type> {
32 let mut visitor = PruneColumn::new(HashSet::from_iter(selected), select_full_types);
33 let result = visit_schema(schema, &mut visitor);
34
35 match result {
36 Ok(s) => {
37 if let Some(struct_type) = s {
38 Ok(struct_type)
39 } else {
40 Ok(Type::Struct(StructType::default()))
41 }
42 }
43 Err(e) => Err(e),
44 }
45}
46
47impl PruneColumn {
48 fn new(selected: HashSet<i32>, select_full_types: bool) -> Self {
49 Self {
50 selected,
51 select_full_types,
52 }
53 }
54
55 fn project_selected_struct(projected_field: Option<Type>) -> Result<StructType> {
56 match projected_field {
57 Some(Type::Struct(s)) => Ok(s),
59 Some(_) => Err(Error::new(
60 ErrorKind::Unexpected,
61 "Projected field with struct type must be struct".to_string(),
62 )),
63 None => Ok(StructType::default()),
65 }
66 }
67 fn project_list(list: &ListType, element_result: Type) -> Result<ListType> {
68 if *list.element_field.field_type == element_result {
69 return Ok(list.clone());
70 }
71 Ok(ListType {
72 element_field: Arc::new(NestedField {
73 id: list.element_field.id,
74 name: list.element_field.name.clone(),
75 required: list.element_field.required,
76 field_type: Box::new(element_result),
77 doc: list.element_field.doc.clone(),
78 initial_default: list.element_field.initial_default.clone(),
79 write_default: list.element_field.write_default.clone(),
80 }),
81 })
82 }
83 fn project_map(map: &MapType, value_result: Type) -> Result<MapType> {
84 if *map.value_field.field_type == value_result {
85 return Ok(map.clone());
86 }
87 Ok(MapType {
88 key_field: map.key_field.clone(),
89 value_field: Arc::new(NestedField {
90 id: map.value_field.id,
91 name: map.value_field.name.clone(),
92 required: map.value_field.required,
93 field_type: Box::new(value_result),
94 doc: map.value_field.doc.clone(),
95 initial_default: map.value_field.initial_default.clone(),
96 write_default: map.value_field.write_default.clone(),
97 }),
98 })
99 }
100}
101
102impl SchemaVisitor for PruneColumn {
103 type T = Option<Type>;
104
105 fn schema(&mut self, _schema: &Schema, value: Option<Type>) -> Result<Option<Type>> {
106 Ok(Some(value.unwrap()))
107 }
108
109 fn field(&mut self, field: &NestedFieldRef, value: Option<Type>) -> Result<Option<Type>> {
110 if self.selected.contains(&field.id) {
111 if self.select_full_types {
112 Ok(Some(*field.field_type.clone()))
113 } else if field.field_type.is_struct() {
114 Ok(Some(Type::Struct(PruneColumn::project_selected_struct(
115 value,
116 )?)))
117 } else if !field.field_type.is_nested() {
118 Ok(Some(*field.field_type.clone()))
119 } else {
120 Err(Error::new(
121 ErrorKind::DataInvalid,
122 "Can't project list or map field directly when not selecting full type."
123 .to_string(),
124 )
125 .with_context("field_id", field.id.to_string())
126 .with_context("field_type", field.field_type.to_string()))
127 }
128 } else {
129 Ok(value)
130 }
131 }
132
133 fn r#struct(
134 &mut self,
135 r#struct: &StructType,
136 results: Vec<Option<Type>>,
137 ) -> Result<Option<Type>> {
138 let fields = r#struct.fields();
139 let mut selected_field = Vec::with_capacity(fields.len());
140 let mut same_type = true;
141
142 for (field, projected_type) in zip_eq(fields.iter(), results.iter()) {
143 if let Some(projected_type) = projected_type {
144 if *field.field_type == *projected_type {
145 selected_field.push(field.clone());
146 } else {
147 same_type = false;
148 let new_field = NestedField {
149 id: field.id,
150 name: field.name.clone(),
151 required: field.required,
152 field_type: Box::new(projected_type.clone()),
153 doc: field.doc.clone(),
154 initial_default: field.initial_default.clone(),
155 write_default: field.write_default.clone(),
156 };
157 selected_field.push(Arc::new(new_field));
158 }
159 }
160 }
161
162 if !selected_field.is_empty() {
163 if selected_field.len() == fields.len() && same_type {
164 return Ok(Some(Type::Struct(r#struct.clone())));
165 } else {
166 return Ok(Some(Type::Struct(StructType::new(selected_field))));
167 }
168 }
169 Ok(None)
170 }
171
172 fn list(&mut self, list: &ListType, value: Option<Type>) -> Result<Option<Type>> {
173 if self.selected.contains(&list.element_field.id) {
174 if self.select_full_types {
175 Ok(Some(Type::List(list.clone())))
176 } else if list.element_field.field_type.is_struct() {
177 let projected_struct = PruneColumn::project_selected_struct(value).unwrap();
178 Ok(Some(Type::List(PruneColumn::project_list(
179 list,
180 Type::Struct(projected_struct),
181 )?)))
182 } else if list.element_field.field_type.is_primitive() {
183 Ok(Some(Type::List(list.clone())))
184 } else {
185 Err(Error::new(
186 ErrorKind::DataInvalid,
187 format!(
188 "Cannot explicitly project List or Map types, List element {} of type {} was selected",
189 list.element_field.id, list.element_field.field_type
190 ),
191 ))
192 }
193 } else if let Some(result) = value {
194 Ok(Some(Type::List(PruneColumn::project_list(list, result)?)))
195 } else {
196 Ok(None)
197 }
198 }
199
200 fn map(
201 &mut self,
202 map: &MapType,
203 _key_value: Option<Type>,
204 value: Option<Type>,
205 ) -> Result<Option<Type>> {
206 if self.selected.contains(&map.value_field.id) {
207 if self.select_full_types {
208 Ok(Some(Type::Map(map.clone())))
209 } else if map.value_field.field_type.is_struct() {
210 let projected_struct =
211 PruneColumn::project_selected_struct(Some(value.unwrap())).unwrap();
212 Ok(Some(Type::Map(PruneColumn::project_map(
213 map,
214 Type::Struct(projected_struct),
215 )?)))
216 } else if map.value_field.field_type.is_primitive() {
217 Ok(Some(Type::Map(map.clone())))
218 } else {
219 Err(Error::new(
220 ErrorKind::DataInvalid,
221 format!(
222 "Cannot explicitly project List or Map types, Map value {} of type {} was selected",
223 map.value_field.id, map.value_field.field_type
224 ),
225 ))
226 }
227 } else if let Some(value_result) = value {
228 Ok(Some(Type::Map(PruneColumn::project_map(
229 map,
230 value_result,
231 )?)))
232 } else if self.selected.contains(&map.key_field.id) {
233 Ok(Some(Type::Map(map.clone())))
234 } else {
235 Ok(None)
236 }
237 }
238
239 fn primitive(&mut self, _p: &PrimitiveType) -> Result<Option<Type>> {
240 Ok(None)
241 }
242
243 fn variant(&mut self, _v: &VariantType) -> Result<Self::T> {
244 Ok(None)
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use Type::Primitive;
251
252 use super::*;
253 use crate::spec::schema::tests::table_schema_nested;
254
255 #[test]
256 fn test_schema_prune_columns_string() {
257 let expected_type = Type::from(
258 Schema::builder()
259 .with_fields(vec![
260 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
261 ])
262 .build()
263 .unwrap()
264 .as_struct()
265 .clone(),
266 );
267 let schema = table_schema_nested();
268 let selected: HashSet<i32> = HashSet::from([1]);
269 let result = prune_columns(&schema, selected, false);
270 assert!(result.is_ok());
271 assert_eq!(result.unwrap(), expected_type);
272 }
273
274 #[test]
275 fn test_schema_prune_columns_string_full() {
276 let expected_type = Type::from(
277 Schema::builder()
278 .with_fields(vec![
279 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
280 ])
281 .build()
282 .unwrap()
283 .as_struct()
284 .clone(),
285 );
286 let schema = table_schema_nested();
287 let selected: HashSet<i32> = HashSet::from([1]);
288 let result = prune_columns(&schema, selected, true);
289 assert!(result.is_ok());
290 assert_eq!(result.unwrap(), expected_type);
291 }
292
293 #[test]
294 fn test_schema_prune_columns_list() {
295 let expected_type = Type::from(
296 Schema::builder()
297 .with_fields(vec![
298 NestedField::required(
299 4,
300 "qux",
301 Type::List(ListType {
302 element_field: NestedField::list_element(
303 5,
304 Primitive(PrimitiveType::String),
305 true,
306 )
307 .into(),
308 }),
309 )
310 .into(),
311 ])
312 .build()
313 .unwrap()
314 .as_struct()
315 .clone(),
316 );
317 let schema = table_schema_nested();
318 let selected: HashSet<i32> = HashSet::from([5]);
319 let result = prune_columns(&schema, selected, false);
320 assert!(result.is_ok());
321 assert_eq!(result.unwrap(), expected_type);
322 }
323
324 #[test]
325 fn test_prune_columns_list_itself() {
326 let schema = table_schema_nested();
327 let selected: HashSet<i32> = HashSet::from([4]);
328 let result = prune_columns(&schema, selected, false);
329 assert!(result.is_err());
330 }
331
332 #[test]
333 fn test_schema_prune_columns_list_full() {
334 let expected_type = Type::from(
335 Schema::builder()
336 .with_fields(vec![
337 NestedField::required(
338 4,
339 "qux",
340 Type::List(ListType {
341 element_field: NestedField::list_element(
342 5,
343 Primitive(PrimitiveType::String),
344 true,
345 )
346 .into(),
347 }),
348 )
349 .into(),
350 ])
351 .build()
352 .unwrap()
353 .as_struct()
354 .clone(),
355 );
356 let schema = table_schema_nested();
357 let selected: HashSet<i32> = HashSet::from([5]);
358 let result = prune_columns(&schema, selected, true);
359 assert!(result.is_ok());
360 assert_eq!(result.unwrap(), expected_type);
361 }
362
363 #[test]
364 fn test_prune_columns_map() {
365 let expected_type = Type::from(
366 Schema::builder()
367 .with_fields(vec![
368 NestedField::required(
369 6,
370 "quux",
371 Type::Map(MapType {
372 key_field: NestedField::map_key_element(
373 7,
374 Primitive(PrimitiveType::String),
375 )
376 .into(),
377 value_field: NestedField::map_value_element(
378 8,
379 Type::Map(MapType {
380 key_field: NestedField::map_key_element(
381 9,
382 Primitive(PrimitiveType::String),
383 )
384 .into(),
385 value_field: NestedField::map_value_element(
386 10,
387 Primitive(PrimitiveType::Int),
388 true,
389 )
390 .into(),
391 }),
392 true,
393 )
394 .into(),
395 }),
396 )
397 .into(),
398 ])
399 .build()
400 .unwrap()
401 .as_struct()
402 .clone(),
403 );
404 let schema = table_schema_nested();
405 let selected: HashSet<i32> = HashSet::from([9]);
406 let result = prune_columns(&schema, selected, false);
407 assert!(result.is_ok());
408 assert_eq!(result.unwrap(), expected_type);
409 }
410
411 #[test]
412 fn test_prune_columns_map_itself() {
413 let schema = table_schema_nested();
414 let selected: HashSet<i32> = HashSet::from([6]);
415 let result = prune_columns(&schema, selected, false);
416 assert!(result.is_err());
417 }
418
419 #[test]
420 fn test_prune_columns_map_full() {
421 let expected_type = Type::from(
422 Schema::builder()
423 .with_fields(vec![
424 NestedField::required(
425 6,
426 "quux",
427 Type::Map(MapType {
428 key_field: NestedField::map_key_element(
429 7,
430 Primitive(PrimitiveType::String),
431 )
432 .into(),
433 value_field: NestedField::map_value_element(
434 8,
435 Type::Map(MapType {
436 key_field: NestedField::map_key_element(
437 9,
438 Primitive(PrimitiveType::String),
439 )
440 .into(),
441 value_field: NestedField::map_value_element(
442 10,
443 Primitive(PrimitiveType::Int),
444 true,
445 )
446 .into(),
447 }),
448 true,
449 )
450 .into(),
451 }),
452 )
453 .into(),
454 ])
455 .build()
456 .unwrap()
457 .as_struct()
458 .clone(),
459 );
460 let schema = table_schema_nested();
461 let selected: HashSet<i32> = HashSet::from([9]);
462 let result = prune_columns(&schema, selected, true);
463 assert!(result.is_ok());
464 assert_eq!(result.unwrap(), expected_type);
465 }
466
467 #[test]
468 fn test_prune_columns_map_key() {
469 let expected_type = Type::from(
470 Schema::builder()
471 .with_fields(vec![
472 NestedField::required(
473 6,
474 "quux",
475 Type::Map(MapType {
476 key_field: NestedField::map_key_element(
477 7,
478 Primitive(PrimitiveType::String),
479 )
480 .into(),
481 value_field: NestedField::map_value_element(
482 8,
483 Type::Map(MapType {
484 key_field: NestedField::map_key_element(
485 9,
486 Primitive(PrimitiveType::String),
487 )
488 .into(),
489 value_field: NestedField::map_value_element(
490 10,
491 Primitive(PrimitiveType::Int),
492 true,
493 )
494 .into(),
495 }),
496 true,
497 )
498 .into(),
499 }),
500 )
501 .into(),
502 ])
503 .build()
504 .unwrap()
505 .as_struct()
506 .clone(),
507 );
508 let schema = table_schema_nested();
509 let selected: HashSet<i32> = HashSet::from([10]);
510 let result = prune_columns(&schema, selected, false);
511 assert!(result.is_ok());
512 assert_eq!(result.unwrap(), expected_type);
513 }
514
515 #[test]
516 fn test_prune_columns_struct() {
517 let expected_type = Type::from(
518 Schema::builder()
519 .with_fields(vec![
520 NestedField::optional(
521 15,
522 "person",
523 Type::Struct(StructType::new(vec![
524 NestedField::optional(16, "name", Primitive(PrimitiveType::String))
525 .into(),
526 ])),
527 )
528 .into(),
529 ])
530 .build()
531 .unwrap()
532 .as_struct()
533 .clone(),
534 );
535 let schema = table_schema_nested();
536 let selected: HashSet<i32> = HashSet::from([16]);
537 let result = prune_columns(&schema, selected, false);
538 assert!(result.is_ok());
539 assert_eq!(result.unwrap(), expected_type);
540 }
541
542 #[test]
543 fn test_prune_columns_struct_full() {
544 let expected_type = Type::from(
545 Schema::builder()
546 .with_fields(vec![
547 NestedField::optional(
548 15,
549 "person",
550 Type::Struct(StructType::new(vec![
551 NestedField::optional(16, "name", Primitive(PrimitiveType::String))
552 .into(),
553 ])),
554 )
555 .into(),
556 ])
557 .build()
558 .unwrap()
559 .as_struct()
560 .clone(),
561 );
562 let schema = table_schema_nested();
563 let selected: HashSet<i32> = HashSet::from([16]);
564 let result = prune_columns(&schema, selected, true);
565 assert!(result.is_ok());
566 assert_eq!(result.unwrap(), expected_type);
567 }
568
569 #[test]
570 fn test_prune_columns_empty_struct() {
571 let schema_with_empty_struct_field = Schema::builder()
572 .with_fields(vec![
573 NestedField::optional(15, "person", Type::Struct(StructType::new(vec![]))).into(),
574 ])
575 .build()
576 .unwrap();
577 let expected_type = Type::from(
578 Schema::builder()
579 .with_fields(vec![
580 NestedField::optional(15, "person", Type::Struct(StructType::new(vec![])))
581 .into(),
582 ])
583 .build()
584 .unwrap()
585 .as_struct()
586 .clone(),
587 );
588 let selected: HashSet<i32> = HashSet::from([15]);
589 let result = prune_columns(&schema_with_empty_struct_field, selected, false);
590 assert!(result.is_ok());
591 assert_eq!(result.unwrap(), expected_type);
592 }
593
594 #[test]
595 fn test_prune_columns_empty_struct_full() {
596 let schema_with_empty_struct_field = Schema::builder()
597 .with_fields(vec![
598 NestedField::optional(15, "person", Type::Struct(StructType::new(vec![]))).into(),
599 ])
600 .build()
601 .unwrap();
602 let expected_type = Type::from(
603 Schema::builder()
604 .with_fields(vec![
605 NestedField::optional(15, "person", Type::Struct(StructType::new(vec![])))
606 .into(),
607 ])
608 .build()
609 .unwrap()
610 .as_struct()
611 .clone(),
612 );
613 let selected: HashSet<i32> = HashSet::from([15]);
614 let result = prune_columns(&schema_with_empty_struct_field, selected, true);
615 assert!(result.is_ok());
616 assert_eq!(result.unwrap(), expected_type);
617 }
618
619 #[test]
620 fn test_prune_columns_struct_in_map() {
621 let schema_with_struct_in_map_field = Schema::builder()
622 .with_schema_id(1)
623 .with_fields(vec![
624 NestedField::required(
625 6,
626 "id_to_person",
627 Type::Map(MapType {
628 key_field: NestedField::map_key_element(7, Primitive(PrimitiveType::Int))
629 .into(),
630 value_field: NestedField::map_value_element(
631 8,
632 Type::Struct(StructType::new(vec![
633 NestedField::optional(10, "name", Primitive(PrimitiveType::String))
634 .into(),
635 NestedField::required(11, "age", Primitive(PrimitiveType::Int))
636 .into(),
637 ])),
638 true,
639 )
640 .into(),
641 }),
642 )
643 .into(),
644 ])
645 .build()
646 .unwrap();
647 let expected_type = Type::from(
648 Schema::builder()
649 .with_fields(vec![
650 NestedField::required(
651 6,
652 "id_to_person",
653 Type::Map(MapType {
654 key_field: NestedField::map_key_element(
655 7,
656 Primitive(PrimitiveType::Int),
657 )
658 .into(),
659 value_field: NestedField::map_value_element(
660 8,
661 Type::Struct(StructType::new(vec![
662 NestedField::required(11, "age", Primitive(PrimitiveType::Int))
663 .into(),
664 ])),
665 true,
666 )
667 .into(),
668 }),
669 )
670 .into(),
671 ])
672 .build()
673 .unwrap()
674 .as_struct()
675 .clone(),
676 );
677 let selected: HashSet<i32> = HashSet::from([11]);
678 let result = prune_columns(&schema_with_struct_in_map_field, selected, false);
679 assert!(result.is_ok());
680 assert_eq!(result.unwrap(), expected_type);
681 }
682 #[test]
683 fn test_prune_columns_struct_in_map_full() {
684 let schema = Schema::builder()
685 .with_schema_id(1)
686 .with_fields(vec![
687 NestedField::required(
688 6,
689 "id_to_person",
690 Type::Map(MapType {
691 key_field: NestedField::map_key_element(7, Primitive(PrimitiveType::Int))
692 .into(),
693 value_field: NestedField::map_value_element(
694 8,
695 Type::Struct(StructType::new(vec![
696 NestedField::optional(10, "name", Primitive(PrimitiveType::String))
697 .into(),
698 NestedField::required(11, "age", Primitive(PrimitiveType::Int))
699 .into(),
700 ])),
701 true,
702 )
703 .into(),
704 }),
705 )
706 .into(),
707 ])
708 .build()
709 .unwrap();
710 let expected_type = Type::from(
711 Schema::builder()
712 .with_fields(vec![
713 NestedField::required(
714 6,
715 "id_to_person",
716 Type::Map(MapType {
717 key_field: NestedField::map_key_element(
718 7,
719 Primitive(PrimitiveType::Int),
720 )
721 .into(),
722 value_field: NestedField::map_value_element(
723 8,
724 Type::Struct(StructType::new(vec![
725 NestedField::required(11, "age", Primitive(PrimitiveType::Int))
726 .into(),
727 ])),
728 true,
729 )
730 .into(),
731 }),
732 )
733 .into(),
734 ])
735 .build()
736 .unwrap()
737 .as_struct()
738 .clone(),
739 );
740 let selected: HashSet<i32> = HashSet::from([11]);
741 let result = prune_columns(&schema, selected, true);
742 assert!(result.is_ok());
743 assert_eq!(result.unwrap(), expected_type);
744 }
745
746 #[test]
747 fn test_prune_columns_select_original_schema() {
748 let schema = table_schema_nested();
749 let selected: HashSet<i32> = (0..schema.highest_field_id() + 1).collect();
750 let result = prune_columns(&schema, selected, true);
751 assert!(result.is_ok());
752 assert_eq!(result.unwrap(), Type::Struct(schema.as_struct().clone()));
753 }
754
755 #[test]
756 fn test_prune_columns_variant() {
757 let schema = Schema::builder()
759 .with_fields(vec![
760 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
761 NestedField::optional(2, "v", Type::Variant(VariantType)).into(),
762 ])
763 .build()
764 .unwrap();
765
766 let only_variant = Type::Struct(StructType::new(vec![
769 NestedField::optional(2, "v", Type::Variant(VariantType)).into(),
770 ]));
771 for full in [false, true] {
772 let result = prune_columns(&schema, HashSet::from([2]), full).unwrap();
773 assert_eq!(result, only_variant, "select_full_types={full}");
774 }
775
776 let only_foo = Type::Struct(StructType::new(vec![
778 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
779 ]));
780 let result = prune_columns(&schema, HashSet::from([1]), false).unwrap();
781 assert_eq!(result, only_foo);
782 }
783}