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