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<BuiltinMaterializedView> = LazyLock::new(|| {
2520    BuiltinMaterializedView {
2521        name: "mz_audit_events",
2522        schema: MZ_CATALOG_SCHEMA,
2523        oid: oid::MV_MZ_AUDIT_EVENTS_OID,
2524        desc: RelationDesc::builder()
2525            .with_column("id", SqlScalarType::UInt64.nullable(false))
2526            .with_column("event_type", SqlScalarType::String.nullable(false))
2527            .with_column("object_type", SqlScalarType::String.nullable(false))
2528            .with_column("details", SqlScalarType::Jsonb.nullable(false))
2529            .with_column("user", SqlScalarType::String.nullable(true))
2530            .with_column(
2531                "occurred_at",
2532                SqlScalarType::TimestampTz { precision: None }.nullable(false),
2533            )
2534            .with_key(vec![0])
2535            .finish(),
2536        column_comments: BTreeMap::from_iter([
2537            (
2538                "id",
2539                "Materialize's unique, monotonically increasing ID for the event.",
2540            ),
2541            (
2542                "event_type",
2543                "The type of the event: `create`, `drop`, `alter`, `grant`, `revoke`, or `comment`.",
2544            ),
2545            (
2546                "object_type",
2547                "The type of the affected object: `cluster`, `cluster-replica`, `connection`, `continual-task`, `database`, `func`, `index`, `materialized-view`, `network-policy`, `role`, `schema`, `secret`, `sink`, `source`, `system`, `table`, `type`, or `view`.",
2548            ),
2549            (
2550                "details",
2551                "Additional details about the event. The shape of the details varies based on `event_type` and `object_type`.",
2552            ),
2553            (
2554                "user",
2555                "The user who triggered the event, or `NULL` if triggered by the system.",
2556            ),
2557            (
2558                "occurred_at",
2559                "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.",
2560            ),
2561        ]),
2562        // `event_type` and `object_type` are stored in `mz_catalog_raw` as the
2563        // numeric `Serialize_repr` of `proto::audit_log_event_v1::{EventType,
2564        // ObjectType}`. The CASE expressions map them to the kebab strings
2565        // that `audit_log::{EventType, ObjectType}`'s Display impls produce.
2566        //
2567        // `event` is `AuditLogEvent::V1(AuditLogEventV1)`, externally tagged,
2568        // so we reach through `key.event.V1` for the inner struct.
2569        //
2570        // `user` is `Option<StringWrapper>`: JSON null when absent, otherwise
2571        // `{"inner": "<name>"}`. `->'user'->>'inner'` collapses both to a
2572        // PostgreSQL NULL or the text.
2573        //
2574        // `details` in `mz_catalog_raw` is the externally-tagged proto
2575        // `Details` enum. `parse_catalog_audit_log_details` reshapes it into
2576        // `audit_log::EventDetails::as_json`'s output, the format the prior
2577        // BuiltinTable populator wrote. See that function's docstring and
2578        // the round-trip test in `src/catalog/tests/audit_log_details.rs`.
2579        //
2580        // `occurred_at` is `EpochMillis` (u64). Dividing by 1000.0 and feeding
2581        // to `to_timestamp` matches `mz_ore::now::to_datetime`'s round-trip
2582        // through `chrono::DateTime::from_timestamp_millis`.
2583        sql: "
2584IN CLUSTER mz_catalog_server
2585WITH (
2586    ASSERT NOT NULL id,
2587    ASSERT NOT NULL event_type,
2588    ASSERT NOT NULL object_type,
2589    ASSERT NOT NULL details,
2590    ASSERT NOT NULL occurred_at
2591) AS
2592WITH ev AS (
2593    SELECT data->'key'->'event'->'V1' AS e
2594    FROM mz_internal.mz_catalog_raw
2595    WHERE data->>'kind' = 'AuditLog'
2596)
2597SELECT
2598    (e->>'id')::uint8                                                       AS id,
2599    CASE (e->>'event_type')
2600        WHEN '1' THEN 'create'
2601        WHEN '2' THEN 'drop'
2602        WHEN '3' THEN 'alter'
2603        WHEN '4' THEN 'grant'
2604        WHEN '5' THEN 'revoke'
2605        WHEN '6' THEN 'comment'
2606    END                                                                     AS event_type,
2607    CASE (e->>'object_type')
2608        WHEN '1'  THEN 'cluster'
2609        WHEN '2'  THEN 'cluster-replica'
2610        WHEN '3'  THEN 'connection'
2611        WHEN '4'  THEN 'database'
2612        WHEN '5'  THEN 'func'
2613        WHEN '6'  THEN 'index'
2614        WHEN '7'  THEN 'materialized-view'
2615        WHEN '8'  THEN 'role'
2616        WHEN '9'  THEN 'secret'
2617        WHEN '10' THEN 'schema'
2618        WHEN '11' THEN 'sink'
2619        WHEN '12' THEN 'source'
2620        WHEN '13' THEN 'table'
2621        WHEN '14' THEN 'type'
2622        WHEN '15' THEN 'view'
2623        WHEN '16' THEN 'system'
2624        WHEN '17' THEN 'continual-task'
2625        WHEN '18' THEN 'network-policy'
2626    END                                                                     AS object_type,
2627    mz_internal.parse_catalog_audit_log_details(e->'details')               AS details,
2628    e->'user'->>'inner'                                                     AS \"user\",
2629    -- `occurred_at` is serialized by `proto::EpochMillis` as
2630    -- `{\"millis\": <u64>}`; reach through `'millis'` to get the integer.
2631    to_timestamp(((e->'occurred_at'->>'millis')::float8) / 1000.0)          AS occurred_at
2632FROM ev",
2633        is_retained_metrics_object: false,
2634        access: vec![PUBLIC_SELECT],
2635        ontology: Some(Ontology {
2636            entity_name: "audit_event",
2637            description: "An audit log entry recording a DDL operation",
2638            links: &const { [] },
2639            column_semantic_types: &const {
2640                [
2641                    ("object_type", SemanticType::ObjectType),
2642                    ("occurred_at", SemanticType::WallclockTimestamp),
2643                ]
2644            },
2645        }),
2646    }
2647});
2648
2649pub static MZ_EGRESS_IPS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2650    name: "mz_egress_ips",
2651    schema: MZ_CATALOG_SCHEMA,
2652    oid: oid::TABLE_MZ_EGRESS_IPS_OID,
2653    desc: RelationDesc::builder()
2654        .with_column("egress_ip", SqlScalarType::String.nullable(false))
2655        .with_column("prefix_length", SqlScalarType::Int32.nullable(false))
2656        .with_column("cidr", SqlScalarType::String.nullable(false))
2657        .finish(),
2658    column_comments: BTreeMap::from_iter([
2659        ("egress_ip", "The start of the range of IP addresses."),
2660        (
2661            "prefix_length",
2662            "The number of leading bits in the CIDR netmask.",
2663        ),
2664        ("cidr", "The CIDR representation."),
2665    ]),
2666    is_retained_metrics_object: false,
2667    access: vec![PUBLIC_SELECT],
2668    ontology: Some(Ontology {
2669        entity_name: "egress_ip",
2670        description: "IP addresses used for outbound connections from Materialize",
2671        links: &const { [] },
2672        column_semantic_types: &[],
2673    }),
2674});
2675
2676pub static MZ_AWS_PRIVATELINK_CONNECTIONS: LazyLock<BuiltinTable> =
2677    LazyLock::new(|| BuiltinTable {
2678        name: "mz_aws_privatelink_connections",
2679        schema: MZ_CATALOG_SCHEMA,
2680        oid: oid::TABLE_MZ_AWS_PRIVATELINK_CONNECTIONS_OID,
2681        desc: RelationDesc::builder()
2682            .with_column("id", SqlScalarType::String.nullable(false))
2683            .with_column("principal", SqlScalarType::String.nullable(false))
2684            .finish(),
2685        column_comments: BTreeMap::from_iter([
2686            ("id", "The ID of the connection."),
2687            (
2688                "principal",
2689                "The AWS Principal that Materialize will use to connect to the VPC endpoint.",
2690            ),
2691        ]),
2692        is_retained_metrics_object: false,
2693        access: vec![PUBLIC_SELECT],
2694        ontology: Some(Ontology {
2695            entity_name: "aws_privatelink_connection",
2696            description: "AWS PrivateLink connection configuration",
2697            links: &const {
2698                [OntologyLink {
2699                    name: "details_of",
2700                    target: "connection",
2701                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
2702                }]
2703            },
2704            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
2705        }),
2706    });
2707
2708pub static MZ_CLUSTER_REPLICA_FRONTIERS: LazyLock<BuiltinSource> =
2709    LazyLock::new(|| BuiltinSource {
2710        name: "mz_cluster_replica_frontiers",
2711        schema: MZ_CATALOG_SCHEMA,
2712        oid: oid::SOURCE_MZ_CLUSTER_REPLICA_FRONTIERS_OID,
2713        data_source: IntrospectionType::ReplicaFrontiers.into(),
2714        desc: RelationDesc::builder()
2715            .with_column("object_id", SqlScalarType::String.nullable(false))
2716            .with_column("replica_id", SqlScalarType::String.nullable(false))
2717            .with_column("write_frontier", SqlScalarType::MzTimestamp.nullable(true))
2718            .finish(),
2719        column_comments: BTreeMap::from_iter([
2720            (
2721                "object_id",
2722                "The ID of the source, sink, index, materialized view, or subscription.",
2723            ),
2724            ("replica_id", "The ID of a cluster replica."),
2725            (
2726                "write_frontier",
2727                "The next timestamp at which the output may change.",
2728            ),
2729        ]),
2730        is_retained_metrics_object: false,
2731        access: vec![PUBLIC_SELECT],
2732        ontology: None,
2733    });
2734
2735pub static MZ_CLUSTER_REPLICA_FRONTIERS_IND: LazyLock<BuiltinIndex> =
2736    LazyLock::new(|| BuiltinIndex {
2737        name: "mz_cluster_replica_frontiers_ind",
2738        schema: MZ_CATALOG_SCHEMA,
2739        oid: oid::INDEX_MZ_CLUSTER_REPLICA_FRONTIERS_IND_OID,
2740        sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_cluster_replica_frontiers (object_id)",
2741        is_retained_metrics_object: false,
2742    });
2743
2744pub static MZ_DEFAULT_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2745    BuiltinMaterializedView {
2746        name: "mz_default_privileges",
2747        schema: MZ_CATALOG_SCHEMA,
2748        oid: oid::MV_MZ_DEFAULT_PRIVILEGES_OID,
2749        desc: RelationDesc::builder()
2750            .with_column("role_id", SqlScalarType::String.nullable(false))
2751            .with_column("database_id", SqlScalarType::String.nullable(true))
2752            .with_column("schema_id", SqlScalarType::String.nullable(true))
2753            .with_column("object_type", SqlScalarType::String.nullable(false))
2754            .with_column("grantee", SqlScalarType::String.nullable(false))
2755            .with_column("privileges", SqlScalarType::String.nullable(false))
2756            .finish(),
2757        column_comments: BTreeMap::from_iter([
2758            (
2759                "role_id",
2760                "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.",
2761            ),
2762            (
2763                "database_id",
2764                "Privileges described in this row will be granted only on objects in the database identified by `database_id` if non-null.",
2765            ),
2766            (
2767                "schema_id",
2768                "Privileges described in this row will be granted only on objects in the schema identified by `schema_id` if non-null.",
2769            ),
2770            (
2771                "object_type",
2772                "Privileges described in this row will be granted only on objects of type `object_type`.",
2773            ),
2774            (
2775                "grantee",
2776                "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.",
2777            ),
2778            ("privileges", "The set of privileges that will be granted."),
2779        ]),
2780        // `object_type` in `mz_catalog_raw` is the numeric `Serialize_repr` form
2781        // of `mz_catalog_protos::ObjectType`. The CASE mirrors
2782        // `mz_sql::catalog::ObjectType`'s `Display` impl, lowercased, matching
2783        // the prior `pack_default_privileges_update` populator. Variant `16` is
2784        // reserved/unused; any new proto variant must add a branch here. The
2785        // `object_type_case_matches_proto_display` test in this crate checks
2786        // the mapping.
2787        sql: "
2788IN CLUSTER mz_catalog_server
2789WITH (
2790    ASSERT NOT NULL role_id,
2791    ASSERT NOT NULL object_type,
2792    ASSERT NOT NULL grantee,
2793    ASSERT NOT NULL privileges
2794) AS
2795SELECT
2796    mz_internal.parse_catalog_id(data->'key'->'role_id') AS role_id,
2797    CASE WHEN data->'key'->'database_id' != 'null'::jsonb
2798         THEN mz_internal.parse_catalog_id(data->'key'->'database_id') END AS database_id,
2799    CASE WHEN data->'key'->'schema_id' != 'null'::jsonb
2800         THEN mz_internal.parse_catalog_id(data->'key'->'schema_id') END AS schema_id,
2801    CASE data->'key'->>'object_type'
2802        WHEN '1'  THEN 'table'
2803        WHEN '2'  THEN 'view'
2804        WHEN '3'  THEN 'materialized view'
2805        WHEN '4'  THEN 'source'
2806        WHEN '5'  THEN 'sink'
2807        WHEN '6'  THEN 'index'
2808        WHEN '7'  THEN 'type'
2809        WHEN '8'  THEN 'role'
2810        WHEN '9'  THEN 'cluster'
2811        WHEN '10' THEN 'cluster replica'
2812        WHEN '11' THEN 'secret'
2813        WHEN '12' THEN 'connection'
2814        WHEN '13' THEN 'database'
2815        WHEN '14' THEN 'schema'
2816        WHEN '15' THEN 'function'
2817        -- variant 16 reserved/unused in mz_catalog_protos::ObjectType.
2818        WHEN '17' THEN 'network policy'
2819    END AS object_type,
2820    mz_internal.parse_catalog_id(data->'key'->'grantee') AS grantee,
2821    mz_internal.parse_catalog_acl_mode(data->'value'->'privileges') AS privileges
2822FROM mz_internal.mz_catalog_raw
2823WHERE data->>'kind' = 'DefaultPrivileges'",
2824        is_retained_metrics_object: false,
2825        access: vec![PUBLIC_SELECT],
2826        ontology: Some(Ontology {
2827            entity_name: "default_privilege",
2828            description: "A default privilege rule applied to newly created objects",
2829            links: &const {
2830                [
2831                    OntologyLink {
2832                        name: "default_priv_for_role",
2833                        target: "role",
2834                        properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
2835                    },
2836                    OntologyLink {
2837                        name: "default_priv_in_database",
2838                        target: "database",
2839                        properties: LinkProperties::fk_nullable(
2840                            "database_id",
2841                            "id",
2842                            Cardinality::ManyToOne,
2843                        ),
2844                    },
2845                    OntologyLink {
2846                        name: "default_priv_in_schema",
2847                        target: "schema",
2848                        properties: LinkProperties::fk_nullable(
2849                            "schema_id",
2850                            "id",
2851                            Cardinality::ManyToOne,
2852                        ),
2853                    },
2854                    OntologyLink {
2855                        name: "default_priv_granted_to",
2856                        target: "role",
2857                        properties: LinkProperties::fk("grantee", "id", Cardinality::ManyToOne),
2858                    },
2859                ]
2860            },
2861            column_semantic_types: &const {
2862                [
2863                    ("role_id", SemanticType::RoleId),
2864                    ("database_id", SemanticType::DatabaseId),
2865                    ("schema_id", SemanticType::SchemaId),
2866                    ("object_type", SemanticType::ObjectType),
2867                    ("grantee", SemanticType::RoleId),
2868                ]
2869            },
2870        }),
2871    }
2872});
2873
2874pub static MZ_SYSTEM_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2875    BuiltinMaterializedView {
2876        name: "mz_system_privileges",
2877        schema: MZ_CATALOG_SCHEMA,
2878        oid: oid::MV_MZ_SYSTEM_PRIVILEGES_OID,
2879        desc: RelationDesc::builder()
2880            .with_column("privileges", SqlScalarType::MzAclItem.nullable(false))
2881            .finish(),
2882        column_comments: BTreeMap::from_iter([(
2883            "privileges",
2884            "The privileges belonging to the system.",
2885        )]),
2886        // The durable row holds `(grantee, grantor, acl_mode)`; we rebuild the
2887        // single-element JSON shape that `parse_catalog_privileges` expects and
2888        // unnest it to recover the `mz_aclitem`.
2889        sql: "
2890IN CLUSTER mz_catalog_server
2891WITH (
2892    ASSERT NOT NULL privileges
2893) AS
2894SELECT
2895    unnest(mz_internal.parse_catalog_privileges(
2896        jsonb_build_array(
2897            jsonb_build_object(
2898                'grantee',  data->'key'->'grantee',
2899                'grantor',  data->'key'->'grantor',
2900                'acl_mode', data->'value'->'acl_mode'
2901            )
2902        )
2903    )) AS privileges
2904FROM mz_internal.mz_catalog_raw
2905WHERE data->>'kind' = 'SystemPrivileges'",
2906        is_retained_metrics_object: false,
2907        access: vec![PUBLIC_SELECT],
2908        ontology: Some(Ontology {
2909            entity_name: "system_privilege",
2910            description: "A system-level privilege grant",
2911            links: &const { [] },
2912            column_semantic_types: &[],
2913        }),
2914    }
2915});
2916
2917pub static MZ_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2918    name: "mz_storage_usage",
2919    schema: MZ_CATALOG_SCHEMA,
2920    oid: oid::VIEW_MZ_STORAGE_USAGE_OID,
2921    desc: RelationDesc::builder()
2922        .with_column("object_id", SqlScalarType::String.nullable(false))
2923        .with_column("size_bytes", SqlScalarType::UInt64.nullable(false))
2924        .with_column(
2925            "collection_timestamp",
2926            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2927        )
2928        .with_key(vec![0, 2])
2929        .finish(),
2930    column_comments: BTreeMap::from_iter([
2931        (
2932            "object_id",
2933            "The ID of the table, source, or materialized view.",
2934        ),
2935        (
2936            "size_bytes",
2937            "The number of storage bytes used by the object.",
2938        ),
2939        (
2940            "collection_timestamp",
2941            "The time at which storage usage of the object was assessed.",
2942        ),
2943    ]),
2944    sql: "
2945SELECT
2946    object_id,
2947    sum(size_bytes)::uint8 AS size_bytes,
2948    collection_timestamp
2949FROM
2950    mz_internal.mz_storage_shards
2951    JOIN mz_internal.mz_storage_usage_by_shard USING (shard_id)
2952GROUP BY object_id, collection_timestamp",
2953    access: vec![PUBLIC_SELECT],
2954    ontology: Some(Ontology {
2955        entity_name: "storage_usage",
2956        description: "Historical storage usage per object over time",
2957        links: &const {
2958            [OntologyLink {
2959                name: "storage_usage_of",
2960                target: "object",
2961                properties: LinkProperties::fk("object_id", "id", Cardinality::ManyToOne),
2962            }]
2963        },
2964        column_semantic_types: &const {
2965            [
2966                ("object_id", SemanticType::CatalogItemId),
2967                ("size_bytes", SemanticType::ByteCount),
2968                ("collection_timestamp", SemanticType::WallclockTimestamp),
2969            ]
2970        },
2971    }),
2972});
2973
2974pub static MZ_RECENT_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| {
2975    BuiltinView {
2976    name: "mz_recent_storage_usage",
2977    schema: MZ_CATALOG_SCHEMA,
2978    oid: oid::VIEW_MZ_RECENT_STORAGE_USAGE_OID,
2979    desc: RelationDesc::builder()
2980        .with_column("object_id", SqlScalarType::String.nullable(false))
2981        .with_column("size_bytes", SqlScalarType::UInt64.nullable(true))
2982        .with_key(vec![0])
2983        .finish(),
2984    column_comments: BTreeMap::from_iter([
2985        ("object_id", "The ID of the table, source, or materialized view."),
2986        ("size_bytes", "The number of storage bytes used by the object in the most recent assessment."),
2987    ]),
2988    sql: "
2989WITH
2990
2991recent_storage_usage_by_shard AS (
2992    SELECT shard_id, size_bytes, collection_timestamp
2993    FROM mz_internal.mz_storage_usage_by_shard
2994    -- Restricting to the last 6 hours makes it feasible to index the view.
2995    WHERE collection_timestamp + '6 hours' >= mz_now()
2996),
2997
2998most_recent_collection_timestamp_by_shard AS (
2999    SELECT shard_id, max(collection_timestamp) AS collection_timestamp
3000    FROM recent_storage_usage_by_shard
3001    GROUP BY shard_id
3002)
3003
3004SELECT
3005    object_id,
3006    sum(size_bytes)::uint8 AS size_bytes
3007FROM
3008    mz_internal.mz_storage_shards
3009    LEFT JOIN most_recent_collection_timestamp_by_shard
3010        ON mz_storage_shards.shard_id = most_recent_collection_timestamp_by_shard.shard_id
3011    LEFT JOIN recent_storage_usage_by_shard
3012        ON mz_storage_shards.shard_id = recent_storage_usage_by_shard.shard_id
3013        AND most_recent_collection_timestamp_by_shard.collection_timestamp = recent_storage_usage_by_shard.collection_timestamp
3014GROUP BY object_id",
3015    access: vec![PUBLIC_SELECT],
3016    ontology: Some(Ontology {
3017        entity_name: "recent_storage",
3018        description: "Most recent storage usage snapshot per object",
3019        links: &const { [
3020            OntologyLink { name: "recent_storage_of", target: "object", properties: LinkProperties::fk("object_id", "id", Cardinality::OneToOne) },
3021        ] },
3022        column_semantic_types: &const {[("object_id", SemanticType::CatalogItemId), ("size_bytes", SemanticType::ByteCount)]},
3023    }),
3024}
3025});
3026
3027pub static MZ_RECENT_STORAGE_USAGE_IND: LazyLock<BuiltinIndex> = LazyLock::new(|| BuiltinIndex {
3028    name: "mz_recent_storage_usage_ind",
3029    schema: MZ_CATALOG_SCHEMA,
3030    oid: oid::INDEX_MZ_RECENT_STORAGE_USAGE_IND_OID,
3031    sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_recent_storage_usage (object_id)",
3032    is_retained_metrics_object: false,
3033});
3034
3035pub static MZ_RELATIONS: LazyLock<BuiltinView> = LazyLock::new(|| {
3036    BuiltinView {
3037        name: "mz_relations",
3038        schema: MZ_CATALOG_SCHEMA,
3039        oid: oid::VIEW_MZ_RELATIONS_OID,
3040        desc: RelationDesc::builder()
3041            .with_column("id", SqlScalarType::String.nullable(false))
3042            .with_column("oid", SqlScalarType::Oid.nullable(false))
3043            .with_column("schema_id", SqlScalarType::String.nullable(false))
3044            .with_column("name", SqlScalarType::String.nullable(false))
3045            .with_column("type", SqlScalarType::String.nullable(false))
3046            .with_column("owner_id", SqlScalarType::String.nullable(false))
3047            .with_column("cluster_id", SqlScalarType::String.nullable(true))
3048            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false))
3049            .finish(),
3050        column_comments: BTreeMap::from_iter([
3051            ("id", "Materialize's unique ID for the relation."),
3052            ("oid", "A PostgreSQL-compatible OID for the relation."),
3053            ("schema_id", "The ID of the schema to which the relation belongs. Corresponds to `mz_schemas.id`."),
3054            ("name", "The name of the relation."),
3055            ("type", "The type of the relation: either `table`, `source`, `view`, or `materialized-view`."),
3056            ("owner_id", "The role ID of the owner of the relation. Corresponds to `mz_roles.id`."),
3057            ("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."),
3058            ("privileges", "The privileges belonging to the relation."),
3059        ]),
3060        sql: "
3061      SELECT id, oid, schema_id, name, 'table' AS type, owner_id, NULL::text AS cluster_id, privileges FROM mz_catalog.mz_tables
3062UNION ALL SELECT id, oid, schema_id, name, 'source', owner_id, cluster_id, privileges FROM mz_catalog.mz_sources
3063UNION ALL SELECT id, oid, schema_id, name, 'view', owner_id, NULL::text, privileges FROM mz_catalog.mz_views
3064UNION ALL SELECT id, oid, schema_id, name, 'materialized-view', owner_id, cluster_id, privileges FROM mz_catalog.mz_materialized_views",
3065        access: vec![PUBLIC_SELECT],
3066        ontology: Some(Ontology {
3067            entity_name: "relation",
3068            description: "Union of all relation types: tables, sources, views, MVs (convenience view)",
3069            links: &const { [
3070                OntologyLink { name: "union_includes", target: "table", properties: LinkProperties::union_disc("type", "table") },
3071                OntologyLink { name: "union_includes", target: "source", properties: LinkProperties::union_disc("type", "source") },
3072                OntologyLink { name: "union_includes", target: "view", properties: LinkProperties::union_disc("type", "view") },
3073                OntologyLink { name: "union_includes", target: "mv", properties: LinkProperties::union_disc("type", "materialized-view") },
3074            ] },
3075            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)]},
3076        }),
3077    }
3078});
3079
3080pub static MZ_OBJECTS: LazyLock<BuiltinView> = LazyLock::new(|| {
3081    BuiltinView {
3082        name: "mz_objects",
3083        schema: MZ_CATALOG_SCHEMA,
3084        oid: oid::VIEW_MZ_OBJECTS_OID,
3085        desc: RelationDesc::builder()
3086            .with_column("id", SqlScalarType::String.nullable(false))
3087            .with_column("oid", SqlScalarType::Oid.nullable(false))
3088            .with_column("schema_id", SqlScalarType::String.nullable(false))
3089            .with_column("name", SqlScalarType::String.nullable(false))
3090            .with_column("type", SqlScalarType::String.nullable(false))
3091            .with_column("owner_id", SqlScalarType::String.nullable(false))
3092            .with_column("cluster_id", SqlScalarType::String.nullable(true))
3093            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(true))
3094            .finish(),
3095        column_comments: BTreeMap::from_iter([
3096            ("id", "Materialize's unique ID for the object."),
3097            ("oid", "A PostgreSQL-compatible OID for the object."),
3098            ("schema_id", "The ID of the schema to which the object belongs. Corresponds to `mz_schemas.id`."),
3099            ("name", "The name of the object."),
3100            ("type", "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`."),
3101            ("owner_id", "The role ID of the owner of the object. Corresponds to `mz_roles.id`."),
3102            ("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."),
3103            ("privileges", "The privileges belonging to the object."),
3104        ]),
3105        sql:
3106        "SELECT id, oid, schema_id, name, type, owner_id, cluster_id, privileges FROM mz_catalog.mz_relations
3107UNION ALL
3108    SELECT id, oid, schema_id, name, 'sink', owner_id, cluster_id, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_sinks
3109UNION ALL
3110    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[]
3111    FROM mz_catalog.mz_indexes
3112    JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
3113UNION ALL
3114    SELECT id, oid, schema_id, name, 'connection', owner_id, NULL::text, privileges FROM mz_catalog.mz_connections
3115UNION ALL
3116    SELECT id, oid, schema_id, name, 'type', owner_id, NULL::text, privileges FROM mz_catalog.mz_types
3117UNION ALL
3118    SELECT id, oid, schema_id, name, 'function', owner_id, NULL::text, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_functions
3119UNION ALL
3120    SELECT id, oid, schema_id, name, 'secret', owner_id, NULL::text, privileges FROM mz_catalog.mz_secrets",
3121        access: vec![PUBLIC_SELECT],
3122        ontology: Some(Ontology {
3123            entity_name: "object",
3124            description: "Union of all object types: relations, indexes, connections, etc. (convenience view)",
3125            links: &const {
3126                [
3127                    OntologyLink {
3128                        name: "union_includes",
3129                        target: "relation",
3130                        properties: LinkProperties::Union {
3131                            discriminator_column: None,
3132                            discriminator_value: None,
3133                            note: Some("covers all mz_relations rows (table, source, view, mv)"),
3134                        },
3135                    },
3136                    OntologyLink {
3137                        name: "union_includes",
3138                        target: "table",
3139                        properties: LinkProperties::union_disc("type", "table"),
3140                    },
3141                    OntologyLink {
3142                        name: "union_includes",
3143                        target: "source",
3144                        properties: LinkProperties::union_disc("type", "source"),
3145                    },
3146                    OntologyLink {
3147                        name: "union_includes",
3148                        target: "view",
3149                        properties: LinkProperties::union_disc("type", "view"),
3150                    },
3151                    OntologyLink {
3152                        name: "union_includes",
3153                        target: "mv",
3154                        properties: LinkProperties::union_disc("type", "materialized-view"),
3155                    },
3156                    OntologyLink {
3157                        name: "union_includes",
3158                        target: "sink",
3159                        properties: LinkProperties::union_disc("type", "sink"),
3160                    },
3161                    OntologyLink {
3162                        name: "union_includes",
3163                        target: "index",
3164                        properties: LinkProperties::union_disc("type", "index"),
3165                    },
3166                    OntologyLink {
3167                        name: "union_includes",
3168                        target: "connection",
3169                        properties: LinkProperties::union_disc("type", "connection"),
3170                    },
3171                    OntologyLink {
3172                        name: "union_includes",
3173                        target: "type",
3174                        properties: LinkProperties::union_disc("type", "type"),
3175                    },
3176                    OntologyLink {
3177                        name: "union_includes",
3178                        target: "function",
3179                        properties: LinkProperties::union_disc("type", "function"),
3180                    },
3181                    OntologyLink {
3182                        name: "union_includes",
3183                        target: "secret",
3184                        properties: LinkProperties::union_disc("type", "secret"),
3185                    },
3186                    OntologyLink {
3187                        name: "in_schema",
3188                        target: "schema",
3189                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
3190                    },
3191                    OntologyLink {
3192                        name: "owned_by",
3193                        target: "role",
3194                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
3195                    },
3196                    OntologyLink {
3197                        name: "on_cluster",
3198                        target: "cluster",
3199                        properties: LinkProperties::fk_nullable(
3200                            "cluster_id",
3201                            "id",
3202                            Cardinality::ManyToOne,
3203                        ),
3204                    },
3205                ]
3206            },
3207            column_semantic_types: &const {
3208                [
3209                    ("id", SemanticType::CatalogItemId),
3210                    ("oid", SemanticType::OID),
3211                    ("schema_id", SemanticType::SchemaId),
3212                    ("type", SemanticType::ObjectType),
3213                    ("owner_id", SemanticType::RoleId),
3214                    ("cluster_id", SemanticType::ClusterId),
3215                ]
3216            },
3217        }),
3218    }
3219});
3220
3221pub static MZ_TIMEZONE_ABBREVIATIONS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3222    name: "mz_timezone_abbreviations",
3223    schema: MZ_CATALOG_SCHEMA,
3224    oid: oid::VIEW_MZ_TIMEZONE_ABBREVIATIONS_OID,
3225    desc: RelationDesc::builder()
3226        .with_column("abbreviation", SqlScalarType::String.nullable(false))
3227        .with_column("utc_offset", SqlScalarType::Interval.nullable(true))
3228        .with_column("dst", SqlScalarType::Bool.nullable(true))
3229        .with_column("timezone_name", SqlScalarType::String.nullable(true))
3230        .with_key(vec![0])
3231        .finish(),
3232    column_comments: BTreeMap::from_iter([
3233        ("abbreviation", "The timezone abbreviation."),
3234        (
3235            "utc_offset",
3236            "The UTC offset of the timezone or `NULL` if fixed.",
3237        ),
3238        (
3239            "dst",
3240            "Whether the timezone is in daylight savings or `NULL` if fixed.",
3241        ),
3242        (
3243            "timezone_name",
3244            "The full name of the non-fixed timezone or `NULL` if not fixed.",
3245        ),
3246    ]),
3247    sql: format!(
3248        "SELECT * FROM ({}) _ (abbreviation, utc_offset, dst, timezone_name)",
3249        mz_pgtz::abbrev::MZ_CATALOG_TIMEZONE_ABBREVIATIONS_SQL,
3250    )
3251    .leak(),
3252    access: vec![PUBLIC_SELECT],
3253    ontology: None,
3254});
3255
3256pub static MZ_TIMEZONE_NAMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3257    name: "mz_timezone_names",
3258    schema: MZ_CATALOG_SCHEMA,
3259    oid: oid::VIEW_MZ_TIMEZONE_NAMES_OID,
3260    desc: RelationDesc::builder()
3261        .with_column("name", SqlScalarType::String.nullable(false))
3262        .with_key(vec![0])
3263        .finish(),
3264    column_comments: BTreeMap::from_iter([("name", "The timezone name.")]),
3265    sql: format!(
3266        "SELECT * FROM ({}) _ (name)",
3267        mz_pgtz::timezone::MZ_CATALOG_TIMEZONE_NAMES_SQL,
3268    )
3269    .leak(),
3270    access: vec![PUBLIC_SELECT],
3271    ontology: None,
3272});
3273
3274pub const MZ_DATABASES_IND: BuiltinIndex = BuiltinIndex {
3275    name: "mz_databases_ind",
3276    schema: MZ_CATALOG_SCHEMA,
3277    oid: oid::INDEX_MZ_DATABASES_IND_OID,
3278    sql: "IN CLUSTER mz_catalog_server
3279ON mz_catalog.mz_databases (name)",
3280    is_retained_metrics_object: false,
3281};
3282
3283pub const MZ_SCHEMAS_IND: BuiltinIndex = BuiltinIndex {
3284    name: "mz_schemas_ind",
3285    schema: MZ_CATALOG_SCHEMA,
3286    oid: oid::INDEX_MZ_SCHEMAS_IND_OID,
3287    sql: "IN CLUSTER mz_catalog_server
3288ON mz_catalog.mz_schemas (database_id)",
3289    is_retained_metrics_object: false,
3290};
3291
3292pub const MZ_CONNECTIONS_IND: BuiltinIndex = BuiltinIndex {
3293    name: "mz_connections_ind",
3294    schema: MZ_CATALOG_SCHEMA,
3295    oid: oid::INDEX_MZ_CONNECTIONS_IND_OID,
3296    sql: "IN CLUSTER mz_catalog_server
3297ON mz_catalog.mz_connections (schema_id)",
3298    is_retained_metrics_object: false,
3299};
3300
3301pub const MZ_TABLES_IND: BuiltinIndex = BuiltinIndex {
3302    name: "mz_tables_ind",
3303    schema: MZ_CATALOG_SCHEMA,
3304    oid: oid::INDEX_MZ_TABLES_IND_OID,
3305    sql: "IN CLUSTER mz_catalog_server
3306ON mz_catalog.mz_tables (schema_id)",
3307    is_retained_metrics_object: false,
3308};
3309
3310pub const MZ_TYPES_IND: BuiltinIndex = BuiltinIndex {
3311    name: "mz_types_ind",
3312    schema: MZ_CATALOG_SCHEMA,
3313    oid: oid::INDEX_MZ_TYPES_IND_OID,
3314    sql: "IN CLUSTER mz_catalog_server
3315ON mz_catalog.mz_types (schema_id)",
3316    is_retained_metrics_object: false,
3317};
3318
3319pub const MZ_OBJECTS_IND: BuiltinIndex = BuiltinIndex {
3320    name: "mz_objects_ind",
3321    schema: MZ_CATALOG_SCHEMA,
3322    oid: oid::INDEX_MZ_OBJECTS_IND_OID,
3323    sql: "IN CLUSTER mz_catalog_server
3324ON mz_catalog.mz_objects (schema_id)",
3325    is_retained_metrics_object: false,
3326};
3327
3328pub const MZ_COLUMNS_IND: BuiltinIndex = BuiltinIndex {
3329    name: "mz_columns_ind",
3330    schema: MZ_CATALOG_SCHEMA,
3331    oid: oid::INDEX_MZ_COLUMNS_IND_OID,
3332    sql: "IN CLUSTER mz_catalog_server
3333ON mz_catalog.mz_columns (name)",
3334    is_retained_metrics_object: false,
3335};
3336
3337pub const MZ_SECRETS_IND: BuiltinIndex = BuiltinIndex {
3338    name: "mz_secrets_ind",
3339    schema: MZ_CATALOG_SCHEMA,
3340    oid: oid::INDEX_MZ_SECRETS_IND_OID,
3341    sql: "IN CLUSTER mz_catalog_server
3342ON mz_catalog.mz_secrets (name)",
3343    is_retained_metrics_object: false,
3344};
3345
3346pub const MZ_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3347    name: "mz_views_ind",
3348    schema: MZ_CATALOG_SCHEMA,
3349    oid: oid::INDEX_MZ_VIEWS_IND_OID,
3350    sql: "IN CLUSTER mz_catalog_server
3351ON mz_catalog.mz_views (schema_id)",
3352    is_retained_metrics_object: false,
3353};
3354
3355pub const MZ_CLUSTERS_IND: BuiltinIndex = BuiltinIndex {
3356    name: "mz_clusters_ind",
3357    schema: MZ_CATALOG_SCHEMA,
3358    oid: oid::INDEX_MZ_CLUSTERS_IND_OID,
3359    sql: "IN CLUSTER mz_catalog_server
3360ON mz_catalog.mz_clusters (id)",
3361    is_retained_metrics_object: false,
3362};
3363
3364pub const MZ_INDEXES_IND: BuiltinIndex = BuiltinIndex {
3365    name: "mz_indexes_ind",
3366    schema: MZ_CATALOG_SCHEMA,
3367    oid: oid::INDEX_MZ_INDEXES_IND_OID,
3368    sql: "IN CLUSTER mz_catalog_server
3369ON mz_catalog.mz_indexes (id)",
3370    is_retained_metrics_object: false,
3371};
3372
3373pub const MZ_ROLES_IND: BuiltinIndex = BuiltinIndex {
3374    name: "mz_roles_ind",
3375    schema: MZ_CATALOG_SCHEMA,
3376    oid: oid::INDEX_MZ_ROLES_IND_OID,
3377    sql: "IN CLUSTER mz_catalog_server
3378ON mz_catalog.mz_roles (id)",
3379    is_retained_metrics_object: false,
3380};
3381
3382pub const MZ_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3383    name: "mz_sources_ind",
3384    schema: MZ_CATALOG_SCHEMA,
3385    oid: oid::INDEX_MZ_SOURCES_IND_OID,
3386    sql: "IN CLUSTER mz_catalog_server
3387ON mz_catalog.mz_sources (id)",
3388    is_retained_metrics_object: true,
3389};
3390
3391pub const MZ_SINKS_IND: BuiltinIndex = BuiltinIndex {
3392    name: "mz_sinks_ind",
3393    schema: MZ_CATALOG_SCHEMA,
3394    oid: oid::INDEX_MZ_SINKS_IND_OID,
3395    sql: "IN CLUSTER mz_catalog_server
3396ON mz_catalog.mz_sinks (id)",
3397    is_retained_metrics_object: true,
3398};
3399
3400pub const MZ_MATERIALIZED_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3401    name: "mz_materialized_views_ind",
3402    schema: MZ_CATALOG_SCHEMA,
3403    oid: oid::INDEX_MZ_MATERIALIZED_VIEWS_IND_OID,
3404    sql: "IN CLUSTER mz_catalog_server
3405ON mz_catalog.mz_materialized_views (id)",
3406    is_retained_metrics_object: false,
3407};
3408
3409pub const MZ_CLUSTER_REPLICAS_IND: BuiltinIndex = BuiltinIndex {
3410    name: "mz_cluster_replicas_ind",
3411    schema: MZ_CATALOG_SCHEMA,
3412    oid: oid::INDEX_MZ_CLUSTER_REPLICAS_IND_OID,
3413    sql: "IN CLUSTER mz_catalog_server
3414ON mz_catalog.mz_cluster_replicas (id)",
3415    is_retained_metrics_object: true,
3416};
3417
3418pub const MZ_CLUSTER_REPLICA_SIZES_IND: BuiltinIndex = BuiltinIndex {
3419    name: "mz_cluster_replica_sizes_ind",
3420    schema: MZ_CATALOG_SCHEMA,
3421    oid: oid::INDEX_MZ_CLUSTER_REPLICA_SIZES_IND_OID,
3422    sql: "IN CLUSTER mz_catalog_server
3423ON mz_catalog.mz_cluster_replica_sizes (size)",
3424    is_retained_metrics_object: true,
3425};
3426
3427pub const MZ_KAFKA_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3428    name: "mz_kafka_sources_ind",
3429    schema: MZ_CATALOG_SCHEMA,
3430    oid: oid::INDEX_MZ_KAFKA_SOURCES_IND_OID,
3431    sql: "IN CLUSTER mz_catalog_server
3432ON mz_catalog.mz_kafka_sources (id)",
3433    is_retained_metrics_object: true,
3434};
3435
3436#[cfg(test)]
3437mod tests {
3438    use mz_catalog_protos::objects::ObjectType as ProtoObjectType;
3439    use mz_sql::catalog::ObjectType as SqlObjectType;
3440
3441    use crate::builtin::mz_catalog::MZ_DEFAULT_PRIVILEGES;
3442
3443    /// Checks the `object_type` CASE in `MZ_DEFAULT_PRIVILEGES` against the
3444    /// proto enum and the SQL Display impl it's meant to mirror.
3445    ///
3446    /// The CASE maps `mz_catalog_protos::ObjectType`'s numeric `Serialize_repr`
3447    /// to `mz_sql::catalog::ObjectType`'s `Display`, lowercased. The match in
3448    /// `expected_for` is exhaustive, so a new proto variant won't compile until
3449    /// it's handled. A wrong string in the CASE fails the substring assertion.
3450    #[mz_ore::test]
3451    fn object_type_case_matches_proto_display() {
3452        // Returns `None` for proto variants that never appear in stored
3453        // `DefaultPrivilege` keys (currently just `Unknown`, the zero-value
3454        // sentinel). Match is exhaustive: a new variant forces an update.
3455        fn expected_for(proto: ProtoObjectType) -> Option<SqlObjectType> {
3456            match proto {
3457                ProtoObjectType::Unknown => None,
3458                ProtoObjectType::Table => Some(SqlObjectType::Table),
3459                ProtoObjectType::View => Some(SqlObjectType::View),
3460                ProtoObjectType::MaterializedView => Some(SqlObjectType::MaterializedView),
3461                ProtoObjectType::Source => Some(SqlObjectType::Source),
3462                ProtoObjectType::Sink => Some(SqlObjectType::Sink),
3463                ProtoObjectType::Index => Some(SqlObjectType::Index),
3464                ProtoObjectType::Type => Some(SqlObjectType::Type),
3465                ProtoObjectType::Role => Some(SqlObjectType::Role),
3466                ProtoObjectType::Cluster => Some(SqlObjectType::Cluster),
3467                ProtoObjectType::ClusterReplica => Some(SqlObjectType::ClusterReplica),
3468                ProtoObjectType::Secret => Some(SqlObjectType::Secret),
3469                ProtoObjectType::Connection => Some(SqlObjectType::Connection),
3470                ProtoObjectType::Database => Some(SqlObjectType::Database),
3471                ProtoObjectType::Schema => Some(SqlObjectType::Schema),
3472                ProtoObjectType::Func => Some(SqlObjectType::Func),
3473                ProtoObjectType::NetworkPolicy => Some(SqlObjectType::NetworkPolicy),
3474            }
3475        }
3476
3477        // The proto enum has no `IntoEnumIterator`, so list every variant by
3478        // hand. A missed entry here just goes unchecked, but the exhaustive
3479        // match in `expected_for` won't compile when a new proto variant lands,
3480        // which is the actual drift defense.
3481        let variants: &[ProtoObjectType] = &[
3482            ProtoObjectType::Unknown,
3483            ProtoObjectType::Table,
3484            ProtoObjectType::View,
3485            ProtoObjectType::MaterializedView,
3486            ProtoObjectType::Source,
3487            ProtoObjectType::Sink,
3488            ProtoObjectType::Index,
3489            ProtoObjectType::Type,
3490            ProtoObjectType::Role,
3491            ProtoObjectType::Cluster,
3492            ProtoObjectType::ClusterReplica,
3493            ProtoObjectType::Secret,
3494            ProtoObjectType::Connection,
3495            ProtoObjectType::Database,
3496            ProtoObjectType::Schema,
3497            ProtoObjectType::Func,
3498            ProtoObjectType::NetworkPolicy,
3499        ];
3500
3501        let sql = MZ_DEFAULT_PRIVILEGES.sql;
3502        for &proto in variants {
3503            // `ObjectType` is `#[repr(u8)]`; the discriminant matches the
3504            // `Serialize_repr` JSON value the SQL CASE arms compare against.
3505            #[allow(clippy::as_conversions)]
3506            let repr = proto as u8;
3507            match expected_for(proto) {
3508                Some(sql_ty) => {
3509                    let display = sql_ty.to_string().to_lowercase();
3510                    // The arms use variable whitespace between `WHEN '..'` and
3511                    // `THEN` to align columns; verify the WHEN and THEN strings
3512                    // appear together with at most one or two spaces between.
3513                    let one_space = format!("WHEN '{repr}' THEN '{display}'");
3514                    let two_spaces = format!("WHEN '{repr}'  THEN '{display}'");
3515                    assert!(
3516                        sql.contains(&one_space) || sql.contains(&two_spaces),
3517                        "missing CASE arm for `{proto:?}`: expected \
3518                         `WHEN '{repr}' THEN '{display}'` (or with double space) \
3519                         in MZ_DEFAULT_PRIVILEGES.sql",
3520                    );
3521                }
3522                None => {
3523                    let pattern = format!("WHEN '{repr}'");
3524                    assert!(
3525                        !sql.contains(&pattern),
3526                        "unexpected CASE arm for sentinel `{proto:?}`: \
3527                         found `{pattern}` in MZ_DEFAULT_PRIVILEGES.sql",
3528                    );
3529                }
3530            }
3531        }
3532    }
3533}