uqa_sql/schema/
schema_layout.rs1use super::{ColumnIdentity, ColumnType, HashSet, RowSchema, NULL_SLOT};
10
11impl RowSchema {
12 pub fn has_same_physical_layout(&self, other: &Self) -> bool {
14 let left = &self.index;
15 let right = &other.index;
16 left.columns == right.columns
17 && left.identities == right.identities
18 && left.slots == right.slots
19 && left.physical_width == right.physical_width
20 && left.aliases == right.aliases
21 && left.executor_attributes == right.executor_attributes
22 && left.cold.columns == right.cold.columns
23 && left.cold.aliases == right.cold.aliases
24 && left.cold.executor_attribute_types == right.cold.executor_attribute_types
25 && left.cold.score_sources == right.cold.score_sources
26 && left.cold.wildcard_hidden == right.cold.wildcard_hidden
27 && left
28 .cold
29 .collations
30 .same_physical_bindings(&right.cold.collations)
31 }
32
33 #[must_use]
35 pub fn wildcard_position_visible(&self, position: usize) -> bool {
36 position < self.len() && !self.index.cold.wildcard_hidden.contains(&position)
37 }
38
39 pub fn columns(&self) -> &[String] {
40 &self.index.columns
41 }
42
43 pub fn len(&self) -> usize {
44 self.index.columns.len()
45 }
46
47 pub fn is_empty(&self) -> bool {
48 self.index.columns.is_empty()
49 }
50
51 pub fn iter(&self) -> std::slice::Iter<'_, String> {
52 self.index.columns.iter()
53 }
54
55 pub fn column_type(&self, logical: usize) -> Option<&ColumnType> {
57 self.index
58 .cold
59 .columns
60 .get(logical)
61 .and_then(Option::as_ref)
62 }
63
64 pub fn column_types(&self) -> &[Option<ColumnType>] {
66 &self.index.cold.columns
67 }
68
69 pub fn identities(&self) -> &[ColumnIdentity] {
71 &self.index.identities
72 }
73
74 pub fn identity(&self, logical: usize) -> Option<&ColumnIdentity> {
75 self.index.identities.get(logical)
76 }
77
78 pub fn public_name(&self, logical: usize) -> Option<&str> {
79 self.identity(logical).map(ColumnIdentity::column)
80 }
81
82 pub fn position(&self, name: &str) -> Option<usize> {
83 self.index.exact.get(name).copied()
84 }
85
86 pub fn unqualified_position(&self, column: &str) -> Option<usize> {
88 if self.index.ambiguous_unqualified.contains(column) {
89 return None;
90 }
91 self.index.unqualified.get(column).copied()
92 }
93
94 pub fn qualified_position(&self, qualifier: &str, column: &str) -> Option<usize> {
96 let identity = ColumnIdentity::qualified(qualifier, column);
97 if self.index.ambiguous_qualified.contains(&identity) {
98 return None;
99 }
100 self.index.qualified.get(&identity).copied()
101 }
102
103 pub fn physical_width(&self) -> usize {
104 self.index.physical_width
105 }
106
107 pub fn physical_slot(&self, logical: usize) -> Option<usize> {
109 self.slot(logical)
110 }
111
112 pub fn physical_type(&self, physical: usize) -> Option<&ColumnType> {
115 self.index
116 .slots
117 .iter()
118 .position(|slot| *slot == physical)
119 .and_then(|logical| self.column_type(logical))
120 .or_else(|| {
121 self.index.aliases.iter().find_map(|(identity, slot)| {
122 (*slot == physical)
123 .then(|| {
124 self.index
125 .cold
126 .aliases
127 .get(identity)
128 .and_then(Option::as_ref)
129 })
130 .flatten()
131 })
132 })
133 .or_else(|| {
134 self.index
135 .executor_attributes
136 .iter()
137 .find_map(|(column, slot)| {
138 (*slot == physical)
139 .then(|| {
140 self.index
141 .cold
142 .executor_attribute_types
143 .get(column)
144 .and_then(Option::as_ref)
145 })
146 .flatten()
147 })
148 })
149 }
150
151 pub fn physical_slot_for_identity(&self, identity: &ColumnIdentity) -> Option<usize> {
153 match identity.qualifier() {
154 Some(qualifier) => self.qualified_slot(qualifier, identity.column()),
155 None => self.column_slot(identity.column()),
156 }
157 }
158
159 pub fn slot(&self, logical: usize) -> Option<usize> {
160 self.index
161 .slots
162 .get(logical)
163 .copied()
164 .filter(|slot| *slot != NULL_SLOT)
165 }
166
167 pub fn exact_slot(&self, name: &str) -> Option<usize> {
168 self.index
169 .exact
170 .get(name)
171 .and_then(|logical| self.slot(*logical))
172 .filter(|slot| *slot != NULL_SLOT)
173 }
174
175 pub(super) fn exact_type(&self, name: &str) -> Option<&ColumnType> {
176 self.index
177 .exact
178 .get(name)
179 .and_then(|logical| self.column_type(*logical))
180 }
181
182 pub fn column_slot(&self, name: &str) -> Option<usize> {
183 if self.index.ambiguous_unqualified.contains(name) {
184 return None;
185 }
186 self.index
187 .unqualified
188 .get(name)
189 .and_then(|logical| self.slot(*logical))
190 .or_else(|| {
191 self.index
192 .aliases
193 .get(&ColumnIdentity::unqualified(name))
194 .copied()
195 })
196 .filter(|slot| *slot != NULL_SLOT)
197 }
198
199 pub fn qualified_star_layout(
201 &self,
202 qualifier: &str,
203 ) -> Vec<(String, usize, Option<ColumnType>)> {
204 self.qualified_star_position_layout(qualifier)
205 .into_iter()
206 .map(|(column, _, slot, ty)| (column, slot, ty))
207 .collect()
208 }
209
210 pub fn qualified_star_position_layout(
212 &self,
213 qualifier: &str,
214 ) -> Vec<(String, Option<usize>, usize, Option<ColumnType>)> {
215 let mut entries = Vec::new();
216 let mut visible_layout = HashSet::new();
217 for (logical, identity) in self.identities().iter().enumerate() {
218 if identity.qualifier() == Some(qualifier) {
219 let slot = self.slot(logical).unwrap_or(NULL_SLOT);
220 visible_layout.insert((identity.clone(), slot));
221 entries.push((
222 identity.clone(),
223 Some(logical),
224 slot,
225 self.column_type(logical).cloned(),
226 ));
227 }
228 }
229 for (identity, slot) in &self.index.aliases {
230 if identity.qualifier() == Some(qualifier)
231 && !visible_layout.contains(&(identity.clone(), *slot))
232 {
233 entries.push((
234 identity.clone(),
235 None,
236 *slot,
237 self.index.cold.aliases.get(identity).cloned().flatten(),
238 ));
239 }
240 }
241 entries.sort_by_key(|(_, _, slot, _)| *slot);
242 entries
243 .into_iter()
244 .map(|(identity, logical, slot, ty)| (identity.column().to_string(), logical, slot, ty))
245 .collect()
246 }
247
248 pub fn qualified_slot(&self, qualifier: &str, column: &str) -> Option<usize> {
249 let identity = ColumnIdentity::qualified(qualifier, column);
250 if self.index.ambiguous_qualified.contains(&identity) {
251 return None;
252 }
253 self.index
254 .qualified
255 .get(&identity)
256 .and_then(|logical| self.slot(*logical))
257 .or_else(|| self.index.aliases.get(&identity).copied())
258 .filter(|slot| *slot != NULL_SLOT)
259 }
260}
261
262impl<'a> IntoIterator for &'a RowSchema {
263 type Item = &'a String;
264 type IntoIter = std::slice::Iter<'a, String>;
265
266 fn into_iter(self) -> Self::IntoIter {
267 self.iter()
268 }
269}