uqa_sql/schema/sequences/
implicit_ownership.rs1use super::implicit::stored_owner_names_current;
9use crate::ast::ColumnDef;
10use uqa_core::RelationIdentity;
11
12pub trait StoredSequenceNames {
13 fn stored_sequence_name(&self, reference: &str) -> Result<String, String>;
14}
15pub struct BoundImplicitSequenceOwner {
16 pub sequence: String,
17 pub table_object_id: [u8; 16],
18 pub column_object_id: [u8; 16],
19 pub identity: bool,
20}
21pub fn bind_implicit_sequence_owners(
22 catalog: &dyn StoredSequenceNames,
23 table_name: &str,
24 table_object_id: [u8; 16],
25 columns: &[ColumnDef],
26) -> Result<Vec<BoundImplicitSequenceOwner>, String> {
27 let relation = RelationIdentity::from_legacy_name(table_name)?;
28 let mut bindings = Vec::new();
29 for column in columns {
30 let Some(provenance) = column.auto_increment.as_ref() else {
31 continue;
32 };
33 let Some(named_owner) = provenance.owner.as_ref() else {
34 continue;
35 };
36 if !stored_owner_names_current(&relation, column, named_owner) {
37 continue;
38 }
39 let Some(sequence) = provenance.sequence.as_deref() else {
40 continue;
41 };
42 let sequence = catalog.stored_sequence_name(sequence)?;
43 let column_object_id = column.object_id.ok_or_else(|| {
44 format!(
45 "column `{table_name}`.`{}` has no object identity",
46 column.name
47 )
48 })?;
49 bindings.push(BoundImplicitSequenceOwner {
50 sequence,
51 table_object_id,
52 column_object_id,
53 identity: provenance.is_identity(),
54 });
55 }
56 Ok(bindings)
57}
58
59pub fn clear_auto_increment_owner_markers(
61 columns: &mut [crate::ast::ColumnDef],
62 target: &uqa_core::RelationIdentity,
63) -> bool {
64 let mut changed = false;
65 for column in columns {
66 let Some(provenance) = column.auto_increment.as_mut() else {
67 continue;
68 };
69 if provenance.owner.is_some()
70 && provenance.sequence.as_deref().is_some_and(|reference| {
71 crate::schema::dependencies::rewrites::stored_relation_reference_matches(
72 reference, target,
73 )
74 })
75 {
76 provenance.owner = None;
77 changed = true;
78 }
79 }
80 changed
81}
82
83pub fn bind_declared_sequence_owners(
85 names: &dyn StoredSequenceNames,
86 catalog: &dyn super::ownership::SequenceOwnerCatalog,
87 table_name: &str,
88 table_object_id: [u8; 16],
89 columns: &[ColumnDef],
90) -> Result<Vec<BoundImplicitSequenceOwner>, crate::SQLError> {
91 let mut bindings = bind_implicit_sequence_owners(names, table_name, table_object_id, columns)
92 .map_err(crate::SQLError::Internal)?;
93 let relation =
94 RelationIdentity::from_legacy_name(table_name).map_err(crate::SQLError::Internal)?;
95 for column in columns {
96 let Some(provenance) = column.auto_increment.as_ref() else {
97 continue;
98 };
99 let (Some(owner), Some(sequence)) = (&provenance.owner, &provenance.sequence) else {
100 continue;
101 };
102 if stored_owner_names_current(&relation, column, owner) {
103 continue;
104 }
105 let sequence = names
106 .stored_sequence_name(sequence)
107 .map_err(crate::SQLError::Internal)?;
108 let ownership = crate::ast::SequenceOwnership::Column {
109 table: owner.table.clone(),
110 column: owner.column.clone(),
111 };
112 let Some(identity) = super::ownership::bind_sequence_owner(catalog, &sequence, &ownership)?
113 else {
114 return Err(crate::SQLError::Internal(
115 "declared sequence owner disappeared".into(),
116 ));
117 };
118 bindings.push(BoundImplicitSequenceOwner {
119 sequence,
120 table_object_id: identity.table_object_id,
121 column_object_id: identity.column_object_id,
122 identity: provenance.is_identity(),
123 });
124 }
125 Ok(bindings)
126}
127
128pub fn identity_column_sequence(
130 catalog: &dyn crate::semantics::partition::PartitionCatalog,
131 table: &str,
132 column: &ColumnDef,
133) -> Result<Option<String>, crate::SQLError> {
134 use crate::SQLError;
135 let Some(provenance) = column
136 .auto_increment
137 .as_ref()
138 .filter(|provenance| provenance.is_identity())
139 else {
140 return Ok(None);
141 };
142 let missing = || SQLError::Routine {
143 sqlstate: "XX000".into(),
144 message: "no owned sequence found".into(),
145 };
146 let owner = provenance.owner.as_ref().ok_or_else(missing)?;
147 let mut current = table.to_string();
148 let mut visited = std::collections::BTreeSet::new();
149 loop {
150 let relation = RelationIdentity::from_legacy_name(¤t).map_err(SQLError::Internal)?;
151 if stored_owner_names_current(&relation, column, owner) {
152 break;
153 }
154 if !visited.insert(current.clone()) {
155 return Err(SQLError::Internal(
156 "cycle in identity sequence ancestry".into(),
157 ));
158 }
159 let hierarchy = catalog
160 .try_table_hierarchy(¤t)
161 .map_err(SQLError::Internal)?;
162 if !hierarchy.is_partition() {
163 return Err(missing());
164 }
165 current.clone_from(hierarchy.parents.first().ok_or_else(missing)?);
166 }
167 provenance.sequence.clone().map(Some).ok_or_else(missing)
168}
169
170#[cfg(test)]
171mod tests;