Skip to content

Commit 4aef963

Browse files
committed
x
1 parent d3105f6 commit 4aef963

4 files changed

Lines changed: 554 additions & 274 deletions

File tree

src/query/sql/src/planner/plans/union_all.rs

Lines changed: 0 additions & 274 deletions
Original file line numberDiff line numberDiff line change
@@ -311,277 +311,3 @@ impl Operator for UnionAll {
311311
Ok(children_required)
312312
}
313313
}
314-
315-
#[cfg(test)]
316-
mod tests {
317-
use std::collections::HashMap;
318-
319-
use databend_common_expression::stat_distribution::NdvEstimate;
320-
use databend_common_expression::stat_distribution::StatCount;
321-
use databend_common_expression::types::ArgType;
322-
use databend_common_expression::types::DataType;
323-
use databend_common_expression::types::Int32Type;
324-
use databend_common_expression::types::Int64Type;
325-
use databend_common_statistics::Datum;
326-
use databend_common_statistics::HistogramBuilder;
327-
328-
use super::*;
329-
use crate::ColumnBindingBuilder;
330-
use crate::Visibility;
331-
use crate::plans::BoundColumnRef;
332-
use crate::plans::CastExpr;
333-
use crate::plans::Join;
334-
use crate::plans::JoinEquiCondition;
335-
use crate::plans::JoinType;
336-
337-
fn stat_info(cardinality: u64, column: Option<(Symbol, ColumnStat)>) -> Arc<StatInfo> {
338-
Arc::new(StatInfo {
339-
cardinality: cardinality as f64,
340-
statistics: Statistics {
341-
precise_cardinality: Some(cardinality),
342-
column_stats: column.into_iter().collect::<HashMap<_, _>>(),
343-
top_n: Default::default(),
344-
count_min_sketch: Default::default(),
345-
},
346-
})
347-
}
348-
349-
fn column_stat(min: i64, max: i64, ndv: f64, null_count: u64) -> ColumnStat {
350-
ColumnStat {
351-
min: Datum::Int(min),
352-
max: Datum::Int(max),
353-
ndv: NdvEstimate::exact(ndv),
354-
null_count: StatCount::exact(null_count),
355-
histogram: None,
356-
}
357-
}
358-
359-
fn typed_column(index: Symbol, data_type: DataType) -> ScalarExpr {
360-
ScalarExpr::BoundColumnRef(BoundColumnRef {
361-
span: None,
362-
column: ColumnBindingBuilder::new(
363-
format!("c{}", index.as_usize()),
364-
index,
365-
Box::new(data_type),
366-
Visibility::Visible,
367-
)
368-
.build(),
369-
})
370-
}
371-
372-
#[test]
373-
fn test_union_stats_merge_and_remap_output_column() -> Result<()> {
374-
let left = Symbol::new(1);
375-
let right = Symbol::new(2);
376-
let output = Symbol::new(10);
377-
let union = UnionAll {
378-
left_outputs: vec![(left, None)],
379-
right_outputs: vec![(right, None)],
380-
cte_scan_names: vec![],
381-
logical_recursive_cte_id: None,
382-
output_indexes: vec![output],
383-
};
384-
let stats = union.derive_union_stats(
385-
stat_info(10, Some((left, column_stat(1, 3, 3.0, 1)))),
386-
stat_info(20, Some((right, column_stat(4, 8, 5.0, 2)))),
387-
)?;
388-
389-
assert_eq!(stats.cardinality, 30.0);
390-
assert_eq!(stats.statistics.precise_cardinality, Some(30));
391-
assert!(!stats.statistics.column_stats.contains_key(&left));
392-
assert!(!stats.statistics.column_stats.contains_key(&right));
393-
let output_stat = &stats.statistics.column_stats[&output];
394-
assert_eq!(output_stat, &ColumnStat {
395-
min: Datum::Int(1),
396-
max: Datum::Int(8),
397-
ndv: NdvEstimate::new(5.0, 8.0),
398-
null_count: StatCount::exact(3),
399-
histogram: None,
400-
});
401-
Ok(())
402-
}
403-
404-
#[test]
405-
fn test_union_stats_apply_lossless_coercion_cast() -> Result<()> {
406-
let left = Symbol::new(1);
407-
let right = Symbol::new(2);
408-
let output = Symbol::new(10);
409-
let union = UnionAll {
410-
left_outputs: vec![(
411-
left,
412-
Some(ScalarExpr::CastExpr(CastExpr {
413-
span: None,
414-
is_try: false,
415-
argument: Box::new(typed_column(left, Int32Type::data_type().wrap_nullable())),
416-
target_type: Box::new(Int64Type::data_type().wrap_nullable()),
417-
})),
418-
)],
419-
right_outputs: vec![(right, None)],
420-
cte_scan_names: vec![],
421-
logical_recursive_cte_id: None,
422-
output_indexes: vec![output],
423-
};
424-
425-
let stats = union.derive_union_stats(
426-
stat_info(10, Some((left, column_stat(1, 3, 3.0, 1)))),
427-
stat_info(20, Some((right, column_stat(4, 8, 5.0, 2)))),
428-
)?;
429-
430-
let output_stat = &stats.statistics.column_stats[&output];
431-
assert_eq!(output_stat, &ColumnStat {
432-
min: Datum::Int(1),
433-
max: Datum::Int(8),
434-
ndv: NdvEstimate::new(5.0, 8.0),
435-
null_count: StatCount::exact(3),
436-
histogram: None,
437-
});
438-
Ok(())
439-
}
440-
441-
#[test]
442-
fn test_union_stats_do_not_keep_partial_stats_from_nonempty_branch() -> Result<()> {
443-
let left = Symbol::new(1);
444-
let right = Symbol::new(2);
445-
let output = Symbol::new(10);
446-
let union = UnionAll {
447-
left_outputs: vec![(left, None)],
448-
right_outputs: vec![(right, None)],
449-
cte_scan_names: vec![],
450-
logical_recursive_cte_id: None,
451-
output_indexes: vec![output],
452-
};
453-
454-
let stats = union.derive_union_stats(
455-
stat_info(10, Some((left, column_stat(1, 3, 3.0, 0)))),
456-
stat_info(20, None),
457-
)?;
458-
assert!(!stats.statistics.column_stats.contains_key(&output));
459-
460-
let stats = union.derive_union_stats(
461-
stat_info(10, Some((left, column_stat(1, 3, 3.0, 0)))),
462-
stat_info(0, None),
463-
)?;
464-
assert_eq!(
465-
stats.statistics.column_stats[&output].ndv,
466-
NdvEstimate::exact(3.0)
467-
);
468-
Ok(())
469-
}
470-
471-
#[test]
472-
fn test_union_ndv_fallback_preserves_known_side() -> Result<()> {
473-
let left = Symbol::new(1);
474-
let right = Symbol::new(2);
475-
let output = Symbol::new(10);
476-
let mut left_stat = column_stat(1, 3, 3.0, 0);
477-
left_stat.ndv = NdvEstimate::upper_bound(3.0);
478-
479-
let union = UnionAll {
480-
left_outputs: vec![(left, None)],
481-
right_outputs: vec![(right, None)],
482-
cte_scan_names: vec![],
483-
logical_recursive_cte_id: None,
484-
output_indexes: vec![output],
485-
};
486-
let stats = union.derive_union_stats(
487-
stat_info(10, Some((left, left_stat))),
488-
stat_info(20, Some((right, column_stat(2, 8, 5.0, 0)))),
489-
)?;
490-
491-
assert_eq!(
492-
stats.statistics.column_stats[&output].ndv,
493-
NdvEstimate::new(5.0, 8.0)
494-
);
495-
Ok(())
496-
}
497-
498-
#[test]
499-
fn test_union_output_stats_are_consumed_by_join_estimation() -> Result<()> {
500-
let left = Symbol::new(1);
501-
let right = Symbol::new(2);
502-
let output = Symbol::new(10);
503-
let join_key = Symbol::new(20);
504-
let union = UnionAll {
505-
left_outputs: vec![(left, None)],
506-
right_outputs: vec![(right, None)],
507-
cte_scan_names: vec![],
508-
logical_recursive_cte_id: None,
509-
output_indexes: vec![output],
510-
};
511-
let union_stats = union.derive_union_stats(
512-
stat_info(10, Some((left, column_stat(1, 3, 3.0, 0)))),
513-
stat_info(20, Some((right, column_stat(2, 8, 5.0, 0)))),
514-
)?;
515-
assert_eq!(
516-
union_stats.statistics.column_stats[&output].ndv,
517-
NdvEstimate::new(5.0, 8.0)
518-
);
519-
let join = Join {
520-
equi_conditions: vec![JoinEquiCondition::new(
521-
typed_column(output, Int64Type::data_type()),
522-
typed_column(join_key, Int64Type::data_type()),
523-
false,
524-
)],
525-
join_type: JoinType::Inner,
526-
..Default::default()
527-
};
528-
529-
let stats = join.derive_join_stats(
530-
union_stats,
531-
stat_info(100, Some((join_key, column_stat(1, 10, 10.0, 0)))),
532-
)?;
533-
534-
// 30 * 100 / max(union estimated NDV 5, right NDV 10), rather than 30 * 100.
535-
assert_eq!(stats.cardinality, 300.0);
536-
Ok(())
537-
}
538-
539-
#[test]
540-
fn test_union_ndv_uses_histogram_intersection() -> Result<()> {
541-
let left = Symbol::new(1);
542-
let right = Symbol::new(2);
543-
let output = Symbol::new(10);
544-
let mut left_histogram =
545-
HistogramBuilder::from_ndv(10, 100, Some((Datum::Int(0), Datum::Int(9))), 2)
546-
.expect("left histogram should be valid");
547-
let mut right_histogram =
548-
HistogramBuilder::from_ndv(10, 100, Some((Datum::Int(5), Datum::Int(14))), 2)
549-
.expect("right histogram should be valid");
550-
left_histogram.scale_counts(0.25);
551-
right_histogram.scale_counts(0.5);
552-
let left_ndv = left_histogram.ndv();
553-
let right_ndv = right_histogram.ndv();
554-
let intersection_ndv = left_histogram
555-
.estimate_join_numeric_compatible(&right_histogram)?
556-
.expect("integer histograms should be compatible")
557-
.ndv
558-
.expected
559-
.expect("histogram intersection should have an expected NDV");
560-
let mut left_stat = column_stat(0, 9, 10.0, 0);
561-
left_stat.ndv = left_ndv;
562-
left_stat.histogram = Some(left_histogram);
563-
let mut right_stat = column_stat(5, 14, 10.0, 0);
564-
right_stat.ndv = right_ndv;
565-
right_stat.histogram = Some(right_histogram);
566-
567-
let union = UnionAll {
568-
left_outputs: vec![(left, None)],
569-
right_outputs: vec![(right, None)],
570-
cte_scan_names: vec![],
571-
logical_recursive_cte_id: None,
572-
output_indexes: vec![output],
573-
};
574-
let stats = union.derive_union_stats(
575-
stat_info(25, Some((left, left_stat))),
576-
stat_info(50, Some((right, right_stat))),
577-
)?;
578-
579-
let expected = stats.statistics.column_stats[&output].ndv.expected.unwrap();
580-
let expected_from_intersection =
581-
left_ndv.expected.unwrap() + right_ndv.expected.unwrap() - intersection_ndv;
582-
assert!((expected - expected_from_intersection).abs() < 1e-9);
583-
assert!(expected > left_ndv.expected.unwrap().max(right_ndv.expected.unwrap()));
584-
assert!(expected < left_ndv.expected.unwrap() + right_ndv.expected.unwrap());
585-
Ok(())
586-
}
587-
}

src/query/sql/tests/it/optimizer/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ mod outer_join_to_anti;
2121
mod push_down_filter_project_set;
2222
mod selectivity;
2323
mod selectivity_smoke;
24+
mod union_all;

0 commit comments

Comments
 (0)