1use super::{
10 ColumnIdentity, ColumnType, RowSchema, SchemaBuildMetadata, SchemaLayoutError,
11 SchemaLayoutResult, NULL_SLOT,
12};
13
14impl RowSchema {
15 pub fn append(input: &Self, names: &[String]) -> Self {
18 let columns = names
19 .iter()
20 .cloned()
21 .map(|name| (name, None))
22 .collect::<Vec<_>>();
23 Self::append_typed(input, &columns)
24 }
25
26 pub fn append_typed(input: &Self, names: &[(String, Option<ColumnType>)]) -> Self {
28 let mut collations = input.index.cold.collations.clone();
29 let mut columns = input.columns().to_vec();
30 let mut identities = input.identities().to_vec();
31 let mut types = input.column_types().to_vec();
32 let mut slots = input.index.slots.to_vec();
33 let mut wildcard_hidden = input.index.cold.wildcard_hidden.clone();
34 let base = input.physical_width();
35 for (offset, (name, ty)) in names.iter().enumerate() {
36 let slot = base + offset;
37 if let Some(position) = columns.iter().position(|column| column == name) {
38 slots[position] = slot;
39 identities[position] = ColumnIdentity::unqualified(name);
40 types[position].clone_from(ty);
41 collations.column(position, None, ty.as_ref());
42 wildcard_hidden.remove(&position);
43 } else {
44 columns.push(name.clone());
45 identities.push(ColumnIdentity::unqualified(name));
46 types.push(ty.clone());
47 slots.push(slot);
48 }
49 }
50 Self::from_typed_parts_with_aliases_and_exact_precedence(
51 columns,
52 identities,
53 types,
54 slots,
55 base + names.len(),
56 SchemaBuildMetadata {
57 collations,
58 record_fields: input.index.cold.record_fields.clone(),
59 aliases: input.index.aliases.clone(),
60 alias_types: input.index.cold.aliases.clone(),
61 internal: input.index.executor_attributes.clone(),
62 internal_types: input.index.cold.executor_attribute_types.clone(),
63 score_sources: input.index.cold.score_sources.clone(),
64 wildcard_hidden,
65 binding_only: input.index.cold.binding_only.clone(),
66 row_types: input.index.cold.row_types.clone(),
67 open_qualifiers: input.index.cold.open_qualifiers.clone(),
68 ..SchemaBuildMetadata::default()
69 },
70 )
71 }
72
73 pub fn append_hidden_typed(input: &Self, types: &[Option<ColumnType>]) -> Self {
77 Self::from_typed_parts_with_aliases_and_exact_precedence(
78 input.columns().to_vec(),
79 input.identities().to_vec(),
80 input.column_types().to_vec(),
81 input.index.slots.to_vec(),
82 input.physical_width() + types.len(),
83 SchemaBuildMetadata {
84 collations: input.index.cold.collations.clone(),
85 record_fields: input.index.cold.record_fields.clone(),
86 aliases: input.index.aliases.clone(),
87 alias_types: input.index.cold.aliases.clone(),
88 internal: input.index.executor_attributes.clone(),
89 internal_types: input.index.cold.executor_attribute_types.clone(),
90 score_sources: input.index.cold.score_sources.clone(),
91 wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
92 binding_only: input.index.cold.binding_only.clone(),
93 row_types: input.index.cold.row_types.clone(),
94 open_qualifiers: input.index.cold.open_qualifiers.clone(),
95 ..SchemaBuildMetadata::default()
96 },
97 )
98 }
99
100 pub fn append_internal_typed(
103 input: &Self,
104 columns: &[(crate::ast::InternalColumnRef, Option<ColumnType>)],
105 ) -> Self {
106 let mut collations = input.index.cold.collations.clone();
107 let base = input.physical_width();
108 let mut internal = input.index.executor_attributes.clone();
109 let mut internal_types = input.index.cold.executor_attribute_types.clone();
110 for (offset, (column, ty)) in columns.iter().enumerate() {
111 collations.internal(*column, None, ty.as_ref());
112 internal.insert(*column, base + offset);
113 internal_types.insert(*column, ty.clone());
114 }
115 Self::from_typed_parts_with_aliases_and_exact_precedence(
116 input.columns().to_vec(),
117 input.identities().to_vec(),
118 input.column_types().to_vec(),
119 input.index.slots.to_vec(),
120 base + columns.len(),
121 SchemaBuildMetadata {
122 collations,
123 record_fields: input.index.cold.record_fields.clone(),
124 aliases: input.index.aliases.clone(),
125 alias_types: input.index.cold.aliases.clone(),
126 internal,
127 internal_types,
128 score_sources: input.index.cold.score_sources.clone(),
129 wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
130 binding_only: input.index.cold.binding_only.clone(),
131 row_types: input.index.cold.row_types.clone(),
132 open_qualifiers: input.index.cold.open_qualifiers.clone(),
133 ..SchemaBuildMetadata::default()
134 },
135 )
136 }
137
138 pub fn join(
142 left: &Self,
143 right: &Self,
144 extra_columns: impl IntoIterator<Item = String>,
145 ) -> Self {
146 let collations = super::Collations::joined(left, right);
147 let mut columns = left.columns().to_vec();
148 let mut identities = left.identities().to_vec();
149 let mut types = left.column_types().to_vec();
150 let mut slots = left.index.slots.to_vec();
151 let right_base = left.physical_width();
152 let mut aliases = left.index.aliases.clone();
153 let mut alias_types = left.index.cold.aliases.clone();
154 let mut internal = left.index.executor_attributes.clone();
155 let mut internal_types = left.index.cold.executor_attribute_types.clone();
156 let mut score_sources = left.index.cold.score_sources.clone();
157 let mut wildcard_hidden = left.index.cold.wildcard_hidden.clone();
158 let open_qualifiers = &left.index.cold.open_qualifiers | &right.index.cold.open_qualifiers;
159 let mut binding_only = left.index.cold.binding_only.clone();
160 aliases.extend(right.index.aliases.iter().map(|(name, slot)| {
161 (
162 name.clone(),
163 if *slot == NULL_SLOT {
164 NULL_SLOT
165 } else {
166 right_base + *slot
167 },
168 )
169 }));
170 let right_aliases = right.index.cold.aliases.iter();
171 alias_types.extend(right_aliases.map(|(name, ty)| (name.clone(), ty.clone())));
172 for (column, slot) in &right.index.executor_attributes {
173 let shifted = if *slot == NULL_SLOT {
174 NULL_SLOT
175 } else {
176 right_base + *slot
177 };
178 assert!(
179 internal.insert(*column, shifted).is_none(),
180 "duplicate internal relation attribute in joined row"
181 );
182 }
183 for (column, ty) in &right.index.cold.executor_attribute_types {
184 assert!(
185 internal_types.insert(*column, ty.clone()).is_none(),
186 "duplicate internal relation attribute type in joined row"
187 );
188 }
189 score_sources.extend(right.index.cold.score_sources.iter().cloned());
190 wildcard_hidden.extend(
191 right
192 .index
193 .cold
194 .wildcard_hidden
195 .iter()
196 .map(|position| left.len() + *position),
197 );
198 binding_only.extend(
199 right
200 .index
201 .cold
202 .binding_only
203 .iter()
204 .map(|(identity, ty)| (identity.clone(), ty.clone())),
205 );
206 for (right_logical, column) in right.columns().iter().enumerate() {
207 let slot = right
208 .slot(right_logical)
209 .map_or(NULL_SLOT, |slot| right_base + slot);
210 columns.push(column.clone());
211 identities.push(right.identities()[right_logical].clone());
212 types.push(right.column_type(right_logical).cloned());
213 slots.push(slot);
214 }
215 for column in extra_columns {
216 if !columns.contains(&column) {
217 identities.push(ColumnIdentity::unqualified(column.clone()));
218 columns.push(column);
219 types.push(None);
220 slots.push(NULL_SLOT);
221 }
222 }
223 Self::from_typed_parts_with_aliases_and_exact_precedence(
224 columns,
225 identities,
226 types,
227 slots,
228 left.physical_width() + right.physical_width(),
229 SchemaBuildMetadata {
230 collations,
231 record_fields: Self::joined_record_fields(left, right, right_base),
232 aliases,
233 alias_types,
234 internal,
235 internal_types,
236 score_sources,
237 wildcard_hidden,
238 binding_only,
239 row_types: super::relation_types::RelationRowTypes::joined(left, right),
240 open_qualifiers,
241 ..SchemaBuildMetadata::default()
242 },
243 )
244 }
245
246 pub fn relayout_slots(&self, target: &Self) -> SchemaLayoutResult<Vec<usize>> {
248 fn assign(
249 source_slots: &mut [Option<usize>],
250 target_slot: usize,
251 source_slot: usize,
252 ) -> SchemaLayoutResult<()> {
253 if target_slot == NULL_SLOT {
254 return Ok(());
255 }
256 match source_slots[target_slot] {
257 Some(existing) if existing != source_slot => Err(SchemaLayoutError(format!(
258 "physical relayout maps target slot {target_slot} to both source slots {existing} and {source_slot}"
259 ))),
260 Some(_) => Ok(()),
261 None => {
262 source_slots[target_slot] = Some(source_slot);
263 Ok(())
264 }
265 }
266 }
267
268 if self.len() != target.len() {
269 return Err(SchemaLayoutError(format!(
270 "cannot relayout {} logical columns as {} logical columns",
271 self.len(),
272 target.len()
273 )));
274 }
275
276 let mut source_slots = vec![None; target.physical_width()];
277
278 for logical in 0..target.len() {
279 assign(
280 &mut source_slots,
281 target.index.slots[logical],
282 self.index.slots[logical],
283 )?;
284 }
285
286 for (identity, target_slot) in &target.index.aliases {
287 if *target_slot == NULL_SLOT {
288 continue;
289 }
290 let mut matching_slots = self
291 .index
292 .identities
293 .iter()
294 .enumerate()
295 .filter_map(|(logical, candidate)| {
296 (candidate == identity).then_some(self.index.slots[logical])
297 })
298 .chain(self.index.aliases.get(identity).copied())
299 .collect::<Vec<_>>();
300 matching_slots.sort_unstable();
301 matching_slots.dedup();
302 let source_slot = match matching_slots.as_slice() {
303 [source_slot] => *source_slot,
304 [] => {
305 return Err(SchemaLayoutError(format!(
306 "physical relayout source is missing lookup identity `{identity:?}`"
307 )))
308 }
309 _ => {
310 return Err(SchemaLayoutError(format!(
311 "physical relayout source has ambiguous lookup identity `{identity:?}`"
312 )))
313 }
314 };
315 assign(&mut source_slots, *target_slot, source_slot)?;
316 }
317
318 for (column, target_slot) in &target.index.executor_attributes {
319 if *target_slot == NULL_SLOT {
320 continue;
321 }
322 if source_slots[*target_slot].is_some() {
324 continue;
325 }
326 let source_slot = self
327 .internal_slot(*column)
328 .or_else(|| {
329 target
330 .index
331 .cold
332 .score_sources
333 .iter()
334 .find(|source| source.column == *column)
335 .map(|source| source.qualifier.as_deref())
336 .and_then(|qualifier| self.score_source_slot(qualifier))
337 })
338 .ok_or_else(|| {
339 SchemaLayoutError(format!(
340 "physical relayout source is missing internal relation attribute `{column:?}`"
341 ))
342 })?;
343 assign(&mut source_slots, *target_slot, source_slot)?;
344 }
345
346 let source_slots = source_slots
347 .into_iter()
348 .map(|slot| slot.unwrap_or(NULL_SLOT))
349 .collect::<Vec<_>>();
350 Ok(source_slots)
351 }
352}