Skip to main content

sim_relation_schema/
physical.rs

1use sim_kernel::{Datum, Symbol};
2use sim_relation_core::{
3    ColumnName, DomainId, IndexName, ProviderName, RelationId, RevisionName, SchemaName,
4    StorageRepr, TableName, ToRelationDatum,
5};
6use std::collections::BTreeSet;
7
8/// A normalized provider-observed column.
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub struct PhysicalColumn {
11    /// Observed column name.
12    pub name: ColumnName,
13    /// Normalized logical domain.
14    pub domain: DomainId,
15    /// Exact provider-boundary representation.
16    pub storage: StorageRepr,
17    /// Observed nullability.
18    pub nullable: bool,
19    /// Provider ordinal preserving semantic column order.
20    pub ordinal: u32,
21}
22/// A normalized provider-observed index.
23#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
24pub struct PhysicalIndex {
25    /// Observed index name.
26    pub name: IndexName,
27    /// Observed key columns in order.
28    pub columns: Vec<ColumnName>,
29    /// Observed uniqueness.
30    pub unique: bool,
31}
32/// A normalized provider-observed table.
33#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
34pub struct PhysicalTable {
35    /// Observed table name.
36    pub name: TableName,
37    /// Observed columns.
38    pub columns: Vec<PhysicalColumn>,
39    /// Observed indexes.
40    pub indexes: Vec<PhysicalIndex>,
41}
42/// Immutable normalized evidence observed from a live provider catalog.
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct PhysicalSchema {
45    provider: ProviderName,
46    schema: SchemaName,
47    revision: RevisionName,
48    tables: Vec<PhysicalTable>,
49}
50impl PhysicalSchema {
51    /// Normalizes an observed catalog. Tables/indexes are unordered; columns are sorted by provider ordinal.
52    pub fn normalize(
53        provider: ProviderName,
54        schema: SchemaName,
55        revision: RevisionName,
56        mut tables: Vec<PhysicalTable>,
57    ) -> Result<Self, &'static str> {
58        let mut table_names = BTreeSet::new();
59        for table in &mut tables {
60            if !table_names.insert(table.name.clone()) {
61                return Err("duplicate physical table");
62            }
63            table.columns.sort_by_key(|v| v.ordinal);
64            if table
65                .columns
66                .windows(2)
67                .any(|v| v[0].ordinal == v[1].ordinal)
68            {
69                return Err("duplicate physical ordinal");
70            }
71            let mut names = BTreeSet::new();
72            if table.columns.iter().any(|v| !names.insert(v.name.clone())) {
73                return Err("duplicate physical column");
74            }
75            table.indexes.sort_by(|a, b| a.name.cmp(&b.name));
76        }
77        tables.sort_by(|a, b| a.name.cmp(&b.name));
78        Ok(Self {
79            provider,
80            schema,
81            revision,
82            tables,
83        })
84    }
85    /// Returns normalized tables.
86    pub fn tables(&self) -> &[PhysicalTable] {
87        &self.tables
88    }
89    /// Returns the distinct physical identity.
90    pub fn id(&self) -> Result<RelationId, sim_kernel::Error> {
91        RelationId::of(self)
92    }
93}
94fn storage(v: StorageRepr) -> Symbol {
95    Symbol::new(match v {
96        StorageRepr::Bool => "bool",
97        StorageRepr::I64 => "i64",
98        StorageRepr::F64 => "f64",
99        StorageRepr::Text => "text",
100        StorageRepr::Bytes => "bytes",
101    })
102}
103impl ToRelationDatum for PhysicalSchema {
104    fn to_datum(&self) -> Datum {
105        Datum::Node {
106            tag: Symbol::qualified("relation-schema", "physical-schema"),
107            fields: vec![
108                (
109                    Symbol::new("provider"),
110                    Datum::Symbol(self.provider.symbol().clone()),
111                ),
112                (
113                    Symbol::new("schema"),
114                    Datum::Symbol(self.schema.symbol().clone()),
115                ),
116                (
117                    Symbol::new("revision"),
118                    Datum::Symbol(self.revision.symbol().clone()),
119                ),
120                (
121                    Symbol::new("tables"),
122                    Datum::Vector(
123                        self.tables
124                            .iter()
125                            .map(|t| Datum::Node {
126                                tag: Symbol::qualified("relation-schema", "physical-table"),
127                                fields: vec![
128                                    (Symbol::new("name"), Datum::Symbol(t.name.symbol().clone())),
129                                    (
130                                        Symbol::new("columns"),
131                                        Datum::Vector(
132                                            t.columns
133                                                .iter()
134                                                .map(|c| Datum::Node {
135                                                    tag: Symbol::qualified(
136                                                        "relation-schema",
137                                                        "physical-column",
138                                                    ),
139                                                    fields: vec![
140                                                        (
141                                                            Symbol::new("name"),
142                                                            Datum::Symbol(c.name.symbol().clone()),
143                                                        ),
144                                                        (
145                                                            Symbol::new("domain"),
146                                                            Datum::Symbol(
147                                                                c.domain.symbol().clone(),
148                                                            ),
149                                                        ),
150                                                        (
151                                                            Symbol::new("storage"),
152                                                            Datum::Symbol(storage(c.storage)),
153                                                        ),
154                                                        (
155                                                            Symbol::new("nullable"),
156                                                            Datum::Bool(c.nullable),
157                                                        ),
158                                                        (
159                                                            Symbol::new("ordinal"),
160                                                            Datum::Number(
161                                                                sim_kernel::NumberLiteral {
162                                                                    domain: Symbol::qualified(
163                                                                        "core", "u32",
164                                                                    ),
165                                                                    canonical: c
166                                                                        .ordinal
167                                                                        .to_string(),
168                                                                },
169                                                            ),
170                                                        ),
171                                                    ],
172                                                })
173                                                .collect(),
174                                        ),
175                                    ),
176                                    (
177                                        Symbol::new("indexes"),
178                                        Datum::Vector(
179                                            t.indexes
180                                                .iter()
181                                                .map(|i| Datum::Node {
182                                                    tag: Symbol::qualified(
183                                                        "relation-schema",
184                                                        "physical-index",
185                                                    ),
186                                                    fields: vec![
187                                                        (
188                                                            Symbol::new("name"),
189                                                            Datum::Symbol(i.name.symbol().clone()),
190                                                        ),
191                                                        (
192                                                            Symbol::new("columns"),
193                                                            Datum::Vector(
194                                                                i.columns
195                                                                    .iter()
196                                                                    .map(|n| {
197                                                                        Datum::Symbol(
198                                                                            n.symbol().clone(),
199                                                                        )
200                                                                    })
201                                                                    .collect(),
202                                                            ),
203                                                        ),
204                                                        (
205                                                            Symbol::new("unique"),
206                                                            Datum::Bool(i.unique),
207                                                        ),
208                                                    ],
209                                                })
210                                                .collect(),
211                                        ),
212                                    ),
213                                ],
214                            })
215                            .collect(),
216                    ),
217                ),
218            ],
219        }
220    }
221}