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