Skip to main content

uqa_sql/schema/
name_binding.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Static SQL name and type binding over a physical row schema.
8
9use crate::ast::{ColumnType, InternalColumnRef};
10
11use super::{ColumnIdentity, RowSchema, SchemaBuildMetadata, ScoreSource, NULL_SLOT};
12
13impl RowSchema {
14    /// Bind every SQL-visible column to one relation qualifier while preserving executor-only internal attributes and rebinding carried retrieval scores to the same relation boundary.
15    pub fn with_relation_qualifier(input: &Self, qualifier: &str) -> Self {
16        let identities = input
17            .columns()
18            .iter()
19            .cloned()
20            .map(|column| ColumnIdentity::qualified(qualifier, column))
21            .collect();
22        let score_sources = input
23            .index
24            .cold
25            .score_sources
26            .iter()
27            .map(|source| ScoreSource {
28                qualifier: Some(Box::<str>::from(qualifier)),
29                column: source.column,
30            })
31            .collect();
32        Self::from_typed_parts_with_aliases_and_exact_precedence(
33            input.columns().to_vec(),
34            identities,
35            input.column_types().to_vec(),
36            input.index.slots.to_vec(),
37            input.physical_width(),
38            SchemaBuildMetadata {
39                collations: input.index.cold.collations.clone(),
40                row_types: input.index.cold.row_types.requalified(input, qualifier),
41                record_fields: input.index.cold.record_fields.clone(),
42                internal: input.index.executor_attributes.clone(),
43                internal_types: input.index.cold.executor_attribute_types.clone(),
44                score_sources,
45                open_qualifiers: input
46                    .columns_are_open(None)
47                    .then(|| Some(Box::<str>::from(qualifier)))
48                    .into_iter()
49                    .collect(),
50                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
51                ..SchemaBuildMetadata::default()
52            },
53        )
54    }
55
56    /// Whether a visible or hidden lookup identity belongs to `qualifier`.
57    #[must_use]
58    pub fn has_qualifier(&self, qualifier: &str) -> bool {
59        self.index
60            .identities
61            .iter()
62            .chain(self.index.aliases.keys())
63            .chain(self.index.cold.binding_only.keys())
64            .any(|identity| identity.qualifier() == Some(qualifier))
65            || self.columns_are_open(Some(qualifier))
66            || self.index.cold.row_types.get(qualifier).is_some()
67    }
68
69    /// Whether a visible, aliased, or static binding-only identity contains this exact unqualified column, independently of its declared type.
70    #[must_use]
71    pub fn has_unqualified_column(&self, column: &str) -> bool {
72        let identity = ColumnIdentity::unqualified(column);
73        self.index.unqualified.contains_key(column)
74            || self.index.aliases.contains_key(&identity)
75            || self.index.cold.binding_only.contains_key(&identity)
76    }
77
78    /// Whether a visible or hidden lookup identity contains this exact qualified column, independently of its static type or ambiguity.
79    #[must_use]
80    pub fn has_qualified_column(&self, qualifier: &str, column: &str) -> bool {
81        let identity = ColumnIdentity::qualified(qualifier, column);
82        self.index.qualified.contains_key(&identity)
83            || self.index.aliases.contains_key(&identity)
84            || self.index.cold.binding_only.contains_key(&identity)
85    }
86
87    /// Iterate over static name-binding identities that deliberately have no physical value slot or wildcard visibility.
88    pub fn typed_virtual_identities(
89        &self,
90    ) -> impl Iterator<Item = (&ColumnIdentity, Option<&ColumnType>)> {
91        self.index
92            .cold
93            .binding_only
94            .iter()
95            .map(|(identity, ty)| (identity, ty.as_ref()))
96    }
97
98    /// Iterate over hidden lookup aliases that resolve to physical value slots while retaining their declared SQL types.
99    pub fn typed_physical_alias_identities(
100        &self,
101    ) -> impl Iterator<Item = (&ColumnIdentity, Option<&ColumnType>)> {
102        self.index.aliases.keys().map(|identity| {
103            (
104                identity,
105                self.index
106                    .cold
107                    .aliases
108                    .get(identity)
109                    .and_then(Option::as_ref),
110            )
111        })
112    }
113
114    /// Resolve an unqualified logical identity to its static type.
115    pub fn type_of(&self, name: &str) -> Option<&ColumnType> {
116        if self.index.ambiguous_unqualified.contains(name) {
117            return None;
118        }
119        self.index
120            .unqualified
121            .get(name)
122            .and_then(|logical| self.column_type(*logical))
123            .or_else(|| {
124                self.index
125                    .cold
126                    .aliases
127                    .get(&ColumnIdentity::unqualified(name))
128                    .and_then(Option::as_ref)
129            })
130            .or_else(|| {
131                self.index
132                    .cold
133                    .binding_only
134                    .get(&ColumnIdentity::unqualified(name))
135                    .and_then(Option::as_ref)
136            })
137    }
138
139    /// Resolve a qualified logical identity to its static type.
140    pub fn qualified_type(&self, qualifier: &str, column: &str) -> Option<&ColumnType> {
141        let identity = ColumnIdentity::qualified(qualifier, column);
142        if self.index.ambiguous_qualified.contains(&identity) {
143            return None;
144        }
145        self.index
146            .qualified
147            .get(&identity)
148            .and_then(|logical| self.column_type(*logical))
149            .or_else(|| {
150                self.index
151                    .cold
152                    .aliases
153                    .get(&identity)
154                    .and_then(Option::as_ref)
155            })
156            .or_else(|| {
157                self.index
158                    .cold
159                    .binding_only
160                    .get(&identity)
161                    .and_then(Option::as_ref)
162            })
163    }
164
165    pub fn column_is_ambiguous(&self, name: &str) -> bool {
166        self.index.ambiguous_unqualified.contains(name)
167    }
168
169    pub fn qualified_column_is_ambiguous(&self, qualifier: &str, column: &str) -> bool {
170        self.index
171            .ambiguous_qualified
172            .contains(&ColumnIdentity::qualified(qualifier, column))
173    }
174
175    /// Add hidden structured lookup identities for existing logical positions.
176    pub fn with_identity_aliases(input: &Self, aliases: &[(ColumnIdentity, usize)]) -> Self {
177        let mut collations = input.index.cold.collations.clone();
178        let mut lookup_aliases = input.index.aliases.clone();
179        let mut alias_types = input.index.cold.aliases.clone();
180        for (identity, logical) in aliases {
181            collations.alias(
182                identity,
183                input.inherited_column_collation(*logical),
184                input.column_type(*logical),
185            );
186            lookup_aliases.insert(identity.clone(), input.slot(*logical).unwrap_or(NULL_SLOT));
187            alias_types.insert(identity.clone(), input.column_type(*logical).cloned());
188        }
189        Self::from_typed_parts_with_aliases_and_exact_precedence(
190            input.columns().to_vec(),
191            input.identities().to_vec(),
192            input.column_types().to_vec(),
193            input.index.slots.to_vec(),
194            input.physical_width(),
195            SchemaBuildMetadata {
196                collations,
197                record_fields: input.index.cold.record_fields.clone(),
198                aliases: lookup_aliases,
199                alias_types,
200                internal: input.index.executor_attributes.clone(),
201                internal_types: input.index.cold.executor_attribute_types.clone(),
202                score_sources: input.index.cold.score_sources.clone(),
203                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
204                binding_only: input.index.cold.binding_only.clone(),
205                row_types: input.index.cold.row_types.clone(),
206                open_qualifiers: input.index.cold.open_qualifiers.clone(),
207                extra_ambiguous_unqualified: input.index.ambiguous_unqualified.clone(),
208                extra_ambiguous_qualified: input.index.ambiguous_qualified.clone(),
209                ..SchemaBuildMetadata::default()
210            },
211        )
212    }
213
214    /// Add hidden SQL lookup identities that point at explicit physical slots.
215    /// Unlike [`Self::with_identity_aliases`], these slots do not need a
216    /// SQL-visible logical column owner.
217    pub fn with_physical_identity_aliases(
218        input: &Self,
219        aliases: &[(ColumnIdentity, usize, Option<ColumnType>)],
220    ) -> Self {
221        let mut collations = input.index.cold.collations.clone();
222        let mut lookup_aliases = input.index.aliases.clone();
223        let mut alias_types = input.index.cold.aliases.clone();
224        for (identity, slot, ty) in aliases {
225            assert!(
226                *slot < input.physical_width(),
227                "physical identity alias is outside row width"
228            );
229            collations.alias(
230                identity,
231                input.inherited_physical_collation(*slot),
232                ty.as_ref(),
233            );
234            lookup_aliases.insert(identity.clone(), *slot);
235            alias_types.insert(identity.clone(), ty.clone());
236        }
237        Self::from_typed_parts_with_aliases_and_exact_precedence(
238            input.columns().to_vec(),
239            input.identities().to_vec(),
240            input.column_types().to_vec(),
241            input.index.slots.to_vec(),
242            input.physical_width(),
243            SchemaBuildMetadata {
244                collations,
245                record_fields: input.index.cold.record_fields.clone(),
246                aliases: lookup_aliases,
247                alias_types,
248                internal: input.index.executor_attributes.clone(),
249                internal_types: input.index.cold.executor_attribute_types.clone(),
250                score_sources: input.index.cold.score_sources.clone(),
251                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
252                binding_only: input.index.cold.binding_only.clone(),
253                row_types: input.index.cold.row_types.clone(),
254                open_qualifiers: input.index.cold.open_qualifiers.clone(),
255                ..SchemaBuildMetadata::default()
256            },
257        )
258    }
259
260    /// Add executor-only relation attributes at explicit physical slots.
261    pub fn with_physical_internal_aliases(
262        input: &Self,
263        aliases: &[(InternalColumnRef, usize, Option<ColumnType>)],
264    ) -> Self {
265        let mut collations = input.index.cold.collations.clone();
266        let mut internal = input.index.executor_attributes.clone();
267        let mut internal_types = input.index.cold.executor_attribute_types.clone();
268        for (column, slot, ty) in aliases {
269            assert!(
270                *slot < input.physical_width(),
271                "physical internal alias is outside row width"
272            );
273            collations.internal(
274                *column,
275                input.inherited_physical_collation(*slot),
276                ty.as_ref(),
277            );
278            internal.insert(*column, *slot);
279            internal_types.insert(*column, ty.clone());
280        }
281        Self::from_typed_parts_with_aliases_and_exact_precedence(
282            input.columns().to_vec(),
283            input.identities().to_vec(),
284            input.column_types().to_vec(),
285            input.index.slots.to_vec(),
286            input.physical_width(),
287            SchemaBuildMetadata {
288                collations,
289                record_fields: input.index.cold.record_fields.clone(),
290                aliases: input.index.aliases.clone(),
291                alias_types: input.index.cold.aliases.clone(),
292                internal,
293                internal_types,
294                score_sources: input.index.cold.score_sources.clone(),
295                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
296                binding_only: input.index.cold.binding_only.clone(),
297                row_types: input.index.cold.row_types.clone(),
298                open_qualifiers: input.index.cold.open_qualifiers.clone(),
299                extra_ambiguous_unqualified: input.index.ambiguous_unqualified.clone(),
300                extra_ambiguous_qualified: input.index.ambiguous_qualified.clone(),
301                ..SchemaBuildMetadata::default()
302            },
303        )
304    }
305
306    /// Restore structural identities from a row scope that occupies the trailing physical slots of this schema.
307    pub fn with_trailing_internal_aliases(input: &Self, trailing: &Self) -> Self {
308        let mut collations = input.index.cold.collations.clone();
309        let offset = input
310            .physical_width()
311            .checked_sub(trailing.physical_width())
312            .expect("trailing internal-alias scope exceeds input row width");
313        let mut internal = input.index.executor_attributes.clone();
314        let mut internal_types = input.index.cold.executor_attribute_types.clone();
315        for (column, slot) in &trailing.index.executor_attributes {
316            let slot = if *slot == NULL_SLOT {
317                NULL_SLOT
318            } else {
319                offset + *slot
320            };
321            if let Some(previous) = internal.insert(*column, slot) {
322                assert_eq!(
323                    previous, slot,
324                    "structural identity moved between row scopes"
325                );
326            }
327        }
328        for (column, ty) in &trailing.index.cold.executor_attribute_types {
329            collations.internal(*column, trailing.internal_collation(*column), ty.as_ref());
330            if let Some(previous) = internal_types.insert(*column, ty.clone()) {
331                assert_eq!(
332                    previous, *ty,
333                    "structural identity type changed between row scopes"
334                );
335            }
336        }
337        Self::from_typed_parts_with_aliases_and_exact_precedence(
338            input.columns().to_vec(),
339            input.identities().to_vec(),
340            input.column_types().to_vec(),
341            input.index.slots.to_vec(),
342            input.physical_width(),
343            SchemaBuildMetadata {
344                collations,
345                record_fields: input.index.cold.record_fields.clone(),
346                aliases: input.index.aliases.clone(),
347                alias_types: input.index.cold.aliases.clone(),
348                internal,
349                internal_types,
350                score_sources: input.index.cold.score_sources.clone(),
351                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
352                binding_only: input.index.cold.binding_only.clone(),
353                row_types: input.index.cold.row_types.clone(),
354                open_qualifiers: input.index.cold.open_qualifiers.clone(),
355                extra_ambiguous_unqualified: input.index.ambiguous_unqualified.clone(),
356                extra_ambiguous_qualified: input.index.ambiguous_qualified.clone(),
357                ..SchemaBuildMetadata::default()
358            },
359        )
360    }
361
362    /// Mark source-owned logical metadata attributes as explicitly
363    /// addressable but absent from `*` expansion. The identity is positional,
364    /// so an ordinary user column with the same text remains visible.
365    pub fn with_wildcard_hidden_positions(
366        input: &Self,
367        positions: impl IntoIterator<Item = usize>,
368    ) -> Self {
369        let wildcard_hidden = positions.into_iter().collect();
370        Self::from_typed_parts_with_aliases_and_exact_precedence(
371            input.columns().to_vec(),
372            input.identities().to_vec(),
373            input.column_types().to_vec(),
374            input.index.slots.to_vec(),
375            input.physical_width(),
376            SchemaBuildMetadata {
377                collations: input.index.cold.collations.clone(),
378                record_fields: input.index.cold.record_fields.clone(),
379                aliases: input.index.aliases.clone(),
380                alias_types: input.index.cold.aliases.clone(),
381                internal: input.index.executor_attributes.clone(),
382                internal_types: input.index.cold.executor_attribute_types.clone(),
383                score_sources: input.index.cold.score_sources.clone(),
384                wildcard_hidden,
385                binding_only: input.index.cold.binding_only.clone(),
386                row_types: input.index.cold.row_types.clone(),
387                open_qualifiers: input.index.cold.open_qualifiers.clone(),
388                ..SchemaBuildMetadata::default()
389            },
390        )
391    }
392
393    #[must_use]
394    pub fn internal_slot(&self, column: InternalColumnRef) -> Option<usize> {
395        self.index
396            .executor_attributes
397            .get(&column)
398            .copied()
399            .filter(|slot| *slot != NULL_SLOT)
400    }
401
402    /// Return the sole executor-only identity attached to a physical slot. Slots with no structural identity or with multiple aliases are intentionally unresolved.
403    #[must_use]
404    pub fn unique_internal_column_for_slot(&self, slot: usize) -> Option<InternalColumnRef> {
405        let mut columns = self
406            .index
407            .executor_attributes
408            .iter()
409            .filter_map(|(column, candidate)| (*candidate == slot).then_some(*column));
410        let column = columns.next()?;
411        columns.next().is_none().then_some(column)
412    }
413
414    #[must_use]
415    pub fn internal_type(&self, column: InternalColumnRef) -> Option<&ColumnType> {
416        self.index
417            .cold
418            .executor_attribute_types
419            .get(&column)
420            .and_then(Option::as_ref)
421    }
422
423    /// Mark an existing internal attribute as the score carried by one retrieval relation. This semantic tag is schema metadata and never enters SQL name lookup or wildcard expansion.
424    pub fn with_score_source(
425        input: &Self,
426        qualifier: Option<&str>,
427        column: InternalColumnRef,
428    ) -> Self {
429        assert!(
430            input.index.executor_attributes.contains_key(&column),
431            "score source must reference an existing internal attribute"
432        );
433        let mut score_sources = input.index.cold.score_sources.clone();
434        score_sources.retain(|source| source.column != column);
435        score_sources.push(ScoreSource {
436            qualifier: qualifier.map(Box::<str>::from),
437            column,
438        });
439        Self::from_typed_parts_with_aliases_and_exact_precedence(
440            input.columns().to_vec(),
441            input.identities().to_vec(),
442            input.column_types().to_vec(),
443            input.index.slots.to_vec(),
444            input.physical_width(),
445            SchemaBuildMetadata {
446                collations: input.index.cold.collations.clone(),
447                record_fields: input.index.cold.record_fields.clone(),
448                aliases: input.index.aliases.clone(),
449                alias_types: input.index.cold.aliases.clone(),
450                internal: input.index.executor_attributes.clone(),
451                internal_types: input.index.cold.executor_attribute_types.clone(),
452                score_sources,
453                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
454                binding_only: input.index.cold.binding_only.clone(),
455                row_types: input.index.cold.row_types.clone(),
456                open_qualifiers: input.index.cold.open_qualifiers.clone(),
457                ..SchemaBuildMetadata::default()
458            },
459        )
460    }
461
462    /// Rebind every carried score source at a relation-alias boundary while retaining its opaque internal attribute identity.
463    pub fn with_rebound_score_sources(input: &Self, qualifier: Option<&str>) -> Self {
464        let score_sources = input
465            .index
466            .cold
467            .score_sources
468            .iter()
469            .map(|source| ScoreSource {
470                qualifier: qualifier.map(Box::<str>::from),
471                column: source.column,
472            })
473            .collect();
474        Self::from_typed_parts_with_aliases_and_exact_precedence(
475            input.columns().to_vec(),
476            input.identities().to_vec(),
477            input.column_types().to_vec(),
478            input.index.slots.to_vec(),
479            input.physical_width(),
480            SchemaBuildMetadata {
481                collations: input.index.cold.collations.clone(),
482                record_fields: input.index.cold.record_fields.clone(),
483                aliases: input.index.aliases.clone(),
484                alias_types: input.index.cold.aliases.clone(),
485                internal: input.index.executor_attributes.clone(),
486                internal_types: input.index.cold.executor_attribute_types.clone(),
487                score_sources,
488                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
489                binding_only: input.index.cold.binding_only.clone(),
490                row_types: input.index.cold.row_types.clone(),
491                open_qualifiers: input.index.cold.open_qualifiers.clone(),
492                ..SchemaBuildMetadata::default()
493            },
494        )
495    }
496
497    fn matching_score_sources<'a>(
498        &'a self,
499        qualifier: Option<&'a str>,
500    ) -> impl Iterator<Item = InternalColumnRef> + 'a {
501        self.index
502            .cold
503            .score_sources
504            .iter()
505            .filter(move |source| {
506                qualifier.is_none_or(|qualifier| source.qualifier.as_deref() == Some(qualifier))
507            })
508            .map(move |source| source.column)
509    }
510
511    #[must_use]
512    pub fn score_source_column(&self, qualifier: Option<&str>) -> Option<InternalColumnRef> {
513        let mut columns = self.matching_score_sources(qualifier);
514        let column = columns.next()?;
515        columns.next().is_none().then_some(column)
516    }
517
518    #[must_use]
519    pub fn score_source_is_ambiguous(&self, qualifier: Option<&str>) -> bool {
520        let mut columns = self.matching_score_sources(qualifier);
521        columns.next().is_some() && columns.next().is_some()
522    }
523
524    #[must_use]
525    pub fn score_source_slot(&self, qualifier: Option<&str>) -> Option<usize> {
526        self.score_source_column(qualifier)
527            .and_then(|column| self.internal_slot(column))
528    }
529
530    /// Add statically typed hidden lookup identities that have no physical value slot. Visible columns and star expansion remain unchanged, while binders can resolve the identities and their declared SQL types.
531    pub fn with_typed_virtual_identities(
532        input: &Self,
533        identities: &[(ColumnIdentity, Option<ColumnType>)],
534    ) -> Self {
535        let mut binding_only = input.index.cold.binding_only.clone();
536        for (identity, ty) in identities {
537            let already_visible = match identity.qualifier() {
538                Some(qualifier) => input.has_qualified_column(qualifier, identity.column()),
539                None => {
540                    input.column_is_ambiguous(identity.column())
541                        || input.has_unqualified_column(identity.column())
542                }
543            };
544            if already_visible {
545                continue;
546            }
547            binding_only
548                .entry(identity.clone())
549                .or_insert_with(|| ty.clone());
550        }
551        Self::from_typed_parts_with_aliases_and_exact_precedence(
552            input.columns().to_vec(),
553            input.identities().to_vec(),
554            input.column_types().to_vec(),
555            input.index.slots.to_vec(),
556            input.physical_width(),
557            SchemaBuildMetadata {
558                collations: input.index.cold.collations.clone(),
559                record_fields: input.index.cold.record_fields.clone(),
560                aliases: input.index.aliases.clone(),
561                alias_types: input.index.cold.aliases.clone(),
562                internal: input.index.executor_attributes.clone(),
563                internal_types: input.index.cold.executor_attribute_types.clone(),
564                score_sources: input.index.cold.score_sources.clone(),
565                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
566                binding_only,
567                row_types: input.index.cold.row_types.clone(),
568                open_qualifiers: input.index.cold.open_qualifiers.clone(),
569                extra_ambiguous_unqualified: input.index.ambiguous_unqualified.clone(),
570                extra_ambiguous_qualified: input.index.ambiguous_qualified.clone(),
571                local_width: input.index.cold.local_width,
572                ..SchemaBuildMetadata::default()
573            },
574        )
575    }
576
577    /// Whether a column reference resolves to one of the schema's own columns rather than to an enclosing query's column overlaid by [`Self::with_outer_schema`], as `contain_vars_of_level(n, 0)` distinguishes them.
578    pub fn resolves_local_column(&self, qualifier: Option<&str>, column: &str) -> bool {
579        let slot = match qualifier {
580            Some(qualifier) => self.qualified_slot(qualifier, column),
581            None => self.column_slot(column),
582        };
583        slot.is_some_and(|slot| self.slot_is_local(slot))
584    }
585
586    pub(crate) fn slot_is_local(&self, slot: usize) -> bool {
587        self.index.cold.local_width.is_none_or(|width| slot < width)
588    }
589
590    pub(crate) fn internal_identities(
591        &self,
592    ) -> impl Iterator<Item = (InternalColumnRef, usize)> + '_ {
593        self.index
594            .executor_attributes
595            .iter()
596            .filter_map(|(column, slot)| (*slot != super::NULL_SLOT).then_some((*column, *slot)))
597    }
598
599    /// Add binding-only identities and preserve collisions as ambiguous names. This models SQL scopes that expose a hidden generated column for explicit lookup while deliberately excluding it from wildcard expansion.
600    pub fn with_typed_conflicting_virtual_identities(
601        input: &Self,
602        identities: &[(ColumnIdentity, Option<ColumnType>)],
603    ) -> Self {
604        let mut binding_only = input.index.cold.binding_only.clone();
605        let mut ambiguous_unqualified = input.index.ambiguous_unqualified.clone();
606        let mut ambiguous_qualified = input.index.ambiguous_qualified.clone();
607        for (identity, ty) in identities {
608            if input.column_is_ambiguous(identity.column())
609                || input.has_unqualified_column(identity.column())
610            {
611                ambiguous_unqualified.insert(Box::<str>::from(identity.column()));
612            }
613            if let Some(qualifier) = identity.qualifier() {
614                if input.has_qualified_column(qualifier, identity.column()) {
615                    ambiguous_qualified.insert(identity.clone());
616                }
617            }
618            binding_only
619                .entry(identity.clone())
620                .or_insert_with(|| ty.clone());
621        }
622        Self::from_typed_parts_with_aliases_and_exact_precedence(
623            input.columns().to_vec(),
624            input.identities().to_vec(),
625            input.column_types().to_vec(),
626            input.index.slots.to_vec(),
627            input.physical_width(),
628            SchemaBuildMetadata {
629                collations: input.index.cold.collations.clone(),
630                record_fields: input.index.cold.record_fields.clone(),
631                aliases: input.index.aliases.clone(),
632                alias_types: input.index.cold.aliases.clone(),
633                internal: input.index.executor_attributes.clone(),
634                internal_types: input.index.cold.executor_attribute_types.clone(),
635                score_sources: input.index.cold.score_sources.clone(),
636                wildcard_hidden: input.index.cold.wildcard_hidden.clone(),
637                binding_only,
638                row_types: input.index.cold.row_types.clone(),
639                open_qualifiers: input.index.cold.open_qualifiers.clone(),
640                extra_ambiguous_unqualified: ambiguous_unqualified,
641                extra_ambiguous_qualified: ambiguous_qualified,
642                ..SchemaBuildMetadata::default()
643            },
644        )
645    }
646}