Skip to main content

mz_catalog/builtin/
mz_catalog.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 for the `mz_catalog` schema.
11
12use std::collections::BTreeMap;
13use std::sync::LazyLock;
14
15use itertools::Itertools;
16use mz_ore::collections::CollectionExt;
17use mz_pgrepr::oid;
18use mz_repr::namespaces::MZ_CATALOG_SCHEMA;
19use mz_repr::{RelationDesc, SemanticType, SqlScalarType};
20use mz_sql::ast::Statement;
21use mz_sql::ast::display::{AstDisplay, escaped_string_literal};
22use mz_sql::catalog::{
23    CatalogType, CatalogTypeDetails, CatalogTypePgMetadata, NameReference, ObjectType,
24};
25use mz_sql::rbac;
26use mz_sql::session::user::{MZ_SYSTEM_ROLE_ID, SUPPORT_USER_NAME, SYSTEM_USER_NAME};
27use mz_storage_client::controller::IntrospectionType;
28
29use super::{
30    BuiltinIndex, BuiltinLog, BuiltinMaterializedView, BuiltinSource, BuiltinTable, BuiltinType,
31    BuiltinView, Cardinality, LinkProperties, Ontology, OntologyLink, PUBLIC_SELECT,
32};
33
34pub const TYPE_LIST: BuiltinType<NameReference> = BuiltinType {
35    name: "list",
36    schema: MZ_CATALOG_SCHEMA,
37    oid: mz_pgrepr::oid::TYPE_LIST_OID,
38    details: CatalogTypeDetails {
39        typ: CatalogType::Pseudo,
40        array_id: None,
41        pg_metadata: None,
42    },
43};
44
45pub const TYPE_MAP: BuiltinType<NameReference> = BuiltinType {
46    name: "map",
47    schema: MZ_CATALOG_SCHEMA,
48    oid: mz_pgrepr::oid::TYPE_MAP_OID,
49    details: CatalogTypeDetails {
50        typ: CatalogType::Pseudo,
51        array_id: None,
52        pg_metadata: None,
53    },
54};
55
56pub const TYPE_ANYCOMPATIBLELIST: BuiltinType<NameReference> = BuiltinType {
57    name: "anycompatiblelist",
58    schema: MZ_CATALOG_SCHEMA,
59    oid: mz_pgrepr::oid::TYPE_ANYCOMPATIBLELIST_OID,
60    details: CatalogTypeDetails {
61        typ: CatalogType::Pseudo,
62        array_id: None,
63        pg_metadata: None,
64    },
65};
66
67pub const TYPE_ANYCOMPATIBLEMAP: BuiltinType<NameReference> = BuiltinType {
68    name: "anycompatiblemap",
69    schema: MZ_CATALOG_SCHEMA,
70    oid: mz_pgrepr::oid::TYPE_ANYCOMPATIBLEMAP_OID,
71    details: CatalogTypeDetails {
72        typ: CatalogType::Pseudo,
73        array_id: None,
74        pg_metadata: None,
75    },
76};
77
78pub const TYPE_UINT2: BuiltinType<NameReference> = BuiltinType {
79    name: "uint2",
80    schema: MZ_CATALOG_SCHEMA,
81    oid: mz_pgrepr::oid::TYPE_UINT2_OID,
82    details: CatalogTypeDetails {
83        typ: CatalogType::UInt16,
84        array_id: None,
85        pg_metadata: None,
86    },
87};
88
89pub const TYPE_UINT2_ARRAY: BuiltinType<NameReference> = BuiltinType {
90    name: "_uint2",
91    schema: MZ_CATALOG_SCHEMA,
92    oid: mz_pgrepr::oid::TYPE_UINT2_ARRAY_OID,
93    details: CatalogTypeDetails {
94        typ: CatalogType::Array {
95            element_reference: TYPE_UINT2.name,
96        },
97        array_id: None,
98        pg_metadata: None,
99    },
100};
101
102pub const TYPE_UINT4: BuiltinType<NameReference> = BuiltinType {
103    name: "uint4",
104    schema: MZ_CATALOG_SCHEMA,
105    oid: mz_pgrepr::oid::TYPE_UINT4_OID,
106    details: CatalogTypeDetails {
107        typ: CatalogType::UInt32,
108        array_id: None,
109        pg_metadata: None,
110    },
111};
112
113pub const TYPE_UINT4_ARRAY: BuiltinType<NameReference> = BuiltinType {
114    name: "_uint4",
115    schema: MZ_CATALOG_SCHEMA,
116    oid: mz_pgrepr::oid::TYPE_UINT4_ARRAY_OID,
117    details: CatalogTypeDetails {
118        typ: CatalogType::Array {
119            element_reference: TYPE_UINT4.name,
120        },
121        array_id: None,
122        pg_metadata: None,
123    },
124};
125
126pub const TYPE_UINT8: BuiltinType<NameReference> = BuiltinType {
127    name: "uint8",
128    schema: MZ_CATALOG_SCHEMA,
129    oid: mz_pgrepr::oid::TYPE_UINT8_OID,
130    details: CatalogTypeDetails {
131        typ: CatalogType::UInt64,
132        array_id: None,
133        pg_metadata: None,
134    },
135};
136
137pub const TYPE_UINT8_ARRAY: BuiltinType<NameReference> = BuiltinType {
138    name: "_uint8",
139    schema: MZ_CATALOG_SCHEMA,
140    oid: mz_pgrepr::oid::TYPE_UINT8_ARRAY_OID,
141    details: CatalogTypeDetails {
142        typ: CatalogType::Array {
143            element_reference: TYPE_UINT8.name,
144        },
145        array_id: None,
146        pg_metadata: None,
147    },
148};
149
150pub const TYPE_MZ_TIMESTAMP: BuiltinType<NameReference> = BuiltinType {
151    name: "mz_timestamp",
152    schema: MZ_CATALOG_SCHEMA,
153    oid: mz_pgrepr::oid::TYPE_MZ_TIMESTAMP_OID,
154    details: CatalogTypeDetails {
155        typ: CatalogType::MzTimestamp,
156        array_id: None,
157        pg_metadata: None,
158    },
159};
160
161pub const TYPE_MZ_TIMESTAMP_ARRAY: BuiltinType<NameReference> = BuiltinType {
162    name: "_mz_timestamp",
163    schema: MZ_CATALOG_SCHEMA,
164    oid: mz_pgrepr::oid::TYPE_MZ_TIMESTAMP_ARRAY_OID,
165    details: CatalogTypeDetails {
166        typ: CatalogType::Array {
167            element_reference: TYPE_MZ_TIMESTAMP.name,
168        },
169        array_id: None,
170        pg_metadata: None,
171    },
172};
173
174pub const TYPE_MZ_ACL_ITEM: BuiltinType<NameReference> = BuiltinType {
175    name: "mz_aclitem",
176    schema: MZ_CATALOG_SCHEMA,
177    oid: mz_pgrepr::oid::TYPE_MZ_ACL_ITEM_OID,
178    details: CatalogTypeDetails {
179        typ: CatalogType::MzAclItem,
180        array_id: None,
181        pg_metadata: None,
182    },
183};
184
185pub const TYPE_MZ_ACL_ITEM_ARRAY: BuiltinType<NameReference> = BuiltinType {
186    name: "_mz_aclitem",
187    schema: MZ_CATALOG_SCHEMA,
188    oid: mz_pgrepr::oid::TYPE_MZ_ACL_ITEM_ARRAY_OID,
189    details: CatalogTypeDetails {
190        typ: CatalogType::Array {
191            element_reference: TYPE_MZ_ACL_ITEM.name,
192        },
193        array_id: None,
194        pg_metadata: Some(CatalogTypePgMetadata {
195            typinput_oid: 750,
196            typreceive_oid: 2400,
197        }),
198    },
199};
200
201pub static MZ_ICEBERG_SINKS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
202    name: "mz_iceberg_sinks",
203    schema: MZ_CATALOG_SCHEMA,
204    oid: oid::TABLE_MZ_ICEBERG_SINKS_OID,
205    desc: RelationDesc::builder()
206        .with_column("id", SqlScalarType::String.nullable(false))
207        .with_column("namespace", SqlScalarType::String.nullable(false))
208        .with_column("table", SqlScalarType::String.nullable(false))
209        .finish(),
210    column_comments: BTreeMap::from_iter([
211        ("id", "The ID of the sink."),
212        (
213            "namespace",
214            "The namespace of the Iceberg table into which the sink is writing.",
215        ),
216        ("table", "The Iceberg table into which the sink is writing."),
217    ]),
218    is_retained_metrics_object: false,
219    access: vec![PUBLIC_SELECT],
220    ontology: Some(Ontology {
221        entity_name: "iceberg_sink",
222        description: "Iceberg-specific sink configuration (namespace, table)",
223        links: &const {
224            [OntologyLink {
225                name: "details_of",
226                target: "sink",
227                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
228            }]
229        },
230        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
231    }),
232});
233
234pub static MZ_KAFKA_SINKS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
235    name: "mz_kafka_sinks",
236    schema: MZ_CATALOG_SCHEMA,
237    oid: oid::TABLE_MZ_KAFKA_SINKS_OID,
238    desc: RelationDesc::builder()
239        .with_column("id", SqlScalarType::String.nullable(false))
240        .with_column("topic", SqlScalarType::String.nullable(false))
241        .with_key(vec![0])
242        .finish(),
243    column_comments: BTreeMap::from_iter([
244        ("id", "The ID of the sink."),
245        (
246            "topic",
247            "The name of the Kafka topic into which the sink is writing.",
248        ),
249    ]),
250    is_retained_metrics_object: false,
251    access: vec![PUBLIC_SELECT],
252    ontology: Some(Ontology {
253        entity_name: "kafka_sink",
254        description: "Kafka-specific sink configuration (topic)",
255        links: &const {
256            [OntologyLink {
257                name: "details_of",
258                target: "sink",
259                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
260            }]
261        },
262        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
263    }),
264});
265pub static MZ_KAFKA_CONNECTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
266    name: "mz_kafka_connections",
267    schema: MZ_CATALOG_SCHEMA,
268    oid: oid::TABLE_MZ_KAFKA_CONNECTIONS_OID,
269    desc: RelationDesc::builder()
270        .with_column("id", SqlScalarType::String.nullable(false))
271        .with_column(
272            "brokers",
273            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
274        )
275        .with_column("sink_progress_topic", SqlScalarType::String.nullable(false))
276        .finish(),
277    column_comments: BTreeMap::from_iter([
278        ("id", "The ID of the connection."),
279        (
280            "brokers",
281            "The addresses of the Kafka brokers to connect to.",
282        ),
283        (
284            "sink_progress_topic",
285            "The name of the Kafka topic where any sinks associated with this connection will track their progress information and other metadata. The contents of this topic are unspecified.",
286        ),
287    ]),
288    is_retained_metrics_object: false,
289    access: vec![PUBLIC_SELECT],
290    ontology: Some(Ontology {
291        entity_name: "kafka_connection",
292        description: "Kafka-specific connection configuration (brokers, progress topic)",
293        links: &const {
294            [OntologyLink {
295                name: "details_of",
296                target: "connection",
297                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
298            }]
299        },
300        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
301    }),
302});
303pub static MZ_KAFKA_SOURCES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
304    name: "mz_kafka_sources",
305    schema: MZ_CATALOG_SCHEMA,
306    oid: oid::TABLE_MZ_KAFKA_SOURCES_OID,
307    desc: RelationDesc::builder()
308        .with_column("id", SqlScalarType::String.nullable(false))
309        .with_column("group_id_prefix", SqlScalarType::String.nullable(false))
310        .with_column("topic", SqlScalarType::String.nullable(false))
311        .finish(),
312    column_comments: BTreeMap::from_iter([
313        (
314            "id",
315            "The ID of the Kafka source. Corresponds to `mz_catalog.mz_sources.id`.",
316        ),
317        (
318            "group_id_prefix",
319            "The value of the `GROUP ID PREFIX` connection option.",
320        ),
321        (
322            "topic",
323            "The name of the Kafka topic the source is reading from.",
324        ),
325    ]),
326    is_retained_metrics_object: false,
327    access: vec![PUBLIC_SELECT],
328    ontology: Some(Ontology {
329        entity_name: "kafka_source",
330        description: "Kafka-specific source configuration (topic, group ID)",
331        links: &const {
332            [OntologyLink {
333                name: "details_of",
334                target: "source",
335                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
336            }]
337        },
338        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
339    }),
340});
341
342pub static MZ_DATABASES: LazyLock<BuiltinMaterializedView> =
343    LazyLock::new(|| BuiltinMaterializedView {
344        name: "mz_databases",
345        schema: MZ_CATALOG_SCHEMA,
346        oid: oid::MV_MZ_DATABASES_OID,
347        desc: RelationDesc::builder()
348            .with_column("id", SqlScalarType::String.nullable(false))
349            .with_column("oid", SqlScalarType::Oid.nullable(false))
350            .with_column("name", SqlScalarType::String.nullable(false))
351            .with_column("owner_id", SqlScalarType::String.nullable(false))
352            .with_column(
353                "privileges",
354                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
355            )
356            .with_key(vec![0])
357            .with_key(vec![1])
358            .finish(),
359        column_comments: BTreeMap::from_iter([
360            ("id", "Materialize's unique ID for the database."),
361            ("oid", "A PostgreSQL-compatible OID for the database."),
362            ("name", "The name of the database."),
363            (
364                "owner_id",
365                "The role ID of the owner of the database. Corresponds to `mz_roles.id`.",
366            ),
367            ("privileges", "The privileges belonging to the database."),
368        ]),
369        sql: "
370IN CLUSTER mz_catalog_server
371WITH (
372    ASSERT NOT NULL id,
373    ASSERT NOT NULL oid,
374    ASSERT NOT NULL name,
375    ASSERT NOT NULL owner_id,
376    ASSERT NOT NULL privileges
377) AS
378SELECT
379    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
380    (data->'value'->>'oid')::oid AS oid,
381    data->'value'->>'name' AS name,
382    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
383    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges
384FROM mz_internal.mz_catalog_raw
385WHERE data->>'kind' = 'Database'",
386        is_retained_metrics_object: false,
387        access: vec![PUBLIC_SELECT],
388        ontology: Some(Ontology {
389            entity_name: "database",
390            description: "A top-level namespace that contains schemas",
391            links: &const {
392                [OntologyLink {
393                    name: "owned_by",
394                    target: "role",
395                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
396                }]
397            },
398            column_semantic_types: &const {
399                [
400                    ("id", SemanticType::DatabaseId),
401                    ("oid", SemanticType::OID),
402                    ("owner_id", SemanticType::RoleId),
403                ]
404            },
405        }),
406    });
407
408pub static MZ_SCHEMAS: LazyLock<BuiltinMaterializedView> =
409    LazyLock::new(|| BuiltinMaterializedView {
410        name: "mz_schemas",
411        schema: MZ_CATALOG_SCHEMA,
412        oid: oid::MV_MZ_SCHEMAS_OID,
413        desc: RelationDesc::builder()
414            .with_column("id", SqlScalarType::String.nullable(false))
415            .with_column("oid", SqlScalarType::Oid.nullable(false))
416            .with_column("database_id", SqlScalarType::String.nullable(true))
417            .with_column("name", SqlScalarType::String.nullable(false))
418            .with_column("owner_id", SqlScalarType::String.nullable(false))
419            .with_column(
420                "privileges",
421                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
422            )
423            .with_key(vec![0])
424            .with_key(vec![1])
425            .finish(),
426        column_comments: BTreeMap::from_iter([
427            ("id", "Materialize's unique ID for the schema."),
428            ("oid", "A PostgreSQL-compatible oid for the schema."),
429            (
430                "database_id",
431                "The ID of the database containing the schema. Corresponds to `mz_databases.id`.",
432            ),
433            ("name", "The name of the schema."),
434            (
435                "owner_id",
436                "The role ID of the owner of the schema. Corresponds to `mz_roles.id`.",
437            ),
438            ("privileges", "The privileges belonging to the schema."),
439        ]),
440        sql: "
441IN CLUSTER mz_catalog_server
442WITH (
443    ASSERT NOT NULL id,
444    ASSERT NOT NULL oid,
445    ASSERT NOT NULL name,
446    ASSERT NOT NULL owner_id,
447    ASSERT NOT NULL privileges
448) AS
449SELECT
450    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
451    (data->'value'->>'oid')::oid AS oid,
452    CASE WHEN data->'value'->'database_id' != 'null'
453         THEN mz_internal.parse_catalog_id(data->'value'->'database_id')
454    END AS database_id,
455    data->'value'->>'name' AS name,
456    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
457    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges
458FROM mz_internal.mz_catalog_raw
459WHERE data->>'kind' = 'Schema'",
460        is_retained_metrics_object: false,
461        access: vec![PUBLIC_SELECT],
462        ontology: Some(Ontology {
463            entity_name: "schema",
464            description: "A namespace within a database that contains objects",
465            links: &const {
466                [
467                    OntologyLink {
468                        name: "in_database",
469                        target: "database",
470                        properties: LinkProperties::fk_nullable(
471                            "database_id",
472                            "id",
473                            Cardinality::ManyToOne,
474                        ),
475                    },
476                    OntologyLink {
477                        name: "owned_by",
478                        target: "role",
479                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
480                    },
481                ]
482            },
483            column_semantic_types: &const {
484                [
485                    ("id", SemanticType::SchemaId),
486                    ("oid", SemanticType::OID),
487                    ("database_id", SemanticType::DatabaseId),
488                    ("owner_id", SemanticType::RoleId),
489                ]
490            },
491        }),
492    });
493
494pub static MZ_COLUMNS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
495    name: "mz_columns",
496    schema: MZ_CATALOG_SCHEMA,
497    oid: oid::TABLE_MZ_COLUMNS_OID,
498    desc: RelationDesc::builder()
499        .with_column("id", SqlScalarType::String.nullable(false)) // not a key
500        .with_column("name", SqlScalarType::String.nullable(false))
501        .with_column("position", SqlScalarType::UInt64.nullable(false))
502        .with_column("nullable", SqlScalarType::Bool.nullable(false))
503        .with_column("type", SqlScalarType::String.nullable(false))
504        .with_column("default", SqlScalarType::String.nullable(true))
505        .with_column("type_oid", SqlScalarType::Oid.nullable(false))
506        .with_column("type_mod", SqlScalarType::Int32.nullable(false))
507        .finish(),
508    column_comments: BTreeMap::from_iter([
509        (
510            "id",
511            "The unique ID of the table, source, or view containing the column.",
512        ),
513        ("name", "The name of the column."),
514        (
515            "position",
516            "The 1-indexed position of the column in its containing table, source, or view.",
517        ),
518        ("nullable", "Can the column contain a `NULL` value?"),
519        ("type", "The data type of the column."),
520        ("default", "The default expression of the column."),
521        (
522            "type_oid",
523            "The OID of the type of the column (references `mz_types`).",
524        ),
525        ("type_mod", "The packed type identifier of the column."),
526    ]),
527    is_retained_metrics_object: false,
528    access: vec![PUBLIC_SELECT],
529    ontology: Some(Ontology {
530        entity_name: "column",
531        description: "A column of a relation, with its name, position, type, and nullability",
532        links: &const {
533            [OntologyLink {
534                name: "belongs_to_relation",
535                target: "object",
536                properties: LinkProperties::ForeignKey {
537                    source_column: "id",
538                    target_column: "id",
539                    cardinality: Cardinality::ManyToOne,
540                    source_id_type: None,
541                    requires_mapping: None,
542                    nullable: false,
543                    note: Some("id in mz_columns is the relation ID, not a unique column ID"),
544                    extra_key_columns: None,
545                },
546            }]
547        },
548        column_semantic_types: &const {
549            [
550                ("id", SemanticType::CatalogItemId),
551                ("type_oid", SemanticType::OID),
552            ]
553        },
554    }),
555});
556// mz_indexes is generated dynamically in BUILTINS_STATIC via mz_catalog::make_mz_indexes()
557
558/// Asserts that `name` is safe to embed unquoted inside a `'...'`-quoted SQL literal
559/// or inside a `"..."`-quoted SQL identifier. Builtin index/log/object names are
560/// concatenated into SQL fragments below, so a quote or backslash would produce
561/// malformed SQL. Builtin names should always be plain ASCII identifiers.
562fn assert_safe_builtin_name(name: &str, kind: &str) {
563    assert!(
564        !name.contains('\'') && !name.contains('"') && !name.contains('\\'),
565        "builtin {kind} name {name:?} contains an unsupported character; \
566         mz_indexes reconstructs SQL via string concatenation and assumes \
567         names contain no quotes or backslashes"
568    );
569}
570
571/// User-created indexes, sourced from `mz_catalog_raw` `Item` entries with
572/// `parse_catalog_create_sql(...)` yielding `type = 'index'`.
573const USER_INDEXES_CTE: &str = "\
574    user_indexes AS (
575        SELECT
576            mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
577            (data->'value'->>'oid')::oid AS oid,
578            data->'value'->>'name' AS name,
579            parsed->>'on_id' AS on_id,
580            parsed->>'cluster_id' AS cluster_id,
581            mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
582            data->'value'->'definition'->'V1'->>'create_sql' AS create_sql,
583            mz_internal.redact_sql(data->'value'->'definition'->'V1'->>'create_sql') AS redacted_create_sql
584        FROM
585            mz_internal.mz_catalog_raw
586            CROSS JOIN LATERAL (
587                SELECT mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')
588            ) AS l(parsed)
589        WHERE
590            data->>'kind' = 'Item' AND
591            parsed->>'type' = 'index'
592    )";
593
594/// Resolves the catalog id of the `mz_catalog_server` cluster at query time so
595/// the MV doesn't hardcode a system id literal.
596const CATALOG_SERVER_CLUSTER_CTE: &str = "\
597    catalog_server_cluster AS (
598        SELECT mz_internal.parse_catalog_id(data->'key'->'id') AS id
599        FROM mz_internal.mz_catalog_raw
600        WHERE data->>'kind' = 'Cluster' AND data->'value'->>'name' = 'mz_catalog_server'
601    )";
602
603/// Helper CTEs over `GidMapping` rows used to look up the system id of a
604/// builtin index by name and of the relation it indexes by (schema, name).
605const GID_MAPPING_CTES: &str = "\
606    builtin_index_gid_mappings AS (
607        SELECT
608            's' || (data->'value'->>'catalog_id') AS id,
609            data->'key'->>'object_name' AS name
610        FROM mz_internal.mz_catalog_raw
611        WHERE
612            data->>'kind' = 'GidMapping' AND
613            data->'key'->>'object_type' = '6'
614    ),
615    on_gid_mappings AS (
616        SELECT
617            's' || (data->'value'->>'catalog_id') AS id,
618            data->'key'->>'schema_name' AS schema_name,
619            data->'key'->>'object_name' AS object_name
620        FROM mz_internal.mz_catalog_raw
621        WHERE data->>'kind' = 'GidMapping'
622    )";
623
624/// Generate the `mz_catalog.mz_indexes` builtin materialized view with builtin
625/// index entries inlined as VALUES clauses.
626///
627/// Inlining the values means the MV's SQL fingerprint changes whenever a builtin
628/// index or log is added or removed, which forces a `MigrationStep::replacement`
629/// for `mz_indexes` and guarantees stale data is never silently served.
630///
631/// Includes user-created indexes (from `mz_catalog_raw` `Item` entries),
632/// system builtin indexes (from `GidMapping` with object_type=6), and
633/// introspection source indexes (from `IntrospectionSourceIndex` entries).
634pub(super) fn make_mz_indexes(
635    builtin_index_iter: impl Iterator<Item = &'static BuiltinIndex>,
636    builtin_log_iter: impl Iterator<Item = &'static BuiltinLog>,
637) -> BuiltinMaterializedView {
638    let builtin_index_values = builtin_index_iter
639        .map(|index| {
640            assert_safe_builtin_name(index.name, "index");
641            let create_sql_str = index.create_sql();
642            let stmt = mz_sql::parse::parse(&create_sql_str)
643                .unwrap_or_else(|e| panic!("invalid sql for builtin index {}: {e}", index.name))
644                .into_element()
645                .ast;
646            let Statement::CreateIndex(idx_stmt) = stmt else {
647                panic!("expected CreateIndex for builtin index {}", index.name);
648            };
649            let mz_sql::ast::RawItemName::Name(on_name) = idx_stmt.on_name else {
650                panic!("expected Name for on_name in builtin index {}", index.name);
651            };
652            assert_eq!(
653                on_name.0.len(),
654                2,
655                "expected schema.name format for on_name in builtin index {}",
656                index.name
657            );
658            let on_schema = on_name.0[0].as_str();
659            let on_name_str = on_name.0[1].as_str();
660            assert_safe_builtin_name(on_schema, "index `on` schema");
661            assert_safe_builtin_name(on_name_str, "index `on` object");
662            let key_exprs = idx_stmt
663                .key_parts
664                .unwrap_or_else(|| {
665                    panic!("builtin index {} must have explicit key parts", index.name)
666                })
667                .iter()
668                .map(|e| e.to_ast_string_stable())
669                .join(", ");
670            // Unlike the identifier names above, key expressions are arbitrary
671            // SQL (column refs, casts, string literals) that can legitimately
672            // contain single quotes — so escape them rather than asserting
673            // them away with `assert_safe_builtin_name`.
674            let key_exprs_escaped = escaped_string_literal(&key_exprs);
675            format!(
676                "({}::oid, '{}', '{}', '{}', {key_exprs_escaped})",
677                index.oid, index.name, on_schema, on_name_str
678            )
679        })
680        .join(",");
681
682    let log_col_values = builtin_log_iter
683        .map(|log| {
684            assert_safe_builtin_name(log.name, "log");
685            let desc = log.variant.desc();
686            let index_by = log.variant.index_by();
687            let col_list = index_by
688                .iter()
689                .map(|&i| match desc.get_unambiguous_name(i) {
690                    Some(name) => {
691                        assert_safe_builtin_name(name, "log column");
692                        format!("\"{}\"", name)
693                    }
694                    None => (i + 1).to_string(),
695                })
696                .join(", ");
697            format!("('{}', '{}')", log.name, col_list)
698        })
699        .join(",");
700
701    // Reconstructs `CREATE INDEX ... IN CLUSTER [<id>] ON [<id> AS "schema"."name"] (<keys>)`
702    // from the (oid, name, on_schema, on_name, key_exprs) VALUES rows joined to
703    // `GidMapping` lookups and the `mz_catalog_server` cluster id.
704    let builtin_indexes_cte = format!("\
705    builtin_indexes AS (
706        SELECT *, mz_internal.redact_sql(create_sql) AS redacted_create_sql
707        FROM (
708            SELECT
709                bigm.id AS id,
710                biv.oid AS oid,
711                biv.name AS name,
712                om.id AS on_id,
713                csc.id AS cluster_id,
714                '{MZ_SYSTEM_ROLE_ID}' AS owner_id,
715                'CREATE INDEX \"' || biv.name || '\" IN CLUSTER [' || csc.id || '] ON [' || om.id || ' AS \"' || biv.on_schema || '\".\"' || biv.on_name || '\"] (' || biv.key_exprs || ')' AS create_sql
716            FROM (VALUES {builtin_index_values}) AS biv(oid, name, on_schema, on_name, key_exprs)
717            JOIN builtin_index_gid_mappings bigm ON bigm.name = biv.name
718            JOIN on_gid_mappings om ON om.schema_name = biv.on_schema AND om.object_name = biv.on_name
719            CROSS JOIN catalog_server_cluster csc
720        ) AS t
721    )");
722
723    let introspection_source_indexes_cte = format!("\
724    introspection_source_indexes AS (
725        SELECT *, mz_internal.redact_sql(create_sql) AS redacted_create_sql
726        FROM (
727            SELECT
728                'si' || (isi.data->'value'->>'catalog_id') AS id,
729                (isi.data->'value'->>'oid')::oid AS oid,
730                idx_name || '_' || cluster_id || '_primary_idx' AS name,
731                's' || (gm.data->'value'->>'catalog_id') AS on_id,
732                cluster_id,
733                '{MZ_SYSTEM_ROLE_ID}' AS owner_id,
734                'CREATE INDEX \"' || idx_name || '_' || cluster_id || '_primary_idx\" IN CLUSTER [' || cluster_id || '] ON \"mz_introspection\".\"' || idx_name || '\" (' || lc.col_list || ')' AS create_sql
735            FROM mz_internal.mz_catalog_raw AS isi
736            CROSS JOIN LATERAL (
737                SELECT isi.data->'key'->>'name', mz_internal.parse_catalog_id(isi.data->'key'->'cluster_id')
738            ) AS l(idx_name, cluster_id)
739            JOIN mz_internal.mz_catalog_raw AS gm ON
740                gm.data->>'kind' = 'GidMapping' AND
741                gm.data->'key'->>'object_type' = '2' AND
742                gm.data->'key'->>'schema_name' = 'mz_introspection' AND
743                gm.data->'key'->>'object_name' = idx_name
744            JOIN (VALUES {log_col_values}) AS lc(log_name, col_list) ON lc.log_name = idx_name
745            WHERE isi.data->>'kind' = 'ClusterIntrospectionSourceIndex'
746        ) AS t
747    )");
748
749    let sql = format!(
750        "
751IN CLUSTER mz_catalog_server
752WITH (
753    ASSERT NOT NULL id,
754    ASSERT NOT NULL oid,
755    ASSERT NOT NULL name,
756    ASSERT NOT NULL on_id,
757    ASSERT NOT NULL cluster_id,
758    ASSERT NOT NULL owner_id,
759    ASSERT NOT NULL create_sql,
760    ASSERT NOT NULL redacted_create_sql
761) AS
762WITH
763{USER_INDEXES_CTE},
764{CATALOG_SERVER_CLUSTER_CTE},
765{GID_MAPPING_CTES},
766{builtin_indexes_cte},
767{introspection_source_indexes_cte}
768SELECT * FROM user_indexes
769UNION ALL
770SELECT * FROM builtin_indexes
771UNION ALL
772SELECT * FROM introspection_source_indexes
773"
774    );
775
776    BuiltinMaterializedView {
777        name: "mz_indexes",
778        schema: MZ_CATALOG_SCHEMA,
779        oid: oid::MV_MZ_INDEXES_OID,
780        desc: RelationDesc::builder()
781            .with_column("id", SqlScalarType::String.nullable(false))
782            .with_column("oid", SqlScalarType::Oid.nullable(false))
783            .with_column("name", SqlScalarType::String.nullable(false))
784            .with_column("on_id", SqlScalarType::String.nullable(false))
785            .with_column("cluster_id", SqlScalarType::String.nullable(false))
786            .with_column("owner_id", SqlScalarType::String.nullable(false))
787            .with_column("create_sql", SqlScalarType::String.nullable(false))
788            .with_column("redacted_create_sql", SqlScalarType::String.nullable(false))
789            .with_key(vec![0])
790            .with_key(vec![1])
791            .finish(),
792        column_comments: BTreeMap::from_iter([
793            ("id", "Materialize's unique ID for the index."),
794            ("oid", "A PostgreSQL-compatible OID for the index."),
795            ("name", "The name of the index."),
796            (
797                "on_id",
798                "The ID of the relation on which the index is built.",
799            ),
800            (
801                "cluster_id",
802                "The ID of the cluster in which the index is built.",
803            ),
804            (
805                "owner_id",
806                "The role ID of the owner of the index. Corresponds to `mz_roles.id`.",
807            ),
808            ("create_sql", "The `CREATE` SQL statement for the index."),
809            (
810                "redacted_create_sql",
811                "The redacted `CREATE` SQL statement for the index.",
812            ),
813        ]),
814        sql: Box::leak(sql.into_boxed_str()),
815        is_retained_metrics_object: false,
816        access: vec![PUBLIC_SELECT],
817        ontology: Some(Ontology {
818            entity_name: "index",
819            description: "An in-memory index on a relation for fast lookups",
820            links: &const {
821                [
822                    OntologyLink {
823                        name: "owned_by",
824                        target: "role",
825                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
826                    },
827                    OntologyLink {
828                        name: "runs_on_cluster",
829                        target: "cluster",
830                        properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
831                    },
832                    OntologyLink {
833                        name: "indexes_relation",
834                        target: "relation",
835                        properties: LinkProperties::fk("on_id", "id", Cardinality::ManyToOne),
836                    },
837                ]
838            },
839            column_semantic_types: &const {
840                [
841                    ("id", SemanticType::CatalogItemId),
842                    ("oid", SemanticType::OID),
843                    ("on_id", SemanticType::CatalogItemId),
844                    ("cluster_id", SemanticType::ClusterId),
845                    ("owner_id", SemanticType::RoleId),
846                    ("create_sql", SemanticType::SqlDefinition),
847                    ("redacted_create_sql", SemanticType::RedactedSqlDefinition),
848                ]
849            },
850        }),
851    }
852}
853pub static MZ_INDEX_COLUMNS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
854    name: "mz_index_columns",
855    schema: MZ_CATALOG_SCHEMA,
856    oid: oid::TABLE_MZ_INDEX_COLUMNS_OID,
857    desc: RelationDesc::builder()
858        .with_column("index_id", SqlScalarType::String.nullable(false))
859        .with_column("index_position", SqlScalarType::UInt64.nullable(false))
860        .with_column("on_position", SqlScalarType::UInt64.nullable(true))
861        .with_column("on_expression", SqlScalarType::String.nullable(true))
862        .with_column("nullable", SqlScalarType::Bool.nullable(false))
863        .finish(),
864    column_comments: BTreeMap::from_iter([
865        (
866            "index_id",
867            "The ID of the index which contains this column. Corresponds to `mz_indexes.id`.",
868        ),
869        (
870            "index_position",
871            "The 1-indexed position of this column within the index. (The order of columns in an index does not necessarily match the order of columns in the relation on which the index is built.)",
872        ),
873        (
874            "on_position",
875            "If not `NULL`, specifies the 1-indexed position of a column in the relation on which this index is built that determines the value of this index column.",
876        ),
877        (
878            "on_expression",
879            "If not `NULL`, specifies a SQL expression that is evaluated to compute the value of this index column. The expression may contain references to any of the columns of the relation.",
880        ),
881        (
882            "nullable",
883            "Can this column of the index evaluate to `NULL`?",
884        ),
885    ]),
886    is_retained_metrics_object: false,
887    access: vec![PUBLIC_SELECT],
888    ontology: Some(Ontology {
889        entity_name: "index_column",
890        description: "A column or expression in an index, with its position",
891        links: &const {
892            [OntologyLink {
893                name: "belongs_to_index",
894                target: "index",
895                properties: LinkProperties::fk("index_id", "id", Cardinality::ManyToOne),
896            }]
897        },
898        column_semantic_types: &[("index_id", SemanticType::CatalogItemId)],
899    }),
900});
901pub static MZ_TABLES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
902    name: "mz_tables",
903    schema: MZ_CATALOG_SCHEMA,
904    oid: oid::TABLE_MZ_TABLES_OID,
905    desc: RelationDesc::builder()
906        .with_column("id", SqlScalarType::String.nullable(false))
907        .with_column("oid", SqlScalarType::Oid.nullable(false))
908        .with_column("schema_id", SqlScalarType::String.nullable(false))
909        .with_column("name", SqlScalarType::String.nullable(false))
910        .with_column("owner_id", SqlScalarType::String.nullable(false))
911        .with_column(
912            "privileges",
913            SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
914        )
915        .with_column("create_sql", SqlScalarType::String.nullable(true))
916        .with_column("redacted_create_sql", SqlScalarType::String.nullable(true))
917        .with_column("source_id", SqlScalarType::String.nullable(true))
918        .with_key(vec![0])
919        .with_key(vec![1])
920        .finish(),
921    column_comments: BTreeMap::from_iter([
922        ("id", "Materialize's unique ID for the table."),
923        ("oid", "A PostgreSQL-compatible OID for the table."),
924        (
925            "schema_id",
926            "The ID of the schema to which the table belongs. Corresponds to `mz_schemas.id`.",
927        ),
928        ("name", "The name of the table."),
929        (
930            "owner_id",
931            "The role ID of the owner of the table. Corresponds to `mz_roles.id`.",
932        ),
933        ("privileges", "The privileges belonging to the table."),
934        ("create_sql", "The `CREATE` SQL statement for the table."),
935        (
936            "redacted_create_sql",
937            "The redacted `CREATE` SQL statement for the table.",
938        ),
939        (
940            "source_id",
941            "The ID of the source associated with the table, if any. Corresponds to `mz_sources.id`.",
942        ),
943    ]),
944    is_retained_metrics_object: true,
945    access: vec![PUBLIC_SELECT],
946    ontology: Some(Ontology {
947        entity_name: "table",
948        description: "A user-writable table that can be inserted into and updated",
949        links: &const {
950            [
951                OntologyLink {
952                    name: "in_schema",
953                    target: "schema",
954                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
955                },
956                OntologyLink {
957                    name: "owned_by",
958                    target: "role",
959                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
960                },
961                OntologyLink {
962                    name: "created_by_source",
963                    target: "source",
964                    properties: LinkProperties::fk_nullable(
965                        "source_id",
966                        "id",
967                        Cardinality::ManyToOne,
968                    ),
969                },
970            ]
971        },
972        column_semantic_types: &const {
973            [
974                ("id", SemanticType::CatalogItemId),
975                ("oid", SemanticType::OID),
976                ("schema_id", SemanticType::SchemaId),
977                ("owner_id", SemanticType::RoleId),
978                ("create_sql", SemanticType::SqlDefinition),
979                ("redacted_create_sql", SemanticType::RedactedSqlDefinition),
980                ("source_id", SemanticType::CatalogItemId),
981            ]
982        },
983    }),
984});
985
986pub static MZ_CONNECTIONS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
987    BuiltinMaterializedView {
988        name: "mz_connections",
989        schema: MZ_CATALOG_SCHEMA,
990        oid: oid::MV_MZ_CONNECTIONS_OID,
991        desc: RelationDesc::builder()
992            .with_column("id", SqlScalarType::String.nullable(false))
993            .with_column("oid", SqlScalarType::Oid.nullable(false))
994            .with_column("schema_id", SqlScalarType::String.nullable(false))
995            .with_column("name", SqlScalarType::String.nullable(false))
996            .with_column("type", SqlScalarType::String.nullable(false))
997            .with_column("owner_id", SqlScalarType::String.nullable(false))
998            .with_column(
999                "privileges",
1000                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
1001            )
1002            .with_column("create_sql", SqlScalarType::String.nullable(false))
1003            .with_column("redacted_create_sql", SqlScalarType::String.nullable(false))
1004            .with_key(vec![0])
1005            .with_key(vec![1])
1006            .finish(),
1007        column_comments: BTreeMap::from_iter([
1008            ("id", "The unique ID of the connection."),
1009            ("oid", "A PostgreSQL-compatible OID for the connection."),
1010            (
1011                "schema_id",
1012                "The ID of the schema to which the connection belongs. Corresponds to `mz_schemas.id`.",
1013            ),
1014            ("name", "The name of the connection."),
1015            (
1016                "type",
1017                "The type of the connection: `confluent-schema-registry`, `kafka`, `postgres`, or `ssh-tunnel`.",
1018            ),
1019            (
1020                "owner_id",
1021                "The role ID of the owner of the connection. Corresponds to `mz_roles.id`.",
1022            ),
1023            ("privileges", "The privileges belonging to the connection."),
1024            (
1025                "create_sql",
1026                "The `CREATE` SQL statement for the connection.",
1027            ),
1028            (
1029                "redacted_create_sql",
1030                "The redacted `CREATE` SQL statement for the connection.",
1031            ),
1032        ]),
1033        sql: "
1034IN CLUSTER mz_catalog_server
1035WITH (
1036    ASSERT NOT NULL id,
1037    ASSERT NOT NULL oid,
1038    ASSERT NOT NULL schema_id,
1039    ASSERT NOT NULL name,
1040    ASSERT NOT NULL type,
1041    ASSERT NOT NULL owner_id,
1042    ASSERT NOT NULL privileges,
1043    ASSERT NOT NULL create_sql,
1044    ASSERT NOT NULL redacted_create_sql
1045) AS
1046SELECT
1047    mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
1048    (data->'value'->>'oid')::oid AS oid,
1049    mz_internal.parse_catalog_id(data->'value'->'schema_id') AS schema_id,
1050    data->'value'->>'name' AS name,
1051    mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'connection_type' AS type,
1052    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
1053    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges,
1054    data->'value'->'definition'->'V1'->>'create_sql' AS create_sql,
1055    mz_internal.redact_sql(data->'value'->'definition'->'V1'->>'create_sql') AS redacted_create_sql
1056FROM mz_internal.mz_catalog_raw
1057WHERE
1058    data->>'kind' = 'Item' AND
1059    mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'type' = 'connection'",
1060        is_retained_metrics_object: false,
1061        access: vec![PUBLIC_SELECT],
1062        ontology: Some(Ontology {
1063            entity_name: "connection",
1064            description: "A reusable connection configuration to an external system",
1065            links: &const { [
1066                OntologyLink {
1067                    name: "in_schema",
1068                    target: "schema",
1069                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
1070                },
1071                OntologyLink {
1072                    name: "owned_by",
1073                    target: "role",
1074                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
1075                },
1076            ] },
1077            column_semantic_types: &const {[("id", SemanticType::CatalogItemId), ("oid", SemanticType::OID), ("schema_id", SemanticType::SchemaId), ("type", SemanticType::ConnectionType), ("owner_id", SemanticType::RoleId), ("create_sql", SemanticType::SqlDefinition), ("redacted_create_sql", SemanticType::RedactedSqlDefinition)]},
1078        }),
1079    }
1080});
1081
1082pub static MZ_SSH_TUNNEL_CONNECTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1083    name: "mz_ssh_tunnel_connections",
1084    schema: MZ_CATALOG_SCHEMA,
1085    oid: oid::TABLE_MZ_SSH_TUNNEL_CONNECTIONS_OID,
1086    desc: RelationDesc::builder()
1087        .with_column("id", SqlScalarType::String.nullable(false))
1088        .with_column("public_key_1", SqlScalarType::String.nullable(false))
1089        .with_column("public_key_2", SqlScalarType::String.nullable(false))
1090        .finish(),
1091    column_comments: BTreeMap::from_iter([
1092        ("id", "The ID of the connection."),
1093        (
1094            "public_key_1",
1095            "The first public key associated with the SSH tunnel.",
1096        ),
1097        (
1098            "public_key_2",
1099            "The second public key associated with the SSH tunnel.",
1100        ),
1101    ]),
1102    is_retained_metrics_object: false,
1103    access: vec![PUBLIC_SELECT],
1104    ontology: Some(Ontology {
1105        entity_name: "ssh_tunnel_connection",
1106        description: "SSH tunnel connection with public keys",
1107        links: &const {
1108            [OntologyLink {
1109                name: "details_of",
1110                target: "connection",
1111                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
1112            }]
1113        },
1114        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
1115    }),
1116});
1117// mz_sources is generated dynamically in BUILTINS_STATIC via builtin::make_mz_sources()
1118// with builtin source/log entries inlined as VALUES. See builtin/builtin.rs.
1119pub static MZ_SINKS: LazyLock<BuiltinTable> = LazyLock::new(|| {
1120    BuiltinTable {
1121        name: "mz_sinks",
1122        schema: MZ_CATALOG_SCHEMA,
1123        oid: oid::TABLE_MZ_SINKS_OID,
1124        desc: RelationDesc::builder()
1125            .with_column("id", SqlScalarType::String.nullable(false))
1126            .with_column("oid", SqlScalarType::Oid.nullable(false))
1127            .with_column("schema_id", SqlScalarType::String.nullable(false))
1128            .with_column("name", SqlScalarType::String.nullable(false))
1129            .with_column("type", SqlScalarType::String.nullable(false))
1130            .with_column("connection_id", SqlScalarType::String.nullable(true))
1131            .with_column("size", SqlScalarType::String.nullable(true))
1132            .with_column("envelope_type", SqlScalarType::String.nullable(true))
1133            // This `format` column is deprecated and replaced by the `key_format` and `value_format` columns
1134            // below. This should be removed in the future.
1135            .with_column("format", SqlScalarType::String.nullable(true))
1136            .with_column("key_format", SqlScalarType::String.nullable(true))
1137            .with_column("value_format", SqlScalarType::String.nullable(true))
1138            .with_column("cluster_id", SqlScalarType::String.nullable(false))
1139            .with_column("owner_id", SqlScalarType::String.nullable(false))
1140            .with_column("create_sql", SqlScalarType::String.nullable(false))
1141            .with_column("redacted_create_sql", SqlScalarType::String.nullable(false))
1142            .with_key(vec![0])
1143            .with_key(vec![1])
1144            .finish(),
1145        column_comments: BTreeMap::from_iter([
1146            ("id", "Materialize's unique ID for the sink."),
1147            ("oid", "A PostgreSQL-compatible OID for the sink."),
1148            (
1149                "schema_id",
1150                "The ID of the schema to which the sink belongs. Corresponds to `mz_schemas.id`.",
1151            ),
1152            ("name", "The name of the sink."),
1153            ("type", "The type of the sink: `kafka`."),
1154            (
1155                "connection_id",
1156                "The ID of the connection associated with the sink, if any. Corresponds to `mz_connections.id`.",
1157            ),
1158            ("size", "The size of the sink."),
1159            (
1160                "envelope_type",
1161                "The envelope of the sink: `upsert`, or `debezium`.",
1162            ),
1163            (
1164                "format",
1165                "*Deprecated* The format of the Kafka messages produced by the sink: `avro`, `json`, `text`, or `bytes`.",
1166            ),
1167            (
1168                "key_format",
1169                "The format of the Kafka message key for messages produced by the sink: `avro`, `json`, `bytes`, `text`, or `NULL`.",
1170            ),
1171            (
1172                "value_format",
1173                "The format of the Kafka message value for messages produced by the sink: `avro`, `json`, `text`, or `bytes`.",
1174            ),
1175            (
1176                "cluster_id",
1177                "The ID of the cluster maintaining the sink. Corresponds to `mz_clusters.id`.",
1178            ),
1179            (
1180                "owner_id",
1181                "The role ID of the owner of the sink. Corresponds to `mz_roles.id`.",
1182            ),
1183            ("create_sql", "The `CREATE` SQL statement for the sink."),
1184            (
1185                "redacted_create_sql",
1186                "The redacted `CREATE` SQL statement for the sink.",
1187            ),
1188        ]),
1189        is_retained_metrics_object: true,
1190        access: vec![PUBLIC_SELECT],
1191        ontology: Some(Ontology {
1192            entity_name: "sink",
1193            description: "An export of data from Materialize to an external system",
1194            links: &const {
1195                [
1196                    OntologyLink {
1197                        name: "in_schema",
1198                        target: "schema",
1199                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
1200                    },
1201                    OntologyLink {
1202                        name: "owned_by",
1203                        target: "role",
1204                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
1205                    },
1206                    OntologyLink {
1207                        name: "runs_on_cluster",
1208                        target: "cluster",
1209                        properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
1210                    },
1211                    OntologyLink {
1212                        name: "uses_connection",
1213                        target: "connection",
1214                        properties: LinkProperties::fk_nullable(
1215                            "connection_id",
1216                            "id",
1217                            Cardinality::ManyToOne,
1218                        ),
1219                    },
1220                ]
1221            },
1222            column_semantic_types: &const {
1223                [
1224                    ("id", SemanticType::CatalogItemId),
1225                    ("oid", SemanticType::OID),
1226                    ("schema_id", SemanticType::SchemaId),
1227                    ("connection_id", SemanticType::CatalogItemId),
1228                    ("cluster_id", SemanticType::ClusterId),
1229                    ("owner_id", SemanticType::RoleId),
1230                    ("create_sql", SemanticType::SqlDefinition),
1231                    ("redacted_create_sql", SemanticType::RedactedSqlDefinition),
1232                ]
1233            },
1234        }),
1235    }
1236});
1237pub static MZ_VIEWS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1238    name: "mz_views",
1239    schema: MZ_CATALOG_SCHEMA,
1240    oid: oid::TABLE_MZ_VIEWS_OID,
1241    desc: RelationDesc::builder()
1242        .with_column("id", SqlScalarType::String.nullable(false))
1243        .with_column("oid", SqlScalarType::Oid.nullable(false))
1244        .with_column("schema_id", SqlScalarType::String.nullable(false))
1245        .with_column("name", SqlScalarType::String.nullable(false))
1246        .with_column("definition", SqlScalarType::String.nullable(false))
1247        .with_column("owner_id", SqlScalarType::String.nullable(false))
1248        .with_column(
1249            "privileges",
1250            SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
1251        )
1252        .with_column("create_sql", SqlScalarType::String.nullable(false))
1253        .with_column("redacted_create_sql", SqlScalarType::String.nullable(false))
1254        .with_key(vec![0])
1255        .with_key(vec![1])
1256        .finish(),
1257    column_comments: BTreeMap::from_iter([
1258        ("id", "Materialize's unique ID for the view."),
1259        ("oid", "A PostgreSQL-compatible OID for the view."),
1260        (
1261            "schema_id",
1262            "The ID of the schema to which the view belongs. Corresponds to `mz_schemas.id`.",
1263        ),
1264        ("name", "The name of the view."),
1265        ("definition", "The view definition (a `SELECT` query)."),
1266        (
1267            "owner_id",
1268            "The role ID of the owner of the view. Corresponds to `mz_roles.id`.",
1269        ),
1270        ("privileges", "The privileges belonging to the view."),
1271        ("create_sql", "The `CREATE` SQL statement for the view."),
1272        (
1273            "redacted_create_sql",
1274            "The redacted `CREATE` SQL statement for the view.",
1275        ),
1276    ]),
1277    is_retained_metrics_object: false,
1278    access: vec![PUBLIC_SELECT],
1279    ontology: Some(Ontology {
1280        entity_name: "view",
1281        description: "A non-materialized view defined by a SQL query",
1282        links: &const {
1283            [
1284                OntologyLink {
1285                    name: "in_schema",
1286                    target: "schema",
1287                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
1288                },
1289                OntologyLink {
1290                    name: "owned_by",
1291                    target: "role",
1292                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
1293                },
1294            ]
1295        },
1296        column_semantic_types: &const {
1297            [
1298                ("id", SemanticType::CatalogItemId),
1299                ("oid", SemanticType::OID),
1300                ("schema_id", SemanticType::SchemaId),
1301                ("definition", SemanticType::SqlDefinition),
1302                ("owner_id", SemanticType::RoleId),
1303                ("create_sql", SemanticType::SqlDefinition),
1304                ("redacted_create_sql", SemanticType::RedactedSqlDefinition),
1305            ]
1306        },
1307    }),
1308});
1309
1310pub static MZ_MATERIALIZED_VIEWS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
1311    BuiltinMaterializedView {
1312        name: "mz_materialized_views",
1313        schema: MZ_CATALOG_SCHEMA,
1314        oid: oid::MV_MZ_MATERIALIZED_VIEWS_OID,
1315        desc: RelationDesc::builder()
1316            .with_column("id", SqlScalarType::String.nullable(false))
1317            .with_column("oid", SqlScalarType::Oid.nullable(false))
1318            .with_column("schema_id", SqlScalarType::String.nullable(false))
1319            .with_column("name", SqlScalarType::String.nullable(false))
1320            .with_column("cluster_id", SqlScalarType::String.nullable(false))
1321            .with_column("definition", SqlScalarType::String.nullable(false))
1322            .with_column("owner_id", SqlScalarType::String.nullable(false))
1323            .with_column(
1324                "privileges",
1325                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
1326            )
1327            .with_column("create_sql", SqlScalarType::String.nullable(false))
1328            .with_column("redacted_create_sql", SqlScalarType::String.nullable(false))
1329            .with_key(vec![0])
1330            .with_key(vec![1])
1331            .finish(),
1332        column_comments: BTreeMap::from_iter([
1333            ("id", "Materialize's unique ID for the materialized view."),
1334            (
1335                "oid",
1336                "A PostgreSQL-compatible OID for the materialized view.",
1337            ),
1338            (
1339                "schema_id",
1340                "The ID of the schema to which the materialized view belongs. Corresponds to `mz_schemas.id`.",
1341            ),
1342            ("name", "The name of the materialized view."),
1343            (
1344                "cluster_id",
1345                "The ID of the cluster maintaining the materialized view. Corresponds to `mz_clusters.id`.",
1346            ),
1347            (
1348                "definition",
1349                "The materialized view definition (a `SELECT` query).",
1350            ),
1351            (
1352                "owner_id",
1353                "The role ID of the owner of the materialized view. Corresponds to `mz_roles.id`.",
1354            ),
1355            (
1356                "privileges",
1357                "The privileges belonging to the materialized view.",
1358            ),
1359            (
1360                "create_sql",
1361                "The `CREATE` SQL statement for the materialized view.",
1362            ),
1363            (
1364                "redacted_create_sql",
1365                "The redacted `CREATE` SQL statement for the materialized view.",
1366            ),
1367        ]),
1368        sql: Box::leak(format!("
1369IN CLUSTER mz_catalog_server
1370WITH (
1371    ASSERT NOT NULL id,
1372    ASSERT NOT NULL oid,
1373    ASSERT NOT NULL schema_id,
1374    ASSERT NOT NULL name,
1375    ASSERT NOT NULL cluster_id,
1376    ASSERT NOT NULL definition,
1377    ASSERT NOT NULL owner_id,
1378    ASSERT NOT NULL privileges,
1379    ASSERT NOT NULL create_sql,
1380    ASSERT NOT NULL redacted_create_sql
1381) AS
1382WITH
1383    user_mvs AS (
1384        SELECT
1385            mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
1386            (data->'value'->>'oid')::oid AS oid,
1387            mz_internal.parse_catalog_id(data->'value'->'schema_id') AS schema_id,
1388            data->'value'->>'name' AS name,
1389            mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'cluster_id' AS cluster_id,
1390            mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'definition' AS definition,
1391            mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
1392            mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges,
1393            data->'value'->'definition'->'V1'->>'create_sql' AS create_sql,
1394            mz_internal.redact_sql(data->'value'->'definition'->'V1'->>'create_sql') AS redacted_create_sql
1395        FROM mz_internal.mz_catalog_raw
1396        WHERE
1397            data->>'kind' = 'Item' AND
1398            mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'type' = 'materialized-view'
1399    ),
1400    builtin_mappings AS (
1401        SELECT
1402            data->'key'->>'schema_name' AS schema_name,
1403            data->'key'->>'object_name' AS name,
1404            's' || (data->'value'->>'catalog_id') AS id
1405        FROM mz_internal.mz_catalog_raw
1406        WHERE
1407            data->>'kind' = 'GidMapping' AND
1408            data->'key'->>'object_type' = '5'
1409    ),
1410    builtin_mvs AS (
1411        SELECT
1412            m.id,
1413            mv.oid,
1414            s.id AS schema_id,
1415            mv.name,
1416            c.id AS cluster_id,
1417            mv.definition,
1418            '{MZ_SYSTEM_ROLE_ID}' AS owner_id,
1419            mv.privileges,
1420            mv.create_sql,
1421            mz_internal.redact_sql(mv.create_sql) AS redacted_create_sql
1422        FROM mz_internal.mz_builtin_materialized_views mv
1423        JOIN builtin_mappings m USING (schema_name, name)
1424        JOIN mz_schemas s ON s.name = mv.schema_name
1425        JOIN mz_clusters c ON c.name = mv.cluster_name
1426        WHERE s.database_id IS NULL
1427    )
1428SELECT * FROM user_mvs
1429UNION ALL
1430SELECT * FROM builtin_mvs").into_boxed_str()),
1431        is_retained_metrics_object: false,
1432        access: vec![PUBLIC_SELECT],
1433        ontology: Some(Ontology {
1434            entity_name: "mv",
1435            description: "A materialized view maintained incrementally on a cluster",
1436            links: &const { [
1437                OntologyLink { name: "in_schema", target: "schema", properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne) },
1438                OntologyLink { name: "owned_by", target: "role", properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne) },
1439                OntologyLink { name: "runs_on_cluster", target: "cluster", properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne) },
1440            ] },
1441            column_semantic_types: &const {[("id", SemanticType::CatalogItemId), ("oid", SemanticType::OID), ("schema_id", SemanticType::SchemaId), ("cluster_id", SemanticType::ClusterId), ("definition", SemanticType::SqlDefinition), ("owner_id", SemanticType::RoleId), ("create_sql", SemanticType::SqlDefinition), ("redacted_create_sql", SemanticType::RedactedSqlDefinition)]},
1442        }),
1443    }
1444});
1445pub static MZ_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1446    name: "mz_types",
1447    schema: MZ_CATALOG_SCHEMA,
1448    oid: oid::TABLE_MZ_TYPES_OID,
1449    desc: RelationDesc::builder()
1450        .with_column("id", SqlScalarType::String.nullable(false))
1451        .with_column("oid", SqlScalarType::Oid.nullable(false))
1452        .with_column("schema_id", SqlScalarType::String.nullable(false))
1453        .with_column("name", SqlScalarType::String.nullable(false))
1454        .with_column("category", SqlScalarType::String.nullable(false))
1455        .with_column("owner_id", SqlScalarType::String.nullable(false))
1456        .with_column(
1457            "privileges",
1458            SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
1459        )
1460        .with_column("create_sql", SqlScalarType::String.nullable(true))
1461        .with_column("redacted_create_sql", SqlScalarType::String.nullable(true))
1462        .with_key(vec![0])
1463        .with_key(vec![1])
1464        .finish(),
1465    column_comments: BTreeMap::from_iter([
1466        ("id", "Materialize's unique ID for the type."),
1467        ("oid", "A PostgreSQL-compatible OID for the type."),
1468        (
1469            "schema_id",
1470            "The ID of the schema to which the type belongs. Corresponds to `mz_schemas.id`.",
1471        ),
1472        ("name", "The name of the type."),
1473        ("category", "The category of the type."),
1474        (
1475            "owner_id",
1476            "The role ID of the owner of the type. Corresponds to `mz_roles.id`.",
1477        ),
1478        ("privileges", "The privileges belonging to the type."),
1479        ("create_sql", "The `CREATE` SQL statement for the type."),
1480        (
1481            "redacted_create_sql",
1482            "The redacted `CREATE` SQL statement for the type.",
1483        ),
1484    ]),
1485    is_retained_metrics_object: false,
1486    access: vec![PUBLIC_SELECT],
1487    ontology: Some(Ontology {
1488        entity_name: "type",
1489        description: "A named data type (base, array, list, map, or pseudo)",
1490        links: &const {
1491            [
1492                OntologyLink {
1493                    name: "in_schema",
1494                    target: "schema",
1495                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
1496                },
1497                OntologyLink {
1498                    name: "owned_by",
1499                    target: "role",
1500                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
1501                },
1502            ]
1503        },
1504        column_semantic_types: &const {
1505            [
1506                ("id", SemanticType::CatalogItemId),
1507                ("oid", SemanticType::OID),
1508                ("schema_id", SemanticType::SchemaId),
1509                ("owner_id", SemanticType::RoleId),
1510                ("create_sql", SemanticType::SqlDefinition),
1511                ("redacted_create_sql", SemanticType::RedactedSqlDefinition),
1512            ]
1513        },
1514    }),
1515});
1516pub static MZ_ARRAY_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1517    name: "mz_array_types",
1518    schema: MZ_CATALOG_SCHEMA,
1519    oid: oid::TABLE_MZ_ARRAY_TYPES_OID,
1520    desc: RelationDesc::builder()
1521        .with_column("id", SqlScalarType::String.nullable(false))
1522        .with_column("element_id", SqlScalarType::String.nullable(false))
1523        .finish(),
1524    column_comments: BTreeMap::from_iter([
1525        ("id", "The ID of the array type."),
1526        ("element_id", "The ID of the array's element type."),
1527    ]),
1528    is_retained_metrics_object: false,
1529    access: vec![PUBLIC_SELECT],
1530    ontology: Some(Ontology {
1531        entity_name: "array_type",
1532        description: "An array type with its element type",
1533        links: &const {
1534            [
1535                OntologyLink {
1536                    name: "detail_of",
1537                    target: "type",
1538                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
1539                },
1540                OntologyLink {
1541                    name: "has_element_type",
1542                    target: "type",
1543                    properties: LinkProperties::fk("element_id", "id", Cardinality::ManyToOne),
1544                },
1545            ]
1546        },
1547        column_semantic_types: &const {
1548            [
1549                ("id", SemanticType::CatalogItemId),
1550                ("element_id", SemanticType::CatalogItemId),
1551            ]
1552        },
1553    }),
1554});
1555pub static MZ_BASE_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1556    name: "mz_base_types",
1557    schema: MZ_CATALOG_SCHEMA,
1558    oid: oid::TABLE_MZ_BASE_TYPES_OID,
1559    desc: RelationDesc::builder()
1560        .with_column("id", SqlScalarType::String.nullable(false))
1561        .finish(),
1562    column_comments: BTreeMap::from_iter([("id", "The ID of the type.")]),
1563    is_retained_metrics_object: false,
1564    access: vec![PUBLIC_SELECT],
1565    ontology: Some(Ontology {
1566        entity_name: "base_type",
1567        description: "A primitive/base data type",
1568        links: &const { [] },
1569        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
1570    }),
1571});
1572pub static MZ_LIST_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1573    name: "mz_list_types",
1574    schema: MZ_CATALOG_SCHEMA,
1575    oid: oid::TABLE_MZ_LIST_TYPES_OID,
1576    desc: RelationDesc::builder()
1577        .with_column("id", SqlScalarType::String.nullable(false))
1578        .with_column("element_id", SqlScalarType::String.nullable(false))
1579        .with_column(
1580            "element_modifiers",
1581            SqlScalarType::List {
1582                element_type: Box::new(SqlScalarType::Int64),
1583                custom_id: None,
1584            }
1585            .nullable(true),
1586        )
1587        .finish(),
1588    column_comments: BTreeMap::from_iter([
1589        ("id", "The ID of the list type."),
1590        ("element_id", "The IID of the list's element type."),
1591        (
1592            "element_modifiers",
1593            "The element type modifiers, or `NULL` if none.",
1594        ),
1595    ]),
1596    is_retained_metrics_object: false,
1597    access: vec![PUBLIC_SELECT],
1598    ontology: Some(Ontology {
1599        entity_name: "list_type",
1600        description: "A list type with its element type",
1601        links: &const {
1602            [
1603                OntologyLink {
1604                    name: "detail_of",
1605                    target: "type",
1606                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
1607                },
1608                OntologyLink {
1609                    name: "has_element_type",
1610                    target: "type",
1611                    properties: LinkProperties::fk("element_id", "id", Cardinality::ManyToOne),
1612                },
1613            ]
1614        },
1615        column_semantic_types: &const {
1616            [
1617                ("id", SemanticType::CatalogItemId),
1618                ("element_id", SemanticType::CatalogItemId),
1619            ]
1620        },
1621    }),
1622});
1623pub static MZ_MAP_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1624    name: "mz_map_types",
1625    schema: MZ_CATALOG_SCHEMA,
1626    oid: oid::TABLE_MZ_MAP_TYPES_OID,
1627    desc: RelationDesc::builder()
1628        .with_column("id", SqlScalarType::String.nullable(false))
1629        .with_column("key_id", SqlScalarType::String.nullable(false))
1630        .with_column("value_id", SqlScalarType::String.nullable(false))
1631        .with_column(
1632            "key_modifiers",
1633            SqlScalarType::List {
1634                element_type: Box::new(SqlScalarType::Int64),
1635                custom_id: None,
1636            }
1637            .nullable(true),
1638        )
1639        .with_column(
1640            "value_modifiers",
1641            SqlScalarType::List {
1642                element_type: Box::new(SqlScalarType::Int64),
1643                custom_id: None,
1644            }
1645            .nullable(true),
1646        )
1647        .finish(),
1648    column_comments: BTreeMap::from_iter([
1649        ("id", "The ID of the map type."),
1650        ("key_id", "The ID of the map's key type."),
1651        ("value_id", "The ID of the map's value type."),
1652        (
1653            "key_modifiers",
1654            "The key type modifiers, or `NULL` if none.",
1655        ),
1656        (
1657            "value_modifiers",
1658            "The value type modifiers, or `NULL` if none.",
1659        ),
1660    ]),
1661    is_retained_metrics_object: false,
1662    access: vec![PUBLIC_SELECT],
1663    ontology: Some(Ontology {
1664        entity_name: "map_type",
1665        description: "A map type with its key and value types",
1666        links: &const {
1667            [
1668                OntologyLink {
1669                    name: "detail_of",
1670                    target: "type",
1671                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
1672                },
1673                OntologyLink {
1674                    name: "has_key_type",
1675                    target: "type",
1676                    properties: LinkProperties::fk("key_id", "id", Cardinality::ManyToOne),
1677                },
1678                OntologyLink {
1679                    name: "has_value_type",
1680                    target: "type",
1681                    properties: LinkProperties::fk("value_id", "id", Cardinality::ManyToOne),
1682                },
1683            ]
1684        },
1685        column_semantic_types: &const {
1686            [
1687                ("id", SemanticType::CatalogItemId),
1688                ("key_id", SemanticType::CatalogItemId),
1689                ("value_id", SemanticType::CatalogItemId),
1690            ]
1691        },
1692    }),
1693});
1694pub static MZ_ROLES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
1695    // The two built-in super-roles are identified by name; their names live as
1696    // compile-time constants in `mz_sql::session::user`, so we interpolate to
1697    // keep the SQL in sync with the Rust definition.
1698    let sql = format!(
1699        "
1700IN CLUSTER mz_catalog_server
1701WITH (
1702    ASSERT NOT NULL id,
1703    ASSERT NOT NULL oid,
1704    ASSERT NOT NULL name,
1705    ASSERT NOT NULL inherit
1706) AS
1707SELECT
1708    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
1709    (data->'value'->>'oid')::oid AS oid,
1710    data->'value'->>'name' AS name,
1711    (data->'value'->'attributes'->>'inherit')::bool AS inherit,
1712    COALESCE(
1713        (data->'value'->'attributes'->>'login')::bool,
1714        data->'value'->>'name' IN ('{system}', '{support}')
1715    ) AS rolcanlogin,
1716    COALESCE(
1717        (data->'value'->'attributes'->>'superuser')::bool,
1718        CASE WHEN data->'value'->>'name' IN ('{system}', '{support}') THEN true END
1719    ) AS rolsuper
1720FROM mz_internal.mz_catalog_raw
1721WHERE data->>'kind' = 'Role' AND data->'key'->'id' != '\"Public\"'::jsonb",
1722        system = SYSTEM_USER_NAME,
1723        support = SUPPORT_USER_NAME,
1724    );
1725
1726    BuiltinMaterializedView {
1727        name: "mz_roles",
1728        schema: MZ_CATALOG_SCHEMA,
1729        oid: oid::MV_MZ_ROLES_OID,
1730        desc: RelationDesc::builder()
1731            .with_column("id", SqlScalarType::String.nullable(false))
1732            .with_column("oid", SqlScalarType::Oid.nullable(false))
1733            .with_column("name", SqlScalarType::String.nullable(false))
1734            .with_column("inherit", SqlScalarType::Bool.nullable(false))
1735            .with_column("rolcanlogin", SqlScalarType::Bool.nullable(true))
1736            .with_column("rolsuper", SqlScalarType::Bool.nullable(true))
1737            .with_key(vec![0])
1738            .with_key(vec![1])
1739            .finish(),
1740        column_comments: BTreeMap::from_iter([
1741            ("id", "Materialize's unique ID for the role."),
1742            ("oid", "A PostgreSQL-compatible OID for the role."),
1743            ("name", "The name of the role."),
1744            (
1745                "inherit",
1746                "Indicates whether the role has inheritance of privileges.",
1747            ),
1748            ("rolcanlogin", "Indicates whether the role can log in."),
1749            ("rolsuper", "Indicates whether the role is a superuser."),
1750        ]),
1751        sql: Box::leak(sql.into_boxed_str()),
1752        is_retained_metrics_object: false,
1753        access: vec![PUBLIC_SELECT],
1754        ontology: Some(Ontology {
1755            entity_name: "role",
1756            description: "A user or role for authentication and access control",
1757            links: &const { [] },
1758            column_semantic_types: &const { [("id", SemanticType::RoleId), ("oid", SemanticType::OID)] },
1759        }),
1760    }
1761});
1762
1763pub static MZ_ROLE_MEMBERS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
1764    BuiltinMaterializedView {
1765        name: "mz_role_members",
1766        schema: MZ_CATALOG_SCHEMA,
1767        oid: oid::MV_MZ_ROLE_MEMBERS_OID,
1768        desc: RelationDesc::builder()
1769            .with_column("role_id", SqlScalarType::String.nullable(false))
1770            .with_column("member", SqlScalarType::String.nullable(false))
1771            .with_column("grantor", SqlScalarType::String.nullable(false))
1772            .finish(),
1773        column_comments: BTreeMap::from_iter([
1774            (
1775                "role_id",
1776                "The ID of the role the `member` is a member of. Corresponds to `mz_roles.id`.",
1777            ),
1778            (
1779                "member",
1780                "The ID of the role that is a member of `role_id`. Corresponds to `mz_roles.id`.",
1781            ),
1782            (
1783                "grantor",
1784                "The ID of the role that granted membership of `member` to `role_id`. Corresponds to `mz_roles.id`.",
1785            ),
1786        ]),
1787        sql: "
1788IN CLUSTER mz_catalog_server
1789WITH (
1790    ASSERT NOT NULL role_id,
1791    ASSERT NOT NULL member,
1792    ASSERT NOT NULL grantor
1793) AS
1794SELECT
1795    mz_internal.parse_catalog_id(entry->'key') AS role_id,
1796    mz_internal.parse_catalog_id(data->'key'->'id') AS member,
1797    mz_internal.parse_catalog_id(entry->'value') AS grantor
1798FROM
1799    mz_internal.mz_catalog_raw,
1800    jsonb_array_elements(data->'value'->'membership'->'map') AS entry
1801WHERE data->>'kind' = 'Role'",
1802        is_retained_metrics_object: false,
1803        access: vec![PUBLIC_SELECT],
1804        ontology: Some(Ontology {
1805            entity_name: "role_membership",
1806            description: "A membership grant: one role is a member of another role",
1807            links: &const {
1808                [
1809                    OntologyLink {
1810                        name: "group_role",
1811                        target: "role",
1812                        properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
1813                    },
1814                    OntologyLink {
1815                        name: "member_role",
1816                        target: "role",
1817                        properties: LinkProperties::fk("member", "id", Cardinality::ManyToOne),
1818                    },
1819                    OntologyLink {
1820                        name: "granted_by",
1821                        target: "role",
1822                        properties: LinkProperties::fk("grantor", "id", Cardinality::ManyToOne),
1823                    },
1824                ]
1825            },
1826            column_semantic_types: &const {
1827                [
1828                    ("role_id", SemanticType::RoleId),
1829                    ("member", SemanticType::RoleId),
1830                    ("grantor", SemanticType::RoleId),
1831                ]
1832            },
1833        }),
1834    }
1835});
1836
1837pub static MZ_ROLE_PARAMETERS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
1838    BuiltinMaterializedView {
1839        name: "mz_role_parameters",
1840        schema: MZ_CATALOG_SCHEMA,
1841        oid: oid::MV_MZ_ROLE_PARAMETERS_OID,
1842        desc: RelationDesc::builder()
1843            .with_column("role_id", SqlScalarType::String.nullable(false))
1844            .with_column("parameter_name", SqlScalarType::String.nullable(false))
1845            .with_column("parameter_value", SqlScalarType::String.nullable(false))
1846            .finish(),
1847        column_comments: BTreeMap::from_iter([
1848            (
1849                "role_id",
1850                "The ID of the role whose configuration parameter default is set. Corresponds to `mz_roles.id`.",
1851            ),
1852            (
1853                "parameter_name",
1854                "The configuration parameter name. One of the supported configuration parameters.",
1855            ),
1856            (
1857                "parameter_value",
1858                "The default value of the parameter for the given role. Can be either a single value, or a comma-separated list of values for configuration parameters that accept a list.",
1859            ),
1860        ]),
1861        sql: "
1862IN CLUSTER mz_catalog_server
1863WITH (
1864    ASSERT NOT NULL role_id,
1865    ASSERT NOT NULL parameter_name,
1866    ASSERT NOT NULL parameter_value
1867) AS
1868SELECT
1869    mz_internal.parse_catalog_id(data->'key'->'id') AS role_id,
1870    entry->>'key' AS parameter_name,
1871    CASE
1872        WHEN entry->'val' ? 'Flat' THEN entry->'val'->>'Flat'
1873        ELSE (
1874            SELECT pg_catalog.string_agg(t.elem, ', ' ORDER BY t.ord)
1875            FROM jsonb_array_elements_text(entry->'val'->'SqlSet')
1876                WITH ORDINALITY AS t(elem, ord)
1877        )
1878    END AS parameter_value
1879FROM
1880    mz_internal.mz_catalog_raw,
1881    jsonb_array_elements(data->'value'->'vars'->'entries') AS entry
1882WHERE data->>'kind' = 'Role'",
1883        is_retained_metrics_object: false,
1884        access: vec![PUBLIC_SELECT],
1885        ontology: Some(Ontology {
1886            entity_name: "role_parameter",
1887            description: "A session parameter default set for a role",
1888            links: &const {
1889                [OntologyLink {
1890                    name: "default_parameter_setting_of",
1891                    target: "role",
1892                    properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
1893                }]
1894            },
1895            column_semantic_types: &[("role_id", SemanticType::RoleId)],
1896        }),
1897    }
1898});
1899pub static MZ_ROLE_AUTH: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1900    name: "mz_role_auth",
1901    schema: MZ_CATALOG_SCHEMA,
1902    oid: oid::TABLE_MZ_ROLE_AUTH_OID,
1903    desc: RelationDesc::builder()
1904        .with_column("role_id", SqlScalarType::String.nullable(false))
1905        .with_column("role_oid", SqlScalarType::Oid.nullable(false))
1906        .with_column("password_hash", SqlScalarType::String.nullable(true))
1907        .with_column(
1908            "updated_at",
1909            SqlScalarType::TimestampTz { precision: None }.nullable(false),
1910        )
1911        .finish(),
1912    column_comments: BTreeMap::from_iter([
1913        (
1914            "role_id",
1915            "The ID of the role. Corresponds to `mz_roles.id`.",
1916        ),
1917        ("role_oid", "A PostgreSQL-compatible OID for the role."),
1918        (
1919            "password_hash",
1920            "The hashed password for the role, if any. Uses the `SCRAM-SHA-256` algorithm.",
1921        ),
1922        (
1923            "updated_at",
1924            "The time at which the password was last updated.",
1925        ),
1926    ]),
1927    is_retained_metrics_object: false,
1928    access: vec![rbac::owner_privilege(ObjectType::Table, MZ_SYSTEM_ROLE_ID)],
1929    ontology: None,
1930});
1931pub static MZ_PSEUDO_TYPES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
1932    name: "mz_pseudo_types",
1933    schema: MZ_CATALOG_SCHEMA,
1934    oid: oid::TABLE_MZ_PSEUDO_TYPES_OID,
1935    desc: RelationDesc::builder()
1936        .with_column("id", SqlScalarType::String.nullable(false))
1937        .finish(),
1938    column_comments: BTreeMap::from_iter([("id", "The ID of the type.")]),
1939    is_retained_metrics_object: false,
1940    access: vec![PUBLIC_SELECT],
1941    ontology: Some(Ontology {
1942        entity_name: "pseudo_type",
1943        description: "A pseudo-type used in function signatures",
1944        links: &const { [] },
1945        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
1946    }),
1947});
1948pub static MZ_FUNCTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| {
1949    BuiltinTable {
1950        name: "mz_functions",
1951        schema: MZ_CATALOG_SCHEMA,
1952        oid: oid::TABLE_MZ_FUNCTIONS_OID,
1953        desc: RelationDesc::builder()
1954            .with_column("id", SqlScalarType::String.nullable(false)) // not a key!
1955            .with_column("oid", SqlScalarType::Oid.nullable(false))
1956            .with_column("schema_id", SqlScalarType::String.nullable(false))
1957            .with_column("name", SqlScalarType::String.nullable(false))
1958            .with_column(
1959                "argument_type_ids",
1960                SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
1961            )
1962            .with_column(
1963                "variadic_argument_type_id",
1964                SqlScalarType::String.nullable(true),
1965            )
1966            .with_column("return_type_id", SqlScalarType::String.nullable(true))
1967            .with_column("returns_set", SqlScalarType::Bool.nullable(false))
1968            .with_column("owner_id", SqlScalarType::String.nullable(false))
1969            .finish(),
1970        column_comments: BTreeMap::from_iter([
1971            ("id", "Materialize's unique ID for the function."),
1972            ("oid", "A PostgreSQL-compatible OID for the function."),
1973            (
1974                "schema_id",
1975                "The ID of the schema to which the function belongs. Corresponds to `mz_schemas.id`.",
1976            ),
1977            ("name", "The name of the function."),
1978            (
1979                "argument_type_ids",
1980                "The ID of each argument's type. Each entry refers to `mz_types.id`.",
1981            ),
1982            (
1983                "variadic_argument_type_id",
1984                "The ID of the variadic argument's type, or `NULL` if the function does not have a variadic argument. Refers to `mz_types.id`.",
1985            ),
1986            (
1987                "return_type_id",
1988                "The returned value's type, or `NULL` if the function does not return a value. Refers to `mz_types.id`. Note that for table functions with > 1 column, this type corresponds to [`record`].",
1989            ),
1990            (
1991                "returns_set",
1992                "Whether the function returns a set, i.e. the function is a table function.",
1993            ),
1994            (
1995                "owner_id",
1996                "The role ID of the owner of the function. Corresponds to `mz_roles.id`.",
1997            ),
1998        ]),
1999        is_retained_metrics_object: false,
2000        access: vec![PUBLIC_SELECT],
2001        ontology: Some(Ontology {
2002            entity_name: "function",
2003            description: "A built-in or user-defined function",
2004            links: &const {
2005                [
2006                    OntologyLink {
2007                        name: "in_schema",
2008                        target: "schema",
2009                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
2010                    },
2011                    OntologyLink {
2012                        name: "owned_by",
2013                        target: "role",
2014                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
2015                    },
2016                    OntologyLink {
2017                        name: "returns_type",
2018                        target: "type",
2019                        properties: LinkProperties::fk_nullable(
2020                            "return_type_id",
2021                            "id",
2022                            Cardinality::ManyToOne,
2023                        ),
2024                    },
2025                    OntologyLink {
2026                        name: "has_variadic_arg_type",
2027                        target: "type",
2028                        properties: LinkProperties::fk_nullable(
2029                            "variadic_argument_type_id",
2030                            "id",
2031                            Cardinality::ManyToOne,
2032                        ),
2033                    },
2034                ]
2035            },
2036            column_semantic_types: &const {
2037                [
2038                    ("id", SemanticType::CatalogItemId),
2039                    ("oid", SemanticType::OID),
2040                    ("schema_id", SemanticType::SchemaId),
2041                    ("variadic_argument_type_id", SemanticType::CatalogItemId),
2042                    ("return_type_id", SemanticType::CatalogItemId),
2043                    ("owner_id", SemanticType::RoleId),
2044                ]
2045            },
2046        }),
2047    }
2048});
2049pub static MZ_OPERATORS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2050    name: "mz_operators",
2051    schema: MZ_CATALOG_SCHEMA,
2052    oid: oid::TABLE_MZ_OPERATORS_OID,
2053    desc: RelationDesc::builder()
2054        .with_column("oid", SqlScalarType::Oid.nullable(false))
2055        .with_column("name", SqlScalarType::String.nullable(false))
2056        .with_column(
2057            "argument_type_ids",
2058            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
2059        )
2060        .with_column("return_type_id", SqlScalarType::String.nullable(true))
2061        .finish(),
2062    column_comments: BTreeMap::new(),
2063    is_retained_metrics_object: false,
2064    access: vec![PUBLIC_SELECT],
2065    ontology: Some(Ontology {
2066        entity_name: "operator",
2067        description: "A built-in SQL operator",
2068        links: &const {
2069            [OntologyLink {
2070                name: "returns_type",
2071                target: "type",
2072                properties: LinkProperties::fk_nullable(
2073                    "return_type_id",
2074                    "id",
2075                    Cardinality::ManyToOne,
2076                ),
2077            }]
2078        },
2079        column_semantic_types: &const {
2080            [
2081                ("oid", SemanticType::OID),
2082                ("return_type_id", SemanticType::CatalogItemId),
2083            ]
2084        },
2085    }),
2086});
2087
2088pub static MZ_CLUSTERS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2089    BuiltinMaterializedView {
2090        name: "mz_clusters",
2091        schema: MZ_CATALOG_SCHEMA,
2092        oid: oid::MV_MZ_CLUSTERS_OID,
2093        desc: RelationDesc::builder()
2094            .with_column("id", SqlScalarType::String.nullable(false))
2095            .with_column("name", SqlScalarType::String.nullable(false))
2096            .with_column("owner_id", SqlScalarType::String.nullable(false))
2097            .with_column(
2098                "privileges",
2099                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
2100            )
2101            .with_column("managed", SqlScalarType::Bool.nullable(false))
2102            .with_column("size", SqlScalarType::String.nullable(true))
2103            .with_column("replication_factor", SqlScalarType::UInt32.nullable(true))
2104            .with_column("disk", SqlScalarType::Bool.nullable(true))
2105            .with_column(
2106                "availability_zones",
2107                SqlScalarType::List {
2108                    element_type: Box::new(SqlScalarType::String),
2109                    custom_id: None,
2110                }
2111                .nullable(true),
2112            )
2113            .with_column(
2114                "introspection_debugging",
2115                SqlScalarType::Bool.nullable(true),
2116            )
2117            .with_column(
2118                "introspection_interval",
2119                SqlScalarType::Interval.nullable(true),
2120            )
2121            .with_key(vec![0])
2122            .finish(),
2123        column_comments: BTreeMap::from_iter([
2124            ("id", "Materialize's unique ID for the cluster."),
2125            ("name", "The name of the cluster."),
2126            (
2127                "owner_id",
2128                "The role ID of the owner of the cluster. Corresponds to `mz_roles.id`.",
2129            ),
2130            ("privileges", "The privileges belonging to the cluster."),
2131            (
2132                "managed",
2133                "Whether the cluster is a managed cluster with automatically managed replicas.",
2134            ),
2135            (
2136                "size",
2137                "If the cluster is managed, the desired size of the cluster's replicas. `NULL` for unmanaged clusters.",
2138            ),
2139            (
2140                "replication_factor",
2141                "If the cluster is managed, the desired number of replicas of the cluster. `NULL` for unmanaged clusters.",
2142            ),
2143            (
2144                "disk",
2145                "**Unstable** If the cluster is managed, `true` if the replicas have the `DISK` option . `NULL` for unmanaged clusters.",
2146            ),
2147            (
2148                "availability_zones",
2149                "**Unstable** If the cluster is managed, the list of availability zones specified in `AVAILABILITY ZONES`. `NULL` for unmanaged clusters.",
2150            ),
2151            (
2152                "introspection_debugging",
2153                "Whether introspection of the gathering of the introspection data is enabled.",
2154            ),
2155            (
2156                "introspection_interval",
2157                "The interval at which to collect introspection data.",
2158            ),
2159        ]),
2160        // `config.variant` is a serde-tagged enum: `"Unmanaged"` (bare string)
2161        // or `{"Managed": {...}}`. Managed-only fields (size, replication_factor,
2162        // availability_zones, logging, schedule) live under `Managed`, and
2163        // resolve to NULL for unmanaged clusters.
2164        //
2165        // `disk` is computed as in
2166        // `CatalogState::cluster_replica_size_has_disk`:
2167        // `NOT swap_enabled AND disk_bytes != 0`. Both flags are sourced from
2168        // `mz_internal.mz_cluster_replica_size_internal`, which the catalog
2169        // populates for every size — including ones flagged `disabled`, which
2170        // the public `mz_cluster_replica_sizes` table omits. Using the internal
2171        // table directly preserves the prior behavior of indexing the in-memory
2172        // size map regardless of `disabled`.
2173        //
2174        // `availability_zones` is normalised to NULL when empty, matching
2175        // `pack_cluster_update`'s `None` for an empty `Vec`. `list_agg` over
2176        // `jsonb_array_elements_text WITH ORDINALITY` preserves the configured
2177        // order, matching the prior `push_list` of the `Vec`.
2178        //
2179        // `introspection_interval` is a Duration `{secs, nanos}` in the JSON;
2180        // we compose a string and cast to interval because Materialize has no
2181        // `make_interval`.
2182        //
2183        // The `kind = 'Cluster'` filter is pushed into a subquery on
2184        // `mz_catalog_raw` by hand. database-issues/8495 keeps the optimizer
2185        // from pushing a top-level `WHERE` below the LEFT JOIN, so without the
2186        // subquery the join scans the entire raw catalog.
2187        sql: "
2188IN CLUSTER mz_catalog_server
2189WITH (
2190    ASSERT NOT NULL id,
2191    ASSERT NOT NULL name,
2192    ASSERT NOT NULL owner_id,
2193    ASSERT NOT NULL privileges,
2194    ASSERT NOT NULL managed
2195) AS
2196SELECT
2197    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
2198    data->'value'->>'name' AS name,
2199    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
2200    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges,
2201    jsonb_typeof(data->'value'->'config'->'variant') = 'object' AS managed,
2202    data->'value'->'config'->'variant'->'Managed'->>'size' AS size,
2203    (data->'value'->'config'->'variant'->'Managed'->>'replication_factor')::uint4 AS replication_factor,
2204    CASE
2205        WHEN jsonb_typeof(data->'value'->'config'->'variant') = 'object' THEN
2206            NOT COALESCE(internal.swap_enabled, false)
2207            AND COALESCE(internal.disk_bytes, 0) != 0
2208    END AS disk,
2209    CASE
2210        WHEN jsonb_array_length(data->'value'->'config'->'variant'->'Managed'->'availability_zones') > 0 THEN
2211            (
2212                SELECT mz_catalog.list_agg(az.value ORDER BY az.ord)
2213                FROM jsonb_array_elements_text(data->'value'->'config'->'variant'->'Managed'->'availability_zones')
2214                     WITH ORDINALITY AS az(value, ord)
2215            )
2216    END AS availability_zones,
2217    (data->'value'->'config'->'variant'->'Managed'->'logging'->>'log_logging')::bool AS introspection_debugging,
2218    CASE
2219        WHEN data->'value'->'config'->'variant'->'Managed'->'logging'->'interval' != 'null'::jsonb THEN
2220            (
2221                (data->'value'->'config'->'variant'->'Managed'->'logging'->'interval'->>'secs')
2222                || ' seconds '
2223                || ((data->'value'->'config'->'variant'->'Managed'->'logging'->'interval'->>'nanos')::bigint / 1000)::text
2224                || ' microseconds'
2225            )::interval
2226    END AS introspection_interval
2227FROM (
2228    SELECT data FROM mz_internal.mz_catalog_raw WHERE data->>'kind' = 'Cluster'
2229) raw
2230LEFT JOIN mz_internal.mz_cluster_replica_size_internal internal
2231    ON internal.size = data->'value'->'config'->'variant'->'Managed'->>'size'",
2232        is_retained_metrics_object: false,
2233        access: vec![PUBLIC_SELECT],
2234        ontology: Some(Ontology {
2235            entity_name: "cluster",
2236            description: "A compute cluster that runs dataflows for sources, sinks, MVs, and indexes",
2237            links: &const {
2238                [
2239                    OntologyLink {
2240                        name: "owned_by",
2241                        target: "role",
2242                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
2243                    },
2244                    OntologyLink {
2245                        name: "has_size",
2246                        target: "replica_size",
2247                        properties: LinkProperties::fk_nullable("size", "size", Cardinality::ManyToOne),
2248                    },
2249                ]
2250            },
2251            column_semantic_types: &const {
2252                [
2253                    ("id", SemanticType::ClusterId),
2254                    ("owner_id", SemanticType::RoleId),
2255                ]
2256            },
2257        }),
2258    }
2259});
2260
2261pub static MZ_SECRETS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2262    BuiltinMaterializedView {
2263        name: "mz_secrets",
2264        schema: MZ_CATALOG_SCHEMA,
2265        oid: oid::MV_MZ_SECRETS_OID,
2266        desc: RelationDesc::builder()
2267            .with_column("id", SqlScalarType::String.nullable(false))
2268            .with_column("oid", SqlScalarType::Oid.nullable(false))
2269            .with_column("schema_id", SqlScalarType::String.nullable(false))
2270            .with_column("name", SqlScalarType::String.nullable(false))
2271            .with_column("owner_id", SqlScalarType::String.nullable(false))
2272            .with_column(
2273                "privileges",
2274                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
2275            )
2276            .finish(),
2277        column_comments: BTreeMap::from_iter([
2278            ("id", "The unique ID of the secret."),
2279            ("oid", "A PostgreSQL-compatible oid for the secret."),
2280            (
2281                "schema_id",
2282                "The ID of the schema to which the secret belongs. Corresponds to `mz_schemas.id`.",
2283            ),
2284            ("name", "The name of the secret."),
2285            (
2286                "owner_id",
2287                "The role ID of the owner of the secret. Corresponds to `mz_roles.id`.",
2288            ),
2289            ("privileges", "The privileges belonging to the secret."),
2290        ]),
2291        sql: "
2292IN CLUSTER mz_catalog_server
2293WITH (
2294    ASSERT NOT NULL id,
2295    ASSERT NOT NULL oid,
2296    ASSERT NOT NULL schema_id,
2297    ASSERT NOT NULL name,
2298    ASSERT NOT NULL owner_id,
2299    ASSERT NOT NULL privileges
2300) AS
2301SELECT
2302    mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
2303    (data->'value'->>'oid')::oid AS oid,
2304    mz_internal.parse_catalog_id(data->'value'->'schema_id') AS schema_id,
2305    data->'value'->>'name' AS name,
2306    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
2307    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges
2308FROM mz_internal.mz_catalog_raw
2309WHERE
2310    data->>'kind' = 'Item' AND
2311    mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'type' = 'secret'",
2312        is_retained_metrics_object: false,
2313        access: vec![PUBLIC_SELECT],
2314        ontology: Some(Ontology {
2315            entity_name: "secret",
2316            description: "A user-defined secret containing sensitive configuration (e.g., credentials)",
2317            links: &const { [
2318                OntologyLink {
2319                    name: "in_schema",
2320                    target: "schema",
2321                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
2322                },
2323                OntologyLink {
2324                    name: "owned_by",
2325                    target: "role",
2326                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
2327                },
2328            ] },
2329            column_semantic_types: &const {[("id", SemanticType::CatalogItemId), ("oid", SemanticType::OID), ("schema_id", SemanticType::SchemaId), ("owner_id", SemanticType::RoleId)]},
2330        }),
2331    }
2332});
2333
2334pub static MZ_CLUSTER_REPLICAS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2335    BuiltinMaterializedView {
2336        name: "mz_cluster_replicas",
2337        schema: MZ_CATALOG_SCHEMA,
2338        oid: oid::MV_MZ_CLUSTER_REPLICAS_OID,
2339        desc: RelationDesc::builder()
2340            .with_column("id", SqlScalarType::String.nullable(false))
2341            .with_column("name", SqlScalarType::String.nullable(false))
2342            .with_column("cluster_id", SqlScalarType::String.nullable(false))
2343            .with_column("size", SqlScalarType::String.nullable(true))
2344            // `NULL` for un-orchestrated clusters and for replicas where the user
2345            // hasn't specified them.
2346            .with_column("availability_zone", SqlScalarType::String.nullable(true))
2347            .with_column("owner_id", SqlScalarType::String.nullable(false))
2348            .with_column("disk", SqlScalarType::Bool.nullable(true))
2349            .with_key(vec![0])
2350            .finish(),
2351        column_comments: BTreeMap::from_iter([
2352            ("id", "Materialize's unique ID for the cluster replica."),
2353            ("name", "The name of the cluster replica."),
2354            (
2355                "cluster_id",
2356                "The ID of the cluster to which the replica belongs. Corresponds to `mz_clusters.id`.",
2357            ),
2358            (
2359                "size",
2360                "The cluster replica's size, selected during creation.",
2361            ),
2362            (
2363                "availability_zone",
2364                "The availability zones the replica is provisioned in, comma-separated. `NULL` if nothing constrains the replica's placement.",
2365            ),
2366            (
2367                "owner_id",
2368                "The role ID of the owner of the cluster replica. Corresponds to `mz_roles.id`.",
2369            ),
2370            ("disk", "If the replica has a local disk."),
2371        ]),
2372        // `config.location` is a serde-tagged enum: `{"Unmanaged": {...}}` or
2373        // `{"Managed": {...}}`. For replicas with an unmanaged location all
2374        // orchestrator-facing fields (size, availability_zone, disk) are NULL.
2375        //
2376        // `availability_zone` surfaces the durable `Managed` location's
2377        // `availability_zones` list as a comma-separated string in stored
2378        // order. For a managed cluster that is the provisioned AVAILABILITY
2379        // ZONES pool. For an unmanaged cluster it is the single AVAILABILITY
2380        // ZONE pin. An empty list (nothing constrains placement) maps to NULL,
2381        // matching the list-to-NULL normalization the `mz_clusters`
2382        // `availability_zones` column uses.
2383        //
2384        // `disk` mirrors `cluster_replica_size_has_disk`, joining
2385        // `mz_cluster_replica_size_internal` for both `swap_enabled` and
2386        // `disk_bytes`. The catalog populates that table for every size
2387        // (including ones flagged `disabled`), so a managed replica pinned to
2388        // a disabled size still resolves correctly. If a row is somehow
2389        // missing, the COALESCEs resolve `disk` to `false` (treating an
2390        // unknown size as having no disk), not NULL.
2391        //
2392        // The `kind = 'ClusterReplica'` filter is pushed into a subquery on
2393        // `mz_catalog_raw` by hand. database-issues/8495 keeps the optimizer
2394        // from pushing a top-level `WHERE` below the LEFT JOIN.
2395        sql: "
2396IN CLUSTER mz_catalog_server
2397WITH (
2398    ASSERT NOT NULL id,
2399    ASSERT NOT NULL name,
2400    ASSERT NOT NULL cluster_id,
2401    ASSERT NOT NULL owner_id
2402) AS
2403SELECT
2404    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
2405    data->'value'->>'name' AS name,
2406    mz_internal.parse_catalog_id(data->'value'->'cluster_id') AS cluster_id,
2407    data->'value'->'config'->'location'->'Managed'->>'size' AS size,
2408    CASE
2409        WHEN jsonb_array_length(data->'value'->'config'->'location'->'Managed'->'availability_zones') > 0 THEN
2410            (
2411                SELECT pg_catalog.string_agg(az.value, ',' ORDER BY az.ord)
2412                FROM jsonb_array_elements_text(data->'value'->'config'->'location'->'Managed'->'availability_zones')
2413                     WITH ORDINALITY AS az(value, ord)
2414            )
2415    END AS availability_zone,
2416    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
2417    CASE
2418        WHEN data->'value'->'config'->'location' ? 'Managed' THEN
2419            NOT COALESCE(internal.swap_enabled, false)
2420            AND COALESCE(internal.disk_bytes, 0) != 0
2421    END AS disk
2422FROM (
2423    SELECT data FROM mz_internal.mz_catalog_raw WHERE data->>'kind' = 'ClusterReplica'
2424) raw
2425LEFT JOIN mz_internal.mz_cluster_replica_size_internal internal
2426    ON internal.size = data->'value'->'config'->'location'->'Managed'->>'size'",
2427        is_retained_metrics_object: true,
2428        access: vec![PUBLIC_SELECT],
2429        ontology: Some(Ontology {
2430            entity_name: "replica",
2431            description: "A physical replica of a cluster providing fault tolerance",
2432            links: &const {
2433                [
2434                    OntologyLink {
2435                        name: "owned_by",
2436                        target: "role",
2437                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
2438                    },
2439                    OntologyLink {
2440                        name: "belongs_to_cluster",
2441                        target: "cluster",
2442                        properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
2443                    },
2444                    OntologyLink {
2445                        name: "has_size",
2446                        target: "replica_size",
2447                        properties: LinkProperties::fk_nullable(
2448                            "size",
2449                            "size",
2450                            Cardinality::ManyToOne,
2451                        ),
2452                    },
2453                ]
2454            },
2455            column_semantic_types: &const {
2456                [
2457                    ("id", SemanticType::ReplicaId),
2458                    ("cluster_id", SemanticType::ClusterId),
2459                    ("owner_id", SemanticType::RoleId),
2460                ]
2461            },
2462        }),
2463    }
2464});
2465
2466pub static MZ_CLUSTER_REPLICA_SIZES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2467    name: "mz_cluster_replica_sizes",
2468    schema: MZ_CATALOG_SCHEMA,
2469    oid: oid::TABLE_MZ_CLUSTER_REPLICA_SIZES_OID,
2470    desc: RelationDesc::builder()
2471        .with_column("size", SqlScalarType::String.nullable(false))
2472        .with_column("processes", SqlScalarType::UInt64.nullable(false))
2473        .with_column("workers", SqlScalarType::UInt64.nullable(false))
2474        .with_column("cpu_nano_cores", SqlScalarType::UInt64.nullable(false))
2475        .with_column("memory_bytes", SqlScalarType::UInt64.nullable(false))
2476        .with_column("disk_bytes", SqlScalarType::UInt64.nullable(true))
2477        .with_column(
2478            "credits_per_hour",
2479            SqlScalarType::Numeric { max_scale: None }.nullable(false),
2480        )
2481        .finish(),
2482    column_comments: BTreeMap::from_iter([
2483        ("size", "The human-readable replica size."),
2484        ("processes", "The number of processes in the replica."),
2485        (
2486            "workers",
2487            "The number of Timely Dataflow workers per process.",
2488        ),
2489        (
2490            "cpu_nano_cores",
2491            "The CPU allocation per process, in billionths of a vCPU core.",
2492        ),
2493        (
2494            "memory_bytes",
2495            "The RAM allocation per process, in billionths of a vCPU core.",
2496        ),
2497        ("disk_bytes", "The disk allocation per process."),
2498        (
2499            "credits_per_hour",
2500            "The number of compute credits consumed per hour.",
2501        ),
2502    ]),
2503    is_retained_metrics_object: true,
2504    access: vec![PUBLIC_SELECT],
2505    ontology: Some(Ontology {
2506        entity_name: "replica_size",
2507        description: "Available cluster replica sizes with CPU, memory, and credit cost",
2508        links: &const { [] },
2509        column_semantic_types: &const {
2510            [
2511                ("memory_bytes", SemanticType::ByteCount),
2512                ("disk_bytes", SemanticType::ByteCount),
2513                ("credits_per_hour", SemanticType::CreditRate),
2514            ]
2515        },
2516    }),
2517});
2518
2519pub static MZ_AUDIT_EVENTS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2520    name: "mz_audit_events",
2521    schema: MZ_CATALOG_SCHEMA,
2522    oid: oid::TABLE_MZ_AUDIT_EVENTS_OID,
2523    desc: RelationDesc::builder()
2524        .with_column("id", SqlScalarType::UInt64.nullable(false))
2525        .with_column("event_type", SqlScalarType::String.nullable(false))
2526        .with_column("object_type", SqlScalarType::String.nullable(false))
2527        .with_column("details", SqlScalarType::Jsonb.nullable(false))
2528        .with_column("user", SqlScalarType::String.nullable(true))
2529        .with_column(
2530            "occurred_at",
2531            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2532        )
2533        .with_key(vec![0])
2534        .finish(),
2535    column_comments: BTreeMap::from_iter([
2536        (
2537            "id",
2538            "Materialize's unique, monotonically increasing ID for the event.",
2539        ),
2540        (
2541            "event_type",
2542            "The type of the event: `create`, `drop`, or `alter`.",
2543        ),
2544        (
2545            "object_type",
2546            "The type of the affected object: `cluster`, `cluster-replica`, `connection`, `database`, `function`, `index`, `materialized-view`, `role`, `schema`, `secret`, `sink`, `source`, `table`, `type`, or `view`.",
2547        ),
2548        (
2549            "details",
2550            "Additional details about the event. The shape of the details varies based on `event_type` and `object_type`.",
2551        ),
2552        (
2553            "user",
2554            "The user who triggered the event, or `NULL` if triggered by the system.",
2555        ),
2556        (
2557            "occurred_at",
2558            "The time at which the event occurred. Guaranteed to be in order of event creation. Events created in the same transaction will have identical values.",
2559        ),
2560    ]),
2561    is_retained_metrics_object: false,
2562    access: vec![PUBLIC_SELECT],
2563    ontology: Some(Ontology {
2564        entity_name: "audit_event",
2565        description: "An audit log entry recording a DDL operation",
2566        links: &const { [] },
2567        column_semantic_types: &const {
2568            [
2569                ("object_type", SemanticType::ObjectType),
2570                ("occurred_at", SemanticType::WallclockTimestamp),
2571            ]
2572        },
2573    }),
2574});
2575
2576pub static MZ_EGRESS_IPS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2577    name: "mz_egress_ips",
2578    schema: MZ_CATALOG_SCHEMA,
2579    oid: oid::TABLE_MZ_EGRESS_IPS_OID,
2580    desc: RelationDesc::builder()
2581        .with_column("egress_ip", SqlScalarType::String.nullable(false))
2582        .with_column("prefix_length", SqlScalarType::Int32.nullable(false))
2583        .with_column("cidr", SqlScalarType::String.nullable(false))
2584        .finish(),
2585    column_comments: BTreeMap::from_iter([
2586        ("egress_ip", "The start of the range of IP addresses."),
2587        (
2588            "prefix_length",
2589            "The number of leading bits in the CIDR netmask.",
2590        ),
2591        ("cidr", "The CIDR representation."),
2592    ]),
2593    is_retained_metrics_object: false,
2594    access: vec![PUBLIC_SELECT],
2595    ontology: Some(Ontology {
2596        entity_name: "egress_ip",
2597        description: "IP addresses used for outbound connections from Materialize",
2598        links: &const { [] },
2599        column_semantic_types: &[],
2600    }),
2601});
2602
2603pub static MZ_AWS_PRIVATELINK_CONNECTIONS: LazyLock<BuiltinTable> =
2604    LazyLock::new(|| BuiltinTable {
2605        name: "mz_aws_privatelink_connections",
2606        schema: MZ_CATALOG_SCHEMA,
2607        oid: oid::TABLE_MZ_AWS_PRIVATELINK_CONNECTIONS_OID,
2608        desc: RelationDesc::builder()
2609            .with_column("id", SqlScalarType::String.nullable(false))
2610            .with_column("principal", SqlScalarType::String.nullable(false))
2611            .finish(),
2612        column_comments: BTreeMap::from_iter([
2613            ("id", "The ID of the connection."),
2614            (
2615                "principal",
2616                "The AWS Principal that Materialize will use to connect to the VPC endpoint.",
2617            ),
2618        ]),
2619        is_retained_metrics_object: false,
2620        access: vec![PUBLIC_SELECT],
2621        ontology: Some(Ontology {
2622            entity_name: "aws_privatelink_connection",
2623            description: "AWS PrivateLink connection configuration",
2624            links: &const {
2625                [OntologyLink {
2626                    name: "details_of",
2627                    target: "connection",
2628                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
2629                }]
2630            },
2631            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
2632        }),
2633    });
2634
2635pub static MZ_CLUSTER_REPLICA_FRONTIERS: LazyLock<BuiltinSource> =
2636    LazyLock::new(|| BuiltinSource {
2637        name: "mz_cluster_replica_frontiers",
2638        schema: MZ_CATALOG_SCHEMA,
2639        oid: oid::SOURCE_MZ_CLUSTER_REPLICA_FRONTIERS_OID,
2640        data_source: IntrospectionType::ReplicaFrontiers.into(),
2641        desc: RelationDesc::builder()
2642            .with_column("object_id", SqlScalarType::String.nullable(false))
2643            .with_column("replica_id", SqlScalarType::String.nullable(false))
2644            .with_column("write_frontier", SqlScalarType::MzTimestamp.nullable(true))
2645            .finish(),
2646        column_comments: BTreeMap::from_iter([
2647            (
2648                "object_id",
2649                "The ID of the source, sink, index, materialized view, or subscription.",
2650            ),
2651            ("replica_id", "The ID of a cluster replica."),
2652            (
2653                "write_frontier",
2654                "The next timestamp at which the output may change.",
2655            ),
2656        ]),
2657        is_retained_metrics_object: false,
2658        access: vec![PUBLIC_SELECT],
2659        ontology: None,
2660    });
2661
2662pub static MZ_CLUSTER_REPLICA_FRONTIERS_IND: LazyLock<BuiltinIndex> =
2663    LazyLock::new(|| BuiltinIndex {
2664        name: "mz_cluster_replica_frontiers_ind",
2665        schema: MZ_CATALOG_SCHEMA,
2666        oid: oid::INDEX_MZ_CLUSTER_REPLICA_FRONTIERS_IND_OID,
2667        sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_cluster_replica_frontiers (object_id)",
2668        is_retained_metrics_object: false,
2669    });
2670
2671pub static MZ_DEFAULT_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2672    BuiltinMaterializedView {
2673        name: "mz_default_privileges",
2674        schema: MZ_CATALOG_SCHEMA,
2675        oid: oid::MV_MZ_DEFAULT_PRIVILEGES_OID,
2676        desc: RelationDesc::builder()
2677            .with_column("role_id", SqlScalarType::String.nullable(false))
2678            .with_column("database_id", SqlScalarType::String.nullable(true))
2679            .with_column("schema_id", SqlScalarType::String.nullable(true))
2680            .with_column("object_type", SqlScalarType::String.nullable(false))
2681            .with_column("grantee", SqlScalarType::String.nullable(false))
2682            .with_column("privileges", SqlScalarType::String.nullable(false))
2683            .finish(),
2684        column_comments: BTreeMap::from_iter([
2685            (
2686                "role_id",
2687                "Privileges described in this row will be granted on objects created by `role_id`. The role ID `p` stands for the `PUBLIC` pseudo-role and applies to all roles.",
2688            ),
2689            (
2690                "database_id",
2691                "Privileges described in this row will be granted only on objects in the database identified by `database_id` if non-null.",
2692            ),
2693            (
2694                "schema_id",
2695                "Privileges described in this row will be granted only on objects in the schema identified by `schema_id` if non-null.",
2696            ),
2697            (
2698                "object_type",
2699                "Privileges described in this row will be granted only on objects of type `object_type`.",
2700            ),
2701            (
2702                "grantee",
2703                "Privileges described in this row will be granted to `grantee`. The role ID `p` stands for the `PUBLIC` pseudo-role and applies to all roles.",
2704            ),
2705            ("privileges", "The set of privileges that will be granted."),
2706        ]),
2707        // `object_type` in `mz_catalog_raw` is the numeric `Serialize_repr` form
2708        // of `mz_catalog_protos::ObjectType`. The CASE mirrors
2709        // `mz_sql::catalog::ObjectType`'s `Display` impl, lowercased, matching
2710        // the prior `pack_default_privileges_update` populator. Variant `16` is
2711        // reserved/unused; any new proto variant must add a branch here. The
2712        // `object_type_case_matches_proto_display` test in this crate checks
2713        // the mapping.
2714        sql: "
2715IN CLUSTER mz_catalog_server
2716WITH (
2717    ASSERT NOT NULL role_id,
2718    ASSERT NOT NULL object_type,
2719    ASSERT NOT NULL grantee,
2720    ASSERT NOT NULL privileges
2721) AS
2722SELECT
2723    mz_internal.parse_catalog_id(data->'key'->'role_id') AS role_id,
2724    CASE WHEN data->'key'->'database_id' != 'null'::jsonb
2725         THEN mz_internal.parse_catalog_id(data->'key'->'database_id') END AS database_id,
2726    CASE WHEN data->'key'->'schema_id' != 'null'::jsonb
2727         THEN mz_internal.parse_catalog_id(data->'key'->'schema_id') END AS schema_id,
2728    CASE data->'key'->>'object_type'
2729        WHEN '1'  THEN 'table'
2730        WHEN '2'  THEN 'view'
2731        WHEN '3'  THEN 'materialized view'
2732        WHEN '4'  THEN 'source'
2733        WHEN '5'  THEN 'sink'
2734        WHEN '6'  THEN 'index'
2735        WHEN '7'  THEN 'type'
2736        WHEN '8'  THEN 'role'
2737        WHEN '9'  THEN 'cluster'
2738        WHEN '10' THEN 'cluster replica'
2739        WHEN '11' THEN 'secret'
2740        WHEN '12' THEN 'connection'
2741        WHEN '13' THEN 'database'
2742        WHEN '14' THEN 'schema'
2743        WHEN '15' THEN 'function'
2744        -- variant 16 reserved/unused in mz_catalog_protos::ObjectType.
2745        WHEN '17' THEN 'network policy'
2746    END AS object_type,
2747    mz_internal.parse_catalog_id(data->'key'->'grantee') AS grantee,
2748    mz_internal.parse_catalog_acl_mode(data->'value'->'privileges') AS privileges
2749FROM mz_internal.mz_catalog_raw
2750WHERE data->>'kind' = 'DefaultPrivileges'",
2751        is_retained_metrics_object: false,
2752        access: vec![PUBLIC_SELECT],
2753        ontology: Some(Ontology {
2754            entity_name: "default_privilege",
2755            description: "A default privilege rule applied to newly created objects",
2756            links: &const {
2757                [
2758                    OntologyLink {
2759                        name: "default_priv_for_role",
2760                        target: "role",
2761                        properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
2762                    },
2763                    OntologyLink {
2764                        name: "default_priv_in_database",
2765                        target: "database",
2766                        properties: LinkProperties::fk_nullable(
2767                            "database_id",
2768                            "id",
2769                            Cardinality::ManyToOne,
2770                        ),
2771                    },
2772                    OntologyLink {
2773                        name: "default_priv_in_schema",
2774                        target: "schema",
2775                        properties: LinkProperties::fk_nullable(
2776                            "schema_id",
2777                            "id",
2778                            Cardinality::ManyToOne,
2779                        ),
2780                    },
2781                    OntologyLink {
2782                        name: "default_priv_granted_to",
2783                        target: "role",
2784                        properties: LinkProperties::fk("grantee", "id", Cardinality::ManyToOne),
2785                    },
2786                ]
2787            },
2788            column_semantic_types: &const {
2789                [
2790                    ("role_id", SemanticType::RoleId),
2791                    ("database_id", SemanticType::DatabaseId),
2792                    ("schema_id", SemanticType::SchemaId),
2793                    ("object_type", SemanticType::ObjectType),
2794                    ("grantee", SemanticType::RoleId),
2795                ]
2796            },
2797        }),
2798    }
2799});
2800
2801pub static MZ_SYSTEM_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2802    BuiltinMaterializedView {
2803        name: "mz_system_privileges",
2804        schema: MZ_CATALOG_SCHEMA,
2805        oid: oid::MV_MZ_SYSTEM_PRIVILEGES_OID,
2806        desc: RelationDesc::builder()
2807            .with_column("privileges", SqlScalarType::MzAclItem.nullable(false))
2808            .finish(),
2809        column_comments: BTreeMap::from_iter([(
2810            "privileges",
2811            "The privileges belonging to the system.",
2812        )]),
2813        // The durable row holds `(grantee, grantor, acl_mode)`; we rebuild the
2814        // single-element JSON shape that `parse_catalog_privileges` expects and
2815        // unnest it to recover the `mz_aclitem`.
2816        sql: "
2817IN CLUSTER mz_catalog_server
2818WITH (
2819    ASSERT NOT NULL privileges
2820) AS
2821SELECT
2822    unnest(mz_internal.parse_catalog_privileges(
2823        jsonb_build_array(
2824            jsonb_build_object(
2825                'grantee',  data->'key'->'grantee',
2826                'grantor',  data->'key'->'grantor',
2827                'acl_mode', data->'value'->'acl_mode'
2828            )
2829        )
2830    )) AS privileges
2831FROM mz_internal.mz_catalog_raw
2832WHERE data->>'kind' = 'SystemPrivileges'",
2833        is_retained_metrics_object: false,
2834        access: vec![PUBLIC_SELECT],
2835        ontology: Some(Ontology {
2836            entity_name: "system_privilege",
2837            description: "A system-level privilege grant",
2838            links: &const { [] },
2839            column_semantic_types: &[],
2840        }),
2841    }
2842});
2843
2844pub static MZ_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2845    name: "mz_storage_usage",
2846    schema: MZ_CATALOG_SCHEMA,
2847    oid: oid::VIEW_MZ_STORAGE_USAGE_OID,
2848    desc: RelationDesc::builder()
2849        .with_column("object_id", SqlScalarType::String.nullable(false))
2850        .with_column("size_bytes", SqlScalarType::UInt64.nullable(false))
2851        .with_column(
2852            "collection_timestamp",
2853            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2854        )
2855        .with_key(vec![0, 2])
2856        .finish(),
2857    column_comments: BTreeMap::from_iter([
2858        (
2859            "object_id",
2860            "The ID of the table, source, or materialized view.",
2861        ),
2862        (
2863            "size_bytes",
2864            "The number of storage bytes used by the object.",
2865        ),
2866        (
2867            "collection_timestamp",
2868            "The time at which storage usage of the object was assessed.",
2869        ),
2870    ]),
2871    sql: "
2872SELECT
2873    object_id,
2874    sum(size_bytes)::uint8 AS size_bytes,
2875    collection_timestamp
2876FROM
2877    mz_internal.mz_storage_shards
2878    JOIN mz_internal.mz_storage_usage_by_shard USING (shard_id)
2879GROUP BY object_id, collection_timestamp",
2880    access: vec![PUBLIC_SELECT],
2881    ontology: Some(Ontology {
2882        entity_name: "storage_usage",
2883        description: "Historical storage usage per object over time",
2884        links: &const {
2885            [OntologyLink {
2886                name: "storage_usage_of",
2887                target: "object",
2888                properties: LinkProperties::fk("object_id", "id", Cardinality::ManyToOne),
2889            }]
2890        },
2891        column_semantic_types: &const {
2892            [
2893                ("object_id", SemanticType::CatalogItemId),
2894                ("size_bytes", SemanticType::ByteCount),
2895                ("collection_timestamp", SemanticType::WallclockTimestamp),
2896            ]
2897        },
2898    }),
2899});
2900
2901pub static MZ_RECENT_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| {
2902    BuiltinView {
2903    name: "mz_recent_storage_usage",
2904    schema: MZ_CATALOG_SCHEMA,
2905    oid: oid::VIEW_MZ_RECENT_STORAGE_USAGE_OID,
2906    desc: RelationDesc::builder()
2907        .with_column("object_id", SqlScalarType::String.nullable(false))
2908        .with_column("size_bytes", SqlScalarType::UInt64.nullable(true))
2909        .with_key(vec![0])
2910        .finish(),
2911    column_comments: BTreeMap::from_iter([
2912        ("object_id", "The ID of the table, source, or materialized view."),
2913        ("size_bytes", "The number of storage bytes used by the object in the most recent assessment."),
2914    ]),
2915    sql: "
2916WITH
2917
2918recent_storage_usage_by_shard AS (
2919    SELECT shard_id, size_bytes, collection_timestamp
2920    FROM mz_internal.mz_storage_usage_by_shard
2921    -- Restricting to the last 6 hours makes it feasible to index the view.
2922    WHERE collection_timestamp + '6 hours' >= mz_now()
2923),
2924
2925most_recent_collection_timestamp_by_shard AS (
2926    SELECT shard_id, max(collection_timestamp) AS collection_timestamp
2927    FROM recent_storage_usage_by_shard
2928    GROUP BY shard_id
2929)
2930
2931SELECT
2932    object_id,
2933    sum(size_bytes)::uint8 AS size_bytes
2934FROM
2935    mz_internal.mz_storage_shards
2936    LEFT JOIN most_recent_collection_timestamp_by_shard
2937        ON mz_storage_shards.shard_id = most_recent_collection_timestamp_by_shard.shard_id
2938    LEFT JOIN recent_storage_usage_by_shard
2939        ON mz_storage_shards.shard_id = recent_storage_usage_by_shard.shard_id
2940        AND most_recent_collection_timestamp_by_shard.collection_timestamp = recent_storage_usage_by_shard.collection_timestamp
2941GROUP BY object_id",
2942    access: vec![PUBLIC_SELECT],
2943    ontology: Some(Ontology {
2944        entity_name: "recent_storage",
2945        description: "Most recent storage usage snapshot per object",
2946        links: &const { [
2947            OntologyLink { name: "recent_storage_of", target: "object", properties: LinkProperties::fk("object_id", "id", Cardinality::OneToOne) },
2948        ] },
2949        column_semantic_types: &const {[("object_id", SemanticType::CatalogItemId), ("size_bytes", SemanticType::ByteCount)]},
2950    }),
2951}
2952});
2953
2954pub static MZ_RECENT_STORAGE_USAGE_IND: LazyLock<BuiltinIndex> = LazyLock::new(|| BuiltinIndex {
2955    name: "mz_recent_storage_usage_ind",
2956    schema: MZ_CATALOG_SCHEMA,
2957    oid: oid::INDEX_MZ_RECENT_STORAGE_USAGE_IND_OID,
2958    sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_recent_storage_usage (object_id)",
2959    is_retained_metrics_object: false,
2960});
2961
2962pub static MZ_RELATIONS: LazyLock<BuiltinView> = LazyLock::new(|| {
2963    BuiltinView {
2964        name: "mz_relations",
2965        schema: MZ_CATALOG_SCHEMA,
2966        oid: oid::VIEW_MZ_RELATIONS_OID,
2967        desc: RelationDesc::builder()
2968            .with_column("id", SqlScalarType::String.nullable(false))
2969            .with_column("oid", SqlScalarType::Oid.nullable(false))
2970            .with_column("schema_id", SqlScalarType::String.nullable(false))
2971            .with_column("name", SqlScalarType::String.nullable(false))
2972            .with_column("type", SqlScalarType::String.nullable(false))
2973            .with_column("owner_id", SqlScalarType::String.nullable(false))
2974            .with_column("cluster_id", SqlScalarType::String.nullable(true))
2975            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false))
2976            .finish(),
2977        column_comments: BTreeMap::from_iter([
2978            ("id", "Materialize's unique ID for the relation."),
2979            ("oid", "A PostgreSQL-compatible OID for the relation."),
2980            ("schema_id", "The ID of the schema to which the relation belongs. Corresponds to `mz_schemas.id`."),
2981            ("name", "The name of the relation."),
2982            ("type", "The type of the relation: either `table`, `source`, `view`, or `materialized-view`."),
2983            ("owner_id", "The role ID of the owner of the relation. Corresponds to `mz_roles.id`."),
2984            ("cluster_id", "The ID of the cluster maintaining the source, materialized view, index, or sink. Corresponds to `mz_clusters.id`. `NULL` for other object types."),
2985            ("privileges", "The privileges belonging to the relation."),
2986        ]),
2987        sql: "
2988      SELECT id, oid, schema_id, name, 'table' AS type, owner_id, NULL::text AS cluster_id, privileges FROM mz_catalog.mz_tables
2989UNION ALL SELECT id, oid, schema_id, name, 'source', owner_id, cluster_id, privileges FROM mz_catalog.mz_sources
2990UNION ALL SELECT id, oid, schema_id, name, 'view', owner_id, NULL::text, privileges FROM mz_catalog.mz_views
2991UNION ALL SELECT id, oid, schema_id, name, 'materialized-view', owner_id, cluster_id, privileges FROM mz_catalog.mz_materialized_views",
2992        access: vec![PUBLIC_SELECT],
2993        ontology: Some(Ontology {
2994            entity_name: "relation",
2995            description: "Union of all relation types: tables, sources, views, MVs (convenience view)",
2996            links: &const { [
2997                OntologyLink { name: "union_includes", target: "table", properties: LinkProperties::union_disc("type", "table") },
2998                OntologyLink { name: "union_includes", target: "source", properties: LinkProperties::union_disc("type", "source") },
2999                OntologyLink { name: "union_includes", target: "view", properties: LinkProperties::union_disc("type", "view") },
3000                OntologyLink { name: "union_includes", target: "mv", properties: LinkProperties::union_disc("type", "materialized-view") },
3001            ] },
3002            column_semantic_types: &const {[("id", SemanticType::CatalogItemId), ("oid", SemanticType::OID), ("schema_id", SemanticType::SchemaId), ("type", SemanticType::ObjectType), ("owner_id", SemanticType::RoleId), ("cluster_id", SemanticType::ClusterId)]},
3003        }),
3004    }
3005});
3006
3007pub static MZ_OBJECTS: LazyLock<BuiltinView> = LazyLock::new(|| {
3008    BuiltinView {
3009        name: "mz_objects",
3010        schema: MZ_CATALOG_SCHEMA,
3011        oid: oid::VIEW_MZ_OBJECTS_OID,
3012        desc: RelationDesc::builder()
3013            .with_column("id", SqlScalarType::String.nullable(false))
3014            .with_column("oid", SqlScalarType::Oid.nullable(false))
3015            .with_column("schema_id", SqlScalarType::String.nullable(false))
3016            .with_column("name", SqlScalarType::String.nullable(false))
3017            .with_column("type", SqlScalarType::String.nullable(false))
3018            .with_column("owner_id", SqlScalarType::String.nullable(false))
3019            .with_column("cluster_id", SqlScalarType::String.nullable(true))
3020            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(true))
3021            .finish(),
3022        column_comments: BTreeMap::from_iter([
3023            ("id", "Materialize's unique ID for the object."),
3024            ("oid", "A PostgreSQL-compatible OID for the object."),
3025            ("schema_id", "The ID of the schema to which the object belongs. Corresponds to `mz_schemas.id`."),
3026            ("name", "The name of the object."),
3027            ("type", "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`."),
3028            ("owner_id", "The role ID of the owner of the object. Corresponds to `mz_roles.id`."),
3029            ("cluster_id", "The ID of the cluster maintaining the source, materialized view, index, or sink. Corresponds to `mz_clusters.id`. `NULL` for other object types."),
3030            ("privileges", "The privileges belonging to the object."),
3031        ]),
3032        sql:
3033        "SELECT id, oid, schema_id, name, type, owner_id, cluster_id, privileges FROM mz_catalog.mz_relations
3034UNION ALL
3035    SELECT id, oid, schema_id, name, 'sink', owner_id, cluster_id, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_sinks
3036UNION ALL
3037    SELECT mz_indexes.id, mz_indexes.oid, mz_relations.schema_id, mz_indexes.name, 'index', mz_indexes.owner_id, mz_indexes.cluster_id, NULL::mz_catalog.mz_aclitem[]
3038    FROM mz_catalog.mz_indexes
3039    JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
3040UNION ALL
3041    SELECT id, oid, schema_id, name, 'connection', owner_id, NULL::text, privileges FROM mz_catalog.mz_connections
3042UNION ALL
3043    SELECT id, oid, schema_id, name, 'type', owner_id, NULL::text, privileges FROM mz_catalog.mz_types
3044UNION ALL
3045    SELECT id, oid, schema_id, name, 'function', owner_id, NULL::text, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_functions
3046UNION ALL
3047    SELECT id, oid, schema_id, name, 'secret', owner_id, NULL::text, privileges FROM mz_catalog.mz_secrets",
3048        access: vec![PUBLIC_SELECT],
3049        ontology: Some(Ontology {
3050            entity_name: "object",
3051            description: "Union of all object types: relations, indexes, connections, etc. (convenience view)",
3052            links: &const {
3053                [
3054                    OntologyLink {
3055                        name: "union_includes",
3056                        target: "relation",
3057                        properties: LinkProperties::Union {
3058                            discriminator_column: None,
3059                            discriminator_value: None,
3060                            note: Some("covers all mz_relations rows (table, source, view, mv)"),
3061                        },
3062                    },
3063                    OntologyLink {
3064                        name: "union_includes",
3065                        target: "table",
3066                        properties: LinkProperties::union_disc("type", "table"),
3067                    },
3068                    OntologyLink {
3069                        name: "union_includes",
3070                        target: "source",
3071                        properties: LinkProperties::union_disc("type", "source"),
3072                    },
3073                    OntologyLink {
3074                        name: "union_includes",
3075                        target: "view",
3076                        properties: LinkProperties::union_disc("type", "view"),
3077                    },
3078                    OntologyLink {
3079                        name: "union_includes",
3080                        target: "mv",
3081                        properties: LinkProperties::union_disc("type", "materialized-view"),
3082                    },
3083                    OntologyLink {
3084                        name: "union_includes",
3085                        target: "sink",
3086                        properties: LinkProperties::union_disc("type", "sink"),
3087                    },
3088                    OntologyLink {
3089                        name: "union_includes",
3090                        target: "index",
3091                        properties: LinkProperties::union_disc("type", "index"),
3092                    },
3093                    OntologyLink {
3094                        name: "union_includes",
3095                        target: "connection",
3096                        properties: LinkProperties::union_disc("type", "connection"),
3097                    },
3098                    OntologyLink {
3099                        name: "union_includes",
3100                        target: "type",
3101                        properties: LinkProperties::union_disc("type", "type"),
3102                    },
3103                    OntologyLink {
3104                        name: "union_includes",
3105                        target: "function",
3106                        properties: LinkProperties::union_disc("type", "function"),
3107                    },
3108                    OntologyLink {
3109                        name: "union_includes",
3110                        target: "secret",
3111                        properties: LinkProperties::union_disc("type", "secret"),
3112                    },
3113                    OntologyLink {
3114                        name: "in_schema",
3115                        target: "schema",
3116                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
3117                    },
3118                    OntologyLink {
3119                        name: "owned_by",
3120                        target: "role",
3121                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
3122                    },
3123                    OntologyLink {
3124                        name: "on_cluster",
3125                        target: "cluster",
3126                        properties: LinkProperties::fk_nullable(
3127                            "cluster_id",
3128                            "id",
3129                            Cardinality::ManyToOne,
3130                        ),
3131                    },
3132                ]
3133            },
3134            column_semantic_types: &const {
3135                [
3136                    ("id", SemanticType::CatalogItemId),
3137                    ("oid", SemanticType::OID),
3138                    ("schema_id", SemanticType::SchemaId),
3139                    ("type", SemanticType::ObjectType),
3140                    ("owner_id", SemanticType::RoleId),
3141                    ("cluster_id", SemanticType::ClusterId),
3142                ]
3143            },
3144        }),
3145    }
3146});
3147
3148pub static MZ_TIMEZONE_ABBREVIATIONS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3149    name: "mz_timezone_abbreviations",
3150    schema: MZ_CATALOG_SCHEMA,
3151    oid: oid::VIEW_MZ_TIMEZONE_ABBREVIATIONS_OID,
3152    desc: RelationDesc::builder()
3153        .with_column("abbreviation", SqlScalarType::String.nullable(false))
3154        .with_column("utc_offset", SqlScalarType::Interval.nullable(true))
3155        .with_column("dst", SqlScalarType::Bool.nullable(true))
3156        .with_column("timezone_name", SqlScalarType::String.nullable(true))
3157        .with_key(vec![0])
3158        .finish(),
3159    column_comments: BTreeMap::from_iter([
3160        ("abbreviation", "The timezone abbreviation."),
3161        (
3162            "utc_offset",
3163            "The UTC offset of the timezone or `NULL` if fixed.",
3164        ),
3165        (
3166            "dst",
3167            "Whether the timezone is in daylight savings or `NULL` if fixed.",
3168        ),
3169        (
3170            "timezone_name",
3171            "The full name of the non-fixed timezone or `NULL` if not fixed.",
3172        ),
3173    ]),
3174    sql: format!(
3175        "SELECT * FROM ({}) _ (abbreviation, utc_offset, dst, timezone_name)",
3176        mz_pgtz::abbrev::MZ_CATALOG_TIMEZONE_ABBREVIATIONS_SQL,
3177    )
3178    .leak(),
3179    access: vec![PUBLIC_SELECT],
3180    ontology: None,
3181});
3182
3183pub static MZ_TIMEZONE_NAMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3184    name: "mz_timezone_names",
3185    schema: MZ_CATALOG_SCHEMA,
3186    oid: oid::VIEW_MZ_TIMEZONE_NAMES_OID,
3187    desc: RelationDesc::builder()
3188        .with_column("name", SqlScalarType::String.nullable(false))
3189        .with_key(vec![0])
3190        .finish(),
3191    column_comments: BTreeMap::from_iter([("name", "The timezone name.")]),
3192    sql: format!(
3193        "SELECT * FROM ({}) _ (name)",
3194        mz_pgtz::timezone::MZ_CATALOG_TIMEZONE_NAMES_SQL,
3195    )
3196    .leak(),
3197    access: vec![PUBLIC_SELECT],
3198    ontology: None,
3199});
3200
3201pub const MZ_DATABASES_IND: BuiltinIndex = BuiltinIndex {
3202    name: "mz_databases_ind",
3203    schema: MZ_CATALOG_SCHEMA,
3204    oid: oid::INDEX_MZ_DATABASES_IND_OID,
3205    sql: "IN CLUSTER mz_catalog_server
3206ON mz_catalog.mz_databases (name)",
3207    is_retained_metrics_object: false,
3208};
3209
3210pub const MZ_SCHEMAS_IND: BuiltinIndex = BuiltinIndex {
3211    name: "mz_schemas_ind",
3212    schema: MZ_CATALOG_SCHEMA,
3213    oid: oid::INDEX_MZ_SCHEMAS_IND_OID,
3214    sql: "IN CLUSTER mz_catalog_server
3215ON mz_catalog.mz_schemas (database_id)",
3216    is_retained_metrics_object: false,
3217};
3218
3219pub const MZ_CONNECTIONS_IND: BuiltinIndex = BuiltinIndex {
3220    name: "mz_connections_ind",
3221    schema: MZ_CATALOG_SCHEMA,
3222    oid: oid::INDEX_MZ_CONNECTIONS_IND_OID,
3223    sql: "IN CLUSTER mz_catalog_server
3224ON mz_catalog.mz_connections (schema_id)",
3225    is_retained_metrics_object: false,
3226};
3227
3228pub const MZ_TABLES_IND: BuiltinIndex = BuiltinIndex {
3229    name: "mz_tables_ind",
3230    schema: MZ_CATALOG_SCHEMA,
3231    oid: oid::INDEX_MZ_TABLES_IND_OID,
3232    sql: "IN CLUSTER mz_catalog_server
3233ON mz_catalog.mz_tables (schema_id)",
3234    is_retained_metrics_object: false,
3235};
3236
3237pub const MZ_TYPES_IND: BuiltinIndex = BuiltinIndex {
3238    name: "mz_types_ind",
3239    schema: MZ_CATALOG_SCHEMA,
3240    oid: oid::INDEX_MZ_TYPES_IND_OID,
3241    sql: "IN CLUSTER mz_catalog_server
3242ON mz_catalog.mz_types (schema_id)",
3243    is_retained_metrics_object: false,
3244};
3245
3246pub const MZ_OBJECTS_IND: BuiltinIndex = BuiltinIndex {
3247    name: "mz_objects_ind",
3248    schema: MZ_CATALOG_SCHEMA,
3249    oid: oid::INDEX_MZ_OBJECTS_IND_OID,
3250    sql: "IN CLUSTER mz_catalog_server
3251ON mz_catalog.mz_objects (schema_id)",
3252    is_retained_metrics_object: false,
3253};
3254
3255pub const MZ_COLUMNS_IND: BuiltinIndex = BuiltinIndex {
3256    name: "mz_columns_ind",
3257    schema: MZ_CATALOG_SCHEMA,
3258    oid: oid::INDEX_MZ_COLUMNS_IND_OID,
3259    sql: "IN CLUSTER mz_catalog_server
3260ON mz_catalog.mz_columns (name)",
3261    is_retained_metrics_object: false,
3262};
3263
3264pub const MZ_SECRETS_IND: BuiltinIndex = BuiltinIndex {
3265    name: "mz_secrets_ind",
3266    schema: MZ_CATALOG_SCHEMA,
3267    oid: oid::INDEX_MZ_SECRETS_IND_OID,
3268    sql: "IN CLUSTER mz_catalog_server
3269ON mz_catalog.mz_secrets (name)",
3270    is_retained_metrics_object: false,
3271};
3272
3273pub const MZ_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3274    name: "mz_views_ind",
3275    schema: MZ_CATALOG_SCHEMA,
3276    oid: oid::INDEX_MZ_VIEWS_IND_OID,
3277    sql: "IN CLUSTER mz_catalog_server
3278ON mz_catalog.mz_views (schema_id)",
3279    is_retained_metrics_object: false,
3280};
3281
3282pub const MZ_CLUSTERS_IND: BuiltinIndex = BuiltinIndex {
3283    name: "mz_clusters_ind",
3284    schema: MZ_CATALOG_SCHEMA,
3285    oid: oid::INDEX_MZ_CLUSTERS_IND_OID,
3286    sql: "IN CLUSTER mz_catalog_server
3287ON mz_catalog.mz_clusters (id)",
3288    is_retained_metrics_object: false,
3289};
3290
3291pub const MZ_INDEXES_IND: BuiltinIndex = BuiltinIndex {
3292    name: "mz_indexes_ind",
3293    schema: MZ_CATALOG_SCHEMA,
3294    oid: oid::INDEX_MZ_INDEXES_IND_OID,
3295    sql: "IN CLUSTER mz_catalog_server
3296ON mz_catalog.mz_indexes (id)",
3297    is_retained_metrics_object: false,
3298};
3299
3300pub const MZ_ROLES_IND: BuiltinIndex = BuiltinIndex {
3301    name: "mz_roles_ind",
3302    schema: MZ_CATALOG_SCHEMA,
3303    oid: oid::INDEX_MZ_ROLES_IND_OID,
3304    sql: "IN CLUSTER mz_catalog_server
3305ON mz_catalog.mz_roles (id)",
3306    is_retained_metrics_object: false,
3307};
3308
3309pub const MZ_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3310    name: "mz_sources_ind",
3311    schema: MZ_CATALOG_SCHEMA,
3312    oid: oid::INDEX_MZ_SOURCES_IND_OID,
3313    sql: "IN CLUSTER mz_catalog_server
3314ON mz_catalog.mz_sources (id)",
3315    is_retained_metrics_object: true,
3316};
3317
3318pub const MZ_SINKS_IND: BuiltinIndex = BuiltinIndex {
3319    name: "mz_sinks_ind",
3320    schema: MZ_CATALOG_SCHEMA,
3321    oid: oid::INDEX_MZ_SINKS_IND_OID,
3322    sql: "IN CLUSTER mz_catalog_server
3323ON mz_catalog.mz_sinks (id)",
3324    is_retained_metrics_object: true,
3325};
3326
3327pub const MZ_MATERIALIZED_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3328    name: "mz_materialized_views_ind",
3329    schema: MZ_CATALOG_SCHEMA,
3330    oid: oid::INDEX_MZ_MATERIALIZED_VIEWS_IND_OID,
3331    sql: "IN CLUSTER mz_catalog_server
3332ON mz_catalog.mz_materialized_views (id)",
3333    is_retained_metrics_object: false,
3334};
3335
3336pub const MZ_CLUSTER_REPLICAS_IND: BuiltinIndex = BuiltinIndex {
3337    name: "mz_cluster_replicas_ind",
3338    schema: MZ_CATALOG_SCHEMA,
3339    oid: oid::INDEX_MZ_CLUSTER_REPLICAS_IND_OID,
3340    sql: "IN CLUSTER mz_catalog_server
3341ON mz_catalog.mz_cluster_replicas (id)",
3342    is_retained_metrics_object: true,
3343};
3344
3345pub const MZ_CLUSTER_REPLICA_SIZES_IND: BuiltinIndex = BuiltinIndex {
3346    name: "mz_cluster_replica_sizes_ind",
3347    schema: MZ_CATALOG_SCHEMA,
3348    oid: oid::INDEX_MZ_CLUSTER_REPLICA_SIZES_IND_OID,
3349    sql: "IN CLUSTER mz_catalog_server
3350ON mz_catalog.mz_cluster_replica_sizes (size)",
3351    is_retained_metrics_object: true,
3352};
3353
3354pub const MZ_KAFKA_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3355    name: "mz_kafka_sources_ind",
3356    schema: MZ_CATALOG_SCHEMA,
3357    oid: oid::INDEX_MZ_KAFKA_SOURCES_IND_OID,
3358    sql: "IN CLUSTER mz_catalog_server
3359ON mz_catalog.mz_kafka_sources (id)",
3360    is_retained_metrics_object: true,
3361};
3362
3363#[cfg(test)]
3364mod tests {
3365    use mz_catalog_protos::objects::ObjectType as ProtoObjectType;
3366    use mz_sql::catalog::ObjectType as SqlObjectType;
3367
3368    use crate::builtin::mz_catalog::MZ_DEFAULT_PRIVILEGES;
3369
3370    /// Checks the `object_type` CASE in `MZ_DEFAULT_PRIVILEGES` against the
3371    /// proto enum and the SQL Display impl it's meant to mirror.
3372    ///
3373    /// The CASE maps `mz_catalog_protos::ObjectType`'s numeric `Serialize_repr`
3374    /// to `mz_sql::catalog::ObjectType`'s `Display`, lowercased. The match in
3375    /// `expected_for` is exhaustive, so a new proto variant won't compile until
3376    /// it's handled. A wrong string in the CASE fails the substring assertion.
3377    #[mz_ore::test]
3378    fn object_type_case_matches_proto_display() {
3379        // Returns `None` for proto variants that never appear in stored
3380        // `DefaultPrivilege` keys (currently just `Unknown`, the zero-value
3381        // sentinel). Match is exhaustive: a new variant forces an update.
3382        fn expected_for(proto: ProtoObjectType) -> Option<SqlObjectType> {
3383            match proto {
3384                ProtoObjectType::Unknown => None,
3385                ProtoObjectType::Table => Some(SqlObjectType::Table),
3386                ProtoObjectType::View => Some(SqlObjectType::View),
3387                ProtoObjectType::MaterializedView => Some(SqlObjectType::MaterializedView),
3388                ProtoObjectType::Source => Some(SqlObjectType::Source),
3389                ProtoObjectType::Sink => Some(SqlObjectType::Sink),
3390                ProtoObjectType::Index => Some(SqlObjectType::Index),
3391                ProtoObjectType::Type => Some(SqlObjectType::Type),
3392                ProtoObjectType::Role => Some(SqlObjectType::Role),
3393                ProtoObjectType::Cluster => Some(SqlObjectType::Cluster),
3394                ProtoObjectType::ClusterReplica => Some(SqlObjectType::ClusterReplica),
3395                ProtoObjectType::Secret => Some(SqlObjectType::Secret),
3396                ProtoObjectType::Connection => Some(SqlObjectType::Connection),
3397                ProtoObjectType::Database => Some(SqlObjectType::Database),
3398                ProtoObjectType::Schema => Some(SqlObjectType::Schema),
3399                ProtoObjectType::Func => Some(SqlObjectType::Func),
3400                ProtoObjectType::NetworkPolicy => Some(SqlObjectType::NetworkPolicy),
3401            }
3402        }
3403
3404        // The proto enum has no `IntoEnumIterator`, so list every variant by
3405        // hand. A missed entry here just goes unchecked, but the exhaustive
3406        // match in `expected_for` won't compile when a new proto variant lands,
3407        // which is the actual drift defense.
3408        let variants: &[ProtoObjectType] = &[
3409            ProtoObjectType::Unknown,
3410            ProtoObjectType::Table,
3411            ProtoObjectType::View,
3412            ProtoObjectType::MaterializedView,
3413            ProtoObjectType::Source,
3414            ProtoObjectType::Sink,
3415            ProtoObjectType::Index,
3416            ProtoObjectType::Type,
3417            ProtoObjectType::Role,
3418            ProtoObjectType::Cluster,
3419            ProtoObjectType::ClusterReplica,
3420            ProtoObjectType::Secret,
3421            ProtoObjectType::Connection,
3422            ProtoObjectType::Database,
3423            ProtoObjectType::Schema,
3424            ProtoObjectType::Func,
3425            ProtoObjectType::NetworkPolicy,
3426        ];
3427
3428        let sql = MZ_DEFAULT_PRIVILEGES.sql;
3429        for &proto in variants {
3430            // `ObjectType` is `#[repr(u8)]`; the discriminant matches the
3431            // `Serialize_repr` JSON value the SQL CASE arms compare against.
3432            #[allow(clippy::as_conversions)]
3433            let repr = proto as u8;
3434            match expected_for(proto) {
3435                Some(sql_ty) => {
3436                    let display = sql_ty.to_string().to_lowercase();
3437                    // The arms use variable whitespace between `WHEN '..'` and
3438                    // `THEN` to align columns; verify the WHEN and THEN strings
3439                    // appear together with at most one or two spaces between.
3440                    let one_space = format!("WHEN '{repr}' THEN '{display}'");
3441                    let two_spaces = format!("WHEN '{repr}'  THEN '{display}'");
3442                    assert!(
3443                        sql.contains(&one_space) || sql.contains(&two_spaces),
3444                        "missing CASE arm for `{proto:?}`: expected \
3445                         `WHEN '{repr}' THEN '{display}'` (or with double space) \
3446                         in MZ_DEFAULT_PRIVILEGES.sql",
3447                    );
3448                }
3449                None => {
3450                    let pattern = format!("WHEN '{repr}'");
3451                    assert!(
3452                        !sql.contains(&pattern),
3453                        "unexpected CASE arm for sentinel `{proto:?}`: \
3454                         found `{pattern}` in MZ_DEFAULT_PRIVILEGES.sql",
3455                    );
3456                }
3457            }
3458        }
3459    }
3460}