Skip to main content

uqa_sql/schema/
mod.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! SQL column identities, declared types, and logical slot layouts.
8
9use crate::ast::{ColumnType, InternalColumnRef};
10use std::collections::{HashMap, HashSet};
11use std::sync::Arc;
12
13mod name_binding;
14mod open_columns;
15mod outer_scope;
16mod record_fields;
17mod schema_composition;
18mod schema_construction;
19mod schema_layout;
20mod schema_projection;
21mod schema_remap;
22pub use record_fields::RecordFields;
23
24const NULL_SLOT: usize = usize::MAX;
25
26/// Invalid schema layout supplied by a planner or a persisted row layout.
27#[derive(Debug, thiserror::Error)]
28#[error("{0}")]
29pub struct SchemaLayoutError(pub String);
30
31type SchemaLayoutResult<T> = Result<T, SchemaLayoutError>;
32
33/// Structured SQL column identity. A qualifier is metadata, never a prefix encoded into the column name, so quoted names containing `.` remain intact.
34#[derive(
35    Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
36)]
37pub struct ColumnIdentity {
38    qualifier: Option<Box<str>>,
39    column: Box<str>,
40}
41
42/// One score-bearing relation carried through the executor under an opaque internal attribute. The optional qualifier is SQL namespace metadata; the score value itself is never addressed by a magic SQL column name.
43#[derive(Debug, Clone, PartialEq, Eq)]
44struct ScoreSource {
45    qualifier: Option<Box<str>>,
46    column: InternalColumnRef,
47}
48
49impl ColumnIdentity {
50    #[must_use]
51    pub fn unqualified(column: impl Into<String>) -> Self {
52        Self {
53            qualifier: None,
54            column: Box::<str>::from(column.into()),
55        }
56    }
57
58    #[must_use]
59    pub fn qualified(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
60        Self {
61            qualifier: Some(Box::<str>::from(qualifier.into())),
62            column: Box::<str>::from(column.into()),
63        }
64    }
65
66    #[must_use]
67    pub fn qualifier(&self) -> Option<&str> {
68        self.qualifier.as_deref()
69    }
70
71    #[must_use]
72    pub fn column(&self) -> &str {
73        &self.column
74    }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78struct SchemaIndex {
79    /// Public/materialized output labels in logical order.
80    columns: Box<[String]>,
81    /// SQL lookup identities aligned with `columns`.
82    identities: Box<[ColumnIdentity]>,
83    /// Logical column position -> flattened physical value position.
84    slots: Box<[usize]>,
85    physical_width: usize,
86    /// Structural lookup by physical/public label. SQL name binding uses `unqualified` or `qualified`, never this map.
87    exact: HashMap<Box<str>, usize>,
88    unqualified: HashMap<Box<str>, usize>,
89    qualified: HashMap<ColumnIdentity, usize>,
90    /// Additional lookup identities that point directly at an existing physical slot without becoming output columns. Correlated table aliases use this to expose `(alias, column)` without duplicating the value.
91    aliases: HashMap<ColumnIdentity, usize>,
92    /// Executor-only relation/attribute identities mapped directly to physical
93    /// slots. These never participate in SQL name lookup or wildcard output.
94    executor_attributes: HashMap<InternalColumnRef, usize>,
95    /// Visible unqualified names with more than one logical owner.
96    ambiguous_unqualified: HashSet<Box<str>>,
97    /// Visible qualified identities with more than one logical owner.
98    ambiguous_qualified: HashSet<ColumnIdentity>,
99    /// Static type metadata stays behind a cold pointer so declared SQL identities do not enlarge or displace the cache-hot row lookup fields above.
100    cold: Box<SchemaColdMetadata>,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
104struct SchemaColdMetadata {
105    record_fields: HashMap<usize, RecordFields>,
106    /// `None` is an as-yet unresolved type, not a runtime NULL value.
107    columns: Box<[Option<ColumnType>]>,
108    aliases: HashMap<ColumnIdentity, Option<ColumnType>>,
109    executor_attribute_types: HashMap<InternalColumnRef, Option<ColumnType>>,
110    score_sources: Vec<ScoreSource>,
111    /// Logical attributes omitted from unqualified and qualified wildcard
112    /// expansion. Explicit references and projections remain ordinary SQL
113    /// columns; only the source-owned metadata positions are hidden.
114    wildcard_hidden: HashSet<usize>,
115    /// Static name-binding identities with no runtime slot. Unlike aliases,
116    /// these are never part of qualified wildcard expansion or spill layout.
117    binding_only: HashMap<ColumnIdentity, Option<ColumnType>>,
118    /// Names supplied only when a document or native-function source opens.
119    open_qualifiers: HashSet<Option<Box<str>>>,
120    identity_layout: bool,
121    /// The physical width of the schema's own columns when [`RowSchema::with_outer_schema`] overlaid an enclosing query's columns after them; `None` when every column is the schema's own.
122    local_width: Option<usize>,
123}
124
125#[derive(Default)]
126struct SchemaBuildMetadata {
127    record_fields: HashMap<usize, RecordFields>,
128    aliases: HashMap<ColumnIdentity, usize>,
129    alias_types: HashMap<ColumnIdentity, Option<ColumnType>>,
130    internal: HashMap<InternalColumnRef, usize>,
131    internal_types: HashMap<InternalColumnRef, Option<ColumnType>>,
132    score_sources: Vec<ScoreSource>,
133    wildcard_hidden: HashSet<usize>,
134    binding_only: HashMap<ColumnIdentity, Option<ColumnType>>,
135    open_qualifiers: HashSet<Option<Box<str>>>,
136    exact_unqualified_precedence: bool,
137    extra_ambiguous_unqualified: HashSet<Box<str>>,
138    extra_ambiguous_qualified: HashSet<ColumnIdentity>,
139    local_width: Option<usize>,
140}
141
142pub struct PhysicalLayout {
143    pub columns: Vec<String>,
144    pub identities: Vec<ColumnIdentity>,
145    pub types: Vec<Option<ColumnType>>,
146    pub slots: Vec<Option<usize>>,
147    pub physical_width: usize,
148    pub aliases: Vec<(ColumnIdentity, Option<usize>, Option<ColumnType>)>,
149    pub internal: Vec<(InternalColumnRef, Option<usize>, Option<ColumnType>)>,
150    pub score_sources: Vec<(Option<String>, InternalColumnRef)>,
151    pub wildcard_hidden: HashSet<usize>,
152}
153
154/// Immutable column layout shared by an operator and all of its batches.
155///
156/// `columns` are the logical output labels. `slots` may point into a wider
157/// composite physical row after a projection/rename, allowing those operators
158/// to change row shape without moving any values.
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct RowSchema {
161    index: Arc<SchemaIndex>,
162}
163
164/// Physical source of one scalar-projection output. Direct input slots stay in the child row; only computed values extend its physical layout.
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum ProjectedSlot {
167    Input(Option<usize>),
168    Computed(usize),
169}
170
171impl RowSchema {
172    /// Logical-to-physical slots; usize::MAX denotes a NULL slot.
173    #[doc(hidden)]
174    pub fn layout_slots(&self) -> &[usize] {
175        &self.index.slots
176    }
177
178    #[doc(hidden)]
179    pub fn is_identity_layout(&self) -> bool {
180        self.index.cold.identity_layout
181    }
182}
183
184pub mod join_output;
185
186mod declarations;
187pub mod expressions;
188pub use declarations::{SchemaBindingContext, SchemaExpressionCatalog};
189pub mod generated;
190pub mod indexes;
191
192pub mod columns;
193pub mod composites;
194pub mod constraints;
195pub mod defaults;
196pub mod domains;
197
198pub mod dependencies;
199
200pub mod check_inheritance;
201
202pub mod table_creation;
203
204pub mod inheritance;
205
206pub mod foreign_keys;
207
208pub mod referenced_partitions;
209
210pub mod sequences;
211
212pub mod constraint_metadata;
213
214pub mod keys;
215
216pub mod constraint_changes;
217
218pub mod constraint_views;
219
220pub mod table_alteration;
221
222pub mod removal;
223
224pub mod namespaces;
225pub mod relation_alteration;
226pub mod view_creation;
227
228pub mod truncate;
229
230pub mod type_objects;
231
232pub mod foreign_servers;
233pub mod foreign_tables;
234pub mod foreign_wrappers;
235
236pub mod retention;
237
238mod type_lookup;
239pub use type_lookup::{ColumnTypeSchema, ScalarTypeSchema};