Skip to main content

mz_catalog/
builtin.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Built-in catalog items.
11//!
12//! Builtins exist in the `mz_catalog` ambient schema. They are automatically
13//! installed into the catalog when it is opened. Their definitions are not
14//! persisted in the catalog, but hardcoded in this module. This makes it easy
15//! to add new builtins, or change the definition of existing builtins, in new
16//! versions of Materialize.
17//!
18//! Builtin's names, columns, and types are part of the stable API of
19//! Materialize. Be careful to maintain backwards compatibility when changing
20//! definitions of existing builtins!
21//!
22//! More information about builtin system tables and types can be found in
23//! <https://materialize.com/docs/sql/system-catalog/>.
24
25mod builtin;
26pub mod notice;
27mod ontology;
28mod pg_catalog;
29pub use pg_catalog::*;
30mod mz_catalog;
31pub use mz_catalog::*;
32mod mz_internal;
33pub use mz_internal::*;
34mod mz_introspection;
35pub use mz_introspection::*;
36mod information_schema;
37pub use information_schema::*;
38
39use std::collections::BTreeMap;
40use std::hash::Hash;
41use std::string::ToString;
42use std::sync::LazyLock;
43
44use mz_compute_client::logging::LogVariant;
45use mz_ore::collections::HashMap;
46use mz_pgrepr::oid;
47use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
48use mz_repr::namespaces::{
49    INFORMATION_SCHEMA, MZ_CATALOG_SCHEMA, MZ_INTERNAL_SCHEMA, MZ_UNSAFE_SCHEMA, PG_CATALOG_SCHEMA,
50};
51use mz_repr::role_id::RoleId;
52use mz_repr::{RelationDesc, SemanticType, SqlRelationType};
53use mz_sql::catalog::RoleAttributesRaw;
54use mz_sql::catalog::{
55    CatalogItemType, CatalogTypeDetails, NameReference, ObjectType, SystemObjectType, TypeReference,
56};
57use mz_sql::rbac;
58use mz_sql::session::user::{
59    ANALYTICS_USER_NAME, JWT_SYNC_ROLE_NAME, MZ_ANALYTICS_ROLE_ID, MZ_JWT_SYNC_ROLE_ID,
60    MZ_MONITOR_REDACTED_ROLE_ID, MZ_MONITOR_ROLE_ID, MZ_SUPPORT_ROLE_ID, MZ_SYSTEM_ROLE_ID,
61    SUPPORT_USER_NAME, SYSTEM_USER_NAME,
62};
63use serde::Serialize;
64
65use crate::durable::objects::SystemObjectDescription;
66use crate::memory::objects::DataSourceDesc;
67
68pub const BUILTIN_PREFIXES: &[&str] = &["mz_", "pg_", "external_"];
69
70/// A sentinel used in place of a fingerprint that indicates that a builtin
71/// object is runtime alterable. Runtime alterable objects don't have meaningful
72/// fingerprints because they may have been intentionally changed by the user
73/// after creation.
74// NOTE(benesch): ideally we'd use a fingerprint type that used a sum type
75// rather than a loosely typed string to represent the runtime alterable
76// state like so:
77//
78//     enum Fingerprint {
79//         SqlText(String),
80//         RuntimeAlterable,
81//     }
82//
83// However, that would entail a complicated migration for the existing system object
84// mapping collection stored on disk.
85pub const RUNTIME_ALTERABLE_FINGERPRINT_SENTINEL: &str = "<RUNTIME-ALTERABLE>";
86
87#[derive(Clone, Debug)]
88pub enum Builtin<T: 'static + TypeReference> {
89    Log(&'static BuiltinLog),
90    Table(&'static BuiltinTable),
91    View(&'static BuiltinView),
92    MaterializedView(&'static BuiltinMaterializedView),
93    Type(&'static BuiltinType<T>),
94    Func(BuiltinFunc),
95    Source(&'static BuiltinSource),
96    Index(&'static BuiltinIndex),
97    Connection(&'static BuiltinConnection),
98}
99
100impl<T: TypeReference> Builtin<T> {
101    pub fn name(&self) -> &'static str {
102        match self {
103            Builtin::Log(log) => log.name,
104            Builtin::Table(table) => table.name,
105            Builtin::View(view) => view.name,
106            Builtin::MaterializedView(mv) => mv.name,
107            Builtin::Type(typ) => typ.name,
108            Builtin::Func(func) => func.name,
109            Builtin::Source(coll) => coll.name,
110            Builtin::Index(index) => index.name,
111            Builtin::Connection(connection) => connection.name,
112        }
113    }
114
115    pub fn schema(&self) -> &'static str {
116        match self {
117            Builtin::Log(log) => log.schema,
118            Builtin::Table(table) => table.schema,
119            Builtin::View(view) => view.schema,
120            Builtin::MaterializedView(mv) => mv.schema,
121            Builtin::Type(typ) => typ.schema,
122            Builtin::Func(func) => func.schema,
123            Builtin::Source(coll) => coll.schema,
124            Builtin::Index(index) => index.schema,
125            Builtin::Connection(connection) => connection.schema,
126        }
127    }
128
129    pub fn catalog_item_type(&self) -> CatalogItemType {
130        match self {
131            Builtin::Log(_) => CatalogItemType::Source,
132            Builtin::Source(_) => CatalogItemType::Source,
133            Builtin::Table(_) => CatalogItemType::Table,
134            Builtin::View(_) => CatalogItemType::View,
135            Builtin::MaterializedView(_) => CatalogItemType::MaterializedView,
136            Builtin::Type(_) => CatalogItemType::Type,
137            Builtin::Func(_) => CatalogItemType::Func,
138            Builtin::Index(_) => CatalogItemType::Index,
139            Builtin::Connection(_) => CatalogItemType::Connection,
140        }
141    }
142
143    /// Whether the object can be altered at runtime by its owner.
144    pub fn runtime_alterable(&self) -> bool {
145        match self {
146            Builtin::Connection(c) => c.runtime_alterable,
147            _ => false,
148        }
149    }
150}
151
152#[derive(Clone, Debug, Hash, Serialize)]
153pub struct BuiltinLog {
154    pub variant: LogVariant,
155    pub name: &'static str,
156    pub schema: &'static str,
157    pub oid: u32,
158    /// ACL items to apply to the object
159    pub access: Vec<MzAclItem>,
160    #[serde(default)]
161    pub ontology: Option<Ontology>,
162}
163
164/// Ontology metadata for a builtin catalog object.
165///
166/// When present on a builtin, it marks it as an ontology entity with an explicit
167/// `entity_name`, `description`, and optional per-column semantic type annotations.
168///
169/// ## Why `column_semantic_types` lives here and not in `RelationDesc`
170///
171/// Semantic types are pure catalog-level metadata: they annotate what an ID
172/// column *means* (e.g. "this is a ClusterId") without affecting the Arrow
173/// data type used for encoding. Keeping them in `RelationDesc` would cause
174/// persist schema mismatches during zero-downtime upgrades: the old binary
175/// registers a schema without semantic types, the new binary tries to register
176/// a schema with them, and `register_schema` returns `None` because the schemas
177/// are not `PartialEq`. Since the only consumers of semantic types are the
178/// ontology views (which already have access to `Ontology`), storing them here
179/// is both correct and avoids the schema-evolution problem entirely.
180#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize)]
181pub struct Ontology {
182    /// The ontology entity name (e.g., "database", "table", "mv"). Names a
183    /// single row of this relation, so prefer singular event/object nouns
184    /// (e.g., "replica_status_event" not "replica_status_history").
185    pub entity_name: &'static str,
186    /// One-line description of this entity.
187    pub description: &'static str,
188    /// Relationships originating from this entity (foreign keys, unions,
189    /// mappings, dependencies, metrics).
190    pub links: &'static [OntologyLink],
191    /// Per-column semantic type annotations: `(column_name, SemanticType)`.
192    /// Only columns that carry a meaningful semantic type need to appear here.
193    pub column_semantic_types: &'static [(&'static str, SemanticType)],
194}
195
196/// Cardinality of an ontology link.
197#[derive(
198    Clone,
199    Copy,
200    Debug,
201    Hash,
202    PartialEq,
203    Eq,
204    serde::Serialize,
205    serde::Deserialize
206)]
207#[serde(rename_all = "snake_case")]
208pub enum Cardinality {
209    OneToOne,
210    ManyToOne,
211}
212
213/// Helper used by serde to skip serializing `false` boolean fields.
214fn is_false(v: &bool) -> bool {
215    !v
216}
217
218/// Typed properties for an ontology link. Serialized to the `properties` JSONB
219/// column in `mz_ontology_link_types`. The `kind` field is inlined from the
220/// enum variant name via `#[serde(tag = "kind")]`.
221///
222/// Choosing the right variant matters:
223///
224/// - [`LinkProperties::ForeignKey`]: the source entity has a column whose
225///   value is an ID that directly references a row in the target entity.
226///   Use this when there is an explicit FK column (e.g. `schema_id` ->
227///   `schema`).
228/// - [`LinkProperties::DependsOn`]: this entity logically depends on the
229///   target entity via a graph-edge table (e.g. `mz_compute_dependencies`
230///   records that a compute object depends on another object). The
231///   `source_column` is the column **in this entity** that holds the
232///   dependent's ID; `target_column` is the column in the target entity
233///   being depended upon. Use this for dependency-graph tables, **not**
234///   `ForeignKey`.
235/// - [`LinkProperties::Union`]: the source entity is a superset view that
236///   contains the target entity as a subset, optionally filtered by a
237///   discriminator column.
238/// - [`LinkProperties::MapsTo`]: the source entity provides an ID translation
239///   to the target entity, possibly via an intermediate table or across ID
240///   namespaces.
241/// - [`LinkProperties::Measures`]: the source entity records metric
242///   measurements about the target entity.
243#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, serde::Serialize)]
244#[serde(tag = "kind", rename_all = "snake_case")]
245pub enum LinkProperties {
246    /// A foreign-key relationship: `source_column` in the source entity
247    /// references `target_column` in the target entity.
248    ForeignKey {
249        /// Column in the source entity that holds the reference.
250        source_column: &'static str,
251        /// Column in the target entity being referenced (usually `id`).
252        target_column: &'static str,
253        /// How many source rows may reference a single target row.
254        cardinality: Cardinality,
255        /// Semantic type of the source column, if it carries an ID that
256        /// requires type-aware resolution (e.g. `CatalogItemId`, `GlobalId`).
257        #[serde(skip_serializing_if = "Option::is_none")]
258        source_id_type: Option<mz_repr::SemanticType>,
259        /// Intermediate mapping relation needed when `source_id_type` does not
260        /// directly match the target entity's ID type (e.g.
261        /// `mz_internal.mz_object_global_ids` to go from `GlobalId` to catalog
262        /// object).
263        #[serde(skip_serializing_if = "Option::is_none")]
264        requires_mapping: Option<&'static str>,
265        /// True when the source column may be NULL (the reference is optional).
266        #[serde(default, skip_serializing_if = "is_false")]
267        nullable: bool,
268        /// Free-form annotation for cases that need extra context.
269        #[serde(skip_serializing_if = "Option::is_none")]
270        note: Option<&'static str>,
271        /// Additional `(source_column, target_column)` pairs that together with
272        /// `source_column`/`target_column` form a composite join key. Used for
273        /// `_per_worker` entities whose primary key is `(id, worker_id)`, and
274        /// for message-count raw relations whose join key includes worker IDs.
275        /// Serialized as an array; omitted when `None`.
276        #[serde(skip_serializing_if = "Option::is_none")]
277        extra_key_columns: Option<&'static [(&'static str, &'static str)]>,
278    },
279    /// A union relationship: the source entity is a superset view that includes
280    /// the target entity, optionally filtered by a discriminator column/value.
281    Union {
282        /// Column used to discriminate between subtypes (e.g. `type`).
283        #[serde(skip_serializing_if = "Option::is_none")]
284        discriminator_column: Option<&'static str>,
285        /// Value of `discriminator_column` that selects the target entity.
286        #[serde(skip_serializing_if = "Option::is_none")]
287        discriminator_value: Option<&'static str>,
288        /// Free-form annotation for cases that need extra context.
289        #[serde(skip_serializing_if = "Option::is_none")]
290        note: Option<&'static str>,
291    },
292    /// A mapping relationship: the source entity maps to the target entity,
293    /// optionally via an intermediate table and/or with an ID-type conversion.
294    MapsTo {
295        /// Column in the source entity that holds the ID to map from.
296        source_column: &'static str,
297        /// Column in the target entity being mapped to.
298        target_column: &'static str,
299        /// Intermediate relation used to perform the mapping.
300        #[serde(skip_serializing_if = "Option::is_none")]
301        via: Option<&'static str>,
302        /// Semantic type of the source ID before mapping.
303        #[serde(skip_serializing_if = "Option::is_none")]
304        from_type: Option<mz_repr::SemanticType>,
305        /// Semantic type of the target ID after mapping.
306        #[serde(skip_serializing_if = "Option::is_none")]
307        to_type: Option<mz_repr::SemanticType>,
308        /// Free-form annotation for cases that need extra context.
309        #[serde(skip_serializing_if = "Option::is_none")]
310        note: Option<&'static str>,
311    },
312    /// A dependency relationship: this entity directly depends on the
313    /// target entity (e.g. a materialization that references an object).
314    DependsOn {
315        /// Column in this entity that holds the dependency ID.
316        source_column: &'static str,
317        /// Column in the target entity being depended upon (usually `id`).
318        target_column: &'static str,
319        /// Semantic type of the source column.
320        #[serde(skip_serializing_if = "Option::is_none")]
321        source_id_type: Option<mz_repr::SemanticType>,
322        /// Intermediate mapping relation needed when `source_id_type` does not
323        /// directly match the target entity's ID type (e.g. GlobalId →
324        /// `mz_internal.mz_object_global_ids` to reach a catalog object).
325        #[serde(skip_serializing_if = "Option::is_none")]
326        requires_mapping: Option<&'static str>,
327    },
328    /// A metric relationship: the source entity records measurements of a named
329    /// metric on the target entity.
330    Measures {
331        /// Column in the source entity that references the target entity.
332        source_column: &'static str,
333        /// Column in the target entity being measured (usually `id`).
334        target_column: &'static str,
335        /// Name of the metric being measured (e.g. `cpu_time_ns`).
336        metric: &'static str,
337        /// Semantic type of the source column, if ID-type resolution is needed.
338        #[serde(skip_serializing_if = "Option::is_none")]
339        source_id_type: Option<mz_repr::SemanticType>,
340        /// Intermediate mapping relation needed when the source ID type differs
341        /// from the target entity's ID type.
342        #[serde(skip_serializing_if = "Option::is_none")]
343        requires_mapping: Option<&'static str>,
344        /// Free-form annotation for cases that need extra context.
345        #[serde(skip_serializing_if = "Option::is_none")]
346        note: Option<&'static str>,
347        /// Additional `(source_column, target_column)` pairs that together with
348        /// `source_column`/`target_column` form a composite join key.
349        #[serde(skip_serializing_if = "Option::is_none")]
350        extra_key_columns: Option<&'static [(&'static str, &'static str)]>,
351    },
352}
353
354impl LinkProperties {
355    /// Basic foreign-key link with no optional fields set.
356    pub const fn fk(
357        source_column: &'static str,
358        target_column: &'static str,
359        cardinality: Cardinality,
360    ) -> Self {
361        Self::ForeignKey {
362            source_column,
363            target_column,
364            cardinality,
365            source_id_type: None,
366            requires_mapping: None,
367            nullable: false,
368            note: None,
369            extra_key_columns: None,
370        }
371    }
372
373    /// Foreign-key link where the source column may be NULL.
374    pub const fn fk_nullable(
375        source_column: &'static str,
376        target_column: &'static str,
377        cardinality: Cardinality,
378    ) -> Self {
379        Self::ForeignKey {
380            source_column,
381            target_column,
382            cardinality,
383            source_id_type: None,
384            requires_mapping: None,
385            nullable: true,
386            note: None,
387            extra_key_columns: None,
388        }
389    }
390
391    /// Foreign-key link whose source column carries a typed ID (e.g.
392    /// `CatalogItemId`) but does not require an intermediate mapping table.
393    pub const fn fk_typed(
394        source_column: &'static str,
395        target_column: &'static str,
396        cardinality: Cardinality,
397        source_id_type: mz_repr::SemanticType,
398    ) -> Self {
399        Self::ForeignKey {
400            source_column,
401            target_column,
402            cardinality,
403            source_id_type: Some(source_id_type),
404            requires_mapping: None,
405            nullable: false,
406            note: None,
407            extra_key_columns: None,
408        }
409    }
410
411    /// Foreign-key link whose source column carries a typed ID that requires
412    /// an intermediate mapping table to resolve (e.g. `GlobalId` →
413    /// `mz_internal.mz_object_global_ids`).
414    pub const fn fk_mapped(
415        source_column: &'static str,
416        target_column: &'static str,
417        cardinality: Cardinality,
418        source_id_type: mz_repr::SemanticType,
419        requires_mapping: &'static str,
420    ) -> Self {
421        Self::ForeignKey {
422            source_column,
423            target_column,
424            cardinality,
425            source_id_type: Some(source_id_type),
426            requires_mapping: Some(requires_mapping),
427            nullable: false,
428            note: None,
429            extra_key_columns: None,
430        }
431    }
432
433    /// Foreign-key link with a composite join key. `extra_key_columns` lists
434    /// additional `(source_column, target_column)` pairs beyond the primary
435    /// `source_column`/`target_column` pair. Examples:
436    /// - `&[("worker_id", "worker_id")]` for `_per_worker` entities
437    /// - `&[("from_worker_id", "worker_id")]` for message-count raw relations
438    pub const fn fk_composite(
439        source_column: &'static str,
440        target_column: &'static str,
441        cardinality: Cardinality,
442        extra_key_columns: &'static [(&'static str, &'static str)],
443    ) -> Self {
444        Self::ForeignKey {
445            source_column,
446            target_column,
447            cardinality,
448            source_id_type: None,
449            requires_mapping: None,
450            nullable: false,
451            note: None,
452            extra_key_columns: Some(extra_key_columns),
453        }
454    }
455
456    /// Union link filtered by a discriminator column/value pair.
457    pub const fn union_disc(
458        discriminator_column: &'static str,
459        discriminator_value: &'static str,
460    ) -> Self {
461        Self::Union {
462            discriminator_column: Some(discriminator_column),
463            discriminator_value: Some(discriminator_value),
464            note: None,
465        }
466    }
467
468    /// Basic measures link with no optional fields set.
469    pub const fn measures(
470        source_column: &'static str,
471        target_column: &'static str,
472        metric: &'static str,
473    ) -> Self {
474        Self::Measures {
475            source_column,
476            target_column,
477            metric,
478            source_id_type: None,
479            requires_mapping: None,
480            note: None,
481            extra_key_columns: None,
482        }
483    }
484
485    /// Measures link with a composite join key.
486    pub const fn measures_composite(
487        source_column: &'static str,
488        target_column: &'static str,
489        metric: &'static str,
490        extra_key_columns: &'static [(&'static str, &'static str)],
491    ) -> Self {
492        Self::Measures {
493            source_column,
494            target_column,
495            metric,
496            source_id_type: None,
497            requires_mapping: None,
498            note: None,
499            extra_key_columns: Some(extra_key_columns),
500        }
501    }
502
503    /// Measures link whose source ID requires an intermediate mapping table.
504    pub const fn measures_mapped(
505        source_column: &'static str,
506        target_column: &'static str,
507        metric: &'static str,
508        source_id_type: mz_repr::SemanticType,
509        requires_mapping: &'static str,
510    ) -> Self {
511        Self::Measures {
512            source_column,
513            target_column,
514            metric,
515            source_id_type: Some(source_id_type),
516            requires_mapping: Some(requires_mapping),
517            note: None,
518            extra_key_columns: None,
519        }
520    }
521}
522
523/// A directed relationship from one ontology entity to another.
524///
525/// Each link has a `name` (the relationship label, e.g. `"owned_by"`), a
526/// `target` entity name, and a [`LinkProperties`] variant that captures the
527/// kind of relationship.
528#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)]
529pub struct OntologyLink {
530    /// Relationship name describing the relationship FROM this entity TO the
531    /// target (e.g., `"owned_by"` means "this entity is owned by the target",
532    /// `"depends_on"` means "this entity depends on the target"). When the
533    /// same name appears on multiple links of the same entity, all links
534    /// share that relationship role (e.g., several `"union_includes"` links).
535    pub name: &'static str,
536    /// Target entity name (e.g., "role", "schema").
537    pub target: &'static str,
538    /// Typed properties for the `properties` JSONB column.
539    pub properties: LinkProperties,
540}
541
542#[derive(Clone, Hash, Debug, PartialEq, Eq)]
543pub struct BuiltinTable {
544    pub name: &'static str,
545    pub schema: &'static str,
546    pub oid: u32,
547    pub desc: RelationDesc,
548    pub column_comments: BTreeMap<&'static str, &'static str>,
549    /// Whether the table's retention policy is controlled by
550    /// the system variable `METRICS_RETENTION`
551    pub is_retained_metrics_object: bool,
552    /// ACL items to apply to the object
553    pub access: Vec<MzAclItem>,
554    /// Ontology metadata. None means this builtin is not an ontology entity.
555    pub ontology: Option<Ontology>,
556}
557
558#[derive(Clone, Debug, PartialEq, Eq)]
559pub struct BuiltinSource {
560    pub name: &'static str,
561    pub schema: &'static str,
562    pub oid: u32,
563    pub desc: RelationDesc,
564    pub column_comments: BTreeMap<&'static str, &'static str>,
565    pub data_source: DataSourceDesc,
566    /// Whether the source's retention policy is controlled by
567    /// the system variable `METRICS_RETENTION`
568    pub is_retained_metrics_object: bool,
569    /// ACL items to apply to the object
570    pub access: Vec<MzAclItem>,
571    /// Ontology metadata. None means this builtin is not an ontology entity.
572    pub ontology: Option<Ontology>,
573}
574
575#[derive(Hash, Debug)]
576pub struct BuiltinView {
577    pub name: &'static str,
578    pub schema: &'static str,
579    pub oid: u32,
580    pub desc: RelationDesc,
581    pub column_comments: BTreeMap<&'static str, &'static str>,
582    pub sql: &'static str,
583    /// ACL items to apply to the object
584    pub access: Vec<MzAclItem>,
585    /// Ontology metadata. None means this builtin is not an ontology entity.
586    pub ontology: Option<Ontology>,
587}
588
589impl BuiltinView {
590    pub fn create_sql(&self) -> String {
591        format!("CREATE VIEW {}.{} AS {}", self.schema, self.name, self.sql)
592    }
593}
594
595#[derive(Hash, Debug)]
596pub struct BuiltinMaterializedView {
597    pub name: &'static str,
598    pub schema: &'static str,
599    pub oid: u32,
600    pub desc: RelationDesc,
601    pub column_comments: BTreeMap<&'static str, &'static str>,
602    /// SQL fragment for the MV, following `CREATE MATERIALIZED VIEW [name]`
603    ///
604    /// Format: `IN CLUSTER [cluster_name] AS [query]`
605    pub sql: &'static str,
606    /// Whether the MV's retention policy is controlled by
607    /// the system variable `METRICS_RETENTION`
608    pub is_retained_metrics_object: bool,
609    /// ACL items to apply to the object
610    pub access: Vec<MzAclItem>,
611    /// Ontology metadata. None means this builtin is not an ontology entity.
612    pub ontology: Option<Ontology>,
613}
614
615impl BuiltinMaterializedView {
616    pub fn create_sql(&self) -> String {
617        format!(
618            "CREATE MATERIALIZED VIEW {}.{} {}",
619            self.schema, self.name, self.sql
620        )
621    }
622}
623
624#[derive(Debug)]
625pub struct BuiltinType<T: TypeReference> {
626    pub name: &'static str,
627    pub schema: &'static str,
628    pub oid: u32,
629    pub details: CatalogTypeDetails<T>,
630}
631
632#[derive(Clone, Debug)]
633pub struct BuiltinFunc {
634    pub schema: &'static str,
635    pub name: &'static str,
636    pub inner: &'static mz_sql::func::Func,
637}
638
639/// Note: When creating a built-in index, it's usually best to choose a key that has only one
640/// component. For example, if you created an index
641/// `ON mz_internal.mz_object_lifetimes (id, object_type)`, then this index couldn't be used for a
642/// lookup for `WHERE object_type = ...`, and neither for joins keyed on just `id`.
643/// See <https://materialize.com/docs/transform-data/optimization/#matching-multi-column-indexes-to-multi-column-where-clauses>
644#[derive(Debug)]
645pub struct BuiltinIndex {
646    pub name: &'static str,
647    pub schema: &'static str,
648    pub oid: u32,
649    /// SQL fragment for the index, following `CREATE INDEX [name]`
650    ///
651    /// Format: `IN CLUSTER [cluster_name] ON [table_name] ([column_exprs])`
652    pub sql: &'static str,
653    pub is_retained_metrics_object: bool,
654}
655
656impl BuiltinIndex {
657    pub fn create_sql(&self) -> String {
658        format!("CREATE INDEX {}\n{}", self.name, self.sql)
659    }
660}
661
662#[derive(Hash, Debug)]
663pub struct BuiltinConnection {
664    pub name: &'static str,
665    pub schema: &'static str,
666    pub oid: u32,
667    pub sql: &'static str,
668    pub access: &'static [MzAclItem],
669    pub owner_id: &'static RoleId,
670    /// Whether the object can be altered at runtime by its owner.
671    ///
672    /// Note that when `runtime_alterable` is true, changing the `sql` in future
673    /// versions does not trigger a migration.
674    pub runtime_alterable: bool,
675}
676
677#[derive(Clone, Debug)]
678pub struct BuiltinRole {
679    pub id: RoleId,
680    /// Name of the builtin role.
681    ///
682    /// IMPORTANT: Must start with a prefix from [`BUILTIN_PREFIXES`].
683    pub name: &'static str,
684    pub oid: u32,
685    pub attributes: RoleAttributesRaw,
686}
687
688#[derive(Clone, Debug)]
689pub struct BuiltinCluster {
690    /// Name of the cluster.
691    ///
692    /// IMPORTANT: Must start with a prefix from [`BUILTIN_PREFIXES`].
693    pub name: &'static str,
694    pub privileges: &'static [MzAclItem],
695    pub owner_id: &'static RoleId,
696}
697
698/// Uniquely identifies the definition of a builtin object.
699pub trait Fingerprint {
700    fn fingerprint(&self) -> String;
701}
702
703impl<T: TypeReference> Fingerprint for &Builtin<T> {
704    fn fingerprint(&self) -> String {
705        match self {
706            Builtin::Log(log) => log.fingerprint(),
707            Builtin::Table(table) => table.fingerprint(),
708            Builtin::View(view) => view.fingerprint(),
709            Builtin::MaterializedView(mv) => mv.fingerprint(),
710            Builtin::Type(typ) => typ.fingerprint(),
711            Builtin::Func(func) => func.fingerprint(),
712            Builtin::Source(coll) => coll.fingerprint(),
713            Builtin::Index(index) => index.fingerprint(),
714            Builtin::Connection(connection) => connection.fingerprint(),
715        }
716    }
717}
718
719// Types and Funcs never change fingerprints so we just return constant 0
720impl<T: TypeReference> Fingerprint for &BuiltinType<T> {
721    fn fingerprint(&self) -> String {
722        "".to_string()
723    }
724}
725
726impl Fingerprint for &BuiltinFunc {
727    fn fingerprint(&self) -> String {
728        "".to_string()
729    }
730}
731
732impl Fingerprint for &BuiltinLog {
733    fn fingerprint(&self) -> String {
734        self.variant.desc().fingerprint()
735    }
736}
737
738impl Fingerprint for &BuiltinTable {
739    fn fingerprint(&self) -> String {
740        self.desc.fingerprint()
741    }
742}
743
744impl Fingerprint for &BuiltinView {
745    fn fingerprint(&self) -> String {
746        self.sql.to_string()
747    }
748}
749
750impl Fingerprint for &BuiltinSource {
751    fn fingerprint(&self) -> String {
752        self.desc.fingerprint()
753    }
754}
755
756impl Fingerprint for &BuiltinMaterializedView {
757    fn fingerprint(&self) -> String {
758        self.create_sql()
759    }
760}
761
762impl Fingerprint for &BuiltinIndex {
763    fn fingerprint(&self) -> String {
764        self.create_sql()
765    }
766}
767
768impl Fingerprint for &BuiltinConnection {
769    fn fingerprint(&self) -> String {
770        self.sql.to_string()
771    }
772}
773
774impl Fingerprint for RelationDesc {
775    fn fingerprint(&self) -> String {
776        self.typ().fingerprint()
777    }
778}
779
780impl Fingerprint for SqlRelationType {
781    fn fingerprint(&self) -> String {
782        serde_json::to_string(self).expect("serialization cannot fail")
783    }
784}
785
786pub(super) const PUBLIC_SELECT: MzAclItem = MzAclItem {
787    grantee: RoleId::Public,
788    grantor: MZ_SYSTEM_ROLE_ID,
789    acl_mode: AclMode::SELECT,
790};
791
792pub(super) const SUPPORT_SELECT: MzAclItem = MzAclItem {
793    grantee: MZ_SUPPORT_ROLE_ID,
794    grantor: MZ_SYSTEM_ROLE_ID,
795    acl_mode: AclMode::SELECT,
796};
797
798pub(super) const ANALYTICS_SELECT: MzAclItem = MzAclItem {
799    grantee: MZ_ANALYTICS_ROLE_ID,
800    grantor: MZ_SYSTEM_ROLE_ID,
801    acl_mode: AclMode::SELECT,
802};
803
804pub(super) const MONITOR_SELECT: MzAclItem = MzAclItem {
805    grantee: MZ_MONITOR_ROLE_ID,
806    grantor: MZ_SYSTEM_ROLE_ID,
807    acl_mode: AclMode::SELECT,
808};
809
810pub(super) const MONITOR_REDACTED_SELECT: MzAclItem = MzAclItem {
811    grantee: MZ_MONITOR_REDACTED_ROLE_ID,
812    grantor: MZ_SYSTEM_ROLE_ID,
813    acl_mode: AclMode::SELECT,
814};
815
816pub static MZ_CATALOG_RAW_DESCRIPTION: LazyLock<SystemObjectDescription> =
817    LazyLock::new(|| SystemObjectDescription {
818        schema_name: MZ_CATALOG_RAW.schema.to_string(),
819        object_type: CatalogItemType::Source,
820        object_name: MZ_CATALOG_RAW.name.to_string(),
821    });
822
823pub static MZ_STORAGE_USAGE_BY_SHARD_DESCRIPTION: LazyLock<SystemObjectDescription> =
824    LazyLock::new(|| SystemObjectDescription {
825        schema_name: MZ_STORAGE_USAGE_BY_SHARD.schema.to_string(),
826        object_type: CatalogItemType::Table,
827        object_name: MZ_STORAGE_USAGE_BY_SHARD.name.to_string(),
828    });
829
830/// Identifies [`MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY`] for the schema-migration
831/// guard in `builtin_schema_migration.rs`, which forbids migrating this table
832/// because its startup pruner assumes it is the only source of retractions.
833pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_DESCRIPTION: LazyLock<SystemObjectDescription> =
834    LazyLock::new(|| SystemObjectDescription {
835        schema_name: MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.schema.to_string(),
836        object_type: CatalogItemType::Table,
837        object_name: MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.name.to_string(),
838    });
839pub const MZ_SYSTEM_ROLE: BuiltinRole = BuiltinRole {
840    id: MZ_SYSTEM_ROLE_ID,
841    name: SYSTEM_USER_NAME,
842    oid: oid::ROLE_MZ_SYSTEM_OID,
843    attributes: RoleAttributesRaw::new().with_all(),
844};
845
846pub const MZ_SUPPORT_ROLE: BuiltinRole = BuiltinRole {
847    id: MZ_SUPPORT_ROLE_ID,
848    name: SUPPORT_USER_NAME,
849    oid: oid::ROLE_MZ_SUPPORT_OID,
850    attributes: RoleAttributesRaw::new(),
851};
852
853pub const MZ_ANALYTICS_ROLE: BuiltinRole = BuiltinRole {
854    id: MZ_ANALYTICS_ROLE_ID,
855    name: ANALYTICS_USER_NAME,
856    oid: oid::ROLE_MZ_ANALYTICS_OID,
857    attributes: RoleAttributesRaw::new(),
858};
859
860/// This role can `SELECT` from various query history objects,
861/// e.g. `mz_prepared_statement_history`.
862pub const MZ_MONITOR_ROLE: BuiltinRole = BuiltinRole {
863    id: MZ_MONITOR_ROLE_ID,
864    name: "mz_monitor",
865    oid: oid::ROLE_MZ_MONITOR_OID,
866    attributes: RoleAttributesRaw::new(),
867};
868
869/// This role is like [`MZ_MONITOR_ROLE`], but can only query
870/// the redacted versions of the objects.
871pub const MZ_MONITOR_REDACTED: BuiltinRole = BuiltinRole {
872    id: MZ_MONITOR_REDACTED_ROLE_ID,
873    name: "mz_monitor_redacted",
874    oid: oid::ROLE_MZ_MONITOR_REDACTED_OID,
875    attributes: RoleAttributesRaw::new(),
876};
877
878/// Sentinel role used as the grantor for JWT group-sync-managed
879/// role memberships. Never logged into directly.
880pub const MZ_JWT_SYNC_ROLE: BuiltinRole = BuiltinRole {
881    id: MZ_JWT_SYNC_ROLE_ID,
882    name: JWT_SYNC_ROLE_NAME,
883    oid: oid::ROLE_MZ_JWT_SYNC_OID,
884    attributes: RoleAttributesRaw::new(),
885};
886
887pub const MZ_SYSTEM_CLUSTER: BuiltinCluster = BuiltinCluster {
888    name: SYSTEM_USER_NAME,
889    owner_id: &MZ_SYSTEM_ROLE_ID,
890    privileges: &[
891        MzAclItem {
892            grantee: MZ_SUPPORT_ROLE_ID,
893            grantor: MZ_SYSTEM_ROLE_ID,
894            acl_mode: AclMode::USAGE,
895        },
896        rbac::owner_privilege(ObjectType::Cluster, MZ_SYSTEM_ROLE_ID),
897    ],
898};
899
900pub const MZ_CATALOG_SERVER_CLUSTER: BuiltinCluster = BuiltinCluster {
901    name: "mz_catalog_server",
902    owner_id: &MZ_SYSTEM_ROLE_ID,
903    privileges: &[
904        MzAclItem {
905            grantee: RoleId::Public,
906            grantor: MZ_SYSTEM_ROLE_ID,
907            acl_mode: AclMode::USAGE,
908        },
909        MzAclItem {
910            grantee: MZ_SUPPORT_ROLE_ID,
911            grantor: MZ_SYSTEM_ROLE_ID,
912            acl_mode: AclMode::USAGE.union(AclMode::CREATE),
913        },
914        rbac::owner_privilege(ObjectType::Cluster, MZ_SYSTEM_ROLE_ID),
915    ],
916};
917
918pub const MZ_PROBE_CLUSTER: BuiltinCluster = BuiltinCluster {
919    name: "mz_probe",
920    owner_id: &MZ_SYSTEM_ROLE_ID,
921    privileges: &[
922        MzAclItem {
923            grantee: MZ_SUPPORT_ROLE_ID,
924            grantor: MZ_SYSTEM_ROLE_ID,
925            acl_mode: AclMode::USAGE,
926        },
927        MzAclItem {
928            grantee: MZ_MONITOR_ROLE_ID,
929            grantor: MZ_SYSTEM_ROLE_ID,
930            acl_mode: AclMode::USAGE,
931        },
932        rbac::owner_privilege(ObjectType::Cluster, MZ_SYSTEM_ROLE_ID),
933    ],
934};
935
936pub const MZ_SUPPORT_CLUSTER: BuiltinCluster = BuiltinCluster {
937    name: "mz_support",
938    owner_id: &MZ_SUPPORT_ROLE_ID,
939    privileges: &[
940        MzAclItem {
941            grantee: MZ_SYSTEM_ROLE_ID,
942            grantor: MZ_SUPPORT_ROLE_ID,
943            acl_mode: rbac::all_object_privileges(SystemObjectType::Object(ObjectType::Cluster)),
944        },
945        rbac::owner_privilege(ObjectType::Cluster, MZ_SUPPORT_ROLE_ID),
946    ],
947};
948
949pub const MZ_ANALYTICS_CLUSTER: BuiltinCluster = BuiltinCluster {
950    name: "mz_analytics",
951    owner_id: &MZ_ANALYTICS_ROLE_ID,
952    privileges: &[
953        MzAclItem {
954            grantee: MZ_SYSTEM_ROLE_ID,
955            grantor: MZ_ANALYTICS_ROLE_ID,
956            acl_mode: rbac::all_object_privileges(SystemObjectType::Object(ObjectType::Cluster)),
957        },
958        rbac::owner_privilege(ObjectType::Cluster, MZ_ANALYTICS_ROLE_ID),
959    ],
960};
961
962/// List of all builtin objects sorted topologically by dependency.
963pub static BUILTINS_STATIC: LazyLock<Vec<Builtin<NameReference>>> = LazyLock::new(|| {
964    let mut builtin_types = vec![
965        Builtin::Type(&TYPE_ANY),
966        Builtin::Type(&TYPE_ANYARRAY),
967        Builtin::Type(&TYPE_ANYELEMENT),
968        Builtin::Type(&TYPE_ANYNONARRAY),
969        Builtin::Type(&TYPE_ANYRANGE),
970        Builtin::Type(&TYPE_BOOL),
971        Builtin::Type(&TYPE_BOOL_ARRAY),
972        Builtin::Type(&TYPE_BYTEA),
973        Builtin::Type(&TYPE_BYTEA_ARRAY),
974        Builtin::Type(&TYPE_BPCHAR),
975        Builtin::Type(&TYPE_BPCHAR_ARRAY),
976        Builtin::Type(&TYPE_CHAR),
977        Builtin::Type(&TYPE_CHAR_ARRAY),
978        Builtin::Type(&TYPE_DATE),
979        Builtin::Type(&TYPE_DATE_ARRAY),
980        Builtin::Type(&TYPE_FLOAT4),
981        Builtin::Type(&TYPE_FLOAT4_ARRAY),
982        Builtin::Type(&TYPE_FLOAT8),
983        Builtin::Type(&TYPE_FLOAT8_ARRAY),
984        Builtin::Type(&TYPE_INT4),
985        Builtin::Type(&TYPE_INT4_ARRAY),
986        Builtin::Type(&TYPE_INT8),
987        Builtin::Type(&TYPE_INT8_ARRAY),
988        Builtin::Type(&TYPE_INTERVAL),
989        Builtin::Type(&TYPE_INTERVAL_ARRAY),
990        Builtin::Type(&TYPE_JSONB),
991        Builtin::Type(&TYPE_JSONB_ARRAY),
992        Builtin::Type(&TYPE_LIST),
993        Builtin::Type(&TYPE_MAP),
994        Builtin::Type(&TYPE_NAME),
995        Builtin::Type(&TYPE_NAME_ARRAY),
996        Builtin::Type(&TYPE_NUMERIC),
997        Builtin::Type(&TYPE_NUMERIC_ARRAY),
998        Builtin::Type(&TYPE_OID),
999        Builtin::Type(&TYPE_OID_ARRAY),
1000        Builtin::Type(&TYPE_RECORD),
1001        Builtin::Type(&TYPE_RECORD_ARRAY),
1002        Builtin::Type(&TYPE_REGCLASS),
1003        Builtin::Type(&TYPE_REGCLASS_ARRAY),
1004        Builtin::Type(&TYPE_REGPROC),
1005        Builtin::Type(&TYPE_REGPROC_ARRAY),
1006        Builtin::Type(&TYPE_REGTYPE),
1007        Builtin::Type(&TYPE_REGTYPE_ARRAY),
1008        Builtin::Type(&TYPE_INT2),
1009        Builtin::Type(&TYPE_INT2_ARRAY),
1010        Builtin::Type(&TYPE_TEXT),
1011        Builtin::Type(&TYPE_TEXT_ARRAY),
1012        Builtin::Type(&TYPE_TIME),
1013        Builtin::Type(&TYPE_TIME_ARRAY),
1014        Builtin::Type(&TYPE_TIMESTAMP),
1015        Builtin::Type(&TYPE_TIMESTAMP_ARRAY),
1016        Builtin::Type(&TYPE_TIMESTAMPTZ),
1017        Builtin::Type(&TYPE_TIMESTAMPTZ_ARRAY),
1018        Builtin::Type(&TYPE_UUID),
1019        Builtin::Type(&TYPE_UUID_ARRAY),
1020        Builtin::Type(&TYPE_VARCHAR),
1021        Builtin::Type(&TYPE_VARCHAR_ARRAY),
1022        Builtin::Type(&TYPE_INT2_VECTOR),
1023        Builtin::Type(&TYPE_INT2_VECTOR_ARRAY),
1024        Builtin::Type(&TYPE_ANYCOMPATIBLE),
1025        Builtin::Type(&TYPE_ANYCOMPATIBLEARRAY),
1026        Builtin::Type(&TYPE_ANYCOMPATIBLENONARRAY),
1027        Builtin::Type(&TYPE_ANYCOMPATIBLELIST),
1028        Builtin::Type(&TYPE_ANYCOMPATIBLEMAP),
1029        Builtin::Type(&TYPE_ANYCOMPATIBLERANGE),
1030        Builtin::Type(&TYPE_UINT2),
1031        Builtin::Type(&TYPE_UINT2_ARRAY),
1032        Builtin::Type(&TYPE_UINT4),
1033        Builtin::Type(&TYPE_UINT4_ARRAY),
1034        Builtin::Type(&TYPE_UINT8),
1035        Builtin::Type(&TYPE_UINT8_ARRAY),
1036        Builtin::Type(&TYPE_MZ_TIMESTAMP),
1037        Builtin::Type(&TYPE_MZ_TIMESTAMP_ARRAY),
1038        Builtin::Type(&TYPE_INT4_RANGE),
1039        Builtin::Type(&TYPE_INT4_RANGE_ARRAY),
1040        Builtin::Type(&TYPE_INT8_RANGE),
1041        Builtin::Type(&TYPE_INT8_RANGE_ARRAY),
1042        Builtin::Type(&TYPE_DATE_RANGE),
1043        Builtin::Type(&TYPE_DATE_RANGE_ARRAY),
1044        Builtin::Type(&TYPE_NUM_RANGE),
1045        Builtin::Type(&TYPE_NUM_RANGE_ARRAY),
1046        Builtin::Type(&TYPE_TS_RANGE),
1047        Builtin::Type(&TYPE_TS_RANGE_ARRAY),
1048        Builtin::Type(&TYPE_TSTZ_RANGE),
1049        Builtin::Type(&TYPE_TSTZ_RANGE_ARRAY),
1050        Builtin::Type(&TYPE_MZ_ACL_ITEM),
1051        Builtin::Type(&TYPE_MZ_ACL_ITEM_ARRAY),
1052        Builtin::Type(&TYPE_ACL_ITEM),
1053        Builtin::Type(&TYPE_ACL_ITEM_ARRAY),
1054        Builtin::Type(&TYPE_INTERNAL),
1055    ];
1056
1057    let mut builtin_funcs = Vec::new();
1058    for (schema, funcs) in &[
1059        (PG_CATALOG_SCHEMA, &*mz_sql::func::PG_CATALOG_BUILTINS),
1060        (
1061            INFORMATION_SCHEMA,
1062            &*mz_sql::func::INFORMATION_SCHEMA_BUILTINS,
1063        ),
1064        (MZ_CATALOG_SCHEMA, &*mz_sql::func::MZ_CATALOG_BUILTINS),
1065        (MZ_INTERNAL_SCHEMA, &*mz_sql::func::MZ_INTERNAL_BUILTINS),
1066        (MZ_UNSAFE_SCHEMA, &*mz_sql::func::MZ_UNSAFE_BUILTINS),
1067    ] {
1068        for (name, func) in funcs.iter() {
1069            builtin_funcs.push(Builtin::Func(BuiltinFunc {
1070                name,
1071                schema,
1072                inner: func,
1073            }));
1074        }
1075    }
1076
1077    let mut builtin_items = vec![
1078        Builtin::Source(&MZ_CATALOG_RAW),
1079        Builtin::Log(&MZ_ARRANGEMENT_SHARING_RAW),
1080        Builtin::Log(&MZ_ARRANGEMENT_BATCHES_RAW),
1081        Builtin::Log(&MZ_ARRANGEMENT_RECORDS_RAW),
1082        Builtin::Log(&MZ_ARRANGEMENT_BATCHER_RECORDS_RAW),
1083        Builtin::Log(&MZ_ARRANGEMENT_BATCHER_SIZE_RAW),
1084        Builtin::Log(&MZ_ARRANGEMENT_BATCHER_CAPACITY_RAW),
1085        Builtin::Log(&MZ_ARRANGEMENT_BATCHER_ALLOCATIONS_RAW),
1086        Builtin::Log(&MZ_DATAFLOW_CHANNELS_PER_WORKER),
1087        Builtin::Log(&MZ_DATAFLOW_OPERATORS_PER_WORKER),
1088        Builtin::Log(&MZ_DATAFLOW_ADDRESSES_PER_WORKER),
1089        Builtin::Log(&MZ_DATAFLOW_OPERATOR_REACHABILITY_RAW),
1090        Builtin::Log(&MZ_COMPUTE_EXPORTS_PER_WORKER),
1091        Builtin::Log(&MZ_COMPUTE_DATAFLOW_GLOBAL_IDS_PER_WORKER),
1092        Builtin::Log(&MZ_CLUSTER_PROMETHEUS_METRICS),
1093        Builtin::Log(&MZ_MESSAGE_COUNTS_RECEIVED_RAW),
1094        Builtin::Log(&MZ_MESSAGE_COUNTS_SENT_RAW),
1095        Builtin::Log(&MZ_MESSAGE_BATCH_COUNTS_RECEIVED_RAW),
1096        Builtin::Log(&MZ_MESSAGE_BATCH_COUNTS_SENT_RAW),
1097        Builtin::Log(&MZ_ACTIVE_PEEKS_PER_WORKER),
1098        Builtin::Log(&MZ_PEEK_DURATIONS_HISTOGRAM_RAW),
1099        Builtin::Log(&MZ_ARRANGEMENT_HEAP_CAPACITY_RAW),
1100        Builtin::Log(&MZ_ARRANGEMENT_HEAP_ALLOCATIONS_RAW),
1101        Builtin::Log(&MZ_ARRANGEMENT_HEAP_SIZE_RAW),
1102        Builtin::Log(&MZ_SCHEDULING_ELAPSED_RAW),
1103        Builtin::Log(&MZ_COMPUTE_OPERATOR_DURATIONS_HISTOGRAM_RAW),
1104        Builtin::Log(&MZ_SCHEDULING_PARKS_HISTOGRAM_RAW),
1105        Builtin::Log(&MZ_COMPUTE_FRONTIERS_PER_WORKER),
1106        Builtin::Log(&MZ_COMPUTE_IMPORT_FRONTIERS_PER_WORKER),
1107        Builtin::Log(&MZ_COMPUTE_ERROR_COUNTS_RAW),
1108        Builtin::Log(&MZ_COMPUTE_HYDRATION_TIMES_PER_WORKER),
1109        Builtin::Log(&MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES_PER_WORKER),
1110        Builtin::MaterializedView(&MZ_KAFKA_SINKS),
1111        Builtin::MaterializedView(&MZ_KAFKA_CONNECTIONS),
1112        Builtin::MaterializedView(&MZ_KAFKA_SOURCES),
1113        Builtin::Table(&MZ_OBJECT_DEPENDENCIES),
1114        Builtin::MaterializedView(&MZ_ICEBERG_SINKS),
1115        Builtin::MaterializedView(&MZ_DATABASES),
1116        Builtin::MaterializedView(&MZ_SCHEMAS),
1117        Builtin::Table(&MZ_COLUMNS),
1118        // mz_indexes is generated dynamically below with inlined builtin VALUES.
1119        Builtin::Table(&MZ_INDEX_COLUMNS),
1120        Builtin::MaterializedView(&MZ_TABLES),
1121        // mz_sources is generated dynamically below with inlined builtin VALUES.
1122        Builtin::Table(&MZ_SOURCE_REFERENCES),
1123        Builtin::MaterializedView(&MZ_POSTGRES_SOURCES),
1124        Builtin::MaterializedView(&MZ_POSTGRES_SOURCE_TABLES),
1125        Builtin::MaterializedView(&MZ_MYSQL_SOURCE_TABLES),
1126        Builtin::MaterializedView(&MZ_SQL_SERVER_SOURCE_TABLES),
1127        Builtin::MaterializedView(&MZ_KAFKA_SOURCE_TABLES),
1128        Builtin::MaterializedView(&MZ_SINKS),
1129        Builtin::MaterializedView(&MZ_VIEWS),
1130        Builtin::Table(&MZ_TYPES),
1131        Builtin::Table(&MZ_TYPE_PG_METADATA),
1132        Builtin::Table(&MZ_ARRAY_TYPES),
1133        Builtin::Table(&MZ_BASE_TYPES),
1134        Builtin::Table(&MZ_LIST_TYPES),
1135        Builtin::Table(&MZ_MAP_TYPES),
1136        Builtin::MaterializedView(&MZ_ROLES),
1137        Builtin::Table(&MZ_ROLE_AUTH),
1138        Builtin::MaterializedView(&MZ_ROLE_MEMBERS),
1139        Builtin::MaterializedView(&MZ_ROLE_PARAMETERS),
1140        Builtin::Table(&MZ_PSEUDO_TYPES),
1141        Builtin::Table(&MZ_FUNCTIONS),
1142        Builtin::Table(&MZ_OPERATORS),
1143        Builtin::Table(&MZ_AGGREGATES),
1144        Builtin::Table(&MZ_CLUSTER_REPLICA_SIZES),
1145        Builtin::Table(&MZ_CLUSTER_REPLICA_SIZE_INTERNAL),
1146        Builtin::MaterializedView(&MZ_CLUSTERS),
1147        Builtin::MaterializedView(&MZ_CLUSTER_WORKLOAD_CLASSES),
1148        Builtin::MaterializedView(&MZ_CLUSTER_SCHEDULES),
1149        Builtin::MaterializedView(&MZ_CLUSTER_RECONFIGURATIONS),
1150        Builtin::MaterializedView(&MZ_CLUSTER_AUTO_SCALING_STRATEGIES),
1151        Builtin::MaterializedView(&MZ_SECRETS),
1152        Builtin::MaterializedView(&MZ_CONNECTIONS),
1153        Builtin::MaterializedView(&MZ_SSH_TUNNEL_CONNECTIONS),
1154        Builtin::MaterializedView(&MZ_CLUSTER_REPLICAS),
1155        Builtin::Source(&MZ_CLUSTER_REPLICA_METRICS_HISTORY),
1156        Builtin::View(&MZ_CLUSTER_REPLICA_METRICS),
1157        Builtin::Source(&MZ_CLUSTER_REPLICA_STATUS_HISTORY),
1158        Builtin::View(&MZ_CLUSTER_REPLICA_STATUSES),
1159        Builtin::MaterializedView(&MZ_INTERNAL_CLUSTER_REPLICAS),
1160        Builtin::MaterializedView(&MZ_PENDING_CLUSTER_REPLICAS),
1161        Builtin::MaterializedView(&MZ_AUDIT_EVENTS),
1162        Builtin::Table(&MZ_STORAGE_USAGE_BY_SHARD),
1163        Builtin::Table(&MZ_EGRESS_IPS),
1164        Builtin::MaterializedView(&MZ_AWS_PRIVATELINK_CONNECTIONS),
1165        Builtin::MaterializedView(&MZ_AWS_CONNECTIONS),
1166        Builtin::Table(&MZ_SUBSCRIPTIONS),
1167        Builtin::Table(&MZ_SESSIONS),
1168        Builtin::MaterializedView(&MZ_OVERRIDDEN_SYSTEM_PARAMETERS),
1169        Builtin::MaterializedView(&MZ_CLUSTER_SYSTEM_PARAMETERS),
1170        Builtin::MaterializedView(&MZ_REPLICA_SYSTEM_PARAMETERS),
1171        Builtin::MaterializedView(&MZ_DEFAULT_PRIVILEGES),
1172        Builtin::MaterializedView(&MZ_SYSTEM_PRIVILEGES),
1173        Builtin::MaterializedView(&MZ_COMMENTS),
1174        Builtin::Table(&MZ_WEBHOOKS_SOURCES),
1175        Builtin::Table(&MZ_HISTORY_RETENTION_STRATEGIES),
1176        Builtin::MaterializedView(&MZ_MATERIALIZED_VIEWS),
1177        Builtin::Table(&MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES),
1178        Builtin::MaterializedView(&MZ_NETWORK_POLICIES),
1179        Builtin::MaterializedView(&MZ_NETWORK_POLICY_RULES),
1180        Builtin::Table(&MZ_LICENSE_KEYS),
1181        Builtin::Table(&MZ_REPLACEMENTS),
1182        Builtin::View(&MZ_RELATIONS),
1183        Builtin::View(&MZ_OBJECT_OID_ALIAS),
1184        Builtin::View(&MZ_OBJECTS),
1185        Builtin::View(&MZ_OBJECT_FULLY_QUALIFIED_NAMES),
1186        Builtin::View(&MZ_OBJECTS_ID_NAMESPACE_TYPES),
1187        Builtin::View(&MZ_OBJECT_HISTORY),
1188        Builtin::View(&MZ_OBJECT_LIFETIMES),
1189        Builtin::Table(&MZ_OBJECT_GLOBAL_IDS),
1190        Builtin::View(&MZ_ARRANGEMENT_SHARING_PER_WORKER),
1191        Builtin::View(&MZ_ARRANGEMENT_SHARING),
1192        Builtin::View(&MZ_ARRANGEMENT_SIZES_PER_WORKER),
1193        Builtin::View(&MZ_ARRANGEMENT_SIZES),
1194        Builtin::View(&MZ_DATAFLOWS_PER_WORKER),
1195        Builtin::View(&MZ_DATAFLOWS),
1196        Builtin::View(&MZ_DATAFLOW_ADDRESSES),
1197        Builtin::View(&MZ_DATAFLOW_CHANNELS),
1198        Builtin::View(&MZ_DATAFLOW_OPERATORS),
1199        Builtin::View(&MZ_DATAFLOW_GLOBAL_IDS),
1200        Builtin::View(&MZ_COMPUTE_EXPORTS),
1201        Builtin::View(&MZ_MAPPABLE_OBJECTS),
1202        Builtin::View(&MZ_DATAFLOW_OPERATOR_DATAFLOWS_PER_WORKER),
1203        Builtin::View(&MZ_DATAFLOW_OPERATOR_DATAFLOWS),
1204        Builtin::View(&MZ_OBJECT_TRANSITIVE_DEPENDENCIES),
1205        Builtin::View(&MZ_DATAFLOW_OPERATOR_REACHABILITY_PER_WORKER),
1206        Builtin::View(&MZ_DATAFLOW_OPERATOR_REACHABILITY),
1207        Builtin::View(&MZ_CLUSTER_REPLICA_UTILIZATION),
1208        Builtin::View(&MZ_CLUSTER_REPLICA_UTILIZATION_HISTORY),
1209        Builtin::View(&MZ_DATAFLOW_OPERATOR_PARENTS_PER_WORKER),
1210        Builtin::View(&MZ_DATAFLOW_OPERATOR_PARENTS),
1211        Builtin::View(&MZ_DATAFLOW_ARRANGEMENT_SIZES),
1212        Builtin::View(&MZ_EXPECTED_GROUP_SIZE_ADVICE),
1213        Builtin::View(&MZ_COMPUTE_FRONTIERS),
1214        Builtin::View(&MZ_DATAFLOW_CHANNEL_OPERATORS_PER_WORKER),
1215        Builtin::View(&MZ_DATAFLOW_CHANNEL_OPERATORS),
1216        Builtin::View(&MZ_COMPUTE_IMPORT_FRONTIERS),
1217        Builtin::View(&MZ_MESSAGE_COUNTS_PER_WORKER),
1218        Builtin::View(&MZ_MESSAGE_COUNTS),
1219        Builtin::View(&MZ_ACTIVE_PEEKS),
1220        Builtin::View(&MZ_COMPUTE_OPERATOR_DURATIONS_HISTOGRAM_PER_WORKER),
1221        Builtin::View(&MZ_COMPUTE_OPERATOR_DURATIONS_HISTOGRAM),
1222        Builtin::View(&MZ_RECORDS_PER_DATAFLOW_OPERATOR_PER_WORKER),
1223        Builtin::View(&MZ_RECORDS_PER_DATAFLOW_OPERATOR),
1224        Builtin::View(&MZ_RECORDS_PER_DATAFLOW_PER_WORKER),
1225        Builtin::View(&MZ_RECORDS_PER_DATAFLOW),
1226        Builtin::View(&MZ_PEEK_DURATIONS_HISTOGRAM_PER_WORKER),
1227        Builtin::View(&MZ_PEEK_DURATIONS_HISTOGRAM),
1228        Builtin::View(&MZ_SCHEDULING_ELAPSED_PER_WORKER),
1229        Builtin::View(&MZ_SCHEDULING_ELAPSED),
1230        Builtin::View(&MZ_SCHEDULING_PARKS_HISTOGRAM_PER_WORKER),
1231        Builtin::View(&MZ_SCHEDULING_PARKS_HISTOGRAM),
1232        Builtin::View(&MZ_SHOW_ALL_OBJECTS),
1233        Builtin::View(&MZ_SHOW_COLUMNS),
1234        Builtin::View(&MZ_SHOW_CLUSTERS),
1235        Builtin::View(&MZ_SHOW_SECRETS),
1236        Builtin::View(&MZ_SHOW_DATABASES),
1237        Builtin::View(&MZ_SHOW_SCHEMAS),
1238        Builtin::View(&MZ_SHOW_TABLES),
1239        Builtin::View(&MZ_SHOW_VIEWS),
1240        Builtin::View(&MZ_SHOW_TYPES),
1241        Builtin::View(&MZ_SHOW_ROLES),
1242        Builtin::View(&MZ_SHOW_CONNECTIONS),
1243        Builtin::View(&MZ_SHOW_SOURCES),
1244        Builtin::View(&MZ_SHOW_SINKS),
1245        Builtin::View(&MZ_SHOW_MATERIALIZED_VIEWS),
1246        Builtin::View(&MZ_SHOW_INDEXES),
1247        Builtin::View(&MZ_CLUSTER_REPLICA_HISTORY),
1248        Builtin::View(&MZ_CLUSTER_REPLICA_NAME_HISTORY),
1249        Builtin::View(&MZ_TIMEZONE_NAMES),
1250        Builtin::View(&MZ_TIMEZONE_ABBREVIATIONS),
1251        Builtin::View(&PG_NAMESPACE_ALL_DATABASES),
1252        Builtin::Index(&PG_NAMESPACE_ALL_DATABASES_IND),
1253        Builtin::View(&PG_NAMESPACE),
1254        Builtin::View(&PG_CLASS_ALL_DATABASES),
1255        Builtin::Index(&PG_CLASS_ALL_DATABASES_IND),
1256        Builtin::View(&PG_CLASS),
1257        Builtin::View(&PG_DEPEND),
1258        Builtin::View(&PG_DATABASE),
1259        Builtin::View(&PG_INDEX),
1260        Builtin::View(&PG_TYPE_ALL_DATABASES),
1261        Builtin::Index(&PG_TYPE_ALL_DATABASES_IND),
1262        Builtin::View(&PG_TYPE),
1263        Builtin::View(&PG_DESCRIPTION_ALL_DATABASES),
1264        Builtin::Index(&PG_DESCRIPTION_ALL_DATABASES_IND),
1265        Builtin::View(&PG_DESCRIPTION),
1266        Builtin::View(&PG_ATTRIBUTE_ALL_DATABASES),
1267        Builtin::Index(&PG_ATTRIBUTE_ALL_DATABASES_IND),
1268        Builtin::View(&PG_ATTRIBUTE),
1269        Builtin::View(&PG_PROC),
1270        Builtin::View(&PG_OPERATOR),
1271        Builtin::View(&PG_RANGE),
1272        Builtin::View(&PG_ENUM),
1273        Builtin::View(&PG_ATTRDEF_ALL_DATABASES),
1274        Builtin::Index(&PG_ATTRDEF_ALL_DATABASES_IND),
1275        Builtin::View(&PG_ATTRDEF),
1276        Builtin::View(&PG_SETTINGS),
1277        Builtin::View(&PG_AUTH_MEMBERS),
1278        Builtin::View(&PG_CONSTRAINT),
1279        Builtin::View(&PG_TABLES),
1280        Builtin::View(&PG_TABLESPACE),
1281        Builtin::View(&PG_ACCESS_METHODS),
1282        Builtin::View(&PG_LOCKS),
1283        Builtin::View(&PG_AUTHID_CORE),
1284        Builtin::Index(&PG_AUTHID_CORE_IND),
1285        Builtin::View(&PG_AUTHID),
1286        Builtin::View(&PG_ROLES),
1287        Builtin::View(&PG_USER),
1288        Builtin::View(&PG_VIEWS),
1289        Builtin::View(&PG_MATVIEWS),
1290        Builtin::View(&PG_COLLATION),
1291        Builtin::View(&PG_POLICY),
1292        Builtin::View(&PG_INHERITS),
1293        Builtin::View(&PG_AGGREGATE),
1294        Builtin::View(&PG_TRIGGER),
1295        Builtin::View(&PG_REWRITE),
1296        Builtin::View(&PG_EXTENSION),
1297        Builtin::View(&PG_EVENT_TRIGGER),
1298        Builtin::View(&PG_LANGUAGE),
1299        Builtin::View(&PG_SHDESCRIPTION),
1300        Builtin::View(&PG_INDEXES),
1301        Builtin::View(&PG_TIMEZONE_ABBREVS),
1302        Builtin::View(&PG_TIMEZONE_NAMES),
1303        Builtin::View(&INFORMATION_SCHEMA_APPLICABLE_ROLES),
1304        Builtin::View(&INFORMATION_SCHEMA_COLUMNS),
1305        Builtin::View(&INFORMATION_SCHEMA_ENABLED_ROLES),
1306        Builtin::View(&INFORMATION_SCHEMA_KEY_COLUMN_USAGE),
1307        Builtin::View(&INFORMATION_SCHEMA_REFERENTIAL_CONSTRAINTS),
1308        Builtin::View(&INFORMATION_SCHEMA_ROUTINES),
1309        Builtin::View(&INFORMATION_SCHEMA_SCHEMATA),
1310        Builtin::View(&INFORMATION_SCHEMA_TABLES),
1311        Builtin::View(&INFORMATION_SCHEMA_TABLE_CONSTRAINTS),
1312        Builtin::View(&INFORMATION_SCHEMA_TABLE_PRIVILEGES),
1313        Builtin::View(&INFORMATION_SCHEMA_ROLE_TABLE_GRANTS),
1314        Builtin::View(&INFORMATION_SCHEMA_TRIGGERS),
1315        Builtin::View(&INFORMATION_SCHEMA_VIEWS),
1316        Builtin::View(&INFORMATION_SCHEMA_CHARACTER_SETS),
1317        Builtin::View(&MZ_SHOW_ROLE_MEMBERS),
1318        Builtin::View(&MZ_SHOW_MY_ROLE_MEMBERS),
1319        Builtin::View(&MZ_SHOW_SYSTEM_PRIVILEGES),
1320        Builtin::View(&MZ_SHOW_MY_SYSTEM_PRIVILEGES),
1321        Builtin::View(&MZ_SHOW_CLUSTER_PRIVILEGES),
1322        Builtin::View(&MZ_SHOW_MY_CLUSTER_PRIVILEGES),
1323        Builtin::View(&MZ_SHOW_DATABASE_PRIVILEGES),
1324        Builtin::View(&MZ_SHOW_MY_DATABASE_PRIVILEGES),
1325        Builtin::View(&MZ_SHOW_SCHEMA_PRIVILEGES),
1326        Builtin::View(&MZ_SHOW_MY_SCHEMA_PRIVILEGES),
1327        Builtin::View(&MZ_SHOW_OBJECT_PRIVILEGES),
1328        Builtin::View(&MZ_SHOW_MY_OBJECT_PRIVILEGES),
1329        Builtin::View(&MZ_SHOW_ALL_PRIVILEGES),
1330        Builtin::View(&MZ_SHOW_ALL_MY_PRIVILEGES),
1331        Builtin::View(&MZ_SHOW_DEFAULT_PRIVILEGES),
1332        Builtin::View(&MZ_SHOW_MY_DEFAULT_PRIVILEGES),
1333        Builtin::Source(&MZ_SINK_STATUS_HISTORY),
1334        Builtin::View(&MZ_SINK_STATUSES),
1335        Builtin::Source(&MZ_SOURCE_STATUS_HISTORY),
1336        Builtin::Source(&MZ_AWS_PRIVATELINK_CONNECTION_STATUS_HISTORY),
1337        Builtin::View(&MZ_AWS_PRIVATELINK_CONNECTION_STATUSES),
1338        Builtin::Source(&MZ_STATEMENT_EXECUTION_HISTORY),
1339        Builtin::View(&MZ_STATEMENT_EXECUTION_HISTORY_REDACTED),
1340        Builtin::Source(&MZ_PREPARED_STATEMENT_HISTORY),
1341        Builtin::Source(&MZ_SESSION_HISTORY),
1342        Builtin::Source(&MZ_SQL_TEXT),
1343        Builtin::View(&MZ_SQL_TEXT_REDACTED),
1344        Builtin::View(&MZ_RECENT_SQL_TEXT),
1345        Builtin::View(&MZ_RECENT_SQL_TEXT_REDACTED),
1346        Builtin::Index(&MZ_RECENT_SQL_TEXT_IND),
1347        Builtin::View(&MZ_ACTIVITY_LOG_THINNED),
1348        Builtin::View(&MZ_RECENT_ACTIVITY_LOG_THINNED),
1349        Builtin::View(&MZ_RECENT_ACTIVITY_LOG),
1350        Builtin::View(&MZ_RECENT_ACTIVITY_LOG_REDACTED),
1351        Builtin::Index(&MZ_RECENT_ACTIVITY_LOG_THINNED_IND),
1352        Builtin::View(&MZ_SOURCE_STATUSES),
1353        Builtin::Source(&MZ_STATEMENT_LIFECYCLE_HISTORY),
1354        Builtin::Source(&MZ_STORAGE_SHARDS),
1355        Builtin::Source(&MZ_SOURCE_STATISTICS_RAW),
1356        Builtin::Source(&MZ_SINK_STATISTICS_RAW),
1357        Builtin::View(&MZ_SOURCE_STATISTICS_WITH_HISTORY),
1358        Builtin::Index(&MZ_SOURCE_STATISTICS_WITH_HISTORY_IND),
1359        Builtin::View(&MZ_SOURCE_STATISTICS),
1360        Builtin::Index(&MZ_SOURCE_STATISTICS_IND),
1361        Builtin::View(&MZ_SINK_STATISTICS),
1362        Builtin::Index(&MZ_SINK_STATISTICS_IND),
1363        Builtin::View(&MZ_STORAGE_USAGE),
1364        Builtin::Source(&MZ_FRONTIERS),
1365        Builtin::View(&MZ_GLOBAL_FRONTIERS),
1366        Builtin::Source(&MZ_WALLCLOCK_LAG_HISTORY),
1367        Builtin::View(&MZ_WALLCLOCK_GLOBAL_LAG_HISTORY),
1368        Builtin::View(&MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY),
1369        Builtin::View(&MZ_WALLCLOCK_GLOBAL_LAG),
1370        Builtin::Source(&MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW),
1371        Builtin::View(&MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM),
1372        Builtin::Source(&MZ_MATERIALIZED_VIEW_REFRESHES),
1373        Builtin::Source(&MZ_COMPUTE_DEPENDENCIES),
1374        Builtin::View(&MZ_MATERIALIZATION_DEPENDENCIES),
1375        Builtin::View(&MZ_OBJECT_GRAPH_EDGES),
1376        Builtin::View(&MZ_MATERIALIZATION_LAG),
1377        Builtin::View(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW),
1378        Builtin::View(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H),
1379        Builtin::View(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H),
1380        Builtin::View(&MZ_COMPUTE_ERROR_COUNTS_PER_WORKER),
1381        Builtin::View(&MZ_COMPUTE_ERROR_COUNTS),
1382        Builtin::Source(&MZ_COMPUTE_ERROR_COUNTS_RAW_UNIFIED),
1383        Builtin::Source(&MZ_COMPUTE_HYDRATION_TIMES),
1384        Builtin::Source(&MZ_OBJECT_ARRANGEMENT_SIZES_UNIFIED),
1385        Builtin::Index(&MZ_OBJECT_ARRANGEMENT_SIZES_IND),
1386        Builtin::Table(&MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY),
1387        Builtin::Index(&MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OBJECT_IND),
1388        Builtin::Index(&MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_TS_IND),
1389        Builtin::Log(&MZ_COMPUTE_LIR_MAPPING_PER_WORKER),
1390        Builtin::View(&MZ_LIR_MAPPING),
1391        Builtin::Source(&MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES),
1392        Builtin::Source(&MZ_CLUSTER_REPLICA_FRONTIERS),
1393        Builtin::View(&MZ_COMPUTE_HYDRATION_STATUSES),
1394        Builtin::View(&MZ_HYDRATION_STATUSES),
1395        Builtin::Index(&MZ_HYDRATION_STATUSES_IND),
1396        Builtin::View(&MZ_SHOW_CLUSTER_REPLICAS),
1397        Builtin::View(&MZ_SHOW_NETWORK_POLICIES),
1398        Builtin::View(&MZ_CLUSTER_DEPLOYMENT_LINEAGE),
1399        Builtin::Index(&MZ_SHOW_DATABASES_IND),
1400        Builtin::Index(&MZ_SHOW_SCHEMAS_IND),
1401        Builtin::Index(&MZ_SHOW_CONNECTIONS_IND),
1402        Builtin::Index(&MZ_SHOW_TABLES_IND),
1403        Builtin::Index(&MZ_SHOW_SOURCES_IND),
1404        Builtin::Index(&MZ_SHOW_VIEWS_IND),
1405        Builtin::Index(&MZ_SHOW_MATERIALIZED_VIEWS_IND),
1406        Builtin::Index(&MZ_SHOW_SINKS_IND),
1407        Builtin::Index(&MZ_SHOW_TYPES_IND),
1408        Builtin::Index(&MZ_SHOW_ALL_OBJECTS_IND),
1409        Builtin::Index(&MZ_SHOW_INDEXES_IND),
1410        Builtin::Index(&MZ_SHOW_COLUMNS_IND),
1411        Builtin::Index(&MZ_SHOW_CLUSTERS_IND),
1412        Builtin::Index(&MZ_SHOW_CLUSTER_REPLICAS_IND),
1413        Builtin::Index(&MZ_SHOW_SECRETS_IND),
1414        Builtin::Index(&MZ_SHOW_ROLES_IND),
1415        Builtin::Index(&MZ_CLUSTERS_IND),
1416        Builtin::Index(&MZ_CLUSTER_RECONFIGURATIONS_IND),
1417        Builtin::Index(&MZ_CLUSTER_AUTO_SCALING_STRATEGIES_IND),
1418        Builtin::Index(&MZ_INDEXES_IND),
1419        Builtin::Index(&MZ_ROLES_IND),
1420        Builtin::Index(&MZ_SOURCES_IND),
1421        Builtin::Index(&MZ_SINKS_IND),
1422        Builtin::Index(&MZ_MATERIALIZED_VIEWS_IND),
1423        Builtin::Index(&MZ_SOURCE_STATUSES_IND),
1424        Builtin::Index(&MZ_SOURCE_STATUS_HISTORY_IND),
1425        Builtin::Index(&MZ_SINK_STATUSES_IND),
1426        Builtin::Index(&MZ_SINK_STATUS_HISTORY_IND),
1427        Builtin::Index(&MZ_CLUSTER_REPLICAS_IND),
1428        Builtin::Index(&MZ_CLUSTER_REPLICA_SIZES_IND),
1429        Builtin::Index(&MZ_CLUSTER_REPLICA_SIZE_INTERNAL_IND),
1430        Builtin::Index(&MZ_CLUSTER_REPLICA_STATUSES_IND),
1431        Builtin::Index(&MZ_CLUSTER_REPLICA_STATUS_HISTORY_IND),
1432        Builtin::Index(&MZ_CLUSTER_REPLICA_METRICS_IND),
1433        Builtin::Index(&MZ_CLUSTER_REPLICA_METRICS_HISTORY_IND),
1434        Builtin::Index(&MZ_CLUSTER_REPLICA_HISTORY_IND),
1435        Builtin::Index(&MZ_CLUSTER_REPLICA_NAME_HISTORY_IND),
1436        Builtin::Index(&MZ_OBJECT_LIFETIMES_IND),
1437        Builtin::Index(&MZ_OBJECT_HISTORY_IND),
1438        Builtin::Index(&MZ_OBJECT_DEPENDENCIES_IND),
1439        Builtin::Index(&MZ_COMPUTE_DEPENDENCIES_IND),
1440        Builtin::Index(&MZ_OBJECT_TRANSITIVE_DEPENDENCIES_IND),
1441        Builtin::Index(&MZ_OBJECT_GRAPH_EDGES_IND),
1442        Builtin::Index(&MZ_FRONTIERS_IND),
1443        Builtin::Index(&MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_IND),
1444        Builtin::Index(&MZ_KAFKA_SOURCES_IND),
1445        Builtin::Index(&MZ_WEBHOOK_SOURCES_IND),
1446        Builtin::Index(&MZ_COMMENTS_IND),
1447        Builtin::Index(&MZ_DATABASES_IND),
1448        Builtin::Index(&MZ_SCHEMAS_IND),
1449        Builtin::Index(&MZ_CONNECTIONS_IND),
1450        Builtin::Index(&MZ_TABLES_IND),
1451        Builtin::Index(&MZ_TYPES_IND),
1452        Builtin::Index(&MZ_OBJECTS_IND),
1453        Builtin::Index(&MZ_COLUMNS_IND),
1454        Builtin::Index(&MZ_SECRETS_IND),
1455        Builtin::Index(&MZ_VIEWS_IND),
1456        Builtin::Index(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_IND),
1457        Builtin::Index(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_IND),
1458        Builtin::Index(&MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_IND),
1459        Builtin::Index(&MZ_CLUSTER_DEPLOYMENT_LINEAGE_IND),
1460        Builtin::Index(&MZ_CLUSTER_REPLICA_FRONTIERS_IND),
1461        Builtin::Index(&MZ_COMPUTE_HYDRATION_TIMES_IND),
1462        Builtin::View(&MZ_RECENT_STORAGE_USAGE),
1463        Builtin::Index(&MZ_RECENT_STORAGE_USAGE_IND),
1464        Builtin::Connection(&MZ_ANALYTICS),
1465        Builtin::View(&MZ_INDEX_ADVICE),
1466        Builtin::View(&MZ_MCP_DATA_PRODUCTS),
1467        Builtin::View(&MZ_MCP_DATA_PRODUCT_DETAILS),
1468    ];
1469
1470    builtin_items.extend(notice::builtins());
1471
1472    // Generate mz_sources with builtin source/log entries inlined as VALUES so
1473    // that its SQL fingerprint changes whenever a builtin source is added or
1474    // removed, forcing an explicit MigrationStep::replacement.
1475    //
1476    // Must happen BEFORE ontology::generate_views so that mz_sources's ontology
1477    // annotation (entity_name = "source") is visible to the ontology index views.
1478    // All sources/logs are already present in builtin_items at this point.
1479    {
1480        let source_iter = builtin_items.iter().filter_map(|b| match b {
1481            Builtin::Source(x) => Some(*x),
1482            _ => None,
1483        });
1484        let log_iter = builtin_items.iter().filter_map(|b| match b {
1485            Builtin::Log(x) => Some(*x),
1486            _ => None,
1487        });
1488        let mz_sources = builtin::make_mz_sources(source_iter, log_iter);
1489        let mz_sources_ref: &'static BuiltinMaterializedView = Box::leak(Box::new(mz_sources));
1490        // Insert at the original position of the old static MZ_SOURCES —
1491        // right before mz_source_references — to preserve stable IDs for
1492        // all items that follow it in the list.
1493        let insert_pos = builtin_items
1494            .iter()
1495            .position(|b| matches!(b, Builtin::Table(t) if t.name == "mz_source_references"))
1496            .expect("mz_source_references must be present in builtin_items");
1497        builtin_items.insert(insert_pos, Builtin::MaterializedView(mz_sources_ref));
1498    }
1499
1500    // Generate mz_indexes with builtin index/log entries inlined as VALUES so
1501    // that its SQL fingerprint changes whenever a builtin index or log is added or
1502    // removed, forcing an explicit MigrationStep::replacement.
1503    //
1504    // Must happen AFTER all builtin indexes and logs have been pushed into
1505    // builtin_items, so that make_mz_indexes sees the complete set. Must happen
1506    // BEFORE ontology::generate_views so the ontology generator sees mz_indexes
1507    // as a materialized view participating in catalog ontology, rather than
1508    // being absent from builtin_items.
1509    {
1510        let index_iter = builtin_items.iter().filter_map(|b| match b {
1511            Builtin::Index(x) => Some(*x),
1512            _ => None,
1513        });
1514        let log_iter = builtin_items.iter().filter_map(|b| match b {
1515            Builtin::Log(x) => Some(*x),
1516            _ => None,
1517        });
1518        let mz_indexes = mz_catalog::make_mz_indexes(index_iter, log_iter);
1519        let mz_indexes_ref: &'static BuiltinMaterializedView = Box::leak(Box::new(mz_indexes));
1520        let insert_pos = builtin_items
1521            .iter()
1522            .position(|b| matches!(b, Builtin::Table(t) if t.name == "mz_index_columns"))
1523            .expect("mz_index_columns must be present in builtin_items");
1524        builtin_items.insert(insert_pos, Builtin::MaterializedView(mz_indexes_ref));
1525    }
1526
1527    // Generate ontology views by enumerating existing builtins.
1528    builtin_items.extend(ontology::generate_views(&builtin_items));
1529
1530    // Generate builtin relations reporting builtin objects last, since they need a complete view
1531    // of all other builtins.
1532    let mut builtin_builtins = builtin::builtins(&builtin_items).collect();
1533
1534    // Construct the full list of builtins, retaining dependency order.
1535    let mut builtins = Vec::new();
1536    builtins.append(&mut builtin_types);
1537    builtins.append(&mut builtin_funcs);
1538    builtins.append(&mut builtin_builtins);
1539    builtins.append(&mut builtin_items);
1540
1541    builtins
1542});
1543pub const BUILTIN_ROLES: &[&BuiltinRole] = &[
1544    &MZ_SYSTEM_ROLE,
1545    &MZ_SUPPORT_ROLE,
1546    &MZ_ANALYTICS_ROLE,
1547    &MZ_MONITOR_ROLE,
1548    &MZ_MONITOR_REDACTED,
1549    &MZ_JWT_SYNC_ROLE,
1550];
1551pub const BUILTIN_CLUSTERS: &[&BuiltinCluster] = &[
1552    &MZ_SYSTEM_CLUSTER,
1553    &MZ_CATALOG_SERVER_CLUSTER,
1554    &MZ_PROBE_CLUSTER,
1555    &MZ_SUPPORT_CLUSTER,
1556    &MZ_ANALYTICS_CLUSTER,
1557];
1558
1559#[allow(non_snake_case)]
1560pub mod BUILTINS {
1561    use super::*;
1562
1563    pub fn logs() -> impl Iterator<Item = &'static BuiltinLog> {
1564        BUILTINS_STATIC.iter().filter_map(|b| match b {
1565            Builtin::Log(log) => Some(*log),
1566            _ => None,
1567        })
1568    }
1569
1570    pub fn types() -> impl Iterator<Item = &'static BuiltinType<NameReference>> {
1571        BUILTINS_STATIC.iter().filter_map(|b| match b {
1572            Builtin::Type(typ) => Some(*typ),
1573            _ => None,
1574        })
1575    }
1576
1577    pub fn views() -> impl Iterator<Item = &'static BuiltinView> {
1578        BUILTINS_STATIC.iter().filter_map(|b| match b {
1579            Builtin::View(view) => Some(*view),
1580            _ => None,
1581        })
1582    }
1583
1584    pub fn materialized_views() -> impl Iterator<Item = &'static BuiltinMaterializedView> {
1585        BUILTINS_STATIC.iter().filter_map(|b| match b {
1586            Builtin::MaterializedView(mv) => Some(*mv),
1587            _ => None,
1588        })
1589    }
1590
1591    pub fn funcs() -> impl Iterator<Item = &'static BuiltinFunc> {
1592        BUILTINS_STATIC.iter().filter_map(|b| match b {
1593            Builtin::Func(func) => Some(func),
1594            _ => None,
1595        })
1596    }
1597
1598    pub fn iter() -> impl Iterator<Item = &'static Builtin<NameReference>> {
1599        BUILTINS_STATIC.iter()
1600    }
1601}
1602
1603pub static BUILTIN_LOG_LOOKUP: LazyLock<HashMap<&'static str, &'static BuiltinLog>> =
1604    LazyLock::new(|| BUILTINS::logs().map(|log| (log.name, log)).collect());
1605/// Keys are builtin object description, values are the builtin index when sorted by dependency and
1606/// the builtin itself.
1607pub static BUILTIN_LOOKUP: LazyLock<
1608    HashMap<SystemObjectDescription, (usize, &'static Builtin<NameReference>)>,
1609> = LazyLock::new(|| {
1610    BUILTINS_STATIC
1611        .iter()
1612        .enumerate()
1613        .map(|(idx, builtin)| {
1614            (
1615                SystemObjectDescription {
1616                    schema_name: builtin.schema().to_string(),
1617                    object_type: builtin.catalog_item_type(),
1618                    object_name: builtin.name().to_string(),
1619                },
1620                (idx, builtin),
1621            )
1622        })
1623        .collect()
1624});
1625
1626#[cfg(test)]
1627mod tests {
1628    use std::collections::{BTreeMap, BTreeSet};
1629
1630    use mz_pgrepr::oid::FIRST_MATERIALIZE_OID;
1631    use mz_sql_parser::ast::visit::{self, Visit};
1632    use mz_sql_parser::ast::{Raw, RawItemName, UnresolvedItemName};
1633
1634    use super::*;
1635
1636    #[mz_ore::test]
1637    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
1638    fn test_builtin_type_schema() {
1639        for typ in BUILTINS::types() {
1640            if typ.oid < FIRST_MATERIALIZE_OID {
1641                assert_eq!(
1642                    typ.schema, PG_CATALOG_SCHEMA,
1643                    "{typ:?} should be in {PG_CATALOG_SCHEMA} schema"
1644                );
1645            } else {
1646                // `mz_pgrepr::Type` resolution relies on all non-PG types existing in the
1647                // mz_catalog schema.
1648                assert_eq!(
1649                    typ.schema, MZ_CATALOG_SCHEMA,
1650                    "{typ:?} should be in {MZ_CATALOG_SCHEMA} schema"
1651                );
1652            }
1653        }
1654    }
1655
1656    /// Visitor that collects the last component of all referenced
1657    /// item names from a SQL AST.
1658    struct ItemNameCollector {
1659        names: BTreeSet<String>,
1660    }
1661
1662    impl<'ast> Visit<'ast, Raw> for ItemNameCollector {
1663        fn visit_item_name(&mut self, name: &'ast <Raw as mz_sql_parser::ast::AstInfo>::ItemName) {
1664            let unresolved: &UnresolvedItemName = match name {
1665                RawItemName::Name(n) | RawItemName::Id(_, n, _) => n,
1666            };
1667            let parts = &unresolved.0;
1668            if !parts.is_empty() {
1669                let obj_name = parts[parts.len() - 1].as_str().to_string();
1670                self.names.insert(obj_name);
1671            }
1672            visit::visit_item_name(self, name);
1673        }
1674    }
1675
1676    /// Tests that `BUILTINS_STATIC` is ordered respecting dependencies:
1677    /// if builtin A references builtin B in its SQL, then B must appear
1678    /// before A in the list. (This ordering is assumed by, e.g.,
1679    /// `sort_updates` during catalog migrations.)
1680    #[mz_ore::test]
1681    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
1682    fn test_builtins_static_dependency_order() {
1683        // Build a map from name -> (schema, index) for all builtins.
1684        // We look up by just the name (last component) to catch
1685        // unqualified references in SQL.
1686        let mut builtin_by_name: BTreeMap<&str, (&str, usize)> = BTreeMap::new();
1687        let mut duplicate_names = Vec::new();
1688        for (idx, builtin) in BUILTINS_STATIC.iter().enumerate() {
1689            if let Some((prev_schema, prev_idx)) =
1690                builtin_by_name.insert(builtin.name(), (builtin.schema(), idx))
1691            {
1692                // Only flag duplicates across different schemas.
1693                // Same-schema duplicates (e.g., range types that
1694                // appear as both Type and Func) are fine because
1695                // they resolve to the same schema.
1696                if prev_schema != builtin.schema() {
1697                    duplicate_names.push(format!(
1698                        "name {:?} appears in both {}.{} (index \
1699                         {}) and {}.{} (index {})",
1700                        builtin.name(),
1701                        prev_schema,
1702                        builtin.name(),
1703                        prev_idx,
1704                        builtin.schema(),
1705                        builtin.name(),
1706                        idx,
1707                    ));
1708                }
1709            }
1710        }
1711        assert!(
1712            duplicate_names.is_empty(),
1713            "BUILTINS_STATIC has duplicate names across different \
1714             schemas (this test needs adjustment if such duplicates \
1715             are intentional):\n{}",
1716            duplicate_names.join("\n"),
1717        );
1718
1719        // Get the `CREATE ...` SQL for builtins that have it.
1720        let get_create_sql = |builtin: &Builtin<NameReference>| -> Option<String> {
1721            match builtin {
1722                Builtin::View(v) => Some(v.create_sql()),
1723                Builtin::MaterializedView(mv) => Some(mv.create_sql()),
1724                Builtin::Index(idx) => Some(idx.create_sql()),
1725                _ => None,
1726            }
1727        };
1728
1729        // For each SQL-bearing builtin, parse its SQL, walk the AST to
1730        // find referenced item names, and check that all referenced
1731        // builtins appear earlier in BUILTINS_STATIC.
1732        let mut violations = Vec::new();
1733        for (idx, builtin) in BUILTINS_STATIC.iter().enumerate() {
1734            let create_sql = match get_create_sql(builtin) {
1735                Some(sql) => sql,
1736                None => continue,
1737            };
1738
1739            let stmts = mz_sql_parser::parser::parse_statements(&create_sql).unwrap_or_else(|e| {
1740                panic!(
1741                    "failed to parse SQL for {}.{}: \
1742                         {e}\nSQL: {create_sql}",
1743                    builtin.schema(),
1744                    builtin.name(),
1745                )
1746            });
1747
1748            let mut collector = ItemNameCollector {
1749                names: BTreeSet::new(),
1750            };
1751            for stmt in &stmts {
1752                collector.visit_statement(&stmt.ast);
1753            }
1754
1755            for ref_name in &collector.names {
1756                if let Some(&(ref_schema, dep_idx)) = builtin_by_name.get(ref_name.as_str()) {
1757                    if dep_idx > idx {
1758                        violations.push(format!(
1759                            "{}.{} (index {}) references \
1760                             {}.{} (index {}), but the \
1761                             dependency appears later in \
1762                             BUILTINS_STATIC",
1763                            builtin.schema(),
1764                            builtin.name(),
1765                            idx,
1766                            ref_schema,
1767                            ref_name,
1768                            dep_idx,
1769                        ));
1770                    }
1771                }
1772            }
1773        }
1774
1775        assert!(
1776            violations.is_empty(),
1777            "BUILTINS_STATIC has dependency ordering violations:\n{}",
1778            violations.join("\n"),
1779        );
1780    }
1781
1782    /// Validates ontology metadata consistency:
1783    /// - Every link target references an entity that exists.
1784    /// - No duplicate entity names.
1785    /// - Every annotated builtin has a non-empty entity_name and description.
1786    #[mz_ore::test]
1787    #[cfg_attr(miri, ignore)]
1788    fn test_ontology_consistency() {
1789        // Collect all entity names from builtins with ontology annotations.
1790        let mut entity_names: BTreeSet<String> = BTreeSet::new();
1791        let mut duplicate_entities = Vec::new();
1792
1793        for builtin in BUILTINS_STATIC.iter() {
1794            let ontology = match builtin {
1795                Builtin::Table(t) => t.ontology.as_ref(),
1796                Builtin::View(v) => v.ontology.as_ref(),
1797                Builtin::MaterializedView(mv) => mv.ontology.as_ref(),
1798                Builtin::Source(s) => s.ontology.as_ref(),
1799                Builtin::Log(l) => l.ontology.as_ref(),
1800                _ => None,
1801            };
1802            if let Some(ont) = ontology {
1803                assert!(
1804                    !ont.entity_name.is_empty(),
1805                    "builtin {} has empty ontology entity_name",
1806                    builtin.name()
1807                );
1808                assert!(
1809                    !ont.description.is_empty(),
1810                    "builtin {} ({}) has empty ontology description",
1811                    builtin.name(),
1812                    ont.entity_name
1813                );
1814                if !entity_names.insert(ont.entity_name.to_string()) {
1815                    duplicate_entities.push(format!(
1816                        "duplicate entity_name {:?} on builtin {}",
1817                        ont.entity_name,
1818                        builtin.name()
1819                    ));
1820                }
1821            }
1822        }
1823        assert!(
1824            duplicate_entities.is_empty(),
1825            "ontology has duplicate entity names:\n{}",
1826            duplicate_entities.join("\n"),
1827        );
1828
1829        // Validate link targets reference existing entities.
1830        let mut bad_targets = Vec::new();
1831        for builtin in BUILTINS_STATIC.iter() {
1832            let ontology = match builtin {
1833                Builtin::Table(t) => t.ontology.as_ref(),
1834                Builtin::View(v) => v.ontology.as_ref(),
1835                Builtin::MaterializedView(mv) => mv.ontology.as_ref(),
1836                Builtin::Source(s) => s.ontology.as_ref(),
1837                Builtin::Log(l) => l.ontology.as_ref(),
1838                _ => None,
1839            };
1840            if let Some(ont) = ontology {
1841                for link in ont.links {
1842                    if !entity_names.contains(link.target) {
1843                        bad_targets.push(format!(
1844                            "entity {:?} link {:?} targets {:?} which is not a known entity",
1845                            ont.entity_name, link.name, link.target
1846                        ));
1847                    }
1848                }
1849            }
1850        }
1851        assert!(
1852            bad_targets.is_empty(),
1853            "ontology has links targeting unknown entities:\n{}",
1854            bad_targets.join("\n"),
1855        );
1856
1857        // Semantic type annotations are typed (SemanticType enum), so validity
1858        // is guaranteed at compile time — no runtime check needed.
1859
1860        // Validate that every "reference" column (one whose semantic type implies
1861        // a FK relationship) is covered by an OntologyLink on entities that
1862        // already have at least one FK-style link.
1863        //
1864        // Scope: only entities that have started FK annotation (at least one
1865        // link with a source_column). Entities with only union/maps_to links,
1866        // or no links at all, are not yet fully annotated and are skipped to
1867        // avoid noise.
1868        //
1869        // Exemptions:
1870        // - Column at index 0 named "id": almost always the entity's own PK,
1871        //   not a FK (e.g. mz_objects.id, mz_functions.id).
1872        // - Columns in the relation's declared key set.
1873        //
1874        // "Reference" types are ID types that imply a FK. Discriminators
1875        // (ObjectType, ConnectionType, SourceType), OID, and metric types
1876        // (ByteCount, etc.) are excluded.
1877        let reference_sem_types: BTreeSet<SemanticType> = BTreeSet::from([
1878            SemanticType::CatalogItemId,
1879            SemanticType::GlobalId,
1880            SemanticType::ClusterId,
1881            SemanticType::ReplicaId,
1882            SemanticType::SchemaId,
1883            SemanticType::DatabaseId,
1884            SemanticType::RoleId,
1885            SemanticType::NetworkPolicyId,
1886        ]);
1887
1888        let mut uncovered_fk_cols = Vec::new();
1889        for builtin in BUILTINS_STATIC.iter() {
1890            let desc_storage;
1891            let (name, desc, ontology): (&str, &RelationDesc, Option<&Ontology>) = match builtin {
1892                Builtin::Table(t) => (t.name, &t.desc, t.ontology.as_ref()),
1893                Builtin::View(v) => (v.name, &v.desc, v.ontology.as_ref()),
1894                Builtin::MaterializedView(mv) => (mv.name, &mv.desc, mv.ontology.as_ref()),
1895                Builtin::Source(s) => (s.name, &s.desc, s.ontology.as_ref()),
1896                Builtin::Log(l) => {
1897                    desc_storage = l.variant.desc();
1898                    (l.name, &desc_storage, l.ontology.as_ref())
1899                }
1900                _ => continue,
1901            };
1902            let Some(ont) = ontology else { continue };
1903
1904            // Collect all source_column values declared by existing links.
1905            let linked_cols: BTreeSet<&str> = ont
1906                .links
1907                .iter()
1908                .filter_map(|link| match &link.properties {
1909                    LinkProperties::ForeignKey { source_column, .. } => Some(*source_column),
1910                    LinkProperties::Measures { source_column, .. } => Some(*source_column),
1911                    LinkProperties::DependsOn { source_column, .. } => Some(*source_column),
1912                    LinkProperties::MapsTo { source_column, .. } => Some(*source_column),
1913                    LinkProperties::Union { .. } => None,
1914                })
1915                .collect();
1916
1917            // Skip entities that have no FK-style links yet — they are either
1918            // unannotated or use only union/maps_to links. Only enforce
1919            // coverage on entities that have started FK annotation.
1920            if linked_cols.is_empty() {
1921                continue;
1922            }
1923
1924            // Column indices that are part of the declared key set.
1925            let pk_indices: BTreeSet<usize> = desc.typ().keys.iter().flatten().copied().collect();
1926
1927            for (col_name, sem) in ont.column_semantic_types {
1928                if !reference_sem_types.contains(sem) {
1929                    continue;
1930                }
1931                let Some(idx) = desc.iter_names().position(|n| n.as_str() == *col_name) else {
1932                    continue;
1933                };
1934                // Exempt the entity's own primary identifier: column 0 named
1935                // "id" is by convention the entity's own PK (not a FK), even
1936                // when no explicit with_key() is declared on the relation.
1937                if idx == 0 && *col_name == "id" {
1938                    continue;
1939                }
1940                if pk_indices.contains(&idx) {
1941                    continue;
1942                }
1943                if linked_cols.contains(*col_name) {
1944                    continue;
1945                }
1946                uncovered_fk_cols.push(format!(
1947                    "entity {:?} (builtin {}) column {:?} has semantic type {:?} but no OntologyLink covers it (add a link with source_column: {:?})",
1948                    ont.entity_name, name, col_name, sem, col_name
1949                ));
1950            }
1951        }
1952        assert!(
1953            uncovered_fk_cols.is_empty(),
1954            "ontology entities have FK-typed columns with no OntologyLink:\n{}",
1955            uncovered_fk_cols.join("\n"),
1956        );
1957
1958        // Validate that every source_column in a link actually names a column
1959        // in the entity's RelationDesc. This catches stale annotations after
1960        // column renames or removals. With typed LinkProperties this is mostly
1961        // belt-and-suspenders since the type system enforces field presence, but
1962        // we still need to verify the string value matches a real column.
1963        let mut bad_source_cols = Vec::new();
1964        for builtin in BUILTINS_STATIC.iter() {
1965            let desc_storage;
1966            let (name, desc, ontology): (&str, &RelationDesc, Option<&Ontology>) = match builtin {
1967                Builtin::Table(t) => (t.name, &t.desc, t.ontology.as_ref()),
1968                Builtin::View(v) => (v.name, &v.desc, v.ontology.as_ref()),
1969                Builtin::MaterializedView(mv) => (mv.name, &mv.desc, mv.ontology.as_ref()),
1970                Builtin::Source(s) => (s.name, &s.desc, s.ontology.as_ref()),
1971                Builtin::Log(l) => {
1972                    desc_storage = l.variant.desc();
1973                    (l.name, &desc_storage, l.ontology.as_ref())
1974                }
1975                _ => continue,
1976            };
1977            let Some(ont) = ontology else { continue };
1978
1979            let col_names: BTreeSet<&str> = desc.iter_names().map(|c| c.as_str()).collect();
1980
1981            for link in ont.links {
1982                let source_col = match &link.properties {
1983                    LinkProperties::ForeignKey { source_column, .. } => Some(*source_column),
1984                    LinkProperties::Measures { source_column, .. } => Some(*source_column),
1985                    LinkProperties::DependsOn { source_column, .. } => Some(*source_column),
1986                    LinkProperties::MapsTo { source_column, .. } => Some(*source_column),
1987                    LinkProperties::Union { .. } => None,
1988                };
1989                let Some(col) = source_col else { continue };
1990                if !col_names.contains(col) {
1991                    bad_source_cols.push(format!(
1992                        "entity {:?} (builtin {}) link {:?} references source_column {:?} which does not exist in the relation",
1993                        ont.entity_name, name, link.name, col
1994                    ));
1995                }
1996                let extra_key_columns = match &link.properties {
1997                    LinkProperties::ForeignKey {
1998                        extra_key_columns: Some(extras),
1999                        ..
2000                    } => Some(*extras),
2001                    LinkProperties::Measures {
2002                        extra_key_columns: Some(extras),
2003                        ..
2004                    } => Some(*extras),
2005                    _ => None,
2006                };
2007                if let Some(extras) = extra_key_columns {
2008                    for (src_col, _) in extras {
2009                        if !col_names.contains(*src_col) {
2010                            bad_source_cols.push(format!(
2011                                "entity {:?} (builtin {}) link {:?} extra_key_columns references {:?} which does not exist in the relation",
2012                                ont.entity_name, name, link.name, src_col
2013                            ));
2014                        }
2015                    }
2016                }
2017            }
2018        }
2019        assert!(
2020            bad_source_cols.is_empty(),
2021            "ontology links reference non-existent source_columns:\n{}",
2022            bad_source_cols.join("\n"),
2023        );
2024
2025        // Sanity check: we have a reasonable number of annotated entities.
2026        assert!(
2027            entity_names.len() > 120,
2028            "expected > 120 ontology entities, found {}",
2029            entity_names.len()
2030        );
2031    }
2032
2033    /// Verify that `LinkProperties` serializes to the same JSON that the old
2034    /// hand-written `properties_json` strings contained. One representative
2035    /// case per constructor/variant is enough — the important thing is that
2036    /// field names, enum tag values, and skip-if-None/false behaviour are all
2037    /// correct.
2038    #[mz_ore::test]
2039    fn test_link_properties_serialization() {
2040        let check = |props: LinkProperties, expected: &str| {
2041            let got = serde_json::to_string(&props).expect("serialize");
2042            let got_val: serde_json::Value = serde_json::from_str(&got).expect("parse got");
2043            let exp_val: serde_json::Value =
2044                serde_json::from_str(expected).expect("parse expected");
2045            assert_eq!(got_val, exp_val, "mismatch for {expected}");
2046        };
2047
2048        // fk — basic, no optional fields
2049        check(
2050            LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
2051            r#"{"kind":"foreign_key","source_column":"owner_id","target_column":"id","cardinality":"many_to_one"}"#,
2052        );
2053        // fk_composite — extra_key_columns present
2054        check(
2055            LinkProperties::fk_composite(
2056                "operator_id",
2057                "id",
2058                Cardinality::ManyToOne,
2059                &[("worker_id", "worker_id")],
2060            ),
2061            r#"{"kind":"foreign_key","source_column":"operator_id","target_column":"id","cardinality":"many_to_one","extra_key_columns":[["worker_id","worker_id"]]}"#,
2062        );
2063        // fk — one_to_one cardinality
2064        check(
2065            LinkProperties::fk("id", "id", Cardinality::OneToOne),
2066            r#"{"kind":"foreign_key","source_column":"id","target_column":"id","cardinality":"one_to_one"}"#,
2067        );
2068        // fk_nullable — nullable field present and true
2069        check(
2070            LinkProperties::fk_nullable("database_id", "id", Cardinality::ManyToOne),
2071            r#"{"kind":"foreign_key","source_column":"database_id","target_column":"id","cardinality":"many_to_one","nullable":true}"#,
2072        );
2073        // fk_typed — source_id_type present, requires_mapping absent
2074        check(
2075            LinkProperties::fk_typed(
2076                "replica_id",
2077                "id",
2078                Cardinality::ManyToOne,
2079                mz_repr::SemanticType::CatalogItemId,
2080            ),
2081            r#"{"kind":"foreign_key","source_column":"replica_id","target_column":"id","cardinality":"many_to_one","source_id_type":"CatalogItemId"}"#,
2082        );
2083        // fk_mapped — source_id_type + requires_mapping both present
2084        check(
2085            LinkProperties::fk_mapped(
2086                "object_id",
2087                "id",
2088                Cardinality::ManyToOne,
2089                mz_repr::SemanticType::GlobalId,
2090                "mz_internal.mz_object_global_ids",
2091            ),
2092            r#"{"kind":"foreign_key","source_column":"object_id","target_column":"id","cardinality":"many_to_one","source_id_type":"GlobalId","requires_mapping":"mz_internal.mz_object_global_ids"}"#,
2093        );
2094        // union_disc — discriminator fields present, note absent
2095        check(
2096            LinkProperties::union_disc("type", "table"),
2097            r#"{"kind":"union","discriminator_column":"type","discriminator_value":"table"}"#,
2098        );
2099        // Union — note only, discriminator absent
2100        check(
2101            LinkProperties::Union {
2102                discriminator_column: None,
2103                discriminator_value: None,
2104                note: Some("example note"),
2105            },
2106            r#"{"kind":"union","note":"example note"}"#,
2107        );
2108        // measures — basic
2109        check(
2110            LinkProperties::measures("id", "id", "cpu_time_ns"),
2111            r#"{"kind":"measures","source_column":"id","target_column":"id","metric":"cpu_time_ns"}"#,
2112        );
2113        // measures_composite — extra_key_columns present
2114        check(
2115            LinkProperties::measures_composite(
2116                "export_id",
2117                "export_id",
2118                "time_ns",
2119                &[("worker_id", "worker_id")],
2120            ),
2121            r#"{"kind":"measures","source_column":"export_id","target_column":"export_id","metric":"time_ns","extra_key_columns":[["worker_id","worker_id"]]}"#,
2122        );
2123        // measures_mapped — source_id_type + requires_mapping present
2124        check(
2125            LinkProperties::measures_mapped(
2126                "object_id",
2127                "id",
2128                "wallclock_lag",
2129                mz_repr::SemanticType::GlobalId,
2130                "mz_internal.mz_object_global_ids",
2131            ),
2132            r#"{"kind":"measures","source_column":"object_id","target_column":"id","metric":"wallclock_lag","source_id_type":"GlobalId","requires_mapping":"mz_internal.mz_object_global_ids"}"#,
2133        );
2134        // DependsOn — with mapping
2135        check(
2136            LinkProperties::DependsOn {
2137                source_column: "object_id",
2138                target_column: "id",
2139                source_id_type: Some(mz_repr::SemanticType::GlobalId),
2140                requires_mapping: Some("mz_internal.mz_object_global_ids"),
2141            },
2142            r#"{"kind":"depends_on","source_column":"object_id","target_column":"id","source_id_type":"GlobalId","requires_mapping":"mz_internal.mz_object_global_ids"}"#,
2143        );
2144        // DependsOn — direct (CatalogItemId, no mapping)
2145        check(
2146            LinkProperties::DependsOn {
2147                source_column: "object_id",
2148                target_column: "id",
2149                source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
2150                requires_mapping: None,
2151            },
2152            r#"{"kind":"depends_on","source_column":"object_id","target_column":"id","source_id_type":"CatalogItemId"}"#,
2153        );
2154        // MapsTo — via + from_type + to_type
2155        check(
2156            LinkProperties::MapsTo {
2157                source_column: "id",
2158                target_column: "global_id",
2159                via: Some("mz_internal.mz_object_global_ids"),
2160                from_type: Some(mz_repr::SemanticType::CatalogItemId),
2161                to_type: Some(mz_repr::SemanticType::GlobalId),
2162                note: None,
2163            },
2164            r#"{"kind":"maps_to","source_column":"id","target_column":"global_id","via":"mz_internal.mz_object_global_ids","from_type":"CatalogItemId","to_type":"GlobalId"}"#,
2165        );
2166    }
2167
2168    /// Verifies that the `mz_sources` materialized view fingerprint changes
2169    /// whenever a new builtin source or log is added.
2170    ///
2171    /// This is the correctness property that `make_mz_sources` provides: by
2172    /// inlining the full set of builtin sources/logs as VALUES in its SQL, any
2173    /// change to those sets is reflected in `fingerprint()`. A stale fingerprint
2174    /// would prevent the catalog migration from replacing `mz_sources`, leaving
2175    /// it with out-of-date data, silently serving stale builtin source rows.
2176    #[mz_ore::test]
2177    #[cfg_attr(miri, ignore)]
2178    fn test_mz_sources_fingerprint_changes_with_new_builtin_source() {
2179        let sources: Vec<&'static BuiltinSource> = BUILTINS_STATIC
2180            .iter()
2181            .filter_map(|b| match b {
2182                Builtin::Source(x) => Some(*x),
2183                _ => None,
2184            })
2185            .collect();
2186        let logs: Vec<&'static BuiltinLog> = BUILTINS_STATIC
2187            .iter()
2188            .filter_map(|b| match b {
2189                Builtin::Log(x) => Some(*x),
2190                _ => None,
2191            })
2192            .collect();
2193
2194        // The fingerprint from make_mz_sources must match the live BUILTINS_STATIC entry.
2195        let mv_base = builtin::make_mz_sources(sources.iter().copied(), logs.iter().copied());
2196        let fp_base = Fingerprint::fingerprint(&&mv_base);
2197
2198        let mz_sources_static = BUILTINS_STATIC
2199            .iter()
2200            .find_map(|b| match b {
2201                Builtin::MaterializedView(mv) if mv.name == "mz_sources" => Some(*mv),
2202                _ => None,
2203            })
2204            .expect("mz_sources must be present in BUILTINS_STATIC");
2205        assert_eq!(
2206            fp_base,
2207            Fingerprint::fingerprint(&mz_sources_static),
2208            "make_mz_sources fingerprint must match the BUILTINS_STATIC mz_sources fingerprint"
2209        );
2210
2211        // Adding an extra source must change the fingerprint, proving that
2212        // make_mz_sources inlines the source list into its SQL.
2213        let extra_source = sources[0];
2214        let mv_extra = builtin::make_mz_sources(
2215            sources.iter().copied().chain(std::iter::once(extra_source)),
2216            logs.iter().copied(),
2217        );
2218        assert_ne!(
2219            fp_base,
2220            Fingerprint::fingerprint(&&mv_extra),
2221            "mz_sources fingerprint must change when a builtin source is added"
2222        );
2223    }
2224
2225    /// Verifies that the `mz_indexes` materialized view fingerprint changes
2226    /// whenever a new builtin index or log is added.
2227    ///
2228    /// This is the correctness property that `make_mz_indexes` provides: by
2229    /// inlining the full set of builtin indexes/logs as VALUES in its SQL,
2230    /// any change to those sets is reflected in `fingerprint()`. A stale
2231    /// fingerprint would prevent the catalog migration from replacing
2232    /// `mz_indexes`, leaving it with out-of-date data, silently serving
2233    /// stale builtin index rows.
2234    #[mz_ore::test]
2235    #[cfg_attr(miri, ignore)]
2236    fn test_mz_indexes_fingerprint_changes_with_new_builtin_index() {
2237        let indexes: Vec<&'static BuiltinIndex> = BUILTINS_STATIC
2238            .iter()
2239            .filter_map(|b| match b {
2240                Builtin::Index(x) => Some(*x),
2241                _ => None,
2242            })
2243            .collect();
2244        let logs: Vec<&'static BuiltinLog> = BUILTINS_STATIC
2245            .iter()
2246            .filter_map(|b| match b {
2247                Builtin::Log(x) => Some(*x),
2248                _ => None,
2249            })
2250            .collect();
2251
2252        // The fingerprint from make_mz_indexes must match the live BUILTINS_STATIC entry.
2253        let mv_base = mz_catalog::make_mz_indexes(indexes.iter().copied(), logs.iter().copied());
2254        let fp_base = Fingerprint::fingerprint(&&mv_base);
2255
2256        let mz_indexes_static = BUILTINS_STATIC
2257            .iter()
2258            .find_map(|b| match b {
2259                Builtin::MaterializedView(mv) if mv.name == "mz_indexes" => Some(*mv),
2260                _ => None,
2261            })
2262            .expect("mz_indexes must be present in BUILTINS_STATIC");
2263        assert_eq!(
2264            fp_base,
2265            Fingerprint::fingerprint(&mz_indexes_static),
2266            "make_mz_indexes fingerprint must match the BUILTINS_STATIC mz_indexes fingerprint"
2267        );
2268
2269        // Adding an extra index must change the fingerprint, proving that
2270        // make_mz_indexes inlines the index list into its SQL.
2271        let extra_index = indexes[0];
2272        let mv_extra_index = mz_catalog::make_mz_indexes(
2273            indexes.iter().copied().chain(std::iter::once(extra_index)),
2274            logs.iter().copied(),
2275        );
2276        assert_ne!(
2277            fp_base,
2278            Fingerprint::fingerprint(&&mv_extra_index),
2279            "mz_indexes fingerprint must change when a builtin index is added"
2280        );
2281
2282        // Adding an extra log must also change the fingerprint, because the
2283        // log set feeds the introspection-source-indexes CTE.
2284        let extra_log = logs[0];
2285        let mv_extra_log = mz_catalog::make_mz_indexes(
2286            indexes.iter().copied(),
2287            logs.iter().copied().chain(std::iter::once(extra_log)),
2288        );
2289        assert_ne!(
2290            fp_base,
2291            Fingerprint::fingerprint(&&mv_extra_log),
2292            "mz_indexes fingerprint must change when a builtin log is added"
2293        );
2294    }
2295}