1use super::{
10 Arc, ColumnIdentity, ColumnType, HashMap, HashSet, RowSchema, SchemaBuildMetadata,
11 SchemaColdMetadata, SchemaIndex, NULL_SLOT,
12};
13use crate::ast::InternalRelationId;
14
15impl Default for RowSchema {
16 fn default() -> Self {
17 Self::new(Vec::new())
18 }
19}
20
21impl From<Vec<String>> for RowSchema {
22 fn from(columns: Vec<String>) -> Self {
23 Self::new(columns)
24 }
25}
26
27impl RowSchema {
28 pub fn new(columns: Vec<String>) -> Self {
29 let width = columns.len();
30 let identities = columns
31 .iter()
32 .cloned()
33 .map(ColumnIdentity::unqualified)
34 .collect();
35 Self::from_parts(columns, identities, (0..width).collect(), width)
36 }
37
38 pub fn with_types(columns: Vec<String>, types: Vec<Option<ColumnType>>) -> Self {
40 let width = columns.len();
41 assert_eq!(width, types.len(), "row schema column/type width mismatch");
42 let identities = columns
43 .iter()
44 .cloned()
45 .map(ColumnIdentity::unqualified)
46 .collect();
47 Self::from_typed_parts_with_aliases_and_exact_precedence(
48 columns,
49 identities,
50 types,
51 (0..width).collect(),
52 width,
53 SchemaBuildMetadata::default(),
54 )
55 }
56
57 pub fn with_qualified_types(
59 qualifier: &str,
60 columns: Vec<String>,
61 types: Vec<Option<ColumnType>>,
62 ) -> Self {
63 let identities = columns
64 .iter()
65 .cloned()
66 .map(|column| ColumnIdentity::qualified(qualifier, column))
67 .collect();
68 Self::with_identities(columns, identities, types)
69 }
70
71 pub fn with_column_definitions(
73 qualifier: Option<&str>,
74 columns: &[crate::ast::ColumnDef],
75 ) -> Self {
76 Self::with_column_metadata(
77 qualifier,
78 columns.iter().map(|column| {
79 (
80 column.name.clone(),
81 Some(column.ty.clone()),
82 column.collation_oid,
83 )
84 }),
85 )
86 }
87
88 pub fn with_column_metadata(
90 qualifier: Option<&str>,
91 columns: impl IntoIterator<Item = (String, Option<ColumnType>, Option<u32>)>,
92 ) -> Self {
93 let columns = columns.into_iter();
94 let capacity = columns.size_hint().0;
95 let mut names = Vec::with_capacity(capacity);
96 let mut identities = Vec::with_capacity(capacity);
97 let mut types = Vec::with_capacity(capacity);
98 let mut metadata = SchemaBuildMetadata::default();
99 for (name, ty, oid) in columns {
100 assert!(
101 oid.is_none_or(|oid| oid == 0
102 || ty
103 .as_ref()
104 .is_some_and(super::composites::type_is_collatable)),
105 "collation requires a collatable column type"
106 );
107 metadata.collations.column(names.len(), oid, ty.as_ref());
108 identities.push(qualifier.map_or_else(
109 || ColumnIdentity::unqualified(&name),
110 |qualifier| ColumnIdentity::qualified(qualifier, &name),
111 ));
112 names.push(name);
113 types.push(ty);
114 }
115 let width = names.len();
116 Self::from_typed_parts_with_aliases_and_exact_precedence(
117 names,
118 identities,
119 types,
120 (0..width).collect(),
121 width,
122 metadata,
123 )
124 }
125
126 pub fn with_identities(
128 columns: Vec<String>,
129 identities: Vec<ColumnIdentity>,
130 types: Vec<Option<ColumnType>>,
131 ) -> Self {
132 let width = columns.len();
133 assert_eq!(
134 width,
135 identities.len(),
136 "row schema column/identity width mismatch"
137 );
138 assert_eq!(width, types.len(), "row schema column/type width mismatch");
139 Self::from_typed_parts_with_aliases_and_exact_precedence(
140 columns,
141 identities,
142 types,
143 (0..width).collect(),
144 width,
145 SchemaBuildMetadata::default(),
146 )
147 }
148
149 pub fn with_internal_relation_types(
153 relation: InternalRelationId,
154 types: Vec<Option<ColumnType>>,
155 ) -> Self {
156 Self::with_internal_relation_metadata(relation, types.into_iter().map(|ty| (ty, None)))
157 }
158
159 pub fn with_internal_relation_metadata(
161 relation: InternalRelationId,
162 columns: impl IntoIterator<Item = (Option<ColumnType>, Option<u32>)>,
163 ) -> Self {
164 let mut metadata = SchemaBuildMetadata::default();
165 let mut width = 0;
166 for (position, (ty, oid)) in columns.into_iter().enumerate() {
167 assert!(
168 oid.is_none_or(|oid| oid == 0
169 || ty
170 .as_ref()
171 .is_some_and(super::composites::type_is_collatable)),
172 "collation requires a collatable column type"
173 );
174 let column = relation.column(position);
175 metadata.collations.internal(column, oid, ty.as_ref());
176 metadata.internal.insert(column, position);
177 metadata.internal_types.insert(column, ty);
178 width += 1;
179 }
180 Self::from_typed_parts_with_aliases_and_exact_precedence(
181 Vec::new(),
182 Vec::new(),
183 Vec::new(),
184 Vec::new(),
185 width,
186 metadata,
187 )
188 }
189
190 pub fn from_named_columns(columns: Vec<String>) -> Self {
195 let width = columns.len();
196 let identities = columns
197 .iter()
198 .cloned()
199 .map(ColumnIdentity::unqualified)
200 .collect();
201 Self::from_parts_with_aliases_and_exact_precedence(
202 columns,
203 identities,
204 (0..width).collect(),
205 width,
206 SchemaBuildMetadata {
207 exact_unqualified_precedence: true,
208 ..SchemaBuildMetadata::default()
209 },
210 )
211 }
212
213 fn from_parts(
214 columns: Vec<String>,
215 identities: Vec<ColumnIdentity>,
216 slots: Vec<usize>,
217 physical_width: usize,
218 ) -> Self {
219 Self::from_parts_with_aliases(columns, identities, slots, physical_width, HashMap::new())
220 }
221
222 fn from_parts_with_aliases(
223 columns: Vec<String>,
224 identities: Vec<ColumnIdentity>,
225 slots: Vec<usize>,
226 physical_width: usize,
227 aliases: HashMap<ColumnIdentity, usize>,
228 ) -> Self {
229 Self::from_parts_with_aliases_and_exact_precedence(
230 columns,
231 identities,
232 slots,
233 physical_width,
234 SchemaBuildMetadata {
235 aliases,
236 ..SchemaBuildMetadata::default()
237 },
238 )
239 }
240
241 fn from_parts_with_aliases_and_exact_precedence(
242 columns: Vec<String>,
243 identities: Vec<ColumnIdentity>,
244 slots: Vec<usize>,
245 physical_width: usize,
246 metadata: SchemaBuildMetadata,
247 ) -> Self {
248 let types = vec![None; columns.len()];
249 Self::from_typed_parts_with_aliases_and_exact_precedence(
250 columns,
251 identities,
252 types,
253 slots,
254 physical_width,
255 metadata,
256 )
257 }
258
259 pub(super) fn from_typed_parts_with_aliases_and_exact_precedence(
260 columns: Vec<String>,
261 identities: Vec<ColumnIdentity>,
262 types: Vec<Option<ColumnType>>,
263 slots: Vec<usize>,
264 physical_width: usize,
265 metadata: SchemaBuildMetadata,
266 ) -> Self {
267 let SchemaBuildMetadata {
268 mut collations,
269 row_types,
270 record_fields,
271 aliases,
272 alias_types,
273 internal,
274 internal_types,
275 score_sources,
276 wildcard_hidden,
277 binding_only,
278 open_qualifiers,
279 exact_unqualified_precedence,
280 extra_ambiguous_unqualified,
281 extra_ambiguous_qualified,
282 local_width,
283 } = metadata;
284 collations.retain(&types, &alias_types, &internal_types, &binding_only);
285 debug_assert_eq!(columns.len(), slots.len());
286 debug_assert_eq!(columns.len(), identities.len());
287 debug_assert_eq!(columns.len(), types.len());
288 debug_assert!(wildcard_hidden
289 .iter()
290 .all(|position| *position < columns.len()));
291 let identity_layout = physical_width == columns.len()
292 && slots
293 .iter()
294 .enumerate()
295 .all(|(position, slot)| position == *slot);
296 let mut exact = HashMap::with_capacity(columns.len());
297 let mut unqualified = HashMap::with_capacity(columns.len());
298 let mut qualified = HashMap::with_capacity(columns.len());
299 let mut unqualified_counts: HashMap<Box<str>, usize> = HashMap::new();
300 let mut qualified_counts: HashMap<ColumnIdentity, usize> = HashMap::new();
301
302 for (logical, (name, identity)) in columns.iter().zip(&identities).enumerate() {
303 exact.insert(Box::<str>::from(name.as_str()), logical);
306 *unqualified_counts
307 .entry(identity.column.clone())
308 .or_default() += 1;
309 unqualified.insert(identity.column.clone(), logical);
310 if identity.qualifier.is_some() {
311 *qualified_counts.entry(identity.clone()).or_default() += 1;
312 qualified.insert(identity.clone(), logical);
313 }
314 }
315 for slot in aliases.values() {
316 debug_assert!(*slot == NULL_SLOT || *slot < physical_width);
317 }
318 for slot in internal.values() {
319 debug_assert!(*slot == NULL_SLOT || *slot < physical_width);
320 }
321 let mut ambiguous_unqualified: HashSet<Box<str>> = unqualified_counts
322 .into_iter()
323 .filter_map(|(column, count)| {
324 (count > 1
325 && !(exact_unqualified_precedence
326 && identities.iter().any(|identity| {
327 identity.qualifier.is_none() && identity.column == column
328 })))
329 .then_some(column)
330 })
331 .collect();
332 ambiguous_unqualified.extend(extra_ambiguous_unqualified);
333 let mut ambiguous_qualified = qualified_counts
334 .into_iter()
335 .filter_map(|(identity, count)| (count > 1).then_some(identity))
336 .collect::<HashSet<_>>();
337 ambiguous_qualified.extend(extra_ambiguous_qualified);
338 Self {
339 index: Arc::new(SchemaIndex {
340 columns: columns.into_boxed_slice(),
341 identities: identities.into_boxed_slice(),
342 slots: slots.into_boxed_slice(),
343 physical_width,
344 exact,
345 unqualified,
346 qualified,
347 aliases,
348 executor_attributes: internal,
349 ambiguous_unqualified,
350 ambiguous_qualified,
351 cold: Box::new(SchemaColdMetadata {
352 collations,
353 row_types,
354 record_fields,
355 columns: types.into_boxed_slice(),
356 aliases: alias_types,
357 executor_attribute_types: internal_types,
358 score_sources,
359 wildcard_hidden,
360 binding_only,
361 open_qualifiers,
362 identity_layout,
363 local_width,
364 }),
365 }),
366 }
367 }
368}