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