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