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        ("memory_bytes", "The RAM allocation per process, in bytes."),
2808        ("disk_bytes", "The disk allocation per process."),
2809        (
2810            "credits_per_hour",
2811            "The number of compute credits consumed per hour.",
2812        ),
2813    ]),
2814    is_retained_metrics_object: true,
2815    access: vec![PUBLIC_SELECT],
2816    ontology: Some(Ontology {
2817        entity_name: "replica_size",
2818        description: "Available cluster replica sizes with CPU, memory, and credit cost",
2819        links: &const { [] },
2820        column_semantic_types: &const {
2821            [
2822                ("memory_bytes", SemanticType::ByteCount),
2823                ("disk_bytes", SemanticType::ByteCount),
2824                ("credits_per_hour", SemanticType::CreditRate),
2825            ]
2826        },
2827    }),
2828});
2829
2830pub static MZ_AUDIT_EVENTS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
2831    BuiltinMaterializedView {
2832        name: "mz_audit_events",
2833        schema: MZ_CATALOG_SCHEMA,
2834        oid: oid::MV_MZ_AUDIT_EVENTS_OID,
2835        desc: RelationDesc::builder()
2836            .with_column("id", SqlScalarType::UInt64.nullable(false))
2837            .with_column("event_type", SqlScalarType::String.nullable(false))
2838            .with_column("object_type", SqlScalarType::String.nullable(false))
2839            .with_column("details", SqlScalarType::Jsonb.nullable(false))
2840            .with_column("user", SqlScalarType::String.nullable(true))
2841            .with_column(
2842                "occurred_at",
2843                SqlScalarType::TimestampTz { precision: None }.nullable(false),
2844            )
2845            .with_key(vec![0])
2846            .finish(),
2847        column_comments: BTreeMap::from_iter([
2848            (
2849                "id",
2850                "Materialize's unique, monotonically increasing ID for the event.",
2851            ),
2852            (
2853                "event_type",
2854                "The type of the event: `create`, `drop`, `alter`, `grant`, `revoke`, or `comment`.",
2855            ),
2856            (
2857                "object_type",
2858                "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`.",
2859            ),
2860            (
2861                "details",
2862                "Additional details about the event. The shape of the details varies based on `event_type` and `object_type`.",
2863            ),
2864            (
2865                "user",
2866                "The user who triggered the event, or `NULL` if triggered by the system.",
2867            ),
2868            (
2869                "occurred_at",
2870                "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.",
2871            ),
2872        ]),
2873        // `event_type` and `object_type` are stored in `mz_catalog_raw` as the
2874        // numeric `Serialize_repr` of `proto::audit_log_event_v1::{EventType,
2875        // ObjectType}`. The CASE expressions map them to the kebab strings
2876        // that `audit_log::{EventType, ObjectType}`'s Display impls produce.
2877        //
2878        // `event` is `AuditLogEvent::V1(AuditLogEventV1)`, externally tagged,
2879        // so we reach through `key.event.V1` for the inner struct.
2880        //
2881        // `user` is `Option<StringWrapper>`: JSON null when absent, otherwise
2882        // `{"inner": "<name>"}`. `->'user'->>'inner'` collapses both to a
2883        // PostgreSQL NULL or the text.
2884        //
2885        // `details` in `mz_catalog_raw` is the externally-tagged proto
2886        // `Details` enum. `parse_catalog_audit_log_details` reshapes it into
2887        // `audit_log::EventDetails::as_json`'s output, the format the prior
2888        // BuiltinTable populator wrote. See that function's docstring and
2889        // the round-trip test in `src/catalog/tests/audit_log_details.rs`.
2890        //
2891        // `occurred_at` is `EpochMillis` (u64). Dividing by 1000.0 and feeding
2892        // to `to_timestamp` matches `mz_ore::now::to_datetime`'s round-trip
2893        // through `chrono::DateTime::from_timestamp_millis`.
2894        sql: "
2895IN CLUSTER mz_catalog_server
2896WITH (
2897    ASSERT NOT NULL id,
2898    ASSERT NOT NULL event_type,
2899    ASSERT NOT NULL object_type,
2900    ASSERT NOT NULL details,
2901    ASSERT NOT NULL occurred_at
2902) AS
2903WITH ev AS (
2904    SELECT data->'key'->'event'->'V1' AS e
2905    FROM mz_internal.mz_catalog_raw
2906    WHERE data->>'kind' = 'AuditLog'
2907)
2908SELECT
2909    (e->>'id')::uint8                                                       AS id,
2910    CASE (e->>'event_type')
2911        WHEN '1' THEN 'create'
2912        WHEN '2' THEN 'drop'
2913        WHEN '3' THEN 'alter'
2914        WHEN '4' THEN 'grant'
2915        WHEN '5' THEN 'revoke'
2916        WHEN '6' THEN 'comment'
2917    END                                                                     AS event_type,
2918    CASE (e->>'object_type')
2919        WHEN '1'  THEN 'cluster'
2920        WHEN '2'  THEN 'cluster-replica'
2921        WHEN '3'  THEN 'connection'
2922        WHEN '4'  THEN 'database'
2923        WHEN '5'  THEN 'func'
2924        WHEN '6'  THEN 'index'
2925        WHEN '7'  THEN 'materialized-view'
2926        WHEN '8'  THEN 'role'
2927        WHEN '9'  THEN 'secret'
2928        WHEN '10' THEN 'schema'
2929        WHEN '11' THEN 'sink'
2930        WHEN '12' THEN 'source'
2931        WHEN '13' THEN 'table'
2932        WHEN '14' THEN 'type'
2933        WHEN '15' THEN 'view'
2934        WHEN '16' THEN 'system'
2935        WHEN '17' THEN 'continual-task'
2936        WHEN '18' THEN 'network-policy'
2937        WHEN '19' THEN 'metric-sink'
2938    END                                                                     AS object_type,
2939    mz_internal.parse_catalog_audit_log_details(e->'details')               AS details,
2940    e->'user'->>'inner'                                                     AS \"user\",
2941    -- `occurred_at` is serialized by `proto::EpochMillis` as
2942    -- `{\"millis\": <u64>}`; reach through `'millis'` to get the integer.
2943    to_timestamp(((e->'occurred_at'->>'millis')::float8) / 1000.0)          AS occurred_at
2944FROM ev",
2945        is_retained_metrics_object: false,
2946        access: vec![PUBLIC_SELECT],
2947        ontology: Some(Ontology {
2948            entity_name: "audit_event",
2949            description: "An audit log entry recording a DDL operation",
2950            links: &const { [] },
2951            column_semantic_types: &const {
2952                [
2953                    ("object_type", SemanticType::ObjectType),
2954                    ("occurred_at", SemanticType::WallclockTimestamp),
2955                ]
2956            },
2957        }),
2958    }
2959});
2960
2961pub static MZ_EGRESS_IPS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2962    name: "mz_egress_ips",
2963    schema: MZ_CATALOG_SCHEMA,
2964    oid: oid::TABLE_MZ_EGRESS_IPS_OID,
2965    desc: RelationDesc::builder()
2966        .with_column("egress_ip", SqlScalarType::String.nullable(false))
2967        .with_column("prefix_length", SqlScalarType::Int32.nullable(false))
2968        .with_column("cidr", SqlScalarType::String.nullable(false))
2969        .finish(),
2970    column_comments: BTreeMap::from_iter([
2971        ("egress_ip", "The start of the range of IP addresses."),
2972        (
2973            "prefix_length",
2974            "The number of leading bits in the CIDR netmask.",
2975        ),
2976        ("cidr", "The CIDR representation."),
2977    ]),
2978    is_retained_metrics_object: false,
2979    access: vec![PUBLIC_SELECT],
2980    ontology: Some(Ontology {
2981        entity_name: "egress_ip",
2982        description: "IP addresses used for outbound connections from Materialize",
2983        links: &const { [] },
2984        column_semantic_types: &[],
2985    }),
2986});
2987
2988// The privatelink principal is entirely context-derived (there is nothing to
2989// parse out of create_sql), so this view selects the aws-privatelink
2990// connections and reconstructs the principal ARN from the plan-time AWS context
2991// functions. `principal` is NOT NULL and the removed packer emitted no row when
2992// the AWS principal context was absent. A view cannot skip a row, but the
2993// context functions fold to NULL without the context, so `WHERE principal IS
2994// NOT NULL` reproduces that skip (zero rows on envs without the context).
2995pub static MZ_AWS_PRIVATELINK_CONNECTIONS: LazyLock<BuiltinMaterializedView> =
2996    LazyLock::new(|| {
2997        BuiltinMaterializedView {
2998            name: "mz_aws_privatelink_connections",
2999            schema: MZ_CATALOG_SCHEMA,
3000            oid: oid::MV_MZ_AWS_PRIVATELINK_CONNECTIONS_OID,
3001            desc: RelationDesc::builder()
3002                .with_column("id", SqlScalarType::String.nullable(false))
3003                .with_column("principal", SqlScalarType::String.nullable(false))
3004                .with_key(vec![0])
3005                .finish(),
3006            column_comments: BTreeMap::from_iter([
3007                ("id", "The ID of the connection."),
3008                (
3009                    "principal",
3010                    "The AWS Principal that Materialize will use to connect to the VPC endpoint.",
3011                ),
3012            ]),
3013            // `principal` reproduces `AwsPrincipalContext::to_principal_string` in
3014            // src/catalog/src/config.rs. Keep the two in sync.
3015            sql: "
3016IN CLUSTER mz_catalog_server
3017WITH (
3018    ASSERT NOT NULL id,
3019    ASSERT NOT NULL principal
3020) AS
3021SELECT id, principal FROM (
3022    SELECT
3023        mz_internal.parse_catalog_id(r.data->'key'->'gid') AS id,
3024        'arn:aws:iam::' || mz_aws_account_id() || ':role/mz_'
3025            || mz_aws_external_id_prefix() || '_'
3026            || mz_internal.parse_catalog_id(r.data->'key'->'gid') AS principal
3027    FROM mz_internal.mz_catalog_raw r
3028    WHERE
3029        r.data->>'kind' = 'Item' AND
3030        mz_internal.parse_catalog_create_sql(
3031            r.data->'value'->'definition'->'V1'->>'create_sql')->>'connection_type'
3032                = 'aws-privatelink'
3033)
3034WHERE principal IS NOT NULL",
3035            is_retained_metrics_object: false,
3036            access: vec![PUBLIC_SELECT],
3037            ontology: Some(Ontology {
3038                entity_name: "aws_privatelink_connection",
3039                description: "AWS PrivateLink connection configuration",
3040                links: &const {
3041                    [OntologyLink {
3042                        name: "details_of",
3043                        target: "connection",
3044                        properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
3045                    }]
3046                },
3047                column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3048            }),
3049        }
3050    });
3051
3052pub static MZ_CLUSTER_REPLICA_FRONTIERS: LazyLock<BuiltinSource> =
3053    LazyLock::new(|| BuiltinSource {
3054        name: "mz_cluster_replica_frontiers",
3055        schema: MZ_CATALOG_SCHEMA,
3056        oid: oid::SOURCE_MZ_CLUSTER_REPLICA_FRONTIERS_OID,
3057        data_source: IntrospectionType::ReplicaFrontiers.into(),
3058        desc: RelationDesc::builder()
3059            .with_column("object_id", SqlScalarType::String.nullable(false))
3060            .with_column("replica_id", SqlScalarType::String.nullable(false))
3061            .with_column("write_frontier", SqlScalarType::MzTimestamp.nullable(true))
3062            .finish(),
3063        column_comments: BTreeMap::from_iter([
3064            (
3065                "object_id",
3066                "The ID of the source, sink, index, materialized view, or subscription.",
3067            ),
3068            ("replica_id", "The ID of a cluster replica."),
3069            (
3070                "write_frontier",
3071                "The next timestamp at which the output may change.",
3072            ),
3073        ]),
3074        is_retained_metrics_object: false,
3075        access: vec![PUBLIC_SELECT],
3076        ontology: None,
3077    });
3078
3079pub static MZ_CLUSTER_REPLICA_FRONTIERS_IND: LazyLock<BuiltinIndex> =
3080    LazyLock::new(|| BuiltinIndex {
3081        name: "mz_cluster_replica_frontiers_ind",
3082        schema: MZ_CATALOG_SCHEMA,
3083        oid: oid::INDEX_MZ_CLUSTER_REPLICA_FRONTIERS_IND_OID,
3084        sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_cluster_replica_frontiers (object_id)",
3085        is_retained_metrics_object: false,
3086    });
3087
3088pub static MZ_DEFAULT_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
3089    BuiltinMaterializedView {
3090        name: "mz_default_privileges",
3091        schema: MZ_CATALOG_SCHEMA,
3092        oid: oid::MV_MZ_DEFAULT_PRIVILEGES_OID,
3093        desc: RelationDesc::builder()
3094            .with_column("role_id", SqlScalarType::String.nullable(false))
3095            .with_column("database_id", SqlScalarType::String.nullable(true))
3096            .with_column("schema_id", SqlScalarType::String.nullable(true))
3097            .with_column("object_type", SqlScalarType::String.nullable(false))
3098            .with_column("grantee", SqlScalarType::String.nullable(false))
3099            .with_column("privileges", SqlScalarType::String.nullable(false))
3100            .finish(),
3101        column_comments: BTreeMap::from_iter([
3102            (
3103                "role_id",
3104                "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.",
3105            ),
3106            (
3107                "database_id",
3108                "Privileges described in this row will be granted only on objects in the database identified by `database_id` if non-null.",
3109            ),
3110            (
3111                "schema_id",
3112                "Privileges described in this row will be granted only on objects in the schema identified by `schema_id` if non-null.",
3113            ),
3114            (
3115                "object_type",
3116                "Privileges described in this row will be granted only on objects of type `object_type`.",
3117            ),
3118            (
3119                "grantee",
3120                "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.",
3121            ),
3122            ("privileges", "The set of privileges that will be granted."),
3123        ]),
3124        // `object_type` in `mz_catalog_raw` is the numeric `Serialize_repr` form
3125        // of `mz_catalog_protos::ObjectType`. The CASE mirrors
3126        // `mz_sql::catalog::ObjectType`'s `Display` impl, lowercased, matching
3127        // the prior `pack_default_privileges_update` populator. Variant `16` is
3128        // reserved/unused; any new proto variant must add a branch here. The
3129        // `object_type_case_matches_proto_display` test in this crate checks
3130        // the mapping.
3131        sql: "
3132IN CLUSTER mz_catalog_server
3133WITH (
3134    ASSERT NOT NULL role_id,
3135    ASSERT NOT NULL object_type,
3136    ASSERT NOT NULL grantee,
3137    ASSERT NOT NULL privileges
3138) AS
3139SELECT
3140    mz_internal.parse_catalog_id(data->'key'->'role_id') AS role_id,
3141    CASE WHEN data->'key'->'database_id' != 'null'::jsonb
3142         THEN mz_internal.parse_catalog_id(data->'key'->'database_id') END AS database_id,
3143    CASE WHEN data->'key'->'schema_id' != 'null'::jsonb
3144         THEN mz_internal.parse_catalog_id(data->'key'->'schema_id') END AS schema_id,
3145    CASE data->'key'->>'object_type'
3146        WHEN '1'  THEN 'table'
3147        WHEN '2'  THEN 'view'
3148        WHEN '3'  THEN 'materialized view'
3149        WHEN '4'  THEN 'source'
3150        WHEN '5'  THEN 'sink'
3151        WHEN '6'  THEN 'index'
3152        WHEN '7'  THEN 'type'
3153        WHEN '8'  THEN 'role'
3154        WHEN '9'  THEN 'cluster'
3155        WHEN '10' THEN 'cluster replica'
3156        WHEN '11' THEN 'secret'
3157        WHEN '12' THEN 'connection'
3158        WHEN '13' THEN 'database'
3159        WHEN '14' THEN 'schema'
3160        WHEN '15' THEN 'function'
3161        -- variant 16 reserved/unused in mz_catalog_protos::ObjectType.
3162        WHEN '17' THEN 'network policy'
3163    END AS object_type,
3164    mz_internal.parse_catalog_id(data->'key'->'grantee') AS grantee,
3165    mz_internal.parse_catalog_acl_mode(data->'value'->'privileges') AS privileges
3166FROM mz_internal.mz_catalog_raw
3167WHERE data->>'kind' = 'DefaultPrivileges'",
3168        is_retained_metrics_object: false,
3169        access: vec![PUBLIC_SELECT],
3170        ontology: Some(Ontology {
3171            entity_name: "default_privilege",
3172            description: "A default privilege rule applied to newly created objects",
3173            links: &const {
3174                [
3175                    OntologyLink {
3176                        name: "default_priv_for_role",
3177                        target: "role",
3178                        properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
3179                    },
3180                    OntologyLink {
3181                        name: "default_priv_in_database",
3182                        target: "database",
3183                        properties: LinkProperties::fk_nullable(
3184                            "database_id",
3185                            "id",
3186                            Cardinality::ManyToOne,
3187                        ),
3188                    },
3189                    OntologyLink {
3190                        name: "default_priv_in_schema",
3191                        target: "schema",
3192                        properties: LinkProperties::fk_nullable(
3193                            "schema_id",
3194                            "id",
3195                            Cardinality::ManyToOne,
3196                        ),
3197                    },
3198                    OntologyLink {
3199                        name: "default_priv_granted_to",
3200                        target: "role",
3201                        properties: LinkProperties::fk("grantee", "id", Cardinality::ManyToOne),
3202                    },
3203                ]
3204            },
3205            column_semantic_types: &const {
3206                [
3207                    ("role_id", SemanticType::RoleId),
3208                    ("database_id", SemanticType::DatabaseId),
3209                    ("schema_id", SemanticType::SchemaId),
3210                    ("object_type", SemanticType::ObjectType),
3211                    ("grantee", SemanticType::RoleId),
3212                ]
3213            },
3214        }),
3215    }
3216});
3217
3218pub static MZ_SYSTEM_PRIVILEGES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
3219    BuiltinMaterializedView {
3220        name: "mz_system_privileges",
3221        schema: MZ_CATALOG_SCHEMA,
3222        oid: oid::MV_MZ_SYSTEM_PRIVILEGES_OID,
3223        desc: RelationDesc::builder()
3224            .with_column("privileges", SqlScalarType::MzAclItem.nullable(false))
3225            .finish(),
3226        column_comments: BTreeMap::from_iter([(
3227            "privileges",
3228            "The privileges belonging to the system.",
3229        )]),
3230        // The durable row holds `(grantee, grantor, acl_mode)`; we rebuild the
3231        // single-element JSON shape that `parse_catalog_privileges` expects and
3232        // unnest it to recover the `mz_aclitem`.
3233        sql: "
3234IN CLUSTER mz_catalog_server
3235WITH (
3236    ASSERT NOT NULL privileges
3237) AS
3238SELECT
3239    unnest(mz_internal.parse_catalog_privileges(
3240        jsonb_build_array(
3241            jsonb_build_object(
3242                'grantee',  data->'key'->'grantee',
3243                'grantor',  data->'key'->'grantor',
3244                'acl_mode', data->'value'->'acl_mode'
3245            )
3246        )
3247    )) AS privileges
3248FROM mz_internal.mz_catalog_raw
3249WHERE data->>'kind' = 'SystemPrivileges'",
3250        is_retained_metrics_object: false,
3251        access: vec![PUBLIC_SELECT],
3252        ontology: Some(Ontology {
3253            entity_name: "system_privilege",
3254            description: "A system-level privilege grant",
3255            links: &const { [] },
3256            column_semantic_types: &[],
3257        }),
3258    }
3259});
3260
3261pub static MZ_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3262    name: "mz_storage_usage",
3263    schema: MZ_CATALOG_SCHEMA,
3264    oid: oid::VIEW_MZ_STORAGE_USAGE_OID,
3265    desc: RelationDesc::builder()
3266        .with_column("object_id", SqlScalarType::String.nullable(false))
3267        .with_column("size_bytes", SqlScalarType::UInt64.nullable(false))
3268        .with_column(
3269            "collection_timestamp",
3270            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3271        )
3272        .with_key(vec![0, 2])
3273        .finish(),
3274    column_comments: BTreeMap::from_iter([
3275        (
3276            "object_id",
3277            "The ID of the table, source, or materialized view.",
3278        ),
3279        (
3280            "size_bytes",
3281            "The number of storage bytes used by the object.",
3282        ),
3283        (
3284            "collection_timestamp",
3285            "The time at which storage usage of the object was assessed.",
3286        ),
3287    ]),
3288    sql: "
3289SELECT
3290    object_id,
3291    sum(size_bytes)::uint8 AS size_bytes,
3292    collection_timestamp
3293FROM
3294    mz_internal.mz_storage_shards
3295    JOIN mz_internal.mz_storage_usage_by_shard USING (shard_id)
3296GROUP BY object_id, collection_timestamp",
3297    access: vec![PUBLIC_SELECT],
3298    ontology: Some(Ontology {
3299        entity_name: "storage_usage",
3300        description: "Historical storage usage per object over time",
3301        links: &const {
3302            [OntologyLink {
3303                name: "storage_usage_of",
3304                target: "object",
3305                properties: LinkProperties::fk("object_id", "id", Cardinality::ManyToOne),
3306            }]
3307        },
3308        column_semantic_types: &const {
3309            [
3310                ("object_id", SemanticType::CatalogItemId),
3311                ("size_bytes", SemanticType::ByteCount),
3312                ("collection_timestamp", SemanticType::WallclockTimestamp),
3313            ]
3314        },
3315    }),
3316});
3317
3318pub static MZ_RECENT_STORAGE_USAGE: LazyLock<BuiltinView> = LazyLock::new(|| {
3319    BuiltinView {
3320    name: "mz_recent_storage_usage",
3321    schema: MZ_CATALOG_SCHEMA,
3322    oid: oid::VIEW_MZ_RECENT_STORAGE_USAGE_OID,
3323    desc: RelationDesc::builder()
3324        .with_column("object_id", SqlScalarType::String.nullable(false))
3325        .with_column("size_bytes", SqlScalarType::UInt64.nullable(true))
3326        .with_key(vec![0])
3327        .finish(),
3328    column_comments: BTreeMap::from_iter([
3329        ("object_id", "The ID of the table, source, or materialized view."),
3330        ("size_bytes", "The number of storage bytes used by the object in the most recent assessment."),
3331    ]),
3332    sql: "
3333WITH
3334
3335recent_storage_usage_by_shard AS (
3336    SELECT shard_id, size_bytes, collection_timestamp
3337    FROM mz_internal.mz_storage_usage_by_shard
3338    -- Restricting to the last 6 hours makes it feasible to index the view.
3339    WHERE collection_timestamp + '6 hours' >= mz_now()
3340),
3341
3342most_recent_collection_timestamp_by_shard AS (
3343    SELECT shard_id, max(collection_timestamp) AS collection_timestamp
3344    FROM recent_storage_usage_by_shard
3345    GROUP BY shard_id
3346)
3347
3348SELECT
3349    object_id,
3350    sum(size_bytes)::uint8 AS size_bytes
3351FROM
3352    mz_internal.mz_storage_shards
3353    LEFT JOIN most_recent_collection_timestamp_by_shard
3354        ON mz_storage_shards.shard_id = most_recent_collection_timestamp_by_shard.shard_id
3355    LEFT JOIN recent_storage_usage_by_shard
3356        ON mz_storage_shards.shard_id = recent_storage_usage_by_shard.shard_id
3357        AND most_recent_collection_timestamp_by_shard.collection_timestamp = recent_storage_usage_by_shard.collection_timestamp
3358GROUP BY object_id",
3359    access: vec![PUBLIC_SELECT],
3360    ontology: Some(Ontology {
3361        entity_name: "recent_storage",
3362        description: "Most recent storage usage snapshot per object",
3363        links: &const { [
3364            OntologyLink { name: "recent_storage_of", target: "object", properties: LinkProperties::fk("object_id", "id", Cardinality::OneToOne) },
3365        ] },
3366        column_semantic_types: &const {[("object_id", SemanticType::CatalogItemId), ("size_bytes", SemanticType::ByteCount)]},
3367    }),
3368}
3369});
3370
3371pub static MZ_RECENT_STORAGE_USAGE_IND: LazyLock<BuiltinIndex> = LazyLock::new(|| BuiltinIndex {
3372    name: "mz_recent_storage_usage_ind",
3373    schema: MZ_CATALOG_SCHEMA,
3374    oid: oid::INDEX_MZ_RECENT_STORAGE_USAGE_IND_OID,
3375    sql: "IN CLUSTER mz_catalog_server ON mz_catalog.mz_recent_storage_usage (object_id)",
3376    is_retained_metrics_object: false,
3377});
3378
3379pub static MZ_RELATIONS: LazyLock<BuiltinView> = LazyLock::new(|| {
3380    BuiltinView {
3381        name: "mz_relations",
3382        schema: MZ_CATALOG_SCHEMA,
3383        oid: oid::VIEW_MZ_RELATIONS_OID,
3384        desc: RelationDesc::builder()
3385            .with_column("id", SqlScalarType::String.nullable(false))
3386            .with_column("oid", SqlScalarType::Oid.nullable(false))
3387            .with_column("schema_id", SqlScalarType::String.nullable(false))
3388            .with_column("name", SqlScalarType::String.nullable(false))
3389            .with_column("type", SqlScalarType::String.nullable(false))
3390            .with_column("owner_id", SqlScalarType::String.nullable(false))
3391            .with_column("cluster_id", SqlScalarType::String.nullable(true))
3392            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false))
3393            .finish(),
3394        column_comments: BTreeMap::from_iter([
3395            ("id", "Materialize's unique ID for the relation."),
3396            ("oid", "A PostgreSQL-compatible OID for the relation."),
3397            ("schema_id", "The ID of the schema to which the relation belongs. Corresponds to `mz_schemas.id`."),
3398            ("name", "The name of the relation."),
3399            ("type", "The type of the relation: either `table`, `source`, `view`, or `materialized-view`."),
3400            ("owner_id", "The role ID of the owner of the relation. Corresponds to `mz_roles.id`."),
3401            ("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."),
3402            ("privileges", "The privileges belonging to the relation."),
3403        ]),
3404        sql: "
3405      SELECT id, oid, schema_id, name, 'table' AS type, owner_id, NULL::text AS cluster_id, privileges FROM mz_catalog.mz_tables
3406UNION ALL SELECT id, oid, schema_id, name, 'source', owner_id, cluster_id, privileges FROM mz_catalog.mz_sources
3407UNION ALL SELECT id, oid, schema_id, name, 'view', owner_id, NULL::text, privileges FROM mz_catalog.mz_views
3408UNION ALL SELECT id, oid, schema_id, name, 'materialized-view', owner_id, cluster_id, privileges FROM mz_catalog.mz_materialized_views",
3409        access: vec![PUBLIC_SELECT],
3410        ontology: Some(Ontology {
3411            entity_name: "relation",
3412            description: "Union of all relation types: tables, sources, views, MVs (convenience view)",
3413            links: &const { [
3414                OntologyLink { name: "union_includes", target: "table", properties: LinkProperties::union_disc("type", "table") },
3415                OntologyLink { name: "union_includes", target: "source", properties: LinkProperties::union_disc("type", "source") },
3416                OntologyLink { name: "union_includes", target: "view", properties: LinkProperties::union_disc("type", "view") },
3417                OntologyLink { name: "union_includes", target: "mv", properties: LinkProperties::union_disc("type", "materialized-view") },
3418            ] },
3419            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)]},
3420        }),
3421    }
3422});
3423
3424pub static MZ_OBJECTS: LazyLock<BuiltinView> = LazyLock::new(|| {
3425    BuiltinView {
3426        name: "mz_objects",
3427        schema: MZ_CATALOG_SCHEMA,
3428        oid: oid::VIEW_MZ_OBJECTS_OID,
3429        desc: RelationDesc::builder()
3430            .with_column("id", SqlScalarType::String.nullable(false))
3431            .with_column("oid", SqlScalarType::Oid.nullable(false))
3432            .with_column("schema_id", SqlScalarType::String.nullable(false))
3433            .with_column("name", SqlScalarType::String.nullable(false))
3434            .with_column("type", SqlScalarType::String.nullable(false))
3435            .with_column("owner_id", SqlScalarType::String.nullable(false))
3436            .with_column("cluster_id", SqlScalarType::String.nullable(true))
3437            .with_column("privileges", SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(true))
3438            .finish(),
3439        column_comments: BTreeMap::from_iter([
3440            ("id", "Materialize's unique ID for the object."),
3441            ("oid", "A PostgreSQL-compatible OID for the object."),
3442            ("schema_id", "The ID of the schema to which the object belongs. Corresponds to `mz_schemas.id`."),
3443            ("name", "The name of the object."),
3444            ("type", "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `metric-sink`, `index`, `connection`, `secret`, `type`, or `function`."),
3445            ("owner_id", "The role ID of the owner of the object. Corresponds to `mz_roles.id`."),
3446            ("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."),
3447            ("privileges", "The privileges belonging to the object."),
3448        ]),
3449        sql:
3450        "SELECT id, oid, schema_id, name, type, owner_id, cluster_id, privileges FROM mz_catalog.mz_relations
3451UNION ALL
3452    SELECT id, oid, schema_id, name, 'sink', owner_id, cluster_id, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_sinks
3453UNION ALL
3454    SELECT id, oid, schema_id, name, 'metric-sink', owner_id, cluster_id, NULL::mz_catalog.mz_aclitem[] FROM mz_internal.mz_metric_sinks
3455UNION ALL
3456    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[]
3457    FROM mz_catalog.mz_indexes
3458    JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
3459UNION ALL
3460    SELECT id, oid, schema_id, name, 'connection', owner_id, NULL::text, privileges FROM mz_catalog.mz_connections
3461UNION ALL
3462    SELECT id, oid, schema_id, name, 'type', owner_id, NULL::text, privileges FROM mz_catalog.mz_types
3463UNION ALL
3464    SELECT id, oid, schema_id, name, 'function', owner_id, NULL::text, NULL::mz_catalog.mz_aclitem[] FROM mz_catalog.mz_functions
3465UNION ALL
3466    SELECT id, oid, schema_id, name, 'secret', owner_id, NULL::text, privileges FROM mz_catalog.mz_secrets",
3467        access: vec![PUBLIC_SELECT],
3468        ontology: Some(Ontology {
3469            entity_name: "object",
3470            description: "Union of all object types: relations, indexes, connections, etc. (convenience view)",
3471            links: &const {
3472                [
3473                    OntologyLink {
3474                        name: "union_includes",
3475                        target: "relation",
3476                        properties: LinkProperties::Union {
3477                            discriminator_column: None,
3478                            discriminator_value: None,
3479                            note: Some("covers all mz_relations rows (table, source, view, mv)"),
3480                        },
3481                    },
3482                    OntologyLink {
3483                        name: "union_includes",
3484                        target: "table",
3485                        properties: LinkProperties::union_disc("type", "table"),
3486                    },
3487                    OntologyLink {
3488                        name: "union_includes",
3489                        target: "source",
3490                        properties: LinkProperties::union_disc("type", "source"),
3491                    },
3492                    OntologyLink {
3493                        name: "union_includes",
3494                        target: "view",
3495                        properties: LinkProperties::union_disc("type", "view"),
3496                    },
3497                    OntologyLink {
3498                        name: "union_includes",
3499                        target: "mv",
3500                        properties: LinkProperties::union_disc("type", "materialized-view"),
3501                    },
3502                    OntologyLink {
3503                        name: "union_includes",
3504                        target: "sink",
3505                        properties: LinkProperties::union_disc("type", "sink"),
3506                    },
3507                    OntologyLink {
3508                        name: "union_includes",
3509                        target: "index",
3510                        properties: LinkProperties::union_disc("type", "index"),
3511                    },
3512                    OntologyLink {
3513                        name: "union_includes",
3514                        target: "connection",
3515                        properties: LinkProperties::union_disc("type", "connection"),
3516                    },
3517                    OntologyLink {
3518                        name: "union_includes",
3519                        target: "type",
3520                        properties: LinkProperties::union_disc("type", "type"),
3521                    },
3522                    OntologyLink {
3523                        name: "union_includes",
3524                        target: "function",
3525                        properties: LinkProperties::union_disc("type", "function"),
3526                    },
3527                    OntologyLink {
3528                        name: "union_includes",
3529                        target: "secret",
3530                        properties: LinkProperties::union_disc("type", "secret"),
3531                    },
3532                    OntologyLink {
3533                        name: "in_schema",
3534                        target: "schema",
3535                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
3536                    },
3537                    OntologyLink {
3538                        name: "owned_by",
3539                        target: "role",
3540                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
3541                    },
3542                    OntologyLink {
3543                        name: "on_cluster",
3544                        target: "cluster",
3545                        properties: LinkProperties::fk_nullable(
3546                            "cluster_id",
3547                            "id",
3548                            Cardinality::ManyToOne,
3549                        ),
3550                    },
3551                ]
3552            },
3553            column_semantic_types: &const {
3554                [
3555                    ("id", SemanticType::CatalogItemId),
3556                    ("oid", SemanticType::OID),
3557                    ("schema_id", SemanticType::SchemaId),
3558                    ("type", SemanticType::ObjectType),
3559                    ("owner_id", SemanticType::RoleId),
3560                    ("cluster_id", SemanticType::ClusterId),
3561                ]
3562            },
3563        }),
3564    }
3565});
3566
3567pub static MZ_TIMEZONE_ABBREVIATIONS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3568    name: "mz_timezone_abbreviations",
3569    schema: MZ_CATALOG_SCHEMA,
3570    oid: oid::VIEW_MZ_TIMEZONE_ABBREVIATIONS_OID,
3571    desc: RelationDesc::builder()
3572        .with_column("abbreviation", SqlScalarType::String.nullable(false))
3573        .with_column("utc_offset", SqlScalarType::Interval.nullable(true))
3574        .with_column("dst", SqlScalarType::Bool.nullable(true))
3575        .with_column("timezone_name", SqlScalarType::String.nullable(true))
3576        .with_key(vec![0])
3577        .finish(),
3578    column_comments: BTreeMap::from_iter([
3579        ("abbreviation", "The timezone abbreviation."),
3580        (
3581            "utc_offset",
3582            "The UTC offset of the timezone or `NULL` if fixed.",
3583        ),
3584        (
3585            "dst",
3586            "Whether the timezone is in daylight savings or `NULL` if fixed.",
3587        ),
3588        (
3589            "timezone_name",
3590            "The full name of the non-fixed timezone or `NULL` if not fixed.",
3591        ),
3592    ]),
3593    sql: format!(
3594        "SELECT * FROM ({}) _ (abbreviation, utc_offset, dst, timezone_name)",
3595        mz_pgtz::abbrev::MZ_CATALOG_TIMEZONE_ABBREVIATIONS_SQL,
3596    )
3597    .leak(),
3598    access: vec![PUBLIC_SELECT],
3599    ontology: None,
3600});
3601
3602pub static MZ_TIMEZONE_NAMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3603    name: "mz_timezone_names",
3604    schema: MZ_CATALOG_SCHEMA,
3605    oid: oid::VIEW_MZ_TIMEZONE_NAMES_OID,
3606    desc: RelationDesc::builder()
3607        .with_column("name", SqlScalarType::String.nullable(false))
3608        .with_key(vec![0])
3609        .finish(),
3610    column_comments: BTreeMap::from_iter([("name", "The timezone name.")]),
3611    sql: format!(
3612        "SELECT * FROM ({}) _ (name)",
3613        mz_pgtz::timezone::MZ_CATALOG_TIMEZONE_NAMES_SQL,
3614    )
3615    .leak(),
3616    access: vec![PUBLIC_SELECT],
3617    ontology: None,
3618});
3619
3620pub const MZ_DATABASES_IND: BuiltinIndex = BuiltinIndex {
3621    name: "mz_databases_ind",
3622    schema: MZ_CATALOG_SCHEMA,
3623    oid: oid::INDEX_MZ_DATABASES_IND_OID,
3624    sql: "IN CLUSTER mz_catalog_server
3625ON mz_catalog.mz_databases (name)",
3626    is_retained_metrics_object: false,
3627};
3628
3629pub const MZ_SCHEMAS_IND: BuiltinIndex = BuiltinIndex {
3630    name: "mz_schemas_ind",
3631    schema: MZ_CATALOG_SCHEMA,
3632    oid: oid::INDEX_MZ_SCHEMAS_IND_OID,
3633    sql: "IN CLUSTER mz_catalog_server
3634ON mz_catalog.mz_schemas (database_id)",
3635    is_retained_metrics_object: false,
3636};
3637
3638pub const MZ_CONNECTIONS_IND: BuiltinIndex = BuiltinIndex {
3639    name: "mz_connections_ind",
3640    schema: MZ_CATALOG_SCHEMA,
3641    oid: oid::INDEX_MZ_CONNECTIONS_IND_OID,
3642    sql: "IN CLUSTER mz_catalog_server
3643ON mz_catalog.mz_connections (schema_id)",
3644    is_retained_metrics_object: false,
3645};
3646
3647pub const MZ_TABLES_IND: BuiltinIndex = BuiltinIndex {
3648    name: "mz_tables_ind",
3649    schema: MZ_CATALOG_SCHEMA,
3650    oid: oid::INDEX_MZ_TABLES_IND_OID,
3651    sql: "IN CLUSTER mz_catalog_server
3652ON mz_catalog.mz_tables (schema_id)",
3653    is_retained_metrics_object: false,
3654};
3655
3656pub const MZ_TYPES_IND: BuiltinIndex = BuiltinIndex {
3657    name: "mz_types_ind",
3658    schema: MZ_CATALOG_SCHEMA,
3659    oid: oid::INDEX_MZ_TYPES_IND_OID,
3660    sql: "IN CLUSTER mz_catalog_server
3661ON mz_catalog.mz_types (schema_id)",
3662    is_retained_metrics_object: false,
3663};
3664
3665pub const MZ_OBJECTS_IND: BuiltinIndex = BuiltinIndex {
3666    name: "mz_objects_ind",
3667    schema: MZ_CATALOG_SCHEMA,
3668    oid: oid::INDEX_MZ_OBJECTS_IND_OID,
3669    sql: "IN CLUSTER mz_catalog_server
3670ON mz_catalog.mz_objects (schema_id)",
3671    is_retained_metrics_object: false,
3672};
3673
3674pub const MZ_COLUMNS_IND: BuiltinIndex = BuiltinIndex {
3675    name: "mz_columns_ind",
3676    schema: MZ_CATALOG_SCHEMA,
3677    oid: oid::INDEX_MZ_COLUMNS_IND_OID,
3678    sql: "IN CLUSTER mz_catalog_server
3679ON mz_catalog.mz_columns (name)",
3680    is_retained_metrics_object: false,
3681};
3682
3683pub const MZ_SECRETS_IND: BuiltinIndex = BuiltinIndex {
3684    name: "mz_secrets_ind",
3685    schema: MZ_CATALOG_SCHEMA,
3686    oid: oid::INDEX_MZ_SECRETS_IND_OID,
3687    sql: "IN CLUSTER mz_catalog_server
3688ON mz_catalog.mz_secrets (name)",
3689    is_retained_metrics_object: false,
3690};
3691
3692pub const MZ_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3693    name: "mz_views_ind",
3694    schema: MZ_CATALOG_SCHEMA,
3695    oid: oid::INDEX_MZ_VIEWS_IND_OID,
3696    sql: "IN CLUSTER mz_catalog_server
3697ON mz_catalog.mz_views (schema_id)",
3698    is_retained_metrics_object: false,
3699};
3700
3701pub const MZ_CLUSTERS_IND: BuiltinIndex = BuiltinIndex {
3702    name: "mz_clusters_ind",
3703    schema: MZ_CATALOG_SCHEMA,
3704    oid: oid::INDEX_MZ_CLUSTERS_IND_OID,
3705    sql: "IN CLUSTER mz_catalog_server
3706ON mz_catalog.mz_clusters (id)",
3707    is_retained_metrics_object: false,
3708};
3709
3710pub const MZ_INDEXES_IND: BuiltinIndex = BuiltinIndex {
3711    name: "mz_indexes_ind",
3712    schema: MZ_CATALOG_SCHEMA,
3713    oid: oid::INDEX_MZ_INDEXES_IND_OID,
3714    sql: "IN CLUSTER mz_catalog_server
3715ON mz_catalog.mz_indexes (id)",
3716    is_retained_metrics_object: false,
3717};
3718
3719pub const MZ_ROLES_IND: BuiltinIndex = BuiltinIndex {
3720    name: "mz_roles_ind",
3721    schema: MZ_CATALOG_SCHEMA,
3722    oid: oid::INDEX_MZ_ROLES_IND_OID,
3723    sql: "IN CLUSTER mz_catalog_server
3724ON mz_catalog.mz_roles (id)",
3725    is_retained_metrics_object: false,
3726};
3727
3728pub const MZ_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3729    name: "mz_sources_ind",
3730    schema: MZ_CATALOG_SCHEMA,
3731    oid: oid::INDEX_MZ_SOURCES_IND_OID,
3732    sql: "IN CLUSTER mz_catalog_server
3733ON mz_catalog.mz_sources (id)",
3734    is_retained_metrics_object: true,
3735};
3736
3737pub const MZ_SINKS_IND: BuiltinIndex = BuiltinIndex {
3738    name: "mz_sinks_ind",
3739    schema: MZ_CATALOG_SCHEMA,
3740    oid: oid::INDEX_MZ_SINKS_IND_OID,
3741    sql: "IN CLUSTER mz_catalog_server
3742ON mz_catalog.mz_sinks (id)",
3743    is_retained_metrics_object: true,
3744};
3745
3746pub const MZ_MATERIALIZED_VIEWS_IND: BuiltinIndex = BuiltinIndex {
3747    name: "mz_materialized_views_ind",
3748    schema: MZ_CATALOG_SCHEMA,
3749    oid: oid::INDEX_MZ_MATERIALIZED_VIEWS_IND_OID,
3750    sql: "IN CLUSTER mz_catalog_server
3751ON mz_catalog.mz_materialized_views (id)",
3752    is_retained_metrics_object: false,
3753};
3754
3755pub const MZ_CLUSTER_REPLICAS_IND: BuiltinIndex = BuiltinIndex {
3756    name: "mz_cluster_replicas_ind",
3757    schema: MZ_CATALOG_SCHEMA,
3758    oid: oid::INDEX_MZ_CLUSTER_REPLICAS_IND_OID,
3759    sql: "IN CLUSTER mz_catalog_server
3760ON mz_catalog.mz_cluster_replicas (id)",
3761    is_retained_metrics_object: true,
3762};
3763
3764pub const MZ_CLUSTER_REPLICA_SIZES_IND: BuiltinIndex = BuiltinIndex {
3765    name: "mz_cluster_replica_sizes_ind",
3766    schema: MZ_CATALOG_SCHEMA,
3767    oid: oid::INDEX_MZ_CLUSTER_REPLICA_SIZES_IND_OID,
3768    sql: "IN CLUSTER mz_catalog_server
3769ON mz_catalog.mz_cluster_replica_sizes (size)",
3770    is_retained_metrics_object: true,
3771};
3772
3773pub const MZ_KAFKA_SOURCES_IND: BuiltinIndex = BuiltinIndex {
3774    name: "mz_kafka_sources_ind",
3775    schema: MZ_CATALOG_SCHEMA,
3776    oid: oid::INDEX_MZ_KAFKA_SOURCES_IND_OID,
3777    sql: "IN CLUSTER mz_catalog_server
3778ON mz_catalog.mz_kafka_sources (id)",
3779    is_retained_metrics_object: true,
3780};
3781
3782#[cfg(test)]
3783mod tests {
3784    use mz_catalog_protos::objects::ObjectType as ProtoObjectType;
3785    use mz_sql::catalog::ObjectType as SqlObjectType;
3786
3787    use crate::builtin::mz_catalog::MZ_DEFAULT_PRIVILEGES;
3788
3789    /// Checks the `object_type` CASE in `MZ_DEFAULT_PRIVILEGES` against the
3790    /// proto enum and the SQL Display impl it's meant to mirror.
3791    ///
3792    /// The CASE maps `mz_catalog_protos::ObjectType`'s numeric `Serialize_repr`
3793    /// to `mz_sql::catalog::ObjectType`'s `Display`, lowercased. The match in
3794    /// `expected_for` is exhaustive, so a new proto variant won't compile until
3795    /// it's handled. A wrong string in the CASE fails the substring assertion.
3796    #[mz_ore::test]
3797    fn object_type_case_matches_proto_display() {
3798        // `None` means the variant never shows up in a stored `DefaultPrivilege`
3799        // key, so the CASE deliberately has no arm for it: `Unknown` is the
3800        // zero-value sentinel, and `ON METRIC SINKS` is rejected by
3801        // `plan_alter_default_privileges` before a key ever gets built. Match is
3802        // exhaustive: a new variant forces an update.
3803        fn expected_for(proto: ProtoObjectType) -> Option<SqlObjectType> {
3804            match proto {
3805                ProtoObjectType::Unknown => None,
3806                ProtoObjectType::MetricSink => None,
3807                ProtoObjectType::Table => Some(SqlObjectType::Table),
3808                ProtoObjectType::View => Some(SqlObjectType::View),
3809                ProtoObjectType::MaterializedView => Some(SqlObjectType::MaterializedView),
3810                ProtoObjectType::Source => Some(SqlObjectType::Source),
3811                ProtoObjectType::Sink => Some(SqlObjectType::Sink),
3812                ProtoObjectType::Index => Some(SqlObjectType::Index),
3813                ProtoObjectType::Type => Some(SqlObjectType::Type),
3814                ProtoObjectType::Role => Some(SqlObjectType::Role),
3815                ProtoObjectType::Cluster => Some(SqlObjectType::Cluster),
3816                ProtoObjectType::ClusterReplica => Some(SqlObjectType::ClusterReplica),
3817                ProtoObjectType::Secret => Some(SqlObjectType::Secret),
3818                ProtoObjectType::Connection => Some(SqlObjectType::Connection),
3819                ProtoObjectType::Database => Some(SqlObjectType::Database),
3820                ProtoObjectType::Schema => Some(SqlObjectType::Schema),
3821                ProtoObjectType::Func => Some(SqlObjectType::Func),
3822                ProtoObjectType::NetworkPolicy => Some(SqlObjectType::NetworkPolicy),
3823            }
3824        }
3825
3826        // The proto enum has no `IntoEnumIterator`, so list every variant by
3827        // hand. A missed entry here just goes unchecked, but the exhaustive
3828        // match in `expected_for` won't compile when a new proto variant lands,
3829        // which is the actual drift defense.
3830        let variants: &[ProtoObjectType] = &[
3831            ProtoObjectType::Unknown,
3832            ProtoObjectType::Table,
3833            ProtoObjectType::View,
3834            ProtoObjectType::MaterializedView,
3835            ProtoObjectType::Source,
3836            ProtoObjectType::Sink,
3837            ProtoObjectType::Index,
3838            ProtoObjectType::Type,
3839            ProtoObjectType::Role,
3840            ProtoObjectType::Cluster,
3841            ProtoObjectType::ClusterReplica,
3842            ProtoObjectType::Secret,
3843            ProtoObjectType::Connection,
3844            ProtoObjectType::Database,
3845            ProtoObjectType::Schema,
3846            ProtoObjectType::Func,
3847            ProtoObjectType::NetworkPolicy,
3848            ProtoObjectType::MetricSink,
3849        ];
3850
3851        let sql = MZ_DEFAULT_PRIVILEGES.sql;
3852        for &proto in variants {
3853            // `ObjectType` is `#[repr(u8)]`; the discriminant matches the
3854            // `Serialize_repr` JSON value the SQL CASE arms compare against.
3855            #[allow(clippy::as_conversions)]
3856            let repr = proto as u8;
3857            match expected_for(proto) {
3858                Some(sql_ty) => {
3859                    let display = sql_ty.to_string().to_lowercase();
3860                    // The arms use variable whitespace between `WHEN '..'` and
3861                    // `THEN` to align columns; verify the WHEN and THEN strings
3862                    // appear together with at most one or two spaces between.
3863                    let one_space = format!("WHEN '{repr}' THEN '{display}'");
3864                    let two_spaces = format!("WHEN '{repr}'  THEN '{display}'");
3865                    assert!(
3866                        sql.contains(&one_space) || sql.contains(&two_spaces),
3867                        "missing CASE arm for `{proto:?}`: expected \
3868                         `WHEN '{repr}' THEN '{display}'` (or with double space) \
3869                         in MZ_DEFAULT_PRIVILEGES.sql",
3870                    );
3871                }
3872                None => {
3873                    let pattern = format!("WHEN '{repr}'");
3874                    assert!(
3875                        !sql.contains(&pattern),
3876                        "unexpected CASE arm for sentinel `{proto:?}`: \
3877                         found `{pattern}` in MZ_DEFAULT_PRIVILEGES.sql",
3878                    );
3879                }
3880            }
3881        }
3882    }
3883}