uqa_sql/schema/sequences/
lifecycle.rs1use crate::{
9 ast::{RelationPersistence, SequenceBound, SequenceLifecycle, SequenceOwnership},
10 SQLError,
11};
12use uqa_core::RelationIdentity;
13pub trait SequenceLifecycleCatalog {
15 fn temporary_schema_name(&self) -> String;
16 fn sequence_is_owned(&self, relation: &RelationIdentity) -> bool;
17 fn schema_exists(&self, schema: &str) -> bool;
18 fn current_user_name(&self) -> String;
19 fn require_schema_create(&self, schema: &str, role: &str) -> Result<(), SQLError>;
20 fn relation_kind_at(&self, name: &str) -> Result<Option<&'static str>, String>;
21}
22pub fn validate_sequence_lifecycle_shape(
23 alter: &crate::ast::AlterSequence,
24) -> Result<(), SQLError> {
25 if alter.restart != crate::ast::SequenceRestart::Unchanged
26 || alter.increment.is_some()
27 || alter.start.is_some()
28 || alter.data_type.is_some()
29 || alter.min_value != SequenceBound::Unchanged
30 || alter.max_value != SequenceBound::Unchanged
31 || alter.cycle.is_some()
32 || alter.cache_size.is_some()
33 || alter.ownership != SequenceOwnership::Unchanged
34 || alter.persistence.is_some()
35 || alter.role_owner.is_some()
36 {
37 return Err(SQLError::Internal(
38 "ALTER SEQUENCE name lifecycle cannot contain definition changes".into(),
39 ));
40 }
41 Ok(())
42}
43
44pub fn sequence_lifecycle_target(
45 catalog: &dyn SequenceLifecycleCatalog,
46 source: &RelationIdentity,
47 persistence: RelationPersistence,
48 lifecycle: &SequenceLifecycle,
49) -> Result<Option<RelationIdentity>, SQLError> {
50 match lifecycle {
51 SequenceLifecycle::Unchanged => Err(SQLError::Internal(
52 "sequence lifecycle executor received no action".into(),
53 )),
54 SequenceLifecycle::RenameTo { name } => {
55 let (schema, target_name) = RelationIdentity::parse_reference(name)
56 .map_err(|error| SQLError::Internal(format!("invalid sequence name: {error}")))?;
57 if schema.is_some() {
58 return Err(SQLError::Internal(
59 "ALTER SEQUENCE RENAME TO produced a qualified target".into(),
60 ));
61 }
62 let target = RelationIdentity::new(&source.schema, target_name);
63 reject_sequence_lifecycle_collision(catalog, source, &target, true)?;
64 Ok(Some(target))
65 }
66 SequenceLifecycle::SetSchema { schema } => {
67 let (qualifier, mut target_schema) = RelationIdentity::parse_reference(schema)
68 .map_err(|error| SQLError::Internal(format!("invalid schema name: {error}")))?;
69 if qualifier.is_some() {
70 return Err(SQLError::Internal(
71 "ALTER SEQUENCE SET SCHEMA produced a qualified schema".into(),
72 ));
73 }
74 let temporary_schema = catalog.temporary_schema_name();
75 if schema == "pg_temp" {
76 target_schema.clone_from(&temporary_schema);
77 }
78 if persistence == RelationPersistence::Temporary || target_schema == temporary_schema {
79 return Err(SQLError::Routine {
80 sqlstate: "0A000".into(),
81 message: "cannot move objects into or out of temporary schemas".into(),
82 });
83 }
84 if catalog.sequence_is_owned(source) {
85 return Err(SQLError::Routine {
86 sqlstate: "0A000".into(),
87 message: "cannot move an owned sequence into another schema".into(),
88 });
89 }
90 if !catalog.schema_exists(&target_schema) {
91 return Err(SQLError::Routine {
92 sqlstate: "3F000".into(),
93 message: format!("schema \"{target_schema}\" does not exist"),
94 });
95 }
96 let current_user = catalog.current_user_name();
97 catalog.require_schema_create(&target_schema, ¤t_user)?;
98 let target = RelationIdentity::new(target_schema, &source.name);
99 if target == *source {
100 return Ok(None);
101 }
102 reject_sequence_lifecycle_collision(catalog, source, &target, false)?;
103 Ok(Some(target))
104 }
105 }
106}
107
108fn reject_sequence_lifecycle_collision(
109 catalog: &dyn SequenceLifecycleCatalog,
110 source: &RelationIdentity,
111 target: &RelationIdentity,
112 rename: bool,
113) -> Result<(), SQLError> {
114 if target == source
115 || catalog
116 .relation_kind_at(&target.qualified_name())
117 .map_err(|error| {
118 SQLError::Internal(format!(
119 "check sequence lifecycle target `{}`: {error}",
120 target.qualified_name()
121 ))
122 })?
123 .is_some()
124 {
125 return Err(SQLError::Routine {
126 sqlstate: "42P07".into(),
127 message: if rename {
128 format!("relation \"{}\" already exists", target.name)
129 } else {
130 format!(
131 "relation \"{}\" already exists in schema \"{}\"",
132 target.name, target.schema
133 )
134 },
135 });
136 }
137 Ok(())
138}
139
140use crate::catalog::resolution::RelationResolution;
141
142pub fn alter_sequence_target_name(
143 resolution: crate::catalog::resolution::RelationResolution,
144 alter: &crate::ast::AlterSequence,
145) -> Result<Option<String>, SQLError> {
146 match resolution {
147 RelationResolution::Found(name, "sequence") => Ok(Some(name)),
148 RelationResolution::Found(_name, _kind) => Err(SQLError::Routine {
149 sqlstate: "42809".into(),
150 message: format!("\"{}\" is not a sequence", alter.name),
151 }),
152 RelationResolution::MissingRelation | RelationResolution::MissingSchema(_)
153 if alter.if_exists =>
154 {
155 Ok(None)
156 }
157 RelationResolution::MissingSchema(schema) => Err(SQLError::Routine {
158 sqlstate: "3F000".into(),
159 message: format!("schema \"{schema}\" does not exist"),
160 }),
161 RelationResolution::MissingRelation => Err(SQLError::Routine {
162 sqlstate: "42P01".into(),
163 message: format!("relation \"{}\" does not exist", alter.name),
164 }),
165 }
166}