Attempts to refine column boundaries and compute a selectivity value. The function accepts boundaries of the input columns in the `context` parameter. It then tries to tighten these boundaries based on the provided `expr`. The resulting selectivity value is calculated by comparing the initial and final boundaries. The computation assumes that the data within the column is uniformly distributed an
(
expr: &Arc<dyn PhysicalExpr>,
context: AnalysisContext,
schema: &Schema,
)
| 162 | /// |
| 163 | /// * `AnalysisContext` constructed by pruned boundaries and a selectivity value. |
| 164 | pub fn analyze( |
| 165 | expr: &Arc<dyn PhysicalExpr>, |
| 166 | context: AnalysisContext, |
| 167 | schema: &Schema, |
| 168 | ) -> Result<AnalysisContext> { |
| 169 | let initial_boundaries = &context.boundaries; |
| 170 | |
| 171 | if initial_boundaries |
| 172 | .iter() |
| 173 | .all(|bound| bound.interval.is_none()) |
| 174 | { |
| 175 | assert_or_internal_err!( |
| 176 | !initial_boundaries |
| 177 | .iter() |
| 178 | .any(|bound| bound.distinct_count != Precision::Exact(0)), |
| 179 | "ExprBoundaries has a non-zero distinct count although it represents an empty table" |
| 180 | ); |
| 181 | assert_or_internal_err!( |
| 182 | context.selectivity.unwrap_or(0.0) == 0.0, |
| 183 | "AnalysisContext has a non-zero selectivity although it represents an empty table" |
| 184 | ); |
| 185 | Ok(context) |
| 186 | } else if initial_boundaries |
| 187 | .iter() |
| 188 | .any(|bound| bound.interval.is_none()) |
| 189 | { |
| 190 | internal_err!( |
| 191 | "AnalysisContext is an inconsistent state. Some columns represent empty table while others don't" |
| 192 | ) |
| 193 | } else { |
| 194 | let mut target_boundaries = context.boundaries; |
| 195 | let mut graph = ExprIntervalGraph::try_new(Arc::clone(expr), schema)?; |
| 196 | let columns = collect_columns(expr) |
| 197 | .into_iter() |
| 198 | .map(|c| Arc::new(c) as _) |
| 199 | .collect::<Vec<_>>(); |
| 200 | |
| 201 | let mut target_indices_and_boundaries = vec![]; |
| 202 | let target_expr_and_indices = graph.gather_node_indices(columns.as_slice()); |
| 203 | |
| 204 | for (expr, index) in &target_expr_and_indices { |
| 205 | if let Some(column) = expr.downcast_ref::<Column>() |
| 206 | && let Some(bound) = |
| 207 | target_boundaries.iter().find(|b| b.column == *column) |
| 208 | { |
| 209 | // Now, it's safe to unwrap |
| 210 | target_indices_and_boundaries |
| 211 | .push((*index, bound.interval.as_ref().unwrap().clone())); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | match graph.update_ranges(&mut target_indices_and_boundaries, Interval::TRUE)? { |
| 216 | PropagationResult::Success => { |
| 217 | shrink_boundaries(&graph, target_boundaries, &target_expr_and_indices) |
| 218 | } |
| 219 | PropagationResult::Infeasible => { |
| 220 | // If the propagation result is infeasible, set intervals to None |
| 221 | target_boundaries |
searching dependent graphs…