1use crate::{RowSchema, SQLError};
8
9pub fn require_materialized_view_population(name: &str, populated: bool) -> Result<(), SQLError> {
11 if populated {
12 return Ok(());
13 }
14 let (_, relation) =
15 uqa_core::RelationIdentity::parse_reference(name).map_err(SQLError::Unsupported)?;
16 Err(SQLError::Diagnostic {
17 sqlstate: "55000".into(),
18 message: format!("materialized view \"{relation}\" has not been populated"),
19 detail: None,
20 hint: Some("Use the REFRESH MATERIALIZED VIEW command.".into()),
21 })
22}
23
24pub fn create_view_output_columns(
25 schema: &RowSchema,
26 declared: &[String],
27) -> Result<Vec<String>, SQLError> {
28 if declared.len() > schema.len() {
29 return Err(SQLError::Routine {
30 sqlstate: "42601".into(),
31 message: "CREATE VIEW specifies more column names than columns".into(),
32 });
33 }
34 let columns = schema
35 .columns()
36 .iter()
37 .enumerate()
38 .map(|(position, column)| {
39 declared
40 .get(position)
41 .cloned()
42 .unwrap_or_else(|| schema.public_name(position).unwrap_or(column).to_string())
43 })
44 .collect::<Vec<_>>();
45 let mut seen = std::collections::BTreeSet::new();
46 for column in &columns {
47 if !seen.insert(column) {
48 return Err(SQLError::Routine {
49 sqlstate: "42701".into(),
50 message: format!("column \"{column}\" specified more than once"),
51 });
52 }
53 }
54 Ok(columns)
55}
56
57pub fn named_view_schema(
58 query_schema: &RowSchema,
59 output_columns: &[String],
60) -> Result<RowSchema, SQLError> {
61 if query_schema.len() != output_columns.len() {
62 return Err(SQLError::Internal(format!(
63 "stored view row type has {} columns but its query has {}",
64 output_columns.len(),
65 query_schema.len()
66 )));
67 }
68 Ok(RowSchema::with_types(
69 output_columns.to_vec(),
70 query_schema.column_types().to_vec(),
71 ))
72}
73
74pub fn validate_view_column_types(
75 query_schema: &RowSchema,
76 output_columns: &[String],
77) -> Result<(), SQLError> {
78 for (position, column) in output_columns.iter().enumerate() {
79 if let Some(ty) = query_schema.column_type(position) {
80 crate::schema::columns::validate_postgres_relation_column_type(column, ty)?;
81 }
82 }
83 Ok(())
84}
85
86#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
87pub enum StoredViewKind {
88 #[default]
89 View,
90 Materialized,
91}
92
93#[derive(Debug, Clone, Copy, Default)]
94pub struct ViewMutationCapabilities {
95 pub insertable: bool,
96 pub updatable: bool,
97 pub deletable: bool,
98}
99impl ViewMutationCapabilities {
100 pub const fn fully_updatable(self) -> bool {
101 self.updatable && self.deletable
102 }
103}
104
105#[derive(Clone)]
106pub struct ViewRewriteDefinition {
107 pub query: crate::plan::QueryPlan,
108 pub output_columns: Option<Vec<String>>,
109 pub options: Vec<(String, String)>,
110 pub kind: StoredViewKind,
111 pub materialized_column_types: Vec<Option<crate::ColumnType>>,
112}
113
114impl ViewRewriteDefinition {
115 pub fn row_schema(
116 &self,
117 routines: &dyn crate::routines::RoutineResolution,
118 catalog: super::analysis::CatalogReadView,
119 resolution: super::resolution::RelationNameResolution,
120 ) -> Result<crate::RowSchema, crate::SQLError> {
121 use crate::catalog::view::{create_view_output_columns, named_view_schema};
122 use crate::SQLError;
123 if self.kind == StoredViewKind::Materialized {
124 let output_columns = self.output_columns.clone().unwrap_or_default();
125 if output_columns.len() != self.materialized_column_types.len() {
126 return Err(SQLError::Internal(
127 "stored materialized view column metadata is inconsistent".into(),
128 ));
129 }
130 return Ok(crate::RowSchema::with_types(
131 output_columns,
132 self.materialized_column_types.clone(),
133 ));
134 }
135 let query_schema = crate::binding::analyze_query_plan_schema_with_catalog(
136 routines,
137 &self.query,
138 &[],
139 catalog,
140 resolution,
141 )?;
142 let output_columns = match &self.output_columns {
143 Some(columns) => columns.clone(),
144 None => create_view_output_columns(&query_schema, &[])?,
145 };
146 named_view_schema(&query_schema, &output_columns)
147 }
148}