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