Skip to main content

uqa_planner/explain/
physical.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Static physical diagnostics over the statement's retained catalog. No query operator is executed here.
8
9use crate::{
10    filter_pushdown::context::{FilterPushdownContext, FilterPushdownScope},
11    retrieval_planning::RetrievalPlanningCatalog,
12    statement_planning::PlannerStatisticsCatalog,
13};
14use std::{collections::BTreeMap, rc::Rc};
15use uqa_sql::{
16    plan::{CtePlan, UnifiedPlan},
17    result::ExplainPhysicalPlan,
18    retrieval::ConstantEvaluator,
19    SQLError, SQLParam,
20};
21
22mod query;
23mod source;
24mod vector;
25
26#[derive(Clone, Copy)]
27pub struct PhysicalExplainContext<'a> {
28    pub retrieval: &'a dyn RetrievalPlanningCatalog,
29    pub statistics: &'a dyn PlannerStatisticsCatalog,
30    pub filters: FilterPushdownContext<'a>,
31    pub evaluate: &'a ConstantEvaluator<'a>,
32    /// Validate the retained provider metadata for a concrete source after CTE and view expansion.
33    pub validate_source: &'a dyn Fn(
34        &uqa_sql::catalog::resolution::RelationNameResolution,
35        &str,
36    ) -> Result<(), SQLError>,
37}
38
39#[derive(Clone)]
40struct DeferredCte {
41    plan: CtePlan,
42    outer: Rc<Scope>,
43}
44type Scope = BTreeMap<String, Option<DeferredCte>>;
45
46pub fn physical_plan(
47    context: PhysicalExplainContext<'_>,
48    body: &UnifiedPlan,
49    params: &[SQLParam],
50) -> Result<ExplainPhysicalPlan, SQLError> {
51    let mut collector = Collector {
52        context,
53        params,
54        output: ExplainPhysicalPlan::default(),
55    };
56    collector.statement(body, &Scope::new(), "Statement")?;
57    Ok(collector.output)
58}
59
60struct Collector<'a> {
61    context: PhysicalExplainContext<'a>,
62    params: &'a [SQLParam],
63    output: ExplainPhysicalPlan,
64}
65
66impl Collector<'_> {
67    fn filter_scope<'a>(&'a self, visible: &'a dyn Fn(&str) -> bool) -> FilterPushdownScope<'a> {
68        FilterPushdownScope {
69            catalog: self.context.filters.correlation.catalog,
70            resolution: self.context.filters.correlation.resolution,
71            is_visible_cte: visible,
72        }
73    }
74}
75
76fn visible_cte(scope: &Scope, name: &str) -> bool {
77    uqa_sql::semantics::cte_reference_name(name).is_some_and(|name| scope.contains_key(&name))
78}