1use std::cmp::min;
50use std::collections::hash_map::Entry::{Occupied, Vacant};
51use std::collections::{BTreeMap, HashMap};
52use std::sync::Arc;
53
54use itertools::Itertools;
55use risingwave_common::array::VectorDistanceType;
56use risingwave_common::catalog::Schema;
57use risingwave_common::types::{
58 DataType, Date, Decimal, Int256, Interval, Serial, Time, Timestamp, Timestamptz,
59};
60use risingwave_common::util::iter_util::ZipEqFast;
61use risingwave_pb::plan_common::JoinType;
62use risingwave_sqlparser::ast::AsOf;
63
64use super::prelude::{PlanRef, *};
65use crate::catalog::index_catalog::TableIndex;
66use crate::expr::{
67 Expr, ExprImpl, ExprRewriter, ExprType, ExprVisitor, FunctionCall, InputRef, to_conjunctions,
68 to_disjunctions,
69};
70use crate::optimizer::optimizer_context::OptimizerContextRef;
71use crate::optimizer::plan_node::generic::GenericPlanRef;
72use crate::optimizer::plan_node::{
73 ColumnPruningContext, LogicalJoin, LogicalScan, LogicalUnion, PlanTreeNode, PlanTreeNodeBinary,
74 PredicatePushdown, PredicatePushdownContext, generic,
75};
76use crate::utils::Condition;
77
78const INDEX_MAX_LEN: usize = 5;
79const INDEX_COST_MATRIX: [[usize; INDEX_MAX_LEN]; 5] = [
80 [1, 1, 1, 1, 1],
81 [10, 8, 5, 5, 5],
82 [600, 50, 20, 10, 10],
83 [1400, 70, 25, 15, 10],
84 [4000, 100, 30, 20, 20],
85];
86const LOOKUP_COST_CONST: usize = 3;
87const MAX_COMBINATION_SIZE: usize = 4;
88const MAX_CONJUNCTION_SIZE: usize = 8;
89
90pub struct IndexSelectionRule {}
91
92impl Rule<Logical> for IndexSelectionRule {
93 fn apply(&self, plan: PlanRef) -> Option<PlanRef> {
94 let logical_scan: &LogicalScan = plan.as_logical_scan()?;
95 let indexes = logical_scan.table_indexes();
96 if indexes.is_empty() {
97 return None;
98 }
99 let primary_table_row_size = TableScanIoEstimator::estimate_row_size(logical_scan);
100 let primary_cost = min(
101 self.estimate_table_scan_cost(logical_scan, primary_table_row_size),
102 self.estimate_full_table_scan_cost(logical_scan, primary_table_row_size),
103 );
104
105 if primary_cost.primary_lookup {
107 return None;
108 }
109
110 let mut final_plan: PlanRef = logical_scan.clone().into();
111 let mut min_cost = primary_cost.clone();
112
113 for index in indexes {
114 if let Some(index_scan) = logical_scan.to_index_scan_if_index_covered(index) {
115 let index_cost = self.estimate_table_scan_cost(
116 &index_scan,
117 TableScanIoEstimator::estimate_row_size(&index_scan),
118 );
119
120 if index_cost.le(&min_cost) {
121 min_cost = index_cost;
122 final_plan = index_scan.into();
123 }
124 } else if let Some((index_lookup, lookup_cost)) =
125 self.gen_index_lookup(logical_scan, index)
126 {
127 if lookup_cost.le(&min_cost) {
129 min_cost = lookup_cost;
130 final_plan = index_lookup;
131 }
132 }
133 }
134
135 if let Some((merge_index, merge_index_cost)) = self.index_merge_selection(logical_scan)
136 && merge_index_cost.le(&min_cost)
137 {
138 min_cost = merge_index_cost;
139 final_plan = merge_index;
140 }
141
142 if min_cost == primary_cost {
143 None
144 } else {
145 Some(final_plan)
146 }
147 }
148}
149
150struct IndexPredicateRewriter<'a> {
151 p2s_mapping: &'a BTreeMap<usize, usize>,
152 function_mapping: &'a HashMap<FunctionCall, usize>,
153 offset: usize,
154 covered_by_index: bool,
155}
156
157impl<'a> IndexPredicateRewriter<'a> {
158 fn new(
159 p2s_mapping: &'a BTreeMap<usize, usize>,
160 function_mapping: &'a HashMap<FunctionCall, usize>,
161 offset: usize,
162 ) -> Self {
163 Self {
164 p2s_mapping,
165 function_mapping,
166 offset,
167 covered_by_index: true,
168 }
169 }
170
171 fn covered_by_index(&self) -> bool {
172 self.covered_by_index
173 }
174}
175
176impl ExprRewriter for IndexPredicateRewriter<'_> {
177 fn rewrite_input_ref(&mut self, input_ref: InputRef) -> ExprImpl {
178 if self.p2s_mapping.contains_key(&input_ref.index) {
180 InputRef::new(
181 *self.p2s_mapping.get(&input_ref.index()).unwrap(),
182 input_ref.return_type(),
183 )
184 .into()
185 } else {
186 self.covered_by_index = false;
187 InputRef::new(input_ref.index() + self.offset, input_ref.return_type()).into()
188 }
189 }
190
191 fn rewrite_function_call(&mut self, func_call: FunctionCall) -> ExprImpl {
192 if let Some(index) = self.function_mapping.get(&func_call) {
193 return InputRef::new(*index, func_call.return_type()).into();
194 }
195
196 let (func_type, inputs, ret) = func_call.decompose();
197 let inputs = inputs
198 .into_iter()
199 .map(|expr| self.rewrite_expr(expr))
200 .collect();
201 FunctionCall::new_unchecked(func_type, inputs, ret).into()
202 }
203}
204
205impl IndexSelectionRule {
206 fn gen_index_lookup(
209 &self,
210 logical_scan: &LogicalScan,
211 index: &TableIndex,
212 ) -> Option<(PlanRef, IndexCost)> {
213 let primary_table_pk_ref = index.primary_table_pk_ref_to_index_table()?;
214
215 let index_scan = LogicalScan::create(
219 index.index_table.clone(),
220 logical_scan.ctx(),
221 logical_scan.as_of(),
222 );
223 let offset = index_scan.table().columns().len();
226
227 let primary_table_scan = LogicalScan::create(
228 index.primary_table.clone(),
229 logical_scan.ctx(),
230 logical_scan.as_of(),
231 );
232
233 let predicate = logical_scan.predicate().clone();
234 let mut rewriter = IndexPredicateRewriter::new(
235 index.primary_to_secondary_mapping(),
236 index.function_mapping(),
237 offset,
238 );
239 let new_predicate = predicate.rewrite_expr(&mut rewriter);
240
241 let conjunctions = primary_table_pk_ref
242 .iter()
243 .zip_eq_fast(index.primary_table.pk.iter())
244 .map(|(x, y)| {
245 Self::create_null_safe_equal_expr(
246 x.column_index,
247 index.index_table.columns[x.column_index]
248 .data_type()
249 .clone(),
250 y.column_index + offset,
251 index.primary_table.columns[y.column_index]
252 .data_type()
253 .clone(),
254 )
255 })
256 .chain(new_predicate)
257 .collect_vec();
258 let on = Condition { conjunctions };
259 let join: PlanRef = LogicalJoin::new(
260 index_scan.into(),
261 primary_table_scan.into(),
262 JoinType::Inner,
263 on,
264 )
265 .into();
266
267 let join_ref = join.predicate_pushdown(
269 Condition::true_cond(),
270 &mut PredicatePushdownContext::new(join.clone()),
271 );
272
273 let join_with_predicate_push_down =
274 join_ref.as_logical_join().expect("must be a logical join");
275 let new_join_left = join_with_predicate_push_down.left();
276 let index_scan_with_predicate: &LogicalScan = new_join_left
277 .as_logical_scan()
278 .expect("must be a logical scan");
279
280 let index_cost = self.estimate_table_scan_cost(
282 index_scan_with_predicate,
283 TableScanIoEstimator::estimate_row_size(logical_scan),
284 );
285 let lookup_cost = index_cost.mul(&IndexCost::new(LOOKUP_COST_CONST, false));
287
288 let scan_output_col_idx = logical_scan.output_col_idx();
290 let lookup_join = join_ref.prune_col(
291 &scan_output_col_idx
292 .iter()
293 .map(|&col_idx| col_idx + offset)
294 .collect_vec(),
295 &mut ColumnPruningContext::new(join_ref.clone()),
296 );
297
298 Some((lookup_join, lookup_cost))
299 }
300
301 fn index_merge_selection(&self, logical_scan: &LogicalScan) -> Option<(PlanRef, IndexCost)> {
305 let predicate = logical_scan.predicate().clone();
306 let primary_table_row_size = TableScanIoEstimator::estimate_row_size(logical_scan);
309 let paths = self.gen_paths(
311 &predicate.conjunctions,
312 logical_scan,
313 primary_table_row_size,
314 );
315 let (index_access, index_access_cost) =
316 self.choose_min_cost_path(&paths, primary_table_row_size)?;
317
318 let schema: &Schema = index_access.schema();
321 let index_access_len = schema.len();
322
323 let mut shift_input_ref_rewriter = ShiftInputRefRewriter {
324 offset: index_access_len,
325 };
326 let new_predicate = predicate.rewrite_expr(&mut shift_input_ref_rewriter);
327
328 let primary_table = logical_scan.table();
329
330 let primary_table_scan = LogicalScan::create(
331 logical_scan.table().clone(),
332 logical_scan.ctx(),
333 logical_scan.as_of(),
334 );
335
336 let conjunctions = primary_table
337 .pk
338 .iter()
339 .enumerate()
340 .map(|(x, y)| {
341 Self::create_null_safe_equal_expr(
342 x,
343 schema.fields[x].data_type.clone(),
344 y.column_index + index_access_len,
345 primary_table.columns[y.column_index].data_type.clone(),
346 )
347 })
348 .chain(new_predicate)
349 .collect_vec();
350
351 let on = Condition { conjunctions };
352 let join: PlanRef =
353 LogicalJoin::new(index_access, primary_table_scan.into(), JoinType::Inner, on).into();
354
355 let join_ref = join.predicate_pushdown(
357 Condition::true_cond(),
358 &mut PredicatePushdownContext::new(join.clone()),
359 );
360
361 let scan_output_col_idx = logical_scan.output_col_idx();
363 let lookup_join = join_ref.prune_col(
364 &scan_output_col_idx
365 .iter()
366 .map(|&col_idx| col_idx + index_access_len)
367 .collect_vec(),
368 &mut ColumnPruningContext::new(join_ref.clone()),
369 );
370
371 Some((
372 lookup_join,
373 index_access_cost.mul(&IndexCost::new(LOOKUP_COST_CONST, false)),
374 ))
375 }
376
377 fn gen_paths(
384 &self,
385 conjunctions: &[ExprImpl],
386 logical_scan: &LogicalScan,
387 primary_table_row_size: usize,
388 ) -> Vec<PlanRef> {
389 let mut result = vec![];
390
391 let (or_conjunctions, not_or_conjunctions): (Vec<ExprImpl>, Vec<ExprImpl>) =
393 conjunctions.iter().cloned().partition(|expr| {
394 if let ExprImpl::FunctionCall(function_call) = expr
395 && function_call.func_type() == ExprType::Or
396 {
397 true
398 } else {
399 false
400 }
401 });
402 let interest_conjunctions: Vec<ExprImpl> = not_or_conjunctions
404 .into_iter()
405 .filter(|expr| {
406 expr.as_eq_const().is_some()
407 || expr
408 .as_in_const_list()
409 .or_else(|| expr.as_some_eq_const_list())
410 .is_some()
411 || expr.as_comparison_const().is_some()
412 })
413 .collect();
414
415 for expr in or_conjunctions {
416 let mut index_to_be_merged = vec![];
418
419 let disjunctions = to_disjunctions(expr.clone());
420
421 let extended_disjunctions = disjunctions
422 .into_iter()
423 .map(|expr| {
424 if interest_conjunctions.is_empty() {
425 expr
426 } else {
427 ExprImpl::FunctionCall(
428 FunctionCall::new_unchecked(
429 ExprType::And,
430 vec![expr]
431 .into_iter()
432 .chain(interest_conjunctions.iter().cloned())
433 .collect(),
434 DataType::Boolean,
435 )
436 .into(),
437 )
438 }
439 })
440 .collect_vec();
441
442 let (map, others) = self.clustering_disjunction(extended_disjunctions);
443 let iter = map
444 .into_iter()
445 .map(|(column_index, expr)| (Some(column_index), expr))
446 .chain(others.into_iter().map(|expr| (None, expr)));
447 for (column_index, expr) in iter {
448 let mut index_paths = vec![];
449 let conjunctions = to_conjunctions(expr);
450 index_paths.extend(self.gen_index_path(column_index, &conjunctions, logical_scan));
451 if conjunctions.len() > 1 {
453 index_paths.extend(self.gen_paths(
454 &conjunctions,
455 logical_scan,
456 primary_table_row_size,
457 ));
458 }
459
460 match self.choose_min_cost_path(&index_paths, primary_table_row_size) {
461 None => {
462 index_to_be_merged.clear();
464 break;
465 }
466 Some((path, _)) => index_to_be_merged.push(path),
467 }
468 }
469
470 if let Some(path) = self.merge(index_to_be_merged) {
471 result.push(path)
472 }
473 }
474
475 result
476 }
477
478 fn clustering_disjunction(
487 &self,
488 disjunctions: Vec<ExprImpl>,
489 ) -> (HashMap<usize, ExprImpl>, Vec<ExprImpl>) {
490 let mut map: HashMap<usize, ExprImpl> = HashMap::new();
491 let mut others = vec![];
492 for expr in disjunctions {
493 let idx = {
494 if let Some((input_ref, _const_expr)) = expr.as_eq_const() {
495 Some(input_ref.index)
496 } else if let Some((input_ref, _in_const_list)) = expr
497 .as_in_const_list()
498 .or_else(|| expr.as_some_eq_const_list())
499 {
500 Some(input_ref.index)
501 } else if let Some((input_ref, _op, _const_expr)) = expr.as_comparison_const() {
502 Some(input_ref.index)
503 } else {
504 None
505 }
506 };
507
508 if let Some(idx) = idx {
509 match map.entry(idx) {
510 Occupied(mut entry) => {
511 let expr2: ExprImpl = entry.get().to_owned();
512 let or_expr = ExprImpl::FunctionCall(
513 FunctionCall::new_unchecked(
514 ExprType::Or,
515 vec![expr, expr2],
516 DataType::Boolean,
517 )
518 .into(),
519 );
520 entry.insert(or_expr);
521 }
522 Vacant(entry) => {
523 entry.insert(expr);
524 }
525 };
526 } else {
527 others.push(expr);
528 continue;
529 }
530 }
531
532 (map, others)
533 }
534
535 fn gen_index_path(
542 &self,
543 column_index: Option<usize>,
544 conjunctions: &[ExprImpl],
545 logical_scan: &LogicalScan,
546 ) -> Vec<PlanRef> {
547 let combinations = conjunctions
550 .iter()
551 .take(min(conjunctions.len(), MAX_CONJUNCTION_SIZE))
552 .combinations(min(conjunctions.len(), MAX_COMBINATION_SIZE))
553 .collect_vec();
554
555 let mut result = vec![];
556
557 for index in logical_scan.table_indexes() {
558 if let Some(column_index) = column_index {
559 assert_eq!(conjunctions.len(), 1);
560 let p2s_mapping = index.primary_to_secondary_mapping();
561 match p2s_mapping.get(&column_index) {
562 None => continue, Some(&idx) => {
564 if index.index_table.pk()[0].column_index != idx {
565 continue;
567 }
568 }
569 }
570 }
571
572 for conj in &combinations {
574 let condition = Condition {
575 conjunctions: conj.iter().map(|&x| x.to_owned()).collect(),
576 };
577 if let Some(index_access) = self.build_index_access(
578 index.clone(),
579 condition,
580 logical_scan.ctx().clone(),
581 logical_scan.as_of().clone(),
582 ) {
583 result.push(index_access);
584 }
585 }
586 }
587
588 let primary_table = logical_scan.table();
590 if let Some(idx) = column_index {
591 assert_eq!(conjunctions.len(), 1);
592 if primary_table.pk[0].column_index != idx {
593 return result;
594 }
595 }
596
597 let primary_access = generic::TableScan::new(
598 primary_table
599 .pk
600 .iter()
601 .map(|x| x.column_index)
602 .collect_vec(),
603 logical_scan.table().clone(),
604 vec![],
605 vec![],
606 logical_scan.ctx(),
607 Condition {
608 conjunctions: conjunctions.to_vec(),
609 },
610 logical_scan.as_of(),
611 );
612
613 result.push(primary_access.into());
614
615 result
616 }
617
618 fn build_index_access(
620 &self,
621 index: Arc<TableIndex>,
622 predicate: Condition,
623 ctx: OptimizerContextRef,
624 as_of: Option<AsOf>,
625 ) -> Option<PlanRef> {
626 let mut rewriter = IndexPredicateRewriter::new(
627 index.primary_to_secondary_mapping(),
628 index.function_mapping(),
629 0,
630 );
631 let new_predicate = predicate.rewrite_expr(&mut rewriter);
632
633 if !rewriter.covered_by_index() {
635 return None;
636 }
637
638 let primary_table_pk_ref = index.primary_table_pk_ref_to_index_table()?;
640
641 Some(
642 generic::TableScan::new(
643 primary_table_pk_ref
644 .iter()
645 .map(|x| x.column_index)
646 .collect_vec(),
647 index.index_table.clone(),
648 vec![],
649 vec![],
650 ctx,
651 new_predicate,
652 as_of,
653 )
654 .into(),
655 )
656 }
657
658 fn merge(&self, paths: Vec<PlanRef>) -> Option<PlanRef> {
659 if paths.is_empty() {
660 return None;
661 }
662
663 let new_paths = paths
664 .iter()
665 .flat_map(|path| {
666 if let Some(union) = path.as_logical_union() {
667 union.inputs().to_vec()
668 } else if let Some(_scan) = path.as_logical_scan() {
669 vec![path.clone()]
670 } else {
671 unreachable!();
672 }
673 })
674 .sorted_by(|a, b| {
675 a.as_logical_scan()
677 .expect("expect to be a logical scan")
678 .table_name()
679 .cmp(
680 b.as_logical_scan()
681 .expect("expect to be a logical scan")
682 .table_name(),
683 )
684 })
685 .collect_vec();
686
687 Some(LogicalUnion::create(false, new_paths))
688 }
689
690 fn choose_min_cost_path(
691 &self,
692 paths: &[PlanRef],
693 primary_table_row_size: usize,
694 ) -> Option<(PlanRef, IndexCost)> {
695 paths
696 .iter()
697 .map(|path| {
698 if let Some(scan) = path.as_logical_scan() {
699 let cost = self.estimate_table_scan_cost(scan, primary_table_row_size);
700 (scan.clone().into(), cost)
701 } else if let Some(union) = path.as_logical_union() {
702 let cost = union
703 .inputs()
704 .iter()
705 .map(|input| {
706 self.estimate_table_scan_cost(
707 input.as_logical_scan().expect("expect to be a scan"),
708 primary_table_row_size,
709 )
710 })
711 .reduce(|a, b| a.add(&b))
712 .unwrap();
713 (union.clone().into(), cost)
714 } else {
715 unreachable!()
716 }
717 })
718 .min_by(|(_, cost1), (_, cost2)| Ord::cmp(cost1, cost2))
719 }
720
721 pub(crate) fn estimate_table_scan_cost(
722 &self,
723 scan: &LogicalScan,
724 row_size: usize,
725 ) -> IndexCost {
726 let mut table_scan_io_estimator = TableScanIoEstimator::new(scan, row_size);
727 table_scan_io_estimator.estimate(scan.predicate())
728 }
729
730 pub(crate) fn estimate_full_table_scan_cost(
731 &self,
732 scan: &LogicalScan,
733 row_size: usize,
734 ) -> IndexCost {
735 let mut table_scan_io_estimator = TableScanIoEstimator::new(scan, row_size);
736 table_scan_io_estimator.estimate(&Condition::true_cond())
737 }
738
739 pub fn create_null_safe_equal_expr(
740 left: usize,
741 left_data_type: DataType,
742 right: usize,
743 right_data_type: DataType,
744 ) -> ExprImpl {
745 ExprImpl::FunctionCall(Box::new(FunctionCall::new_unchecked(
746 ExprType::IsNotDistinctFrom,
747 vec![
748 ExprImpl::InputRef(Box::new(InputRef::new(left, left_data_type))),
749 ExprImpl::InputRef(Box::new(InputRef::new(right, right_data_type))),
750 ],
751 DataType::Boolean,
752 )))
753 }
754}
755
756pub(crate) struct TableScanIoEstimator<'a> {
757 table_scan: &'a LogicalScan,
758 row_size: usize,
759 cost: Option<IndexCost>,
760}
761
762impl<'a> TableScanIoEstimator<'a> {
763 pub fn new(table_scan: &'a LogicalScan, row_size: usize) -> Self {
764 Self {
765 table_scan,
766 row_size,
767 cost: None,
768 }
769 }
770
771 pub fn estimate_row_size(table_scan: &LogicalScan) -> usize {
772 let row_meta_field_estimate_size = 14_usize;
774 let table = table_scan.table();
775 row_meta_field_estimate_size
776 + table
777 .columns
778 .iter()
779 .chain(table.pk.iter().map(|x| &table.columns[x.column_index]))
781 .map(|x| TableScanIoEstimator::estimate_data_type_size(&x.data_type))
782 .sum::<usize>()
783 }
784
785 fn estimate_data_type_size(data_type: &DataType) -> usize {
786 use std::mem::size_of;
787
788 match data_type {
789 DataType::Boolean => size_of::<bool>(),
790 DataType::Int16 => size_of::<i16>(),
791 DataType::Int32 => size_of::<i32>(),
792 DataType::Int64 => size_of::<i64>(),
793 DataType::Serial => size_of::<Serial>(),
794 DataType::Float32 => size_of::<f32>(),
795 DataType::Float64 => size_of::<f64>(),
796 DataType::Decimal => size_of::<Decimal>(),
797 DataType::Date => size_of::<Date>(),
798 DataType::Time => size_of::<Time>(),
799 DataType::Timestamp => size_of::<Timestamp>(),
800 DataType::Timestamptz => size_of::<Timestamptz>(),
801 DataType::Interval => size_of::<Interval>(),
802 DataType::Int256 => Int256::size(),
803 DataType::Varchar => 20,
804 DataType::Bytea => 20,
805 DataType::Jsonb => 20,
806 DataType::Variant => 20,
807 DataType::Struct { .. } => 20,
808 DataType::List { .. } => 20,
809 DataType::Map(_) => 20,
810 DataType::Vector(d) => d * size_of::<VectorDistanceType>(),
811 }
812 }
813
814 pub fn estimate(&mut self, predicate: &Condition) -> IndexCost {
815 if predicate.conjunctions.len() == 1 {
817 self.visit_expr(&predicate.conjunctions[0]);
818 self.cost.take().unwrap_or_default()
819 } else {
820 self.estimate_conjunctions(&predicate.conjunctions)
821 }
822 }
823
824 fn estimate_conjunctions(&mut self, conjunctions: &[ExprImpl]) -> IndexCost {
825 let mut new_conjunctions = conjunctions.to_owned();
826
827 let mut match_item_vec = vec![];
828
829 for column_idx in self.table_scan.table().order_column_indices() {
830 let match_item = self.match_index_column(column_idx, &mut new_conjunctions);
831 let should_break = match match_item {
833 MatchItem::Equal | MatchItem::In(_) => false,
834 MatchItem::RangeOneSideBound | MatchItem::RangeTwoSideBound | MatchItem::All => {
835 true
836 }
837 };
838 match_item_vec.push(match_item);
839 if should_break {
840 break;
841 }
842 }
843
844 let index_cost: usize = match_item_vec
847 .iter()
848 .enumerate()
849 .take(INDEX_MAX_LEN)
850 .map(|(i, match_item)| match match_item {
851 MatchItem::Equal => INDEX_COST_MATRIX[0][i],
852 MatchItem::In(num) => min(INDEX_COST_MATRIX[1][i], *num),
853 MatchItem::RangeTwoSideBound => INDEX_COST_MATRIX[2][i],
854 MatchItem::RangeOneSideBound => INDEX_COST_MATRIX[3][i],
855 MatchItem::All => INDEX_COST_MATRIX[4][i],
856 })
857 .product();
858
859 let primary_lookup = index_cost == 1;
861
862 IndexCost::new(index_cost, primary_lookup)
863 .mul(&IndexCost::new(self.row_size, primary_lookup))
864 }
865
866 fn match_index_column(
867 &mut self,
868 column_idx: usize,
869 conjunctions: &mut Vec<ExprImpl>,
870 ) -> MatchItem {
871 for (i, expr) in conjunctions.iter().enumerate() {
873 if let Some((input_ref, _const_expr)) = expr.as_eq_const()
874 && input_ref.index == column_idx
875 {
876 conjunctions.remove(i);
877 return MatchItem::Equal;
878 }
879 }
880
881 for (i, expr) in conjunctions.iter().enumerate() {
883 if let Some((input_ref, in_const_list)) = expr
884 .as_in_const_list()
885 .or_else(|| expr.as_some_eq_const_list())
886 && input_ref.index == column_idx
887 {
888 conjunctions.remove(i);
889 return MatchItem::In(in_const_list.len());
890 }
891 }
892
893 let mut left_side_bound = false;
895 let mut right_side_bound = false;
896 let mut i = 0;
897 while i < conjunctions.len() {
898 let expr = &conjunctions[i];
899 if let Some((input_ref, op, _const_expr)) = expr.as_comparison_const()
900 && input_ref.index == column_idx
901 {
902 conjunctions.remove(i);
903 match op {
904 ExprType::LessThan | ExprType::LessThanOrEqual => right_side_bound = true,
905 ExprType::GreaterThan | ExprType::GreaterThanOrEqual => left_side_bound = true,
906 _ => unreachable!(),
907 };
908 } else {
909 i += 1;
910 }
911 }
912
913 if left_side_bound && right_side_bound {
914 MatchItem::RangeTwoSideBound
915 } else if left_side_bound || right_side_bound {
916 MatchItem::RangeOneSideBound
917 } else {
918 MatchItem::All
919 }
920 }
921}
922
923enum MatchItem {
924 Equal,
925 In(usize),
926 RangeTwoSideBound,
927 RangeOneSideBound,
928 All,
929}
930
931#[derive(PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
932pub(crate) struct IndexCost {
933 cost: usize,
934 pub(crate) primary_lookup: bool,
935}
936
937impl Default for IndexCost {
938 fn default() -> Self {
939 Self {
940 cost: IndexCost::maximum(),
941 primary_lookup: false,
942 }
943 }
944}
945
946impl IndexCost {
947 fn new(cost: usize, primary_lookup: bool) -> IndexCost {
948 Self {
949 cost: min(cost, IndexCost::maximum()),
950 primary_lookup,
951 }
952 }
953
954 fn maximum() -> usize {
955 10000000
956 }
957
958 fn add(&self, other: &IndexCost) -> IndexCost {
959 IndexCost::new(
960 self.cost
961 .checked_add(other.cost)
962 .unwrap_or_else(IndexCost::maximum),
963 self.primary_lookup && other.primary_lookup,
964 )
965 }
966
967 fn mul(&self, other: &IndexCost) -> IndexCost {
968 IndexCost::new(
969 self.cost
970 .checked_mul(other.cost)
971 .unwrap_or_else(IndexCost::maximum),
972 self.primary_lookup && other.primary_lookup,
973 )
974 }
975
976 pub(crate) fn le(&self, other: &IndexCost) -> bool {
977 self.cost < other.cost
978 }
979}
980
981impl ExprVisitor for TableScanIoEstimator<'_> {
982 fn visit_function_call(&mut self, func_call: &FunctionCall) {
983 let cost = match func_call.func_type() {
984 ExprType::Or => func_call
985 .inputs()
986 .iter()
987 .map(|x| {
988 let mut estimator = TableScanIoEstimator::new(self.table_scan, self.row_size);
989 estimator.visit_expr(x);
990 estimator.cost.take().unwrap_or_default()
991 })
992 .reduce(|x, y| x.add(&y))
993 .unwrap(),
994 ExprType::And => self.estimate_conjunctions(func_call.inputs()),
995 _ => {
996 let single = vec![ExprImpl::FunctionCall(func_call.clone().into())];
997 self.estimate_conjunctions(&single)
998 }
999 };
1000 self.cost = Some(cost);
1001 }
1002}
1003
1004struct ShiftInputRefRewriter {
1005 offset: usize,
1006}
1007impl ExprRewriter for ShiftInputRefRewriter {
1008 fn rewrite_input_ref(&mut self, input_ref: InputRef) -> ExprImpl {
1009 InputRef::new(input_ref.index() + self.offset, input_ref.return_type()).into()
1010 }
1011}
1012
1013impl IndexSelectionRule {
1014 pub fn create() -> BoxedRule {
1015 Box::new(IndexSelectionRule {})
1016 }
1017}