uqa_sql/schema/
view_creation.rs1use crate::{ast::RelationPersistence, RowSchema, SQLError};
9use uqa_core::RelationIdentity;
10
11pub trait ViewCreationNamespace {
12 fn temporary_schema_name(&self) -> String;
13 fn temporary_target(&self, name: &str) -> Result<String, SQLError>;
14 fn persistent_target(&self, name: &str) -> Result<String, SQLError>;
15}
16
17pub fn view_creation_target(
18 namespace: &dyn ViewCreationNamespace,
19 name: &str,
20 persistence: RelationPersistence,
21 uses_temporary_relation: bool,
22) -> Result<(String, RelationPersistence), SQLError> {
23 let persistence = if uses_temporary_relation {
24 RelationPersistence::Temporary
25 } else {
26 persistence
27 };
28 let name = if persistence == RelationPersistence::Temporary {
29 let (schema, _) = RelationIdentity::parse_reference(name).map_err(SQLError::Unsupported)?;
30 if uses_temporary_relation
31 && schema.as_deref().is_some_and(|schema| {
32 schema != "pg_temp" && schema != namespace.temporary_schema_name()
33 })
34 {
35 namespace.persistent_target(name)?;
36 }
37 namespace.temporary_target(name)?
38 } else {
39 namespace.persistent_target(name)?
40 };
41 Ok((name, persistence))
42}
43
44pub fn replacement_is_view(
45 name: &str,
46 kind: Option<&str>,
47 or_replace: bool,
48) -> Result<bool, SQLError> {
49 match kind {
50 Some(_) if !or_replace => Err(SQLError::Routine {
51 sqlstate: "42P07".into(),
52 message: format!("relation \"{name}\" already exists"),
53 }),
54 Some("view") => Ok(true),
55 Some(kind) => Err(SQLError::Routine {
56 sqlstate: "42809".into(),
57 message: format!("\"{name}\" is not a view; it is a {kind}"),
58 }),
59 None => Ok(false),
60 }
61}
62
63pub fn validate_replacement_schema(old: &RowSchema, new: &RowSchema) -> Result<(), SQLError> {
64 if new.len() < old.len() {
65 return Err(SQLError::Routine {
66 sqlstate: "42P16".into(),
67 message: "cannot drop columns from view".into(),
68 });
69 }
70 for position in 0..old.len() {
71 let old_name = old
72 .public_name(position)
73 .unwrap_or(&old.columns()[position]);
74 let new_name = new
75 .public_name(position)
76 .unwrap_or(&new.columns()[position]);
77 if old_name != new_name {
78 return Err(SQLError::Routine {
79 sqlstate: "42P16".into(),
80 message: format!(
81 "cannot change name of view column \"{old_name}\" to \"{new_name}\""
82 ),
83 });
84 }
85 if old.column_type(position) != new.column_type(position) {
86 return Err(SQLError::Routine {
87 sqlstate: "42P16".into(),
88 message: format!("cannot change data type of view column \"{old_name}\""),
89 });
90 }
91 }
92 Ok(())
93}
94
95#[cfg(test)]
96mod tests;