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