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