Skip to main content

uqa_sql/catalog/
stored_view.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use super::security::BoundTableSecurity;
8use std::ops::{Deref, DerefMut};
9
10/// One bound view query together with the fixed public column names captured when the view was created. `None` exists only while the catalog-opening migration reads formats written before column metadata was persisted.
11#[derive(Debug, Clone, serde::Serialize)]
12pub struct StoredView {
13    /// Security is loaded independently from the durable catalog row.
14    #[serde(skip)]
15    pub security: BoundTableSecurity,
16    #[serde(flatten)]
17    pub definition: StoredViewDefinition,
18}
19
20/// Query-definition JSON carries no authorization state. Restoration supplies validated security before publishing a view.
21#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
22pub struct StoredViewDefinition {
23    /// Stable logical relation identity. Renames and replacement preserve it; a zero value marks a legacy catalog row upgraded during initial open.
24    #[serde(default)]
25    pub object_id: [u8; 16],
26    pub query: crate::plan::QueryPlan,
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub output_columns: Option<Vec<String>>,
29    #[serde(default)]
30    pub persistence: crate::ast::RelationPersistence,
31    #[serde(default, skip_serializing_if = "Vec::is_empty")]
32    pub options: Vec<(String, String)>,
33    #[serde(default)]
34    pub kind: StoredViewKind,
35    #[serde(default, skip_serializing_if = "Vec::is_empty")]
36    pub materialized_rows: Vec<crate::ResultRow>,
37    #[serde(default, skip_serializing_if = "Vec::is_empty")]
38    pub materialized_column_types: Vec<Option<crate::ast::ColumnType>>,
39    #[serde(default = "default_view_populated")]
40    pub populated: bool,
41    /// The view's public OIDs, allocated when it was created; replacement keeps them. Views created before OIDs were recorded derive them from their identity.
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub catalog_oids: Option<super::relation_oids::RelationCatalogOids>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub row_type_array_name: Option<String>,
46}
47
48impl StoredViewDefinition {
49    /// The view's public OIDs: the recorded ones, or those its identity derives.
50    #[must_use]
51    pub fn relation_oids(&self) -> super::relation_oids::RelationCatalogOids {
52        self.catalog_oids.unwrap_or_else(|| {
53            super::relation_oids::RelationCatalogOids::legacy(
54                super::relation_oids::RelationOidKind::View,
55                &self.object_id,
56            )
57        })
58    }
59}
60
61use super::view::StoredViewKind;
62
63const fn default_view_populated() -> bool {
64    true
65}
66
67impl Deref for StoredView {
68    type Target = StoredViewDefinition;
69    fn deref(&self) -> &Self::Target {
70        &self.definition
71    }
72}
73
74impl DerefMut for StoredView {
75    fn deref_mut(&mut self) -> &mut Self::Target {
76        &mut self.definition
77    }
78}
79
80impl StoredView {
81    pub fn security(&self) -> BoundTableSecurity {
82        self.security.clone()
83    }
84
85    pub fn set_security(&mut self, security: BoundTableSecurity) {
86        self.security = security;
87    }
88
89    pub fn security_invoker(&self) -> bool {
90        self.options.iter().any(|(name, value)| {
91            name == "security_invoker" && matches!(value.as_str(), "true" | "on" | "yes" | "1")
92        })
93    }
94}
95
96impl StoredView {
97    pub fn rewrite_definition(&self) -> crate::catalog::view::ViewRewriteDefinition {
98        crate::catalog::view::ViewRewriteDefinition {
99            query: self.query.clone(),
100            output_columns: self.output_columns.clone(),
101            options: self.options.clone(),
102            kind: self.kind,
103            materialized_column_types: self.materialized_column_types.clone(),
104        }
105    }
106    pub fn row_schema(
107        &self,
108        routines: &dyn crate::routines::RoutineResolution,
109        catalog: super::analysis::CatalogReadView,
110        resolution: super::resolution::RelationNameResolution,
111    ) -> Result<crate::RowSchema, crate::SQLError> {
112        self.rewrite_definition()
113            .row_schema(routines, catalog, resolution)
114    }
115}
116
117pub mod dependencies;
118
119pub mod restoration;
120
121pub mod references;
122pub mod removal;