Skip to main content

uqa_sql/semantics/view_rewrite/
context.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Catalog and authorization inputs for automatic view analysis and DML rewriting.
8
9use crate::{
10    ast::{ColumnDef, RuleEvent, TriggerEvent, TriggerTiming},
11    binding::snapshot::BindingSnapshot,
12    catalog::{
13        analysis::CatalogReadView,
14        events::StoredRule,
15        resolution::RelationNameResolution,
16        view::{StoredViewKind, ViewRewriteDefinition},
17    },
18    plan::{QueryPlan, SourcePlan},
19    semantics::sets::SetFunctionCatalog,
20    RowSchema, SQLError, SQLParam,
21};
22use std::collections::BTreeSet;
23
24pub trait ViewRewriteCatalog: SetFunctionCatalog {
25    fn view_definition(&self, name: &str) -> Result<Option<ViewRewriteDefinition>, SQLError>;
26    fn try_resolve_view_name(&self, name: &str) -> Result<Option<String>, String>;
27    fn try_describe_table(&self, name: &str) -> Result<Option<Vec<ColumnDef>>, String>;
28    fn try_table_columns(&self, name: &str) -> Result<Vec<String>, String>;
29    fn rules_for(&self, name: &str, event: RuleEvent) -> Result<Vec<StoredRule>, SQLError>;
30    fn rule_definitions_for(
31        &self,
32        name: &str,
33        event: RuleEvent,
34    ) -> Result<Vec<StoredRule>, SQLError>;
35    fn has_trigger_definition(
36        &self,
37        name: &str,
38        timing: TriggerTiming,
39        event: TriggerEvent,
40        row: bool,
41    ) -> Result<bool, SQLError>;
42    fn rule_new_row_columns(&self, rule: &StoredRule)
43        -> Result<Option<BTreeSet<String>>, SQLError>;
44    fn target_view_kind(&self, name: &str) -> Result<Option<StoredViewKind>, SQLError>;
45    fn binding_scope(&self) -> Result<BindingSnapshot, SQLError>;
46    fn restored_view_catalog(&self) -> (CatalogReadView, RelationNameResolution);
47}
48
49#[derive(Clone, Copy)]
50pub struct ViewRewriteContext<'a> {
51    pub catalog: &'a dyn ViewRewriteCatalog,
52    pub authorization: &'a dyn super::super::view_privileges::ViewPrivilegeCatalog,
53}
54
55pub fn stored_view_schema(
56    services: ViewRewriteContext<'_>,
57    definition: &ViewRewriteDefinition,
58) -> Result<RowSchema, SQLError> {
59    let (catalog, resolution) = services.catalog.restored_view_catalog();
60    definition.row_schema(services.catalog, catalog, resolution)
61}
62
63pub(super) fn analyze_source_plan_schema(
64    services: ViewRewriteContext<'_>,
65    source: &SourcePlan,
66    params: &[SQLParam],
67    scope: &BindingSnapshot,
68    outer: Option<&RowSchema>,
69) -> Result<RowSchema, SQLError> {
70    crate::binding::analyze_source_plan_schema(
71        services.catalog,
72        source,
73        params,
74        &scope.context(),
75        outer,
76    )
77}
78pub(super) fn analyze_query_plan_schema(
79    services: ViewRewriteContext<'_>,
80    query: &QueryPlan,
81    params: &[SQLParam],
82    scope: &BindingSnapshot,
83    outer: Option<&RowSchema>,
84) -> Result<RowSchema, SQLError> {
85    crate::binding::analyze_query_plan_schema(
86        services.catalog,
87        query,
88        params,
89        &scope.context(),
90        outer,
91    )
92}
93pub(super) fn target_is_view(
94    services: ViewRewriteContext<'_>,
95    name: &str,
96) -> Result<bool, SQLError> {
97    Ok(services.catalog.target_view_kind(name)?.is_some())
98}
99
100pub(super) fn relation_suppresses_original_query(
101    services: ViewRewriteContext<'_>,
102    table: &str,
103    event: RuleEvent,
104) -> Result<bool, SQLError> {
105    Ok(services
106        .catalog
107        .rules_for(table, event)?
108        .iter()
109        .any(|rule| rule.definition.instead && rule.definition.condition.is_none()))
110}
111pub(super) fn relation_has_returning_provider(
112    services: ViewRewriteContext<'_>,
113    table: &str,
114    event: RuleEvent,
115) -> Result<bool, SQLError> {
116    Ok(services
117        .catalog
118        .rules_for(table, event)?
119        .iter()
120        .any(|rule| {
121            rule.definition
122                .actions
123                .iter()
124                .any(crate::semantics::rules::statement_has_returning)
125        }))
126}