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
59pub fn validate_view_column_types(
60 query_schema: &RowSchema,
61 output_columns: &[String],
62) -> Result<(), SQLError> {
63 for (position, column) in output_columns.iter().enumerate() {
64 if let Some(ty) = query_schema.column_type(position) {
65 crate::schema::columns::validate_postgres_relation_column_type(column, ty)?;
66 }
67 }
68 Ok(())
69}
70
71#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
72pub enum StoredViewKind {
73 #[default]
74 View,
75 Materialized,
76}
77
78#[derive(Debug, Clone, Copy, Default)]
79pub struct ViewMutationCapabilities {
80 pub insertable: bool,
81 pub updatable: bool,
82 pub deletable: bool,
83}
84impl ViewMutationCapabilities {
85 pub const fn fully_updatable(self) -> bool {
86 self.updatable && self.deletable
87 }
88}
89
90#[derive(Clone)]
91pub struct ViewRewriteDefinition {
92 pub query: crate::plan::QueryPlan,
93 pub output_columns: Option<Vec<String>>,
94 pub options: Vec<(String, String)>,
95 pub kind: StoredViewKind,
96 pub materialized_column_types: Vec<Option<crate::ColumnType>>,
97}
98
99impl ViewRewriteDefinition {
100 pub fn row_schema(
101 &self,
102 routines: &dyn crate::routines::RoutineResolution,
103 catalog: super::analysis::CatalogReadView,
104 resolution: super::resolution::RelationNameResolution,
105 ) -> Result<crate::RowSchema, crate::SQLError> {
106 use crate::catalog::view::{create_view_output_columns, named_view_schema};
107 use crate::SQLError;
108 if self.kind == StoredViewKind::Materialized {
109 let output_columns = self.output_columns.clone().unwrap_or_default();
110 if output_columns.len() != self.materialized_column_types.len() {
111 return Err(SQLError::Internal(
112 "stored materialized view column metadata is inconsistent".into(),
113 ));
114 }
115 return Ok(crate::RowSchema::with_types(
116 output_columns,
117 self.materialized_column_types.clone(),
118 ));
119 }
120 let query_schema = crate::binding::analyze_query_plan_schema_with_catalog(
121 routines,
122 &self.query,
123 &[],
124 catalog,
125 resolution,
126 )?;
127 let output_columns = match &self.output_columns {
128 Some(columns) => columns.clone(),
129 None => create_view_output_columns(&query_schema, &[])?,
130 };
131 named_view_schema(&query_schema, &output_columns)
132 }
133}