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