Skip to main content

mz_catalog/builtin/
mz_internal.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_internal` schema.
11
12use std::collections::BTreeMap;
13use std::sync::LazyLock;
14
15use mz_pgrepr::oid;
16use mz_repr::adt::mz_acl_item::MzAclItem;
17use mz_repr::namespaces::MZ_INTERNAL_SCHEMA;
18use mz_repr::{RelationDesc, SemanticType, SqlScalarType};
19use mz_sql::catalog::{ObjectType, SystemObjectType};
20use mz_sql::rbac;
21use mz_sql::session::user::{MZ_ANALYTICS_ROLE_ID, MZ_SYSTEM_ROLE_ID};
22use mz_storage_client::controller::IntrospectionType;
23use mz_storage_client::healthcheck::{
24    MZ_AWS_PRIVATELINK_CONNECTION_STATUS_HISTORY_DESC, MZ_PREPARED_STATEMENT_HISTORY_DESC,
25    MZ_SESSION_HISTORY_DESC, MZ_SINK_STATUS_HISTORY_DESC, MZ_SOURCE_STATUS_HISTORY_DESC,
26    MZ_SQL_TEXT_DESC, MZ_STATEMENT_EXECUTION_HISTORY_DESC, REPLICA_METRICS_HISTORY_DESC,
27    REPLICA_STATUS_HISTORY_DESC, WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW_DESC,
28    WALLCLOCK_LAG_HISTORY_DESC,
29};
30use mz_storage_client::statistics::{MZ_SINK_STATISTICS_RAW_DESC, MZ_SOURCE_STATISTICS_RAW_DESC};
31
32use crate::memory::objects::DataSourceDesc;
33
34use super::{
35    ANALYTICS_SELECT, BuiltinConnection, BuiltinIndex, BuiltinMaterializedView, BuiltinSource,
36    BuiltinTable, BuiltinView, Cardinality, LinkProperties, MONITOR_REDACTED_SELECT,
37    MONITOR_SELECT, Ontology, OntologyLink, PUBLIC_SELECT, SUPPORT_SELECT,
38};
39
40pub static MZ_CATALOG_RAW: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
41    name: "mz_catalog_raw",
42    schema: MZ_INTERNAL_SCHEMA,
43    oid: oid::SOURCE_MZ_CATALOG_RAW_OID,
44    data_source: DataSourceDesc::Catalog,
45    desc: crate::durable::persist_desc(),
46    column_comments: BTreeMap::new(),
47    is_retained_metrics_object: false,
48    // The raw catalog contains unredacted SQL statements, so we limit access to the system user.
49    access: vec![],
50    ontology: None,
51});
52pub static MZ_POSTGRES_SOURCES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
53    BuiltinMaterializedView {
54        name: "mz_postgres_sources",
55        schema: MZ_INTERNAL_SCHEMA,
56        oid: oid::MV_MZ_POSTGRES_SOURCES_OID,
57        desc: RelationDesc::builder()
58            .with_column("id", SqlScalarType::String.nullable(false))
59            .with_column("replication_slot", SqlScalarType::String.nullable(false))
60            .with_column("timeline_id", SqlScalarType::UInt64.nullable(true))
61            .with_key(vec![0])
62            .finish(),
63        column_comments: BTreeMap::from_iter([
64            (
65                "id",
66                "The ID of the source. Corresponds to `mz_catalog.mz_sources.id`.",
67            ),
68            (
69                "replication_slot",
70                "The name of the replication slot in the PostgreSQL database that Materialize will create and stream data from.",
71            ),
72            (
73                "timeline_id",
74                "The PostgreSQL timeline ID determined on source creation.",
75            ),
76        ]),
77        // `parse_postgres_source_details` extracts `slot` and `timeline_id`
78        // from the hex-encoded protobuf `DETAILS` option on the persisted
79        // `CREATE SOURCE`. Any row where that decode fails poisons the whole
80        // MV, so this MV MUST be filtered to postgres sources first via
81        // `parse_catalog_create_sql`.
82        sql: "
83IN CLUSTER mz_catalog_server
84WITH (
85    ASSERT NOT NULL id,
86    ASSERT NOT NULL replication_slot
87) AS
88SELECT
89    mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
90    details->>'slot' AS replication_slot,
91    (details->>'timeline_id')::uint8 AS timeline_id
92FROM
93    mz_internal.mz_catalog_raw,
94    LATERAL (
95        SELECT mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')
96    ) AS l(parsed),
97    LATERAL (
98        SELECT mz_internal.parse_postgres_source_details(data->'value'->'definition'->'V1'->>'create_sql')
99    ) AS d(details)
100WHERE
101    data->>'kind' = 'Item' AND
102    parsed->>'source_type' = 'postgres'",
103        is_retained_metrics_object: false,
104        access: vec![PUBLIC_SELECT],
105        ontology: Some(Ontology {
106            entity_name: "postgres_source",
107            description: "Postgres source-level details",
108            links: &const {
109                [OntologyLink {
110                    name: "details_of",
111                    target: "source",
112                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
113                }]
114            },
115            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
116        }),
117    }
118});
119// The three relational source-table views (postgres, mysql, sql-server) share
120// the same shape. Each reads Item rows from `mz_catalog_raw`, pulls the parent
121// source id and external reference out of the persisted `create_sql` via
122// `parse_source_export_details`, and joins `mz_sources` to keep only exports
123// whose parent is of the matching connection type. The external reference for
124// postgres and sql-server is `[database, schema, table]`, so schema/table come
125// from positions 1 and 2. MySQL references are `[schema, table]`, so positions
126// 0 and 1. This is the same slicing the removed packers applied.
127pub static MZ_POSTGRES_SOURCE_TABLES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
128    BuiltinMaterializedView {
129        name: "mz_postgres_source_tables",
130        schema: MZ_INTERNAL_SCHEMA,
131        oid: oid::MV_MZ_POSTGRES_SOURCE_TABLES_OID,
132        desc: RelationDesc::builder()
133            .with_column("id", SqlScalarType::String.nullable(false))
134            .with_column("schema_name", SqlScalarType::String.nullable(false))
135            .with_column("table_name", SqlScalarType::String.nullable(false))
136            .with_key(vec![0])
137            .finish(),
138        column_comments: BTreeMap::from_iter([
139            (
140                "id",
141                "The ID of the subsource or table. Corresponds to `mz_catalog.mz_sources.id` or `mz_catalog.mz_tables.id`.",
142            ),
143            (
144                "schema_name",
145                "The schema of the upstream table being ingested.",
146            ),
147            (
148                "table_name",
149                "The name of the upstream table being ingested.",
150            ),
151        ]),
152        sql: "
153IN CLUSTER mz_catalog_server
154WITH (
155    ASSERT NOT NULL id,
156    ASSERT NOT NULL schema_name,
157    ASSERT NOT NULL table_name
158) AS
159SELECT
160    mz_internal.parse_catalog_id(r.data->'key'->'gid') AS id,
161    details->'external_reference'->>1 AS schema_name,
162    details->'external_reference'->>2 AS table_name
163FROM
164    mz_internal.mz_catalog_raw r,
165    LATERAL (
166        SELECT mz_internal.parse_source_export_details(
167            r.data->'value'->'definition'->'V1'->>'create_sql')
168    ) AS d(details)
169    JOIN mz_catalog.mz_sources s
170        ON s.id = details->>'source_id' AND s.type = 'postgres'
171WHERE
172    r.data->>'kind' = 'Item' AND
173    details IS NOT NULL",
174        is_retained_metrics_object: true,
175        access: vec![PUBLIC_SELECT],
176        ontology: Some(Ontology {
177            entity_name: "postgres_source_table",
178            description: "Postgres source table-level details",
179            links: &const {
180                [OntologyLink {
181                    name: "describes_source_table",
182                    target: "table",
183                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
184                }]
185            },
186            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
187        }),
188    }
189});
190pub static MZ_MYSQL_SOURCE_TABLES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
191    BuiltinMaterializedView {
192        name: "mz_mysql_source_tables",
193        schema: MZ_INTERNAL_SCHEMA,
194        oid: oid::MV_MZ_MYSQL_SOURCE_TABLES_OID,
195        desc: RelationDesc::builder()
196            .with_column("id", SqlScalarType::String.nullable(false))
197            .with_column("schema_name", SqlScalarType::String.nullable(false))
198            .with_column("table_name", SqlScalarType::String.nullable(false))
199            .with_key(vec![0])
200            .finish(),
201        column_comments: BTreeMap::from_iter([
202            (
203                "id",
204                "The ID of the subsource or table. Corresponds to `mz_catalog.mz_sources.id` or `mz_catalog.mz_tables.id`.",
205            ),
206            (
207                "schema_name",
208                "The schema (or, database) of the upstream table being ingested.",
209            ),
210            (
211                "table_name",
212                "The name of the upstream table being ingested.",
213            ),
214        ]),
215        sql: "
216IN CLUSTER mz_catalog_server
217WITH (
218    ASSERT NOT NULL id,
219    ASSERT NOT NULL schema_name,
220    ASSERT NOT NULL table_name
221) AS
222SELECT
223    mz_internal.parse_catalog_id(r.data->'key'->'gid') AS id,
224    details->'external_reference'->>0 AS schema_name,
225    details->'external_reference'->>1 AS table_name
226FROM
227    mz_internal.mz_catalog_raw r,
228    LATERAL (
229        SELECT mz_internal.parse_source_export_details(
230            r.data->'value'->'definition'->'V1'->>'create_sql')
231    ) AS d(details)
232    JOIN mz_catalog.mz_sources s
233        ON s.id = details->>'source_id' AND s.type = 'mysql'
234WHERE
235    r.data->>'kind' = 'Item' AND
236    details IS NOT NULL",
237        is_retained_metrics_object: true,
238        access: vec![PUBLIC_SELECT],
239        ontology: Some(Ontology {
240            entity_name: "mysql_source_table",
241            description: "MySQL source table-level details",
242            links: &const {
243                [OntologyLink {
244                    name: "describes_source_table",
245                    target: "table",
246                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
247                }]
248            },
249            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
250        }),
251    }
252});
253pub static MZ_SQL_SERVER_SOURCE_TABLES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
254    BuiltinMaterializedView {
255        name: "mz_sql_server_source_tables",
256        schema: MZ_INTERNAL_SCHEMA,
257        oid: oid::MV_MZ_SQL_SERVER_SOURCE_TABLES_OID,
258        desc: RelationDesc::builder()
259            .with_column("id", SqlScalarType::String.nullable(false))
260            .with_column("schema_name", SqlScalarType::String.nullable(false))
261            .with_column("table_name", SqlScalarType::String.nullable(false))
262            .with_key(vec![0])
263            .finish(),
264        column_comments: BTreeMap::from_iter([
265            (
266                "id",
267                "The ID of the subsource or table. Corresponds to `mz_catalog.mz_sources.id` or `mz_catalog.mz_tables.id`.",
268            ),
269            (
270                "schema_name",
271                "The schema of the upstream table being ingested.",
272            ),
273            (
274                "table_name",
275                "The name of the upstream table being ingested.",
276            ),
277        ]),
278        sql: "
279IN CLUSTER mz_catalog_server
280WITH (
281    ASSERT NOT NULL id,
282    ASSERT NOT NULL schema_name,
283    ASSERT NOT NULL table_name
284) AS
285SELECT
286    mz_internal.parse_catalog_id(r.data->'key'->'gid') AS id,
287    details->'external_reference'->>1 AS schema_name,
288    details->'external_reference'->>2 AS table_name
289FROM
290    mz_internal.mz_catalog_raw r,
291    LATERAL (
292        SELECT mz_internal.parse_source_export_details(
293            r.data->'value'->'definition'->'V1'->>'create_sql')
294    ) AS d(details)
295    JOIN mz_catalog.mz_sources s
296        ON s.id = details->>'source_id' AND s.type = 'sql-server'
297WHERE
298    r.data->>'kind' = 'Item' AND
299    details IS NOT NULL",
300        is_retained_metrics_object: true,
301        access: vec![PUBLIC_SELECT],
302        ontology: Some(Ontology {
303            entity_name: "sql_server_source_table",
304            description: "SQL Server source table-level details",
305            links: &const {
306                [OntologyLink {
307                    name: "describes_source_table",
308                    target: "table",
309                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
310                }]
311            },
312            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
313        }),
314    }
315});
316pub static MZ_KAFKA_SOURCE_TABLES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
317    BuiltinMaterializedView {
318        name: "mz_kafka_source_tables",
319        schema: MZ_INTERNAL_SCHEMA,
320        oid: oid::MV_MZ_KAFKA_SOURCE_TABLES_OID,
321        desc: RelationDesc::builder()
322            .with_column("id", SqlScalarType::String.nullable(false))
323            .with_column("topic", SqlScalarType::String.nullable(false))
324            .with_column("envelope_type", SqlScalarType::String.nullable(true))
325            .with_column("key_format", SqlScalarType::String.nullable(true))
326            .with_column("value_format", SqlScalarType::String.nullable(true))
327            .with_key(vec![0])
328            .finish(),
329        column_comments: BTreeMap::from_iter([
330            (
331                "id",
332                "The ID of the table. Corresponds to `mz_catalog.mz_tables.id`.",
333            ),
334            ("topic", "The topic being ingested."),
335            (
336                "envelope_type",
337                "The envelope type: `none`, `upsert`, or `debezium`. `NULL` for other source types.",
338            ),
339            (
340                "key_format",
341                "The format of the Kafka message key: `avro`, `csv`, `regex`, `bytes`, `json`, `text`, or `NULL`.",
342            ),
343            (
344                "value_format",
345                "The format of the Kafka message value: `avro`, `csv`, `regex`, `bytes`, `json`, `text`. `NULL` for other source types.",
346            ),
347        ]),
348        // Kafka exports are only ever created with the new
349        // `CREATE TABLE ... FROM SOURCE` syntax (kafka has no subsource path),
350        // so the topic sits at position 0 of the single-part external
351        // reference. `parse_source_export_details` resolves the envelope and
352        // key/value formats straight from the table's own `create_sql`,
353        // reproducing the runtime `DataSourceDesc::formats()`/`envelope()` the
354        // old packer read.
355        sql: "
356IN CLUSTER mz_catalog_server
357WITH (
358    ASSERT NOT NULL id,
359    ASSERT NOT NULL topic
360) AS
361SELECT
362    mz_internal.parse_catalog_id(r.data->'key'->'gid') AS id,
363    details->'external_reference'->>0 AS topic,
364    details->>'envelope_type' AS envelope_type,
365    details->>'key_format' AS key_format,
366    details->>'value_format' AS value_format
367FROM
368    mz_internal.mz_catalog_raw r,
369    LATERAL (
370        SELECT mz_internal.parse_source_export_details(
371            r.data->'value'->'definition'->'V1'->>'create_sql')
372    ) AS d(details)
373    JOIN mz_catalog.mz_sources s
374        ON s.id = details->>'source_id' AND s.type = 'kafka'
375WHERE
376    r.data->>'kind' = 'Item' AND
377    details IS NOT NULL",
378        is_retained_metrics_object: true,
379        access: vec![PUBLIC_SELECT],
380        ontology: Some(Ontology {
381            entity_name: "kafka_source_table",
382            description: "Kafka source table-level details",
383            links: &const {
384                [OntologyLink {
385                    name: "describes_source_table",
386                    target: "table",
387                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
388                }]
389            },
390            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
391        }),
392    }
393});
394pub static MZ_OBJECT_DEPENDENCIES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
395    name: "mz_object_dependencies",
396    schema: MZ_INTERNAL_SCHEMA,
397    oid: oid::TABLE_MZ_OBJECT_DEPENDENCIES_OID,
398    desc: RelationDesc::builder()
399        .with_column("object_id", SqlScalarType::String.nullable(false))
400        .with_column(
401            "referenced_object_id",
402            SqlScalarType::String.nullable(false),
403        )
404        .finish(),
405    column_comments: BTreeMap::from_iter([
406        (
407            "object_id",
408            "The ID of the dependent object. Corresponds to `mz_objects.id`.",
409        ),
410        (
411            "referenced_object_id",
412            "The ID of the referenced object. Corresponds to `mz_objects.id`.",
413        ),
414    ]),
415    is_retained_metrics_object: true,
416    access: vec![PUBLIC_SELECT],
417    ontology: Some(Ontology {
418        entity_name: "object_dependency",
419        description: "A dependency edge: one object depends on another",
420        links: &const {
421            [
422                OntologyLink {
423                    name: "depends_on",
424                    target: "object",
425                    properties: LinkProperties::DependsOn {
426                        source_column: "object_id",
427                        target_column: "id",
428                        source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
429                        requires_mapping: None,
430                    },
431                },
432                OntologyLink {
433                    name: "dependency_is",
434                    target: "object",
435                    properties: LinkProperties::DependsOn {
436                        source_column: "referenced_object_id",
437                        target_column: "id",
438                        source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
439                        requires_mapping: None,
440                    },
441                },
442            ]
443        },
444        column_semantic_types: &const {
445            [
446                ("object_id", SemanticType::CatalogItemId),
447                ("referenced_object_id", SemanticType::CatalogItemId),
448            ]
449        },
450    }),
451});
452pub static MZ_COMPUTE_DEPENDENCIES: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
453    name: "mz_compute_dependencies",
454    schema: MZ_INTERNAL_SCHEMA,
455    oid: oid::SOURCE_MZ_COMPUTE_DEPENDENCIES_OID,
456    data_source: IntrospectionType::ComputeDependencies.into(),
457    desc: RelationDesc::builder()
458        .with_column("object_id", SqlScalarType::String.nullable(false))
459        .with_column("dependency_id", SqlScalarType::String.nullable(false))
460        .finish(),
461    column_comments: BTreeMap::from_iter([
462        (
463            "object_id",
464            "The ID of a compute object. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, or `mz_internal.mz_subscriptions.id`.",
465        ),
466        (
467            "dependency_id",
468            "The ID of a compute dependency. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, `mz_catalog.mz_sources.id`, or `mz_catalog.mz_tables.id`.",
469        ),
470    ]),
471    is_retained_metrics_object: false,
472    access: vec![PUBLIC_SELECT],
473    ontology: Some(Ontology {
474        entity_name: "compute_dependency",
475        description: "Dependency edge from a compute object (index, materialized view, or subscription) to one of the sources of its data",
476        links: &const {
477            [
478                OntologyLink {
479                    name: "depends_on",
480                    target: "object",
481                    properties: LinkProperties::DependsOn {
482                        source_column: "object_id",
483                        target_column: "id",
484                        source_id_type: Some(mz_repr::SemanticType::GlobalId),
485                        requires_mapping: Some("mz_internal.mz_object_global_ids"),
486                    },
487                },
488                OntologyLink {
489                    name: "dependency_is",
490                    target: "object",
491                    properties: LinkProperties::DependsOn {
492                        source_column: "dependency_id",
493                        target_column: "id",
494                        source_id_type: Some(mz_repr::SemanticType::GlobalId),
495                        requires_mapping: Some("mz_internal.mz_object_global_ids"),
496                    },
497                },
498            ]
499        },
500        column_semantic_types: &const {
501            [
502                ("object_id", SemanticType::GlobalId),
503                ("dependency_id", SemanticType::GlobalId),
504            ]
505        },
506    }),
507});
508
509pub static MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES: LazyLock<BuiltinTable> = LazyLock::new(|| {
510    BuiltinTable {
511        name: "mz_materialized_view_refresh_strategies",
512        schema: MZ_INTERNAL_SCHEMA,
513        oid: oid::TABLE_MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES_OID,
514        desc: RelationDesc::builder()
515            .with_column(
516                "materialized_view_id",
517                SqlScalarType::String.nullable(false),
518            )
519            .with_column("type", SqlScalarType::String.nullable(false))
520            .with_column("interval", SqlScalarType::Interval.nullable(true))
521            .with_column(
522                "aligned_to",
523                SqlScalarType::TimestampTz { precision: None }.nullable(true),
524            )
525            .with_column(
526                "at",
527                SqlScalarType::TimestampTz { precision: None }.nullable(true),
528            )
529            .finish(),
530        column_comments: BTreeMap::from_iter([
531            (
532                "materialized_view_id",
533                "The ID of the materialized view. Corresponds to `mz_catalog.mz_materialized_views.id`",
534            ),
535            (
536                "type",
537                "`at`, `every`, or `on-commit`. Default: `on-commit`",
538            ),
539            (
540                "interval",
541                "The refresh interval of a `REFRESH EVERY` option, or `NULL` if the `type` is not `every`.",
542            ),
543            (
544                "aligned_to",
545                "The `ALIGNED TO` option of a `REFRESH EVERY` option, or `NULL` if the `type` is not `every`.",
546            ),
547            (
548                "at",
549                "The time of a `REFRESH AT`, or `NULL` if the `type` is not `at`.",
550            ),
551        ]),
552        is_retained_metrics_object: false,
553        access: vec![PUBLIC_SELECT],
554        ontology: None,
555    }
556});
557
558pub static MZ_NETWORK_POLICIES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
559    BuiltinMaterializedView {
560        name: "mz_network_policies",
561        schema: MZ_INTERNAL_SCHEMA,
562        oid: oid::MV_MZ_NETWORK_POLICIES_OID,
563        desc: RelationDesc::builder()
564            .with_column("id", SqlScalarType::String.nullable(false))
565            .with_column("name", SqlScalarType::String.nullable(false))
566            .with_column("owner_id", SqlScalarType::String.nullable(false))
567            .with_column(
568                "privileges",
569                SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false),
570            )
571            .with_column("oid", SqlScalarType::Oid.nullable(false))
572            .with_key(vec![0])
573            .with_key(vec![4])
574            .finish(),
575        column_comments: BTreeMap::from_iter([
576            ("id", "The ID of the network policy."),
577            ("name", "The name of the network policy."),
578            (
579                "owner_id",
580                "The role ID of the owner of the network policy. Corresponds to `mz_catalog.mz_roles.id`.",
581            ),
582            (
583                "privileges",
584                "The privileges belonging to the network policy.",
585            ),
586            ("oid", "A PostgreSQL-compatible OID for the network policy."),
587        ]),
588        sql: "
589IN CLUSTER mz_catalog_server
590WITH (
591    ASSERT NOT NULL id,
592    ASSERT NOT NULL name,
593    ASSERT NOT NULL owner_id,
594    ASSERT NOT NULL privileges,
595    ASSERT NOT NULL oid
596) AS
597SELECT
598    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
599    data->'value'->>'name' AS name,
600    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id,
601    mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges,
602    (data->'value'->>'oid')::oid AS oid
603FROM mz_internal.mz_catalog_raw
604WHERE data->>'kind' = 'NetworkPolicy'",
605        is_retained_metrics_object: false,
606        access: vec![PUBLIC_SELECT],
607        ontology: Some(Ontology {
608            entity_name: "network_policy",
609            description: "Network access policies",
610            links: &const {
611                [OntologyLink {
612                    name: "owned_by",
613                    target: "role",
614                    properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
615                }]
616            },
617            column_semantic_types: &const {
618                [
619                    ("id", SemanticType::NetworkPolicyId),
620                    ("owner_id", SemanticType::RoleId),
621                    ("oid", SemanticType::OID),
622                ]
623            },
624        }),
625    }
626});
627
628pub static MZ_NETWORK_POLICY_RULES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
629    BuiltinMaterializedView {
630        name: "mz_network_policy_rules",
631        schema: MZ_INTERNAL_SCHEMA,
632        oid: oid::MV_MZ_NETWORK_POLICY_RULES_OID,
633        desc: RelationDesc::builder()
634            .with_column("name", SqlScalarType::String.nullable(false))
635            .with_column("policy_id", SqlScalarType::String.nullable(false))
636            .with_column("action", SqlScalarType::String.nullable(false))
637            .with_column("address", SqlScalarType::String.nullable(false))
638            .with_column("direction", SqlScalarType::String.nullable(false))
639            .finish(),
640        column_comments: BTreeMap::from_iter([
641            (
642                "name",
643                "The name of the network policy rule. Can be combined with `policy_id` to form a unique identifier.",
644            ),
645            (
646                "policy_id",
647                "The ID the network policy the rule is part of. Corresponds to `mz_internal.mz_network_policies.id`.",
648            ),
649            (
650                "action",
651                "The action of the rule. `allow` is the only supported action.",
652            ),
653            ("address", "The address the rule will take action on."),
654            (
655                "direction",
656                "The direction of traffic the rule applies to. `ingress` is the only supported direction.",
657            ),
658        ]),
659        sql: "
660IN CLUSTER mz_catalog_server
661WITH (
662    ASSERT NOT NULL name,
663    ASSERT NOT NULL policy_id,
664    ASSERT NOT NULL action,
665    ASSERT NOT NULL address,
666    ASSERT NOT NULL direction
667) AS
668SELECT
669    rule->>'name' AS name,
670    mz_internal.parse_catalog_id(data->'key'->'id') AS policy_id,
671    lower(rule->>'action') AS action,
672    rule->>'address' AS address,
673    lower(rule->>'direction') AS direction
674FROM
675    mz_internal.mz_catalog_raw,
676    jsonb_array_elements(data->'value'->'rules') AS rule
677WHERE data->>'kind' = 'NetworkPolicy'",
678        is_retained_metrics_object: false,
679        access: vec![PUBLIC_SELECT],
680        ontology: Some(Ontology {
681            entity_name: "network_policy_rule",
682            description: "Individual rules within a network policy",
683            links: &const {
684                [OntologyLink {
685                    name: "belongs_to_policy",
686                    target: "network_policy",
687                    properties: LinkProperties::fk("policy_id", "id", Cardinality::ManyToOne),
688                }]
689            },
690            column_semantic_types: &[],
691        }),
692    }
693});
694
695/// PostgreSQL-specific metadata about types that doesn't make sense to expose
696/// in the `mz_types` table as part of our public, stable API.
697pub static MZ_TYPE_PG_METADATA: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
698    name: "mz_type_pg_metadata",
699    schema: MZ_INTERNAL_SCHEMA,
700    oid: oid::TABLE_MZ_TYPE_PG_METADATA_OID,
701    desc: RelationDesc::builder()
702        .with_column("id", SqlScalarType::String.nullable(false))
703        .with_column("typinput", SqlScalarType::Oid.nullable(false))
704        .with_column("typreceive", SqlScalarType::Oid.nullable(false))
705        .finish(),
706    column_comments: BTreeMap::new(),
707    is_retained_metrics_object: false,
708    access: vec![PUBLIC_SELECT],
709    ontology: None,
710});
711pub static MZ_AGGREGATES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
712    name: "mz_aggregates",
713    schema: MZ_INTERNAL_SCHEMA,
714    oid: oid::TABLE_MZ_AGGREGATES_OID,
715    desc: RelationDesc::builder()
716        .with_column("oid", SqlScalarType::Oid.nullable(false))
717        .with_column("agg_kind", SqlScalarType::String.nullable(false))
718        .with_column("agg_num_direct_args", SqlScalarType::Int16.nullable(false))
719        .finish(),
720    column_comments: BTreeMap::new(),
721    is_retained_metrics_object: false,
722    access: vec![PUBLIC_SELECT],
723    ontology: Some(Ontology {
724        entity_name: "aggregate",
725        description: "Aggregate function metadata",
726        links: &const { [] },
727        column_semantic_types: &[("oid", SemanticType::OID)],
728    }),
729});
730
731pub static MZ_CLUSTER_WORKLOAD_CLASSES: LazyLock<BuiltinMaterializedView> =
732    LazyLock::new(|| BuiltinMaterializedView {
733        name: "mz_cluster_workload_classes",
734        schema: MZ_INTERNAL_SCHEMA,
735        oid: oid::MV_MZ_CLUSTER_WORKLOAD_CLASSES_OID,
736        desc: RelationDesc::builder()
737            .with_column("id", SqlScalarType::String.nullable(false))
738            .with_column("workload_class", SqlScalarType::String.nullable(true))
739            .with_key(vec![0])
740            .finish(),
741        column_comments: BTreeMap::new(),
742        sql: "
743IN CLUSTER mz_catalog_server
744WITH (
745    ASSERT NOT NULL id
746) AS
747SELECT
748    mz_internal.parse_catalog_id(data->'key'->'id') AS id,
749    CASE WHEN data->'value'->'config'->'workload_class' != 'null'
750         THEN data->'value'->'config'->>'workload_class'
751    END AS workload_class
752FROM mz_internal.mz_catalog_raw
753WHERE data->>'kind' = 'Cluster'",
754        is_retained_metrics_object: false,
755        access: vec![PUBLIC_SELECT],
756        ontology: None,
757    });
758
759pub const MZ_CLUSTER_WORKLOAD_CLASSES_IND: BuiltinIndex = BuiltinIndex {
760    name: "mz_cluster_workload_classes_ind",
761    schema: MZ_INTERNAL_SCHEMA,
762    oid: oid::INDEX_MZ_CLUSTER_WORKLOAD_CLASSES_IND_OID,
763    sql: "IN CLUSTER mz_catalog_server
764ON mz_internal.mz_cluster_workload_classes (id)",
765    is_retained_metrics_object: false,
766};
767
768pub static MZ_CLUSTER_SCHEDULES: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
769    BuiltinMaterializedView {
770        name: "mz_cluster_schedules",
771        schema: MZ_INTERNAL_SCHEMA,
772        oid: oid::MV_MZ_CLUSTER_SCHEDULES_OID,
773        desc: RelationDesc::builder()
774            .with_column("cluster_id", SqlScalarType::String.nullable(false))
775            .with_column("type", SqlScalarType::String.nullable(false))
776            .with_column(
777                "refresh_hydration_time_estimate",
778                SqlScalarType::Interval.nullable(true),
779            )
780            .with_key(vec![0])
781            .finish(),
782        column_comments: BTreeMap::from_iter([
783            (
784                "cluster_id",
785                "The ID of the cluster. Corresponds to `mz_clusters.id`.",
786            ),
787            ("type", "`on-refresh`, or `manual`. Default: `manual`"),
788            (
789                "refresh_hydration_time_estimate",
790                "The interval given in the `HYDRATION TIME ESTIMATE` option.",
791            ),
792        ]),
793        // Only managed clusters produce a schedule row. The `schedule` field on
794        // `ManagedCluster` is a serde-tagged enum: the `Manual` unit variant
795        // serializes to the bare string "Manual", while `Refresh(opts)`
796        // serializes to `{"Refresh": {"rehydration_time_estimate": {"secs":..,
797        // "nanos":..}}}`. Convert the Duration to an Interval by composing a
798        // string and casting — Materialize has no `make_interval`.
799        sql: "
800IN CLUSTER mz_catalog_server
801WITH (
802    ASSERT NOT NULL cluster_id,
803    ASSERT NOT NULL type
804) AS
805SELECT
806    mz_internal.parse_catalog_id(data->'key'->'id') AS cluster_id,
807    CASE
808        WHEN data->'value'->'config'->'variant'->'Managed'->'schedule' = '\"Manual\"'::jsonb
809            THEN 'manual'
810        WHEN data->'value'->'config'->'variant'->'Managed'->'schedule' ? 'Refresh'
811            THEN 'on-refresh'
812    END AS type,
813    CASE
814        WHEN data->'value'->'config'->'variant'->'Managed'->'schedule' ? 'Refresh' THEN
815            (
816                (data->'value'->'config'->'variant'->'Managed'->'schedule'->'Refresh'->'rehydration_time_estimate'->>'secs')
817                || ' seconds '
818                || ((data->'value'->'config'->'variant'->'Managed'->'schedule'->'Refresh'->'rehydration_time_estimate'->>'nanos')::bigint / 1000)::text
819                || ' microseconds'
820            )::interval
821    END AS refresh_hydration_time_estimate
822FROM mz_internal.mz_catalog_raw
823WHERE
824    data->>'kind' = 'Cluster' AND
825    jsonb_typeof(data->'value'->'config'->'variant') = 'object'",
826        is_retained_metrics_object: false,
827        access: vec![PUBLIC_SELECT],
828        ontology: Some(Ontology {
829            entity_name: "cluster_schedule",
830            description: "Cluster scheduling configuration",
831            links: &const {
832                [OntologyLink {
833                    name: "belongs_to_cluster",
834                    target: "cluster",
835                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
836                }]
837            },
838            column_semantic_types: &[("cluster_id", SemanticType::ClusterId)],
839        }),
840    }
841});
842
843pub static MZ_CLUSTER_RECONFIGURATIONS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
844    BuiltinMaterializedView {
845        name: "mz_cluster_reconfigurations",
846        schema: MZ_INTERNAL_SCHEMA,
847        oid: oid::MV_MZ_CLUSTER_RECONFIGURATIONS_OID,
848        desc: RelationDesc::builder()
849            .with_column("cluster_id", SqlScalarType::String.nullable(false))
850            .with_column("status", SqlScalarType::String.nullable(false))
851            .with_column("deadline", SqlScalarType::MzTimestamp.nullable(false))
852            .with_column("on_timeout", SqlScalarType::String.nullable(false))
853            .with_column("target", SqlScalarType::Jsonb.nullable(false))
854            .with_column("changes", SqlScalarType::Jsonb.nullable(false))
855            .with_key(vec![0])
856            .finish(),
857        column_comments: BTreeMap::from_iter([
858            (
859                "cluster_id",
860                "The ID of the cluster. Corresponds to `mz_clusters.id`.",
861            ),
862            (
863                "status",
864                "The lifecycle status of the reconfiguration: `in-progress` while the controller converges on the target, then a terminal `finalized`, `timed-out`, `cancelled`, or `resource-exhausted`. The record is retained after it settles, so the latest outcome stays inspectable until a later reconfiguration overwrites it.",
865            ),
866            (
867                "deadline",
868                "The deadline by which the reconfiguration must complete. After it passes, the `on_timeout` action applies.",
869            ),
870            (
871                "on_timeout",
872                "The action applied if `deadline` passes before the target hydrates: `commit` (cut over to the not-yet-hydrated target) or `rollback` (revert to the pre-reconfiguration shape).",
873            ),
874            (
875                "target",
876                "The config shape the cluster is reconfiguring to, as JSON: `size`, `replication_factor`, `availability_zones`, and `logging`. The realized (current) shape is in `mz_clusters`.",
877            ),
878            (
879                "changes",
880                "The dimensions in which `target` differs from the cluster's realized configuration, as a JSON object holding the target value per changed dimension. Empty (`{}`) once a record settles with its target applied. A rolled-back record keeps the abandoned diff.",
881            ),
882        ]),
883        // One row per managed cluster with a reconfiguration record, retained
884        // with a terminal `status` after it settles until the next `ALTER`
885        // overwrites it. Two null flavors get filtered: unmanaged clusters
886        // store their config under the `Unmanaged` variant, so the `Managed`
887        // lookup is SQL NULL (the CTE's WHERE), and a managed cluster that has
888        // never gracefully reconfigured has the optional field unset, which
889        // `mz_catalog_raw` serializes as explicit JSON `null` rather than
890        // omitting the key (hence `!= 'null'`, `IS NOT NULL` would not filter
891        // it). Status values are kebab-case like the
892        // catalog's other multi-word values, and the ELSE arms pass unmapped
893        // enum variants through verbatim: falling to NULL would trip the
894        // ASSERT NOT NULL and error every read of this relation and of
895        // `mz_show_clusters`, which joins it. `changes` diffs `target` against
896        // the realized config per dimension. Both sides come from the same raw
897        // catalog document, so the jsonb comparison is trivially canonical,
898        // and it matches the routing's shape-equality (an AZ reorder counts
899        // as a change in both).
900        sql: "
901IN CLUSTER mz_catalog_server
902WITH (
903    ASSERT NOT NULL cluster_id,
904    ASSERT NOT NULL status,
905    ASSERT NOT NULL deadline,
906    ASSERT NOT NULL on_timeout,
907    ASSERT NOT NULL target,
908    ASSERT NOT NULL changes
909) AS
910WITH
911    managed AS (
912        SELECT
913            mz_internal.parse_catalog_id(data->'key'->'id') AS cluster_id,
914            data->'value'->'config'->'variant'->'Managed' AS config
915        FROM mz_internal.mz_catalog_raw
916        WHERE
917            data->>'kind' = 'Cluster' AND
918            data->'value'->'config'->'variant'->'Managed' IS NOT NULL
919    ),
920    records AS (
921        SELECT
922            cluster_id,
923            config,
924            config->'reconfiguration' AS reconfiguration,
925            config->'reconfiguration'->'target' AS target
926        FROM managed
927        WHERE config->'reconfiguration' != 'null'
928    )
929SELECT
930    r.cluster_id,
931    CASE r.reconfiguration->>'status'
932        WHEN 'InProgress' THEN 'in-progress'
933        WHEN 'Finalized' THEN 'finalized'
934        WHEN 'TimedOut' THEN 'timed-out'
935        WHEN 'Cancelled' THEN 'cancelled'
936        WHEN 'ResourceExhausted' THEN 'resource-exhausted'
937        ELSE r.reconfiguration->>'status'
938    END AS status,
939    (r.reconfiguration->>'deadline')::mz_timestamp AS deadline,
940    CASE r.reconfiguration->>'on_timeout'
941        WHEN 'Commit' THEN 'commit'
942        WHEN 'Rollback' THEN 'rollback'
943        ELSE r.reconfiguration->>'on_timeout'
944    END AS on_timeout,
945    r.target,
946    CASE WHEN r.target->'size' != r.config->'size'
947        THEN jsonb_build_object('size', r.target->'size') ELSE '{}'::jsonb END ||
948    CASE WHEN r.target->'replication_factor' != r.config->'replication_factor'
949        THEN jsonb_build_object('replication_factor', r.target->'replication_factor') ELSE '{}'::jsonb END ||
950    CASE WHEN r.target->'availability_zones' != r.config->'availability_zones'
951        THEN jsonb_build_object('availability_zones', r.target->'availability_zones') ELSE '{}'::jsonb END ||
952    CASE WHEN r.target->'logging' != r.config->'logging'
953        THEN jsonb_build_object('logging', r.target->'logging') ELSE '{}'::jsonb END
954    AS changes
955FROM records r",
956        is_retained_metrics_object: false,
957        access: vec![PUBLIC_SELECT],
958        ontology: Some(Ontology {
959            entity_name: "cluster_reconfiguration",
960            description: "Latest graceful cluster reconfiguration",
961            links: &const {
962                [OntologyLink {
963                    // At most one reconfiguration record per cluster (unique
964                    // key on `cluster_id`), so the FK is one-to-one.
965                    name: "belongs_to_cluster",
966                    target: "cluster",
967                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::OneToOne),
968                }]
969            },
970            column_semantic_types: &[("cluster_id", SemanticType::ClusterId)],
971        }),
972    }
973});
974
975pub const MZ_CLUSTER_RECONFIGURATIONS_IND: BuiltinIndex = BuiltinIndex {
976    name: "mz_cluster_reconfigurations_ind",
977    schema: MZ_INTERNAL_SCHEMA,
978    oid: oid::INDEX_MZ_CLUSTER_RECONFIGURATIONS_IND_OID,
979    sql: "IN CLUSTER mz_catalog_server
980ON mz_internal.mz_cluster_reconfigurations (cluster_id)",
981    is_retained_metrics_object: false,
982};
983
984pub static MZ_CLUSTER_AUTO_SCALING_STRATEGIES: LazyLock<BuiltinMaterializedView> = LazyLock::new(
985    || {
986        BuiltinMaterializedView {
987            name: "mz_cluster_auto_scaling_strategies",
988            schema: MZ_INTERNAL_SCHEMA,
989            oid: oid::MV_MZ_CLUSTER_AUTO_SCALING_STRATEGIES_OID,
990            desc: RelationDesc::builder()
991                .with_column("cluster_id", SqlScalarType::String.nullable(false))
992                .with_column("strategy", SqlScalarType::Jsonb.nullable(false))
993                .with_column("state", SqlScalarType::Jsonb.nullable(true))
994                .with_key(vec![0])
995                .finish(),
996            column_comments: BTreeMap::from_iter([
997                (
998                    "cluster_id",
999                    "The ID of the cluster. Corresponds to `mz_clusters.id`.",
1000                ),
1001                (
1002                    "strategy",
1003                    "**Unstable** The configured autoscaling policy, as JSON. Currently an `on_hydration` sub-policy carrying its `hydration_size` and optional `linger_duration`.",
1004                ),
1005                (
1006                    "state",
1007                    "**Unstable** The in-flight autoscaling runtime state, as JSON keyed by strategy, or `NULL` when nothing is running. Currently a `burst` key carrying the active hydration burst: its `burst_size`, `linger_duration`, and `steady_hydrated_at`.",
1008                ),
1009            ]),
1010            // One row per managed cluster with a strategy configured or a burst
1011            // running (a burst can briefly outlive a just-removed policy).
1012            // Absent fields serialize as JSON `null`. `state` is keyed by
1013            // strategy so a future strategy's state is another key, not a
1014            // schema change.
1015            sql: "
1016IN CLUSTER mz_catalog_server
1017WITH (
1018    ASSERT NOT NULL cluster_id,
1019    ASSERT NOT NULL strategy
1020) AS
1021WITH
1022    managed AS (
1023        SELECT
1024            mz_internal.parse_catalog_id(data->'key'->'id') AS cluster_id,
1025            data->'value'->'config'->'variant'->'Managed'->'auto_scaling_strategy' AS strategy,
1026            data->'value'->'config'->'variant'->'Managed'->'burst' AS burst
1027        FROM mz_internal.mz_catalog_raw
1028        WHERE
1029            data->>'kind' = 'Cluster' AND
1030            data->'value'->'config'->'variant'->'Managed' IS NOT NULL
1031    )
1032SELECT
1033    m.cluster_id,
1034    COALESCE(m.strategy, 'null'::jsonb) AS strategy,
1035    CASE WHEN m.burst != 'null' THEN jsonb_build_object('burst', m.burst) END AS state
1036FROM managed m
1037WHERE m.strategy != 'null' OR m.burst != 'null'",
1038            is_retained_metrics_object: false,
1039            access: vec![PUBLIC_SELECT],
1040            ontology: Some(Ontology {
1041                entity_name: "cluster_auto_scaling_strategy",
1042                description: "Configured cluster autoscaling strategy and in-flight state",
1043                links: &const {
1044                    [OntologyLink {
1045                        // At most one row per managed cluster (unique key on
1046                        // `cluster_id`), so the FK is one-to-one.
1047                        name: "belongs_to_cluster",
1048                        target: "cluster",
1049                        properties: LinkProperties::fk("cluster_id", "id", Cardinality::OneToOne),
1050                    }]
1051                },
1052                column_semantic_types: &[("cluster_id", SemanticType::ClusterId)],
1053            }),
1054        }
1055    },
1056);
1057
1058pub const MZ_CLUSTER_AUTO_SCALING_STRATEGIES_IND: BuiltinIndex = BuiltinIndex {
1059    name: "mz_cluster_auto_scaling_strategies_ind",
1060    schema: MZ_INTERNAL_SCHEMA,
1061    oid: oid::INDEX_MZ_CLUSTER_AUTO_SCALING_STRATEGIES_IND_OID,
1062    sql: "IN CLUSTER mz_catalog_server
1063ON mz_internal.mz_cluster_auto_scaling_strategies (cluster_id)",
1064    is_retained_metrics_object: false,
1065};
1066
1067pub static MZ_INTERNAL_CLUSTER_REPLICAS: LazyLock<BuiltinMaterializedView> =
1068    LazyLock::new(|| BuiltinMaterializedView {
1069        name: "mz_internal_cluster_replicas",
1070        schema: MZ_INTERNAL_SCHEMA,
1071        oid: oid::MV_MZ_INTERNAL_CLUSTER_REPLICAS_OID,
1072        desc: RelationDesc::builder()
1073            .with_column("id", SqlScalarType::String.nullable(false))
1074            .with_key(vec![0])
1075            .finish(),
1076        column_comments: BTreeMap::from_iter([(
1077            "id",
1078            "The ID of a cluster replica. Corresponds to `mz_cluster_replicas.id`.",
1079        )]),
1080        sql: "
1081IN CLUSTER mz_catalog_server
1082WITH (
1083    ASSERT NOT NULL id
1084) AS
1085SELECT mz_internal.parse_catalog_id(data->'key'->'id') AS id
1086FROM mz_internal.mz_catalog_raw
1087WHERE
1088    data->>'kind' = 'ClusterReplica' AND
1089    (data->'value'->'config'->'location'->'Managed'->>'internal')::bool = true",
1090        is_retained_metrics_object: false,
1091        access: vec![PUBLIC_SELECT],
1092        ontology: None,
1093    });
1094
1095pub static MZ_PENDING_CLUSTER_REPLICAS: LazyLock<BuiltinMaterializedView> =
1096    LazyLock::new(|| BuiltinMaterializedView {
1097        name: "mz_pending_cluster_replicas",
1098        schema: MZ_INTERNAL_SCHEMA,
1099        oid: oid::MV_MZ_PENDING_CLUSTER_REPLICAS_OID,
1100        desc: RelationDesc::builder()
1101            .with_column("id", SqlScalarType::String.nullable(false))
1102            .with_key(vec![0])
1103            .finish(),
1104        column_comments: BTreeMap::from_iter([(
1105            "id",
1106            "The ID of a cluster replica. Corresponds to `mz_cluster_replicas.id`.",
1107        )]),
1108        sql: "
1109IN CLUSTER mz_catalog_server
1110WITH (
1111    ASSERT NOT NULL id
1112) AS
1113SELECT mz_internal.parse_catalog_id(data->'key'->'id') AS id
1114FROM mz_internal.mz_catalog_raw
1115WHERE
1116    data->>'kind' = 'ClusterReplica' AND
1117    (data->'value'->'config'->'location'->'Managed'->>'pending')::bool = true",
1118        is_retained_metrics_object: false,
1119        access: vec![PUBLIC_SELECT],
1120        ontology: None,
1121    });
1122
1123/// System-only sidecar to `mz_cluster_replica_sizes`, exposing per-size
1124/// configuration that the cluster MaterializedViews need to compute the
1125/// `disk` column.
1126///
1127/// `mz_clusters.disk` and `mz_cluster_replicas.disk` are computed as
1128/// `NOT swap_enabled AND disk_bytes != 0`. The orchestrator-supplied
1129/// `swap_enabled` flag wasn't SQL-visible before the table→MV conversion,
1130/// so this table is locked down with `access: vec![]` (same pattern as
1131/// `mz_catalog_raw`): builtin MVs read it at bootstrap, but direct user
1132/// `SELECT` is denied.
1133///
1134/// Unlike `mz_cluster_replica_sizes`, this table includes rows for sizes
1135/// flagged `disabled` — `CatalogState::cluster_replica_size_has_disk`
1136/// indexed the in-memory map without checking `disabled`, so a managed
1137/// cluster pinned to a disabled size still resolved its `disk` column from
1138/// the size's real `swap_enabled` / `disk_limit`. Including disabled sizes
1139/// here preserves that behavior.
1140pub static MZ_CLUSTER_REPLICA_SIZE_INTERNAL: LazyLock<BuiltinTable> = LazyLock::new(|| {
1141    BuiltinTable {
1142        name: "mz_cluster_replica_size_internal",
1143        schema: MZ_INTERNAL_SCHEMA,
1144        oid: oid::TABLE_MZ_CLUSTER_REPLICA_SIZE_INTERNAL_OID,
1145        desc: RelationDesc::builder()
1146            .with_column("size", SqlScalarType::String.nullable(false))
1147            .with_column("swap_enabled", SqlScalarType::Bool.nullable(false))
1148            .with_column("disk_bytes", SqlScalarType::UInt64.nullable(false))
1149            .with_key(vec![0])
1150            .finish(),
1151        column_comments: BTreeMap::from_iter([
1152            ("size", "The human-readable replica size."),
1153            (
1154                "swap_enabled",
1155                "Whether the replica size's pods are configured to allow swap. Used internally to compute the public `disk` column.",
1156            ),
1157            (
1158                "disk_bytes",
1159                "The replica size's disk limit in bytes (0 if explicitly disabled). Used internally to compute the public `disk` column.",
1160            ),
1161        ]),
1162        is_retained_metrics_object: true,
1163        access: vec![],
1164        ontology: None,
1165    }
1166});
1167
1168pub const MZ_CLUSTER_REPLICA_SIZE_INTERNAL_IND: BuiltinIndex = BuiltinIndex {
1169    name: "mz_cluster_replica_size_internal_ind",
1170    schema: MZ_INTERNAL_SCHEMA,
1171    oid: oid::INDEX_MZ_CLUSTER_REPLICA_SIZE_INTERNAL_IND_OID,
1172    sql: "IN CLUSTER mz_catalog_server
1173ON mz_internal.mz_cluster_replica_size_internal (size)",
1174    is_retained_metrics_object: true,
1175};
1176
1177pub static MZ_CLUSTER_REPLICA_STATUS_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| {
1178    BuiltinSource {
1179        name: "mz_cluster_replica_status_history",
1180        schema: MZ_INTERNAL_SCHEMA,
1181        oid: oid::SOURCE_MZ_CLUSTER_REPLICA_STATUS_HISTORY_OID,
1182        data_source: IntrospectionType::ReplicaStatusHistory.into(),
1183        desc: REPLICA_STATUS_HISTORY_DESC.clone(),
1184        column_comments: BTreeMap::from_iter([
1185            ("replica_id", "The ID of a cluster replica."),
1186            ("process_id", "The ID of a process within the replica."),
1187            (
1188                "status",
1189                "The status of the cluster replica: `online` or `offline`.",
1190            ),
1191            (
1192                "reason",
1193                "If the cluster replica is in an `offline` state, the reason (if available). For example, `oom-killed`.",
1194            ),
1195            (
1196                "occurred_at",
1197                "Wall-clock timestamp at which the event occurred.",
1198            ),
1199        ]),
1200        is_retained_metrics_object: false,
1201        access: vec![PUBLIC_SELECT],
1202        ontology: Some(Ontology {
1203            entity_name: "replica_status_event",
1204            description: "Historical replica status events (ready, not-ready, etc.)",
1205            links: &const {
1206                [OntologyLink {
1207                    name: "status_event_of_replica",
1208                    target: "replica",
1209                    properties: LinkProperties::fk_typed(
1210                        "replica_id",
1211                        "id",
1212                        Cardinality::ManyToOne,
1213                        mz_repr::SemanticType::CatalogItemId,
1214                    ),
1215                }]
1216            },
1217            column_semantic_types: &[("replica_id", SemanticType::ReplicaId)],
1218        }),
1219    }
1220});
1221
1222pub static MZ_CLUSTER_REPLICA_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
1223    name: "mz_cluster_replica_statuses",
1224    schema: MZ_INTERNAL_SCHEMA,
1225    oid: oid::VIEW_MZ_CLUSTER_REPLICA_STATUSES_OID,
1226    desc: RelationDesc::builder()
1227        .with_column("replica_id", SqlScalarType::String.nullable(false))
1228        .with_column("process_id", SqlScalarType::UInt64.nullable(false))
1229        .with_column("status", SqlScalarType::String.nullable(false))
1230        .with_column("reason", SqlScalarType::String.nullable(true))
1231        .with_column(
1232            "updated_at",
1233            SqlScalarType::TimestampTz { precision: None }.nullable(false),
1234        )
1235        .with_key(vec![0, 1])
1236        .finish(),
1237    column_comments: BTreeMap::from_iter([
1238        (
1239            "replica_id",
1240            "Materialize's unique ID for the cluster replica.",
1241        ),
1242        (
1243            "process_id",
1244            "The ID of the process within the cluster replica.",
1245        ),
1246        (
1247            "status",
1248            "The status of the cluster replica: `online` or `offline`.",
1249        ),
1250        (
1251            "reason",
1252            "If the cluster replica is in a `offline` state, the reason (if available). For example, `oom-killed`.",
1253        ),
1254        (
1255            "updated_at",
1256            "The time at which the status was last updated.",
1257        ),
1258    ]),
1259    sql: "
1260SELECT
1261    DISTINCT ON (replica_id, process_id)
1262    replica_id,
1263    process_id,
1264    status,
1265    reason,
1266    occurred_at as updated_at
1267FROM mz_internal.mz_cluster_replica_status_history
1268JOIN mz_cluster_replicas r ON r.id = replica_id
1269ORDER BY replica_id, process_id, occurred_at DESC",
1270    access: vec![PUBLIC_SELECT],
1271    ontology: Some(Ontology {
1272        entity_name: "replica_status",
1273        description: "Current status of each replica",
1274        links: &const {
1275            [OntologyLink {
1276                name: "status_of_replica",
1277                target: "replica",
1278                properties: LinkProperties::fk_typed(
1279                    "replica_id",
1280                    "id",
1281                    Cardinality::ManyToOne,
1282                    mz_repr::SemanticType::ReplicaId,
1283                ),
1284            }]
1285        },
1286        column_semantic_types: &const {
1287            [
1288                ("replica_id", SemanticType::ReplicaId),
1289                ("updated_at", SemanticType::WallclockTimestamp),
1290            ]
1291        },
1292    }),
1293});
1294
1295pub static MZ_SOURCE_STATUS_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
1296    name: "mz_source_status_history",
1297    schema: MZ_INTERNAL_SCHEMA,
1298    oid: oid::SOURCE_MZ_SOURCE_STATUS_HISTORY_OID,
1299    data_source: IntrospectionType::SourceStatusHistory.into(),
1300    desc: MZ_SOURCE_STATUS_HISTORY_DESC.clone(),
1301    column_comments: BTreeMap::from_iter([
1302        (
1303            "occurred_at",
1304            "Wall-clock timestamp of the source status change.",
1305        ),
1306        (
1307            "source_id",
1308            "The ID of the source. Corresponds to `mz_catalog.mz_sources.id`.",
1309        ),
1310        (
1311            "status",
1312            "The status of the source: one of `created`, `starting`, `running`, `paused`, `stalled`, `failed`, or `dropped`.",
1313        ),
1314        (
1315            "error",
1316            "If the source is in an error state, the error message.",
1317        ),
1318        (
1319            "details",
1320            "Additional metadata provided by the source. In case of error, may contain a `hint` field with helpful suggestions.",
1321        ),
1322        (
1323            "replica_id",
1324            "The ID of the replica that an instance of a source is running on.",
1325        ),
1326    ]),
1327    is_retained_metrics_object: false,
1328    access: vec![PUBLIC_SELECT],
1329    ontology: Some(Ontology {
1330        entity_name: "source_status_event",
1331        description: "Historical source status events",
1332        links: &const {
1333            [
1334                OntologyLink {
1335                    name: "status_event_of_source",
1336                    target: "source",
1337                    properties: LinkProperties::fk_mapped(
1338                        "source_id",
1339                        "id",
1340                        Cardinality::ManyToOne,
1341                        mz_repr::SemanticType::GlobalId,
1342                        "mz_internal.mz_object_global_ids",
1343                    ),
1344                },
1345                OntologyLink {
1346                    name: "on_replica",
1347                    target: "replica",
1348                    properties: LinkProperties::fk_nullable(
1349                        "replica_id",
1350                        "id",
1351                        Cardinality::ManyToOne,
1352                    ),
1353                },
1354            ]
1355        },
1356        column_semantic_types: &const {
1357            [
1358                ("occurred_at", SemanticType::WallclockTimestamp),
1359                ("source_id", SemanticType::GlobalId),
1360                ("replica_id", SemanticType::ReplicaId),
1361            ]
1362        },
1363    }),
1364});
1365
1366pub static MZ_AWS_PRIVATELINK_CONNECTION_STATUS_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(
1367    || BuiltinSource {
1368        name: "mz_aws_privatelink_connection_status_history",
1369        schema: MZ_INTERNAL_SCHEMA,
1370        oid: oid::SOURCE_MZ_AWS_PRIVATELINK_CONNECTION_STATUS_HISTORY_OID,
1371        data_source: DataSourceDesc::Introspection(
1372            IntrospectionType::PrivatelinkConnectionStatusHistory,
1373        ),
1374        desc: MZ_AWS_PRIVATELINK_CONNECTION_STATUS_HISTORY_DESC.clone(),
1375        column_comments: BTreeMap::from_iter([
1376            ("occurred_at", "Wall-clock timestamp of the status change."),
1377            (
1378                "connection_id",
1379                "The unique identifier of the AWS PrivateLink connection. Corresponds to `mz_catalog.mz_connections.id`.",
1380            ),
1381            (
1382                "status",
1383                "The status of the connection: one of `pending-service-discovery`, `creating-endpoint`, `recreating-endpoint`, `updating-endpoint`, `available`, `deleted`, `deleting`, `expired`, `failed`, `pending`, `pending-acceptance`, `rejected`, or `unknown`.",
1384            ),
1385        ]),
1386        is_retained_metrics_object: false,
1387        access: vec![PUBLIC_SELECT],
1388        ontology: None,
1389    },
1390);
1391
1392pub static MZ_AWS_PRIVATELINK_CONNECTION_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| {
1393    BuiltinView {
1394        name: "mz_aws_privatelink_connection_statuses",
1395        schema: MZ_INTERNAL_SCHEMA,
1396        oid: oid::VIEW_MZ_AWS_PRIVATELINK_CONNECTION_STATUSES_OID,
1397        desc: RelationDesc::builder()
1398            .with_column("id", SqlScalarType::String.nullable(false))
1399            .with_column("name", SqlScalarType::String.nullable(false))
1400            .with_column(
1401                "last_status_change_at",
1402                SqlScalarType::TimestampTz { precision: None }.nullable(true),
1403            )
1404            .with_column("status", SqlScalarType::String.nullable(true))
1405            .with_key(vec![0])
1406            .finish(),
1407        column_comments: BTreeMap::from_iter([
1408            (
1409                "id",
1410                "The ID of the connection. Corresponds to `mz_catalog.mz_connections.id`.",
1411            ),
1412            ("name", "The name of the connection."),
1413            (
1414                "last_status_change_at",
1415                "Wall-clock timestamp of the connection status change.",
1416            ),
1417            (
1418                "status",
1419                "The status of the connection: one of `pending-service-discovery`, `creating-endpoint`, `recreating-endpoint`, `updating-endpoint`, `available`, `deleted`, `deleting`, `expired`, `failed`, `pending`, `pending-acceptance`, `rejected`, or `unknown`.",
1420            ),
1421        ]),
1422        sql: "
1423    WITH statuses_w_last_status AS (
1424        SELECT
1425            connection_id,
1426            occurred_at,
1427            status,
1428            lag(status) OVER (PARTITION BY connection_id ORDER BY occurred_at) AS last_status
1429        FROM mz_internal.mz_aws_privatelink_connection_status_history
1430    ),
1431    latest_events AS (
1432        -- Only take the most recent transition for each ID
1433        SELECT DISTINCT ON(connection_id) connection_id, occurred_at, status
1434        FROM statuses_w_last_status
1435        -- Only keep first status transitions
1436        WHERE status <> last_status OR last_status IS NULL
1437        ORDER BY connection_id, occurred_at DESC
1438    )
1439    SELECT
1440        conns.id,
1441        name,
1442        occurred_at as last_status_change_at,
1443        status
1444    FROM latest_events
1445    JOIN mz_catalog.mz_connections AS conns
1446    ON conns.id = latest_events.connection_id",
1447        access: vec![PUBLIC_SELECT],
1448        ontology: Some(Ontology {
1449            entity_name: "privatelink_status",
1450            description: "PrivateLink connection health status",
1451            links: &const {
1452                [OntologyLink {
1453                    name: "status_of",
1454                    target: "connection",
1455                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
1456                }]
1457            },
1458            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
1459        }),
1460    }
1461});
1462
1463pub static MZ_STATEMENT_EXECUTION_HISTORY: LazyLock<BuiltinSource> =
1464    LazyLock::new(|| BuiltinSource {
1465        name: "mz_statement_execution_history",
1466        schema: MZ_INTERNAL_SCHEMA,
1467        oid: oid::SOURCE_MZ_STATEMENT_EXECUTION_HISTORY_OID,
1468        data_source: IntrospectionType::StatementExecutionHistory.into(),
1469        desc: MZ_STATEMENT_EXECUTION_HISTORY_DESC.clone(),
1470        column_comments: BTreeMap::new(),
1471        is_retained_metrics_object: false,
1472        access: vec![MONITOR_SELECT],
1473        ontology: None,
1474    });
1475
1476pub static MZ_STATEMENT_EXECUTION_HISTORY_REDACTED: LazyLock<BuiltinView> = LazyLock::new(|| {
1477    BuiltinView {
1478    name: "mz_statement_execution_history_redacted",
1479    schema: MZ_INTERNAL_SCHEMA,
1480    oid: oid::VIEW_MZ_STATEMENT_EXECUTION_HISTORY_REDACTED_OID,
1481    // everything but `params` and `error_message`
1482    desc: RelationDesc::builder()
1483        .with_column("id", SqlScalarType::Uuid.nullable(false))
1484        .with_column("prepared_statement_id", SqlScalarType::Uuid.nullable(false))
1485        .with_column("sample_rate", SqlScalarType::Float64.nullable(false))
1486        .with_column("cluster_id", SqlScalarType::String.nullable(true))
1487        .with_column("application_name", SqlScalarType::String.nullable(false))
1488        .with_column("cluster_name", SqlScalarType::String.nullable(true))
1489        .with_column("database_name", SqlScalarType::String.nullable(false))
1490        .with_column("search_path", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(false))
1491        .with_column("transaction_isolation", SqlScalarType::String.nullable(false))
1492        .with_column("execution_timestamp", SqlScalarType::UInt64.nullable(true))
1493        .with_column("transaction_id", SqlScalarType::UInt64.nullable(false))
1494        .with_column("transient_index_id", SqlScalarType::String.nullable(true))
1495        .with_column("mz_version", SqlScalarType::String.nullable(false))
1496        .with_column("began_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1497        .with_column("finished_at", SqlScalarType::TimestampTz { precision: None }.nullable(true))
1498        .with_column("finished_status", SqlScalarType::String.nullable(true))
1499        .with_column("result_size", SqlScalarType::Int64.nullable(true))
1500        .with_column("rows_returned", SqlScalarType::Int64.nullable(true))
1501        .with_column("execution_strategy", SqlScalarType::String.nullable(true))
1502        .finish(),
1503    column_comments: BTreeMap::new(),
1504    sql: "
1505SELECT id, prepared_statement_id, sample_rate, cluster_id, application_name,
1506cluster_name, database_name, search_path, transaction_isolation, execution_timestamp, transaction_id,
1507transient_index_id, mz_version, began_at, finished_at, finished_status,
1508result_size, rows_returned, execution_strategy
1509FROM mz_internal.mz_statement_execution_history",
1510    access: vec![SUPPORT_SELECT, ANALYTICS_SELECT, MONITOR_REDACTED_SELECT, MONITOR_SELECT],
1511    ontology: None,
1512}
1513});
1514
1515pub static MZ_PREPARED_STATEMENT_HISTORY: LazyLock<BuiltinSource> =
1516    LazyLock::new(|| BuiltinSource {
1517        name: "mz_prepared_statement_history",
1518        schema: MZ_INTERNAL_SCHEMA,
1519        oid: oid::SOURCE_MZ_PREPARED_STATEMENT_HISTORY_OID,
1520        data_source: IntrospectionType::PreparedStatementHistory.into(),
1521        desc: MZ_PREPARED_STATEMENT_HISTORY_DESC.clone(),
1522        column_comments: BTreeMap::new(),
1523        is_retained_metrics_object: false,
1524        access: vec![
1525            SUPPORT_SELECT,
1526            ANALYTICS_SELECT,
1527            MONITOR_REDACTED_SELECT,
1528            MONITOR_SELECT,
1529        ],
1530        ontology: None,
1531    });
1532
1533pub static MZ_SQL_TEXT: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
1534    name: "mz_sql_text",
1535    schema: MZ_INTERNAL_SCHEMA,
1536    oid: oid::SOURCE_MZ_SQL_TEXT_OID,
1537    desc: MZ_SQL_TEXT_DESC.clone(),
1538    data_source: IntrospectionType::SqlText.into(),
1539    column_comments: BTreeMap::new(),
1540    is_retained_metrics_object: false,
1541    access: vec![MONITOR_SELECT],
1542    ontology: Some(Ontology {
1543        entity_name: "sql_text",
1544        description: "Raw SQL text of executed statements",
1545        links: &const { [] },
1546        column_semantic_types: &[],
1547    }),
1548});
1549
1550pub static MZ_SQL_TEXT_REDACTED: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
1551    name: "mz_sql_text_redacted",
1552    schema: MZ_INTERNAL_SCHEMA,
1553    oid: oid::VIEW_MZ_SQL_TEXT_REDACTED_OID,
1554    desc: RelationDesc::builder()
1555        .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1556        .with_column("redacted_sql", SqlScalarType::String.nullable(false))
1557        .finish(),
1558    column_comments: BTreeMap::new(),
1559    sql: "SELECT sql_hash, redacted_sql FROM mz_internal.mz_sql_text",
1560    access: vec![
1561        MONITOR_SELECT,
1562        MONITOR_REDACTED_SELECT,
1563        SUPPORT_SELECT,
1564        ANALYTICS_SELECT,
1565    ],
1566    ontology: None,
1567});
1568
1569pub static MZ_RECENT_SQL_TEXT: LazyLock<BuiltinView> = LazyLock::new(|| {
1570    BuiltinView {
1571        name: "mz_recent_sql_text",
1572        schema: MZ_INTERNAL_SCHEMA,
1573        oid: oid::VIEW_MZ_RECENT_SQL_TEXT_OID,
1574        // This should always be 1 day more than the interval in
1575        // `MZ_RECENT_THINNED_ACTIVITY_LOG` , because `prepared_day`
1576        // is rounded down to the nearest day.  Thus something that actually happened three days ago
1577        // could have a `prepared day` anywhere from 3 to 4 days back.
1578        desc: RelationDesc::builder()
1579            .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1580            .with_column("sql", SqlScalarType::String.nullable(false))
1581            .with_column("redacted_sql", SqlScalarType::String.nullable(false))
1582            .with_key(vec![0, 1, 2])
1583            .finish(),
1584        column_comments: BTreeMap::new(),
1585        sql: "SELECT DISTINCT sql_hash, sql, redacted_sql FROM mz_internal.mz_sql_text WHERE prepared_day + INTERVAL '4 days' >= mz_now()",
1586        access: vec![MONITOR_SELECT],
1587        ontology: Some(Ontology {
1588            entity_name: "recent_sql_text",
1589            description: "Recent SQL text (indexed, last ~3-4 days)",
1590            links: &const { [] },
1591            column_semantic_types: &[("sql", SemanticType::SqlDefinition)],
1592        }),
1593    }
1594});
1595
1596pub static MZ_RECENT_SQL_TEXT_REDACTED: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
1597    name: "mz_recent_sql_text_redacted",
1598    schema: MZ_INTERNAL_SCHEMA,
1599    oid: oid::VIEW_MZ_RECENT_SQL_TEXT_REDACTED_OID,
1600    desc: RelationDesc::builder()
1601        .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1602        .with_column("redacted_sql", SqlScalarType::String.nullable(false))
1603        .finish(),
1604    column_comments: BTreeMap::new(),
1605    sql: "SELECT sql_hash, redacted_sql FROM mz_internal.mz_recent_sql_text",
1606    access: vec![
1607        MONITOR_SELECT,
1608        MONITOR_REDACTED_SELECT,
1609        SUPPORT_SELECT,
1610        ANALYTICS_SELECT,
1611    ],
1612    ontology: None,
1613});
1614
1615pub static MZ_RECENT_SQL_TEXT_IND: LazyLock<BuiltinIndex> = LazyLock::new(|| BuiltinIndex {
1616    name: "mz_recent_sql_text_ind",
1617    schema: MZ_INTERNAL_SCHEMA,
1618    oid: oid::INDEX_MZ_RECENT_SQL_TEXT_IND_OID,
1619    sql: "IN CLUSTER mz_catalog_server ON mz_internal.mz_recent_sql_text (sql_hash)",
1620    is_retained_metrics_object: false,
1621});
1622
1623pub static MZ_SESSION_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
1624    name: "mz_session_history",
1625    schema: MZ_INTERNAL_SCHEMA,
1626    oid: oid::SOURCE_MZ_SESSION_HISTORY_OID,
1627    data_source: IntrospectionType::SessionHistory.into(),
1628    desc: MZ_SESSION_HISTORY_DESC.clone(),
1629    column_comments: BTreeMap::from_iter([
1630        (
1631            "session_id",
1632            "The globally unique ID of the session. Corresponds to `mz_sessions.id`.",
1633        ),
1634        (
1635            "connected_at",
1636            "The time at which the session was established.",
1637        ),
1638        (
1639            "initial_application_name",
1640            "The `application_name` session metadata field.",
1641        ),
1642        (
1643            "authenticated_user",
1644            "The name of the user for which the session was established.",
1645        ),
1646    ]),
1647    is_retained_metrics_object: false,
1648    access: vec![PUBLIC_SELECT],
1649    ontology: Some(Ontology {
1650        entity_name: "session",
1651        description: "Historical session connection events",
1652        links: &const {
1653            [OntologyLink {
1654                name: "active_as",
1655                target: "active_session",
1656                properties: LinkProperties::fk_nullable("session_id", "id", Cardinality::ManyToOne),
1657            }]
1658        },
1659        column_semantic_types: &[("connected_at", SemanticType::WallclockTimestamp)],
1660    }),
1661});
1662
1663pub static MZ_ACTIVITY_LOG_THINNED: LazyLock<BuiltinView> = LazyLock::new(|| {
1664    BuiltinView {
1665        name: "mz_activity_log_thinned",
1666        schema: MZ_INTERNAL_SCHEMA,
1667        oid: oid::VIEW_MZ_ACTIVITY_LOG_THINNED_OID,
1668        desc: RelationDesc::builder()
1669            .with_column("execution_id", SqlScalarType::Uuid.nullable(false))
1670            .with_column("sample_rate", SqlScalarType::Float64.nullable(false))
1671            .with_column("cluster_id", SqlScalarType::String.nullable(true))
1672            .with_column("application_name", SqlScalarType::String.nullable(false))
1673            .with_column("cluster_name", SqlScalarType::String.nullable(true))
1674            .with_column("database_name", SqlScalarType::String.nullable(false))
1675            .with_column("search_path", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(false))
1676            .with_column("transaction_isolation", SqlScalarType::String.nullable(false))
1677            .with_column("execution_timestamp", SqlScalarType::UInt64.nullable(true))
1678            .with_column("transient_index_id", SqlScalarType::String.nullable(true))
1679            .with_column("params", SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false))
1680            .with_column("mz_version", SqlScalarType::String.nullable(false))
1681            .with_column("began_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1682            .with_column("finished_at", SqlScalarType::TimestampTz { precision: None }.nullable(true))
1683            .with_column("finished_status", SqlScalarType::String.nullable(true))
1684            .with_column("error_message", SqlScalarType::String.nullable(true))
1685            .with_column("result_size", SqlScalarType::Int64.nullable(true))
1686            .with_column("rows_returned", SqlScalarType::Int64.nullable(true))
1687            .with_column("execution_strategy", SqlScalarType::String.nullable(true))
1688            .with_column("transaction_id", SqlScalarType::UInt64.nullable(false))
1689            .with_column("prepared_statement_id", SqlScalarType::Uuid.nullable(false))
1690            .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1691            .with_column("prepared_statement_name", SqlScalarType::String.nullable(false))
1692            .with_column("session_id", SqlScalarType::Uuid.nullable(false))
1693            .with_column("prepared_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1694            .with_column("statement_type", SqlScalarType::String.nullable(true))
1695            .with_column("throttled_count", SqlScalarType::UInt64.nullable(false))
1696            .with_column("connected_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1697            .with_column("initial_application_name", SqlScalarType::String.nullable(false))
1698            .with_column("authenticated_user", SqlScalarType::String.nullable(false))
1699            .finish(),
1700        column_comments: BTreeMap::new(),
1701        sql: "
1702SELECT mseh.id AS execution_id, sample_rate, cluster_id, application_name, cluster_name, database_name, search_path,
1703transaction_isolation, execution_timestamp, transient_index_id, params, mz_version, began_at, finished_at, finished_status,
1704error_message, result_size, rows_returned, execution_strategy, transaction_id,
1705mpsh.id AS prepared_statement_id, sql_hash, mpsh.name AS prepared_statement_name,
1706mpsh.session_id, prepared_at, statement_type, throttled_count,
1707connected_at, initial_application_name, authenticated_user
1708FROM mz_internal.mz_statement_execution_history mseh,
1709     mz_internal.mz_prepared_statement_history mpsh,
1710     mz_internal.mz_session_history msh
1711WHERE mseh.prepared_statement_id = mpsh.id
1712AND mpsh.session_id = msh.session_id",
1713        access: vec![MONITOR_SELECT],
1714        ontology: None,
1715    }
1716});
1717
1718pub static MZ_RECENT_ACTIVITY_LOG_THINNED: LazyLock<BuiltinView> = LazyLock::new(|| {
1719    BuiltinView {
1720        name: "mz_recent_activity_log_thinned",
1721        schema: MZ_INTERNAL_SCHEMA,
1722        oid: oid::VIEW_MZ_RECENT_ACTIVITY_LOG_THINNED_OID,
1723        desc: RelationDesc::builder()
1724            .with_column("execution_id", SqlScalarType::Uuid.nullable(false))
1725            .with_column("sample_rate", SqlScalarType::Float64.nullable(false))
1726            .with_column("cluster_id", SqlScalarType::String.nullable(true))
1727            .with_column("application_name", SqlScalarType::String.nullable(false))
1728            .with_column("cluster_name", SqlScalarType::String.nullable(true))
1729            .with_column("database_name", SqlScalarType::String.nullable(false))
1730            .with_column("search_path", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(false))
1731            .with_column("transaction_isolation", SqlScalarType::String.nullable(false))
1732            .with_column("execution_timestamp", SqlScalarType::UInt64.nullable(true))
1733            .with_column("transient_index_id", SqlScalarType::String.nullable(true))
1734            .with_column("params", SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false))
1735            .with_column("mz_version", SqlScalarType::String.nullable(false))
1736            .with_column("began_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1737            .with_column("finished_at", SqlScalarType::TimestampTz { precision: None }.nullable(true))
1738            .with_column("finished_status", SqlScalarType::String.nullable(true))
1739            .with_column("error_message", SqlScalarType::String.nullable(true))
1740            .with_column("result_size", SqlScalarType::Int64.nullable(true))
1741            .with_column("rows_returned", SqlScalarType::Int64.nullable(true))
1742            .with_column("execution_strategy", SqlScalarType::String.nullable(true))
1743            .with_column("transaction_id", SqlScalarType::UInt64.nullable(false))
1744            .with_column("prepared_statement_id", SqlScalarType::Uuid.nullable(false))
1745            .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1746            .with_column("prepared_statement_name", SqlScalarType::String.nullable(false))
1747            .with_column("session_id", SqlScalarType::Uuid.nullable(false))
1748            .with_column("prepared_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1749            .with_column("statement_type", SqlScalarType::String.nullable(true))
1750            .with_column("throttled_count", SqlScalarType::UInt64.nullable(false))
1751            .with_column("connected_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
1752            .with_column("initial_application_name", SqlScalarType::String.nullable(false))
1753            .with_column("authenticated_user", SqlScalarType::String.nullable(false))
1754            .finish(),
1755        column_comments: BTreeMap::new(),
1756        // We use a temporal window of 2 days rather than 1 day for `mz_session_history`'s `connected_at` since a statement execution at
1757        // the edge of the 1 day temporal window could've been executed in a session that was established an hour before the 1 day window.
1758        sql:
1759        "SELECT * FROM mz_internal.mz_activity_log_thinned WHERE prepared_at + INTERVAL '1 day' > mz_now()
1760AND began_at + INTERVAL '1 day' > mz_now() AND connected_at + INTERVAL '2 days' > mz_now()",
1761        access: vec![MONITOR_SELECT],
1762        ontology: None,
1763    }
1764});
1765
1766pub static MZ_RECENT_ACTIVITY_LOG: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
1767    name: "mz_recent_activity_log",
1768    schema: MZ_INTERNAL_SCHEMA,
1769    oid: oid::VIEW_MZ_RECENT_ACTIVITY_LOG_OID,
1770    desc: RelationDesc::builder()
1771        .with_column("execution_id", SqlScalarType::Uuid.nullable(false))
1772        .with_column("sample_rate", SqlScalarType::Float64.nullable(false))
1773        .with_column("cluster_id", SqlScalarType::String.nullable(true))
1774        .with_column("application_name", SqlScalarType::String.nullable(false))
1775        .with_column("cluster_name", SqlScalarType::String.nullable(true))
1776        .with_column("database_name", SqlScalarType::String.nullable(false))
1777        .with_column(
1778            "search_path",
1779            SqlScalarType::List {
1780                element_type: Box::new(SqlScalarType::String),
1781                custom_id: None,
1782            }
1783            .nullable(false),
1784        )
1785        .with_column(
1786            "transaction_isolation",
1787            SqlScalarType::String.nullable(false),
1788        )
1789        .with_column("execution_timestamp", SqlScalarType::UInt64.nullable(true))
1790        .with_column("transient_index_id", SqlScalarType::String.nullable(true))
1791        .with_column(
1792            "params",
1793            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
1794        )
1795        .with_column("mz_version", SqlScalarType::String.nullable(false))
1796        .with_column(
1797            "began_at",
1798            SqlScalarType::TimestampTz { precision: None }.nullable(false),
1799        )
1800        .with_column(
1801            "finished_at",
1802            SqlScalarType::TimestampTz { precision: None }.nullable(true),
1803        )
1804        .with_column("finished_status", SqlScalarType::String.nullable(true))
1805        .with_column("error_message", SqlScalarType::String.nullable(true))
1806        .with_column("result_size", SqlScalarType::Int64.nullable(true))
1807        .with_column("rows_returned", SqlScalarType::Int64.nullable(true))
1808        .with_column("execution_strategy", SqlScalarType::String.nullable(true))
1809        .with_column("transaction_id", SqlScalarType::UInt64.nullable(false))
1810        .with_column("prepared_statement_id", SqlScalarType::Uuid.nullable(false))
1811        .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
1812        .with_column(
1813            "prepared_statement_name",
1814            SqlScalarType::String.nullable(false),
1815        )
1816        .with_column("session_id", SqlScalarType::Uuid.nullable(false))
1817        .with_column(
1818            "prepared_at",
1819            SqlScalarType::TimestampTz { precision: None }.nullable(false),
1820        )
1821        .with_column("statement_type", SqlScalarType::String.nullable(true))
1822        .with_column("throttled_count", SqlScalarType::UInt64.nullable(false))
1823        .with_column(
1824            "connected_at",
1825            SqlScalarType::TimestampTz { precision: None }.nullable(false),
1826        )
1827        .with_column(
1828            "initial_application_name",
1829            SqlScalarType::String.nullable(false),
1830        )
1831        .with_column("authenticated_user", SqlScalarType::String.nullable(false))
1832        .with_column("sql", SqlScalarType::String.nullable(false))
1833        .finish(),
1834    column_comments: BTreeMap::from_iter([
1835        (
1836            "execution_id",
1837            "An ID that is unique for each executed statement.",
1838        ),
1839        (
1840            "sample_rate",
1841            "The actual rate at which the statement was sampled.",
1842        ),
1843        (
1844            "cluster_id",
1845            "The ID of the cluster the statement execution was directed to. Corresponds to mz_clusters.id.",
1846        ),
1847        (
1848            "application_name",
1849            "The value of the `application_name` configuration parameter at execution time.",
1850        ),
1851        (
1852            "cluster_name",
1853            "The name of the cluster with ID `cluster_id` at execution time.",
1854        ),
1855        (
1856            "database_name",
1857            "The value of the `database` configuration parameter at execution time.",
1858        ),
1859        (
1860            "search_path",
1861            "The value of the `search_path` configuration parameter at execution time.",
1862        ),
1863        (
1864            "transaction_isolation",
1865            "The value of the `transaction_isolation` configuration parameter at execution time.",
1866        ),
1867        (
1868            "execution_timestamp",
1869            "The logical timestamp at which execution was scheduled.",
1870        ),
1871        (
1872            "transient_index_id",
1873            "The internal index of the compute dataflow created for the query, if any.",
1874        ),
1875        (
1876            "params",
1877            "The parameters with which the statement was executed.",
1878        ),
1879        (
1880            "mz_version",
1881            "The version of Materialize that was running when the statement was executed.",
1882        ),
1883        (
1884            "began_at",
1885            "The wall-clock time at which the statement began executing.",
1886        ),
1887        (
1888            "finished_at",
1889            "The wall-clock time at which the statement finished executing.",
1890        ),
1891        (
1892            "finished_status",
1893            "The final status of the statement (e.g., `success`, `canceled`, `error`, or `aborted`). \
1894            `aborted` means that the client disconnected before the statement finished executing.",
1895        ),
1896        (
1897            "error_message",
1898            "The error message, if the statement failed.",
1899        ),
1900        (
1901            "result_size",
1902            "The size in bytes of the result, for statements that return rows.",
1903        ),
1904        (
1905            "rows_returned",
1906            "The number of rows returned, for statements that return rows.",
1907        ),
1908        (
1909            "execution_strategy",
1910            "For `SELECT` statements (and similar statement types), the strategy for executing the query. \
1911             `standard` means computed by a temporary dataflow, \
1912             `fast-path` means read by a cluster directly from an in-memory index, \
1913             `persist-fast-path` means read a source, table, or materialized view from blob storage (without an index or dataflow), \
1914             and `constant` means computed in the control plane without the involvement of a cluster. \
1915             (It's `NULL` for statements that errored/canceled/aborted and for non-query-like statement types.)",
1916        ),
1917        (
1918            "transaction_id",
1919            "The ID of the transaction that the statement was part of. Note that transaction IDs are only unique per session.",
1920        ),
1921        (
1922            "prepared_statement_id",
1923            "An ID that is unique for each prepared statement. For example, if a statement is prepared once and then executed multiple times, all executions will have the same value for this column (but different values for `execution_id`).",
1924        ),
1925        (
1926            "sql_hash",
1927            "An opaque value uniquely identifying the text of the query.",
1928        ),
1929        (
1930            "prepared_statement_name",
1931            "The name given by the client library to the prepared statement.",
1932        ),
1933        (
1934            "session_id",
1935            "An ID that is unique for each session. Corresponds to mz_sessions.id.",
1936        ),
1937        (
1938            "prepared_at",
1939            "The time at which the statement was prepared.",
1940        ),
1941        (
1942            "statement_type",
1943            "The _type_ of the statement, e.g. `select` for a `SELECT` query, or `NULL` if the statement was empty.",
1944        ),
1945        (
1946            "throttled_count",
1947            "The number of statement executions dropped due to throttling between the previously logged statement and this one. If you have a very high volume of queries and need to log them without throttling, contact our team.",
1948        ),
1949        (
1950            "connected_at",
1951            "The time at which the session was established.",
1952        ),
1953        (
1954            "initial_application_name",
1955            "The initial value of `application_name` at the beginning of the session.",
1956        ),
1957        (
1958            "authenticated_user",
1959            "The name of the user for which the session was established.",
1960        ),
1961        ("sql", "The SQL text of the statement."),
1962    ]),
1963    sql: "SELECT mralt.*, mrst.sql
1964FROM mz_internal.mz_recent_activity_log_thinned mralt,
1965     mz_internal.mz_recent_sql_text mrst
1966WHERE mralt.sql_hash = mrst.sql_hash",
1967    access: vec![MONITOR_SELECT],
1968    ontology: Some(Ontology {
1969        entity_name: "activity_log",
1970        description: "Recent query activity with execution stats",
1971        links: &const {
1972            [
1973                OntologyLink {
1974                    name: "in_session",
1975                    target: "session",
1976                    properties: LinkProperties::fk("session_id", "id", Cardinality::ManyToOne),
1977                },
1978                OntologyLink {
1979                    name: "in_active_session",
1980                    target: "active_session",
1981                    properties: LinkProperties::fk_nullable(
1982                        "session_id",
1983                        "id",
1984                        Cardinality::ManyToOne,
1985                    ),
1986                },
1987                OntologyLink {
1988                    name: "ran_on_cluster",
1989                    target: "cluster",
1990                    properties: LinkProperties::fk_nullable(
1991                        "cluster_id",
1992                        "id",
1993                        Cardinality::ManyToOne,
1994                    ),
1995                },
1996                OntologyLink {
1997                    name: "used_transient_index",
1998                    target: "object",
1999                    properties: LinkProperties::ForeignKey {
2000                        source_column: "transient_index_id",
2001                        target_column: "id",
2002                        cardinality: Cardinality::ManyToOne,
2003                        source_id_type: Some(mz_repr::SemanticType::GlobalId),
2004                        requires_mapping: Some("mz_internal.mz_object_global_ids"),
2005                        nullable: true,
2006                        note: None,
2007                        extra_key_columns: None,
2008                    },
2009                },
2010            ]
2011        },
2012        column_semantic_types: &const {
2013            [
2014                ("cluster_id", SemanticType::ClusterId),
2015                ("execution_timestamp", SemanticType::MzTimestamp),
2016                ("transient_index_id", SemanticType::GlobalId),
2017                ("began_at", SemanticType::WallclockTimestamp),
2018                ("finished_at", SemanticType::WallclockTimestamp),
2019                ("prepared_at", SemanticType::WallclockTimestamp),
2020                ("connected_at", SemanticType::WallclockTimestamp),
2021                ("sql", SemanticType::SqlDefinition),
2022            ]
2023        },
2024    }),
2025});
2026
2027pub static MZ_RECENT_ACTIVITY_LOG_REDACTED: LazyLock<BuiltinView> = LazyLock::new(|| {
2028    BuiltinView {
2029    name: "mz_recent_activity_log_redacted",
2030    schema: MZ_INTERNAL_SCHEMA,
2031    oid: oid::VIEW_MZ_RECENT_ACTIVITY_LOG_REDACTED_OID,
2032    // Includes all the columns in mz_recent_activity_log_thinned except 'error_message'.
2033    desc: RelationDesc::builder()
2034        .with_column("execution_id", SqlScalarType::Uuid.nullable(false))
2035        .with_column("sample_rate", SqlScalarType::Float64.nullable(false))
2036        .with_column("cluster_id", SqlScalarType::String.nullable(true))
2037        .with_column("application_name", SqlScalarType::String.nullable(false))
2038        .with_column("cluster_name", SqlScalarType::String.nullable(true))
2039        .with_column("database_name", SqlScalarType::String.nullable(false))
2040        .with_column("search_path", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(false))
2041        .with_column("transaction_isolation", SqlScalarType::String.nullable(false))
2042        .with_column("execution_timestamp", SqlScalarType::UInt64.nullable(true))
2043        .with_column("transient_index_id", SqlScalarType::String.nullable(true))
2044        .with_column("mz_version", SqlScalarType::String.nullable(false))
2045        .with_column("began_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
2046        .with_column("finished_at", SqlScalarType::TimestampTz { precision: None }.nullable(true))
2047        .with_column("finished_status", SqlScalarType::String.nullable(true))
2048        .with_column("result_size", SqlScalarType::Int64.nullable(true))
2049        .with_column("rows_returned", SqlScalarType::Int64.nullable(true))
2050        .with_column("execution_strategy", SqlScalarType::String.nullable(true))
2051        .with_column("transaction_id", SqlScalarType::UInt64.nullable(false))
2052        .with_column("prepared_statement_id", SqlScalarType::Uuid.nullable(false))
2053        .with_column("sql_hash", SqlScalarType::Bytes.nullable(false))
2054        .with_column("prepared_statement_name", SqlScalarType::String.nullable(false))
2055        .with_column("session_id", SqlScalarType::Uuid.nullable(false))
2056        .with_column("prepared_at", SqlScalarType::TimestampTz { precision: None }.nullable(false))
2057        .with_column("statement_type", SqlScalarType::String.nullable(true))
2058        .with_column("throttled_count", SqlScalarType::UInt64.nullable(false))
2059        .with_column("initial_application_name", SqlScalarType::String.nullable(false))
2060        .with_column("authenticated_user", SqlScalarType::String.nullable(false))
2061        .with_column("redacted_sql", SqlScalarType::String.nullable(false))
2062        .finish(),
2063    column_comments: BTreeMap::new(),
2064    sql: "SELECT mralt.execution_id, mralt.sample_rate, mralt.cluster_id, mralt.application_name,
2065    mralt.cluster_name, mralt.database_name, mralt.search_path, mralt.transaction_isolation, mralt.execution_timestamp,
2066    mralt.transient_index_id, mralt.mz_version, mralt.began_at, mralt.finished_at,
2067    mralt.finished_status, mralt.result_size, mralt.rows_returned, mralt.execution_strategy, mralt.transaction_id,
2068    mralt.prepared_statement_id, mralt.sql_hash, mralt.prepared_statement_name, mralt.session_id,
2069    mralt.prepared_at, mralt.statement_type, mralt.throttled_count,
2070    mralt.initial_application_name, mralt.authenticated_user,
2071    mrst.redacted_sql
2072FROM mz_internal.mz_recent_activity_log_thinned mralt,
2073     mz_internal.mz_recent_sql_text mrst
2074WHERE mralt.sql_hash = mrst.sql_hash",
2075    access: vec![MONITOR_SELECT, MONITOR_REDACTED_SELECT, SUPPORT_SELECT, ANALYTICS_SELECT],
2076    ontology: None,
2077}
2078});
2079
2080pub static MZ_STATEMENT_LIFECYCLE_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| {
2081    BuiltinSource {
2082        name: "mz_statement_lifecycle_history",
2083        schema: MZ_INTERNAL_SCHEMA,
2084        oid: oid::SOURCE_MZ_STATEMENT_LIFECYCLE_HISTORY_OID,
2085        desc: RelationDesc::builder()
2086            .with_column("statement_id", SqlScalarType::Uuid.nullable(false))
2087            .with_column("event_type", SqlScalarType::String.nullable(false))
2088            .with_column(
2089                "occurred_at",
2090                SqlScalarType::TimestampTz { precision: None }.nullable(false),
2091            )
2092            .finish(),
2093        data_source: IntrospectionType::StatementLifecycleHistory.into(),
2094        column_comments: BTreeMap::from_iter([
2095            (
2096                "statement_id",
2097                "The ID of the execution event. Corresponds to `mz_recent_activity_log.execution_id`",
2098            ),
2099            (
2100                "event_type",
2101                "The type of lifecycle event, e.g. `'execution-began'`, `'storage-dependencies-finished'`, `'compute-dependencies-finished'`, or `'execution-finished'`",
2102            ),
2103            ("occurred_at", "The time at which the event took place."),
2104        ]),
2105        is_retained_metrics_object: false,
2106        // TODO[btv]: Maybe this should be public instead of
2107        // `MONITOR_REDACTED`, but since that would be a backwards-compatible
2108        // change, we probably don't need to worry about it now.
2109        access: vec![
2110            SUPPORT_SELECT,
2111            ANALYTICS_SELECT,
2112            MONITOR_REDACTED_SELECT,
2113            MONITOR_SELECT,
2114        ],
2115        ontology: Some(Ontology {
2116            entity_name: "statement_lifecycle_event",
2117            description: "Statement lifecycle events (parse, bind, execute)",
2118            links: &const {
2119                [OntologyLink {
2120                    name: "for_execution",
2121                    target: "activity_log",
2122                    properties: LinkProperties::fk(
2123                        "statement_id",
2124                        "execution_id",
2125                        Cardinality::ManyToOne,
2126                    ),
2127                }]
2128            },
2129            column_semantic_types: &[],
2130        }),
2131    }
2132});
2133
2134pub static MZ_SOURCE_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2135    name: "mz_source_statuses",
2136    schema: MZ_INTERNAL_SCHEMA,
2137    oid: oid::VIEW_MZ_SOURCE_STATUSES_OID,
2138    desc: RelationDesc::builder()
2139        .with_column("id", SqlScalarType::String.nullable(false))
2140        .with_column("name", SqlScalarType::String.nullable(false))
2141        .with_column("type", SqlScalarType::String.nullable(false))
2142        .with_column(
2143            "last_status_change_at",
2144            SqlScalarType::TimestampTz { precision: None }.nullable(true),
2145        )
2146        .with_column("status", SqlScalarType::String.nullable(false))
2147        .with_column("error", SqlScalarType::String.nullable(true))
2148        .with_column("details", SqlScalarType::Jsonb.nullable(true))
2149        .finish(),
2150    column_comments: BTreeMap::from_iter([
2151        (
2152            "id",
2153            "The ID of the source. Corresponds to `mz_catalog.mz_sources.id`.",
2154        ),
2155        ("name", "The name of the source."),
2156        ("type", "The type of the source."),
2157        (
2158            "last_status_change_at",
2159            "Wall-clock timestamp of the source status change.",
2160        ),
2161        (
2162            "status",
2163            "The status of the source: one of `created`, `starting`, `running`, `paused`, `stalled`, `failed`, or `dropped`.",
2164        ),
2165        (
2166            "error",
2167            "If the source is in an error state, the error message.",
2168        ),
2169        (
2170            "details",
2171            "Additional metadata provided by the source. In case of error, may contain a `hint` field with helpful suggestions.",
2172        ),
2173    ]),
2174    sql: "
2175    WITH
2176    -- The status history contains per-replica events and source-global events.
2177    -- For the latter, replica_id is NULL. We turn these into '<source>', so that
2178    -- we can treat them uniformly below.
2179    uniform_status_history AS
2180    (
2181        SELECT
2182            s.source_id,
2183            COALESCE(s.replica_id, '<source>') as replica_id,
2184            s.occurred_at,
2185            s.status,
2186            s.error,
2187            s.details
2188        FROM mz_internal.mz_source_status_history s
2189    ),
2190    -- For getting the latest events, we first determine the latest per-replica
2191    -- events here and then apply precedence rules below.
2192    --
2193    -- We ignore per-replica events from replicas that no longer exist. A dropped
2194    -- replica's last reported status is stale: without this filter a defunct
2195    -- replica's lingering 'running' can outrank (see precedence below) a live
2196    -- replica's 'stalled', hiding a genuinely broken source. We always retain
2197    -- source-global events ('<source>' is the sentinel for replica_id NULL)
2198    -- and 'paused' events. A per-replica 'paused' is only written when the
2199    -- replica is dropped, so it is a terminal drop marker, not a stale report.
2200    latest_per_replica_events AS
2201    (
2202        SELECT DISTINCT ON (source_id, replica_id)
2203            occurred_at, source_id, replica_id, status, error, details
2204        FROM uniform_status_history
2205        WHERE replica_id = '<source>'
2206            OR replica_id IN (SELECT id FROM mz_catalog.mz_cluster_replicas)
2207            OR status = 'paused'
2208        ORDER BY source_id, replica_id, occurred_at DESC
2209    ),
2210    -- We have a precedence list that determines the overall status in case
2211    -- there is differing per-replica (including source-global) statuses. If
2212    -- there is no 'dropped' status, and any replica reports 'running', the
2213    -- overall status is 'running' even if there might be some replica that has
2214    -- errors or is paused. Precedence ties are broken by recency, so a dropped
2215    -- replica's 'paused' wins over an older source-global 'paused'.
2216    latest_events AS
2217    (
2218       SELECT DISTINCT ON (source_id)
2219            source_id,
2220            occurred_at,
2221            status,
2222            error,
2223            details
2224        FROM latest_per_replica_events
2225        ORDER BY source_id, CASE status
2226                    WHEN 'dropped' THEN 1
2227                    WHEN 'running' THEN 2
2228                    WHEN 'stalled' THEN 3
2229                    WHEN 'starting' THEN 4
2230                    WHEN 'paused' THEN 5
2231                    WHEN 'ceased' THEN 6
2232                    ELSE 7  -- For any other status values
2233                END, occurred_at DESC
2234    ),
2235    -- Determine which sources are subsources and which are parent sources
2236    subsources AS
2237    (
2238        SELECT subsources.id AS self, sources.id AS parent
2239        FROM
2240            mz_catalog.mz_sources AS subsources
2241                JOIN
2242                    mz_internal.mz_object_dependencies AS deps
2243                    ON subsources.id = deps.object_id
2244                JOIN mz_catalog.mz_sources AS sources ON sources.id = deps.referenced_object_id
2245    ),
2246    -- Determine which sources are source tables
2247    tables AS
2248    (
2249        SELECT tables.id AS self, tables.source_id AS parent, tables.name
2250        FROM mz_catalog.mz_tables AS tables
2251        WHERE tables.source_id IS NOT NULL
2252    ),
2253    -- Determine which collection's ID to use for the status
2254    id_of_status_to_use AS
2255    (
2256        SELECT
2257            self_events.source_id,
2258            -- If self not errored, but parent is, use parent; else self
2259            CASE
2260                WHEN
2261                    self_events.status <> 'ceased' AND
2262                    parent_events.status = 'stalled'
2263                THEN parent_events.source_id
2264                ELSE self_events.source_id
2265            END AS id_to_use
2266        FROM
2267            latest_events AS self_events
2268                LEFT JOIN subsources ON self_events.source_id = subsources.self
2269                LEFT JOIN tables ON self_events.source_id = tables.self
2270                LEFT JOIN
2271                    latest_events AS parent_events
2272                    ON parent_events.source_id = COALESCE(subsources.parent, tables.parent)
2273    ),
2274    -- Swap out events for the ID of the event we plan to use instead
2275    latest_events_to_use AS
2276    (
2277        SELECT occurred_at, s.source_id, status, error, details
2278        FROM
2279            id_of_status_to_use AS s
2280                JOIN latest_events AS e ON e.source_id = s.id_to_use
2281    ),
2282    combined AS (
2283        SELECT
2284            mz_sources.id,
2285            mz_sources.name,
2286            mz_sources.type,
2287            occurred_at,
2288            status,
2289            error,
2290            details
2291        FROM
2292            mz_catalog.mz_sources
2293            LEFT JOIN latest_events_to_use AS e ON mz_sources.id = e.source_id
2294        UNION ALL
2295        SELECT
2296            tables.self AS id,
2297            tables.name,
2298            'table' AS type,
2299            occurred_at,
2300            status,
2301            error,
2302            details
2303        FROM
2304            tables
2305            LEFT JOIN latest_events_to_use AS e ON tables.self = e.source_id
2306    )
2307SELECT
2308    id,
2309    name,
2310    type,
2311    occurred_at AS last_status_change_at,
2312    -- TODO(parkmycar): Report status of webhook source once database-issues#5986 is closed.
2313    CASE
2314        WHEN
2315            type = 'webhook' OR
2316            type = 'progress'
2317        THEN 'running'
2318        ELSE COALESCE(status, 'created')
2319    END AS status,
2320    error,
2321    details
2322FROM combined
2323WHERE id NOT LIKE 's%';",
2324    access: vec![PUBLIC_SELECT],
2325    ontology: Some(Ontology {
2326        entity_name: "source_status",
2327        description: "Current source status (running, stalled, etc.)",
2328        links: &const {
2329            [OntologyLink {
2330                name: "status_of_source",
2331                target: "source",
2332                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
2333            }]
2334        },
2335        column_semantic_types: &const {
2336            [
2337                ("id", SemanticType::CatalogItemId),
2338                ("type", SemanticType::SourceType),
2339                ("last_status_change_at", SemanticType::WallclockTimestamp),
2340            ]
2341        },
2342    }),
2343});
2344
2345pub static MZ_SINK_STATUS_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
2346    name: "mz_sink_status_history",
2347    schema: MZ_INTERNAL_SCHEMA,
2348    oid: oid::SOURCE_MZ_SINK_STATUS_HISTORY_OID,
2349    data_source: IntrospectionType::SinkStatusHistory.into(),
2350    desc: MZ_SINK_STATUS_HISTORY_DESC.clone(),
2351    column_comments: BTreeMap::from_iter([
2352        (
2353            "occurred_at",
2354            "Wall-clock timestamp of the sink status change.",
2355        ),
2356        (
2357            "sink_id",
2358            "The ID of the sink. Corresponds to `mz_catalog.mz_sinks.id`.",
2359        ),
2360        (
2361            "status",
2362            "The status of the sink: one of `created`, `starting`, `running`, `stalled`, `failed`, or `dropped`.",
2363        ),
2364        (
2365            "error",
2366            "If the sink is in an error state, the error message.",
2367        ),
2368        (
2369            "details",
2370            "Additional metadata provided by the sink. In case of error, may contain a `hint` field with helpful suggestions.",
2371        ),
2372        (
2373            "replica_id",
2374            "The ID of the replica that an instance of a sink is running on.",
2375        ),
2376    ]),
2377    is_retained_metrics_object: false,
2378    access: vec![PUBLIC_SELECT],
2379    ontology: Some(Ontology {
2380        entity_name: "sink_status_event",
2381        description: "Historical sink status events",
2382        links: &const {
2383            [
2384                OntologyLink {
2385                    name: "status_event_of_sink",
2386                    target: "sink",
2387                    properties: LinkProperties::fk_mapped(
2388                        "sink_id",
2389                        "id",
2390                        Cardinality::ManyToOne,
2391                        mz_repr::SemanticType::GlobalId,
2392                        "mz_internal.mz_object_global_ids",
2393                    ),
2394                },
2395                OntologyLink {
2396                    name: "on_replica",
2397                    target: "replica",
2398                    properties: LinkProperties::fk_nullable(
2399                        "replica_id",
2400                        "id",
2401                        Cardinality::ManyToOne,
2402                    ),
2403                },
2404            ]
2405        },
2406        column_semantic_types: &const {
2407            [
2408                ("occurred_at", SemanticType::WallclockTimestamp),
2409                ("sink_id", SemanticType::GlobalId),
2410                ("replica_id", SemanticType::ReplicaId),
2411            ]
2412        },
2413    }),
2414});
2415
2416pub static MZ_SINK_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2417    name: "mz_sink_statuses",
2418    schema: MZ_INTERNAL_SCHEMA,
2419    oid: oid::VIEW_MZ_SINK_STATUSES_OID,
2420    desc: RelationDesc::builder()
2421        .with_column("id", SqlScalarType::String.nullable(false))
2422        .with_column("name", SqlScalarType::String.nullable(false))
2423        .with_column("type", SqlScalarType::String.nullable(false))
2424        .with_column(
2425            "last_status_change_at",
2426            SqlScalarType::TimestampTz { precision: None }.nullable(true),
2427        )
2428        .with_column("status", SqlScalarType::String.nullable(false))
2429        .with_column("error", SqlScalarType::String.nullable(true))
2430        .with_column("details", SqlScalarType::Jsonb.nullable(true))
2431        .finish(),
2432    column_comments: BTreeMap::from_iter([
2433        (
2434            "id",
2435            "The ID of the sink. Corresponds to `mz_catalog.mz_sinks.id`.",
2436        ),
2437        ("name", "The name of the sink."),
2438        ("type", "The type of the sink."),
2439        (
2440            "last_status_change_at",
2441            "Wall-clock timestamp of the sink status change.",
2442        ),
2443        (
2444            "status",
2445            "The status of the sink: one of `created`, `starting`, `running`, `stalled`, `failed`, or `dropped`.",
2446        ),
2447        (
2448            "error",
2449            "If the sink is in an error state, the error message.",
2450        ),
2451        (
2452            "details",
2453            "Additional metadata provided by the sink. In case of error, may contain a `hint` field with helpful suggestions.",
2454        ),
2455    ]),
2456    sql: "
2457WITH
2458-- The status history contains per-replica events and sink-global events.
2459-- For the latter, replica_id is NULL. We turn these into '<sink>', so that
2460-- we can treat them uniformly below.
2461uniform_status_history AS
2462(
2463    SELECT
2464        s.sink_id,
2465        COALESCE(s.replica_id, '<sink>') as replica_id,
2466        s.occurred_at,
2467        s.status,
2468        s.error,
2469        s.details
2470    FROM mz_internal.mz_sink_status_history s
2471),
2472-- For getting the latest events, we first determine the latest per-replica
2473-- events here and then apply precedence rules below.
2474--
2475-- We ignore per-replica events from replicas that no longer exist. A dropped
2476-- replica's last reported status is stale: without this filter a defunct
2477-- replica's lingering 'running' can outrank (see precedence below) a live
2478-- replica's 'stalled', hiding a genuinely broken sink. We always retain
2479-- sink-global events ('<sink>' is the sentinel for replica_id NULL)
2480-- and 'paused' events. A per-replica 'paused' is only written when the
2481-- replica is dropped, so it is a terminal drop marker, not a stale report.
2482latest_per_replica_events AS
2483(
2484    SELECT DISTINCT ON (sink_id, replica_id)
2485        occurred_at, sink_id, replica_id, status, error, details
2486    FROM uniform_status_history
2487    WHERE replica_id = '<sink>'
2488        OR replica_id IN (SELECT id FROM mz_catalog.mz_cluster_replicas)
2489        OR status = 'paused'
2490    ORDER BY sink_id, replica_id, occurred_at DESC
2491),
2492-- We have a precedence list that determines the overall status in case
2493-- there is differing per-replica (including sink-global) statuses. If
2494-- there is no 'dropped' status, and any replica reports 'running', the
2495-- overall status is 'running' even if there might be some replica that has
2496-- errors or is paused. Precedence ties are broken by recency, so a dropped
2497-- replica's 'paused' wins over an older sink-global 'paused'.
2498latest_events AS
2499(
2500    SELECT DISTINCT ON (sink_id)
2501        sink_id,
2502        occurred_at,
2503        status,
2504        error,
2505        details
2506    FROM latest_per_replica_events
2507    ORDER BY sink_id, CASE status
2508                WHEN 'dropped' THEN 1
2509                WHEN 'running' THEN 2
2510                WHEN 'stalled' THEN 3
2511                WHEN 'starting' THEN 4
2512                WHEN 'paused' THEN 5
2513                WHEN 'ceased' THEN 6
2514                ELSE 7  -- For any other status values
2515            END, occurred_at DESC
2516)
2517SELECT
2518    mz_sinks.id,
2519    name,
2520    mz_sinks.type,
2521    occurred_at as last_status_change_at,
2522    coalesce(status, 'created') as status,
2523    error,
2524    details
2525FROM mz_catalog.mz_sinks
2526LEFT JOIN latest_events ON mz_sinks.id = latest_events.sink_id
2527WHERE
2528    -- This is a convenient way to filter out system sinks, like the status_history table itself.
2529    mz_sinks.id NOT LIKE 's%'",
2530    access: vec![PUBLIC_SELECT],
2531    ontology: Some(Ontology {
2532        entity_name: "sink_status",
2533        description: "Current sink status",
2534        links: &const {
2535            [OntologyLink {
2536                name: "status_of_sink",
2537                target: "sink",
2538                properties: LinkProperties::fk_typed(
2539                    "id",
2540                    "id",
2541                    Cardinality::OneToOne,
2542                    mz_repr::SemanticType::CatalogItemId,
2543                ),
2544            }]
2545        },
2546        column_semantic_types: &const {
2547            [
2548                ("id", SemanticType::CatalogItemId),
2549                ("last_status_change_at", SemanticType::WallclockTimestamp),
2550            ]
2551        },
2552    }),
2553});
2554
2555pub static MZ_STORAGE_USAGE_BY_SHARD: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2556    name: "mz_storage_usage_by_shard",
2557    schema: MZ_INTERNAL_SCHEMA,
2558    oid: oid::TABLE_MZ_STORAGE_USAGE_BY_SHARD_OID,
2559    desc: RelationDesc::builder()
2560        .with_column("id", SqlScalarType::UInt64.nullable(false))
2561        .with_column("shard_id", SqlScalarType::String.nullable(true))
2562        .with_column("size_bytes", SqlScalarType::UInt64.nullable(false))
2563        .with_column(
2564            "collection_timestamp",
2565            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2566        )
2567        .finish(),
2568    column_comments: BTreeMap::new(),
2569    is_retained_metrics_object: false,
2570    access: vec![PUBLIC_SELECT],
2571    ontology: Some(Ontology {
2572        entity_name: "storage_usage_by_shard",
2573        description: "Storage usage broken down by shard",
2574        links: &const { [] },
2575        column_semantic_types: &const {
2576            [
2577                ("shard_id", SemanticType::ShardId),
2578                ("size_bytes", SemanticType::ByteCount),
2579                ("collection_timestamp", SemanticType::WallclockTimestamp),
2580            ]
2581        },
2582    }),
2583});
2584
2585pub static MZ_AWS_CONNECTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
2586    name: "mz_aws_connections",
2587    schema: MZ_INTERNAL_SCHEMA,
2588    oid: oid::TABLE_MZ_AWS_CONNECTIONS_OID,
2589    desc: RelationDesc::builder()
2590        .with_column("id", SqlScalarType::String.nullable(false))
2591        .with_column("endpoint", SqlScalarType::String.nullable(true))
2592        .with_column("region", SqlScalarType::String.nullable(true))
2593        .with_column("access_key_id", SqlScalarType::String.nullable(true))
2594        .with_column(
2595            "access_key_id_secret_id",
2596            SqlScalarType::String.nullable(true),
2597        )
2598        .with_column(
2599            "secret_access_key_secret_id",
2600            SqlScalarType::String.nullable(true),
2601        )
2602        .with_column("session_token", SqlScalarType::String.nullable(true))
2603        .with_column(
2604            "session_token_secret_id",
2605            SqlScalarType::String.nullable(true),
2606        )
2607        .with_column("assume_role_arn", SqlScalarType::String.nullable(true))
2608        .with_column(
2609            "assume_role_session_name",
2610            SqlScalarType::String.nullable(true),
2611        )
2612        .with_column("principal", SqlScalarType::String.nullable(true))
2613        .with_column("external_id", SqlScalarType::String.nullable(true))
2614        .with_column("example_trust_policy", SqlScalarType::Jsonb.nullable(true))
2615        .finish(),
2616    column_comments: BTreeMap::from_iter([
2617        ("id", "The ID of the connection."),
2618        ("endpoint", "The value of the `ENDPOINT` option, if set."),
2619        ("region", "The value of the `REGION` option, if set."),
2620        (
2621            "access_key_id",
2622            "The value of the `ACCESS KEY ID` option, if provided in line.",
2623        ),
2624        (
2625            "access_key_id_secret_id",
2626            "The ID of the secret referenced by the `ACCESS KEY ID` option, if provided via a secret.",
2627        ),
2628        (
2629            "secret_access_key_secret_id",
2630            "The ID of the secret referenced by the `SECRET ACCESS KEY` option, if set.",
2631        ),
2632        (
2633            "session_token",
2634            "The value of the `SESSION TOKEN` option, if provided in line.",
2635        ),
2636        (
2637            "session_token_secret_id",
2638            "The ID of the secret referenced by the `SESSION TOKEN` option, if provided via a secret.",
2639        ),
2640        (
2641            "assume_role_arn",
2642            "The value of the `ASSUME ROLE ARN` option, if set.",
2643        ),
2644        (
2645            "assume_role_session_name",
2646            "The value of the `ASSUME ROLE SESSION NAME` option, if set.",
2647        ),
2648        (
2649            "principal",
2650            "The ARN of the AWS principal Materialize will use when assuming the provided role, if the connection is configured to use role assumption.",
2651        ),
2652        (
2653            "external_id",
2654            "The external ID Materialize will use when assuming the provided role, if the connection is configured to use role assumption.",
2655        ),
2656        (
2657            "example_trust_policy",
2658            "An example of an IAM role trust policy that allows this connection's principal and external ID to assume the role.",
2659        ),
2660    ]),
2661    is_retained_metrics_object: false,
2662    access: vec![PUBLIC_SELECT],
2663    ontology: Some(Ontology {
2664        entity_name: "aws_connection",
2665        description: "AWS connection configuration details",
2666        links: &const {
2667            [OntologyLink {
2668                name: "details_of",
2669                target: "connection",
2670                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
2671            }]
2672        },
2673        column_semantic_types: &[],
2674    }),
2675});
2676
2677pub static MZ_CLUSTER_REPLICA_METRICS_HISTORY: LazyLock<BuiltinSource> =
2678    LazyLock::new(|| BuiltinSource {
2679        name: "mz_cluster_replica_metrics_history",
2680        schema: MZ_INTERNAL_SCHEMA,
2681        oid: oid::SOURCE_MZ_CLUSTER_REPLICA_METRICS_HISTORY_OID,
2682        data_source: IntrospectionType::ReplicaMetricsHistory.into(),
2683        desc: REPLICA_METRICS_HISTORY_DESC.clone(),
2684        column_comments: BTreeMap::from_iter([
2685            ("replica_id", "The ID of a cluster replica."),
2686            ("process_id", "The ID of a process within the replica."),
2687            (
2688                "cpu_nano_cores",
2689                "Approximate CPU usage, in billionths of a vCPU core.",
2690            ),
2691            ("memory_bytes", "Approximate memory usage, in bytes."),
2692            ("disk_bytes", "Approximate disk usage, in bytes."),
2693            (
2694                "occurred_at",
2695                "Wall-clock timestamp at which the event occurred.",
2696            ),
2697            (
2698                "heap_bytes",
2699                "Approximate heap (RAM + swap) usage, in bytes.",
2700            ),
2701            ("heap_limit", "Available heap (RAM + swap) space, in bytes."),
2702        ]),
2703        is_retained_metrics_object: false,
2704        access: vec![PUBLIC_SELECT],
2705        ontology: None,
2706    });
2707
2708pub static MZ_CLUSTER_REPLICA_METRICS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2709    name: "mz_cluster_replica_metrics",
2710    schema: MZ_INTERNAL_SCHEMA,
2711    oid: oid::VIEW_MZ_CLUSTER_REPLICA_METRICS_OID,
2712    desc: RelationDesc::builder()
2713        .with_column("replica_id", SqlScalarType::String.nullable(false))
2714        .with_column("process_id", SqlScalarType::UInt64.nullable(false))
2715        .with_column("cpu_nano_cores", SqlScalarType::UInt64.nullable(true))
2716        .with_column("memory_bytes", SqlScalarType::UInt64.nullable(true))
2717        .with_column("disk_bytes", SqlScalarType::UInt64.nullable(true))
2718        .with_column("heap_bytes", SqlScalarType::UInt64.nullable(true))
2719        .with_column("heap_limit", SqlScalarType::UInt64.nullable(true))
2720        .with_key(vec![0, 1])
2721        .finish(),
2722    column_comments: BTreeMap::from_iter([
2723        ("replica_id", "The ID of a cluster replica."),
2724        ("process_id", "The ID of a process within the replica."),
2725        (
2726            "cpu_nano_cores",
2727            "Approximate CPU usage, in billionths of a vCPU core.",
2728        ),
2729        ("memory_bytes", "Approximate RAM usage, in bytes."),
2730        ("disk_bytes", "Approximate disk usage, in bytes."),
2731        (
2732            "heap_bytes",
2733            "Approximate heap (RAM + swap) usage, in bytes.",
2734        ),
2735        ("heap_limit", "Available heap (RAM + swap) space, in bytes."),
2736    ]),
2737    sql: "
2738SELECT
2739    DISTINCT ON (replica_id, process_id)
2740    replica_id,
2741    process_id,
2742    cpu_nano_cores,
2743    memory_bytes,
2744    disk_bytes,
2745    heap_bytes,
2746    heap_limit
2747FROM mz_internal.mz_cluster_replica_metrics_history
2748JOIN mz_cluster_replicas r ON r.id = replica_id
2749ORDER BY replica_id, process_id, occurred_at DESC",
2750    access: vec![PUBLIC_SELECT],
2751    ontology: Some(Ontology {
2752        entity_name: "replica_metrics",
2753        description: "CPU and memory metrics per replica",
2754        links: &const {
2755            [OntologyLink {
2756                name: "metrics_of_replica",
2757                target: "replica",
2758                properties: LinkProperties::fk_typed(
2759                    "replica_id",
2760                    "id",
2761                    Cardinality::OneToOne,
2762                    mz_repr::SemanticType::CatalogItemId,
2763                ),
2764            }]
2765        },
2766        column_semantic_types: &const {
2767            [
2768                ("replica_id", SemanticType::ReplicaId),
2769                ("memory_bytes", SemanticType::ByteCount),
2770                ("disk_bytes", SemanticType::ByteCount),
2771                ("heap_bytes", SemanticType::ByteCount),
2772                ("heap_limit", SemanticType::ByteCount),
2773            ]
2774        },
2775    }),
2776});
2777
2778pub static MZ_FRONTIERS: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
2779    name: "mz_frontiers",
2780    schema: MZ_INTERNAL_SCHEMA,
2781    oid: oid::SOURCE_MZ_FRONTIERS_OID,
2782    data_source: IntrospectionType::Frontiers.into(),
2783    desc: RelationDesc::builder()
2784        .with_column("object_id", SqlScalarType::String.nullable(false))
2785        .with_column("read_frontier", SqlScalarType::MzTimestamp.nullable(true))
2786        .with_column("write_frontier", SqlScalarType::MzTimestamp.nullable(true))
2787        .finish(),
2788    column_comments: BTreeMap::from_iter([
2789        (
2790            "object_id",
2791            "The ID of the source, sink, table, index, materialized view, or subscription.",
2792        ),
2793        (
2794            "read_frontier",
2795            "The earliest timestamp at which the output is still readable.",
2796        ),
2797        (
2798            "write_frontier",
2799            "The next timestamp at which the output may change.",
2800        ),
2801    ]),
2802    is_retained_metrics_object: false,
2803    access: vec![PUBLIC_SELECT],
2804    ontology: Some(Ontology {
2805        entity_name: "frontier",
2806        description: "Current read/write frontiers for sources, sinks, tables, materialized views, indexes, and subscriptions",
2807        links: &const {
2808            [OntologyLink {
2809                name: "frontier_of",
2810                target: "object",
2811                properties: LinkProperties::fk_mapped(
2812                    "object_id",
2813                    "id",
2814                    Cardinality::ManyToOne,
2815                    mz_repr::SemanticType::GlobalId,
2816                    "mz_internal.mz_object_global_ids",
2817                ),
2818            }]
2819        },
2820        column_semantic_types: &const {
2821            [
2822                ("object_id", SemanticType::GlobalId),
2823                ("read_frontier", SemanticType::MzTimestamp),
2824                ("write_frontier", SemanticType::MzTimestamp),
2825            ]
2826        },
2827    }),
2828});
2829
2830/// DEPRECATED and scheduled for removal! Use `mz_frontiers` instead.
2831pub static MZ_GLOBAL_FRONTIERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2832    name: "mz_global_frontiers",
2833    schema: MZ_INTERNAL_SCHEMA,
2834    oid: oid::VIEW_MZ_GLOBAL_FRONTIERS_OID,
2835    desc: RelationDesc::builder()
2836        .with_column("object_id", SqlScalarType::String.nullable(false))
2837        .with_column("time", SqlScalarType::MzTimestamp.nullable(false))
2838        .finish(),
2839    column_comments: BTreeMap::new(),
2840    sql: "
2841SELECT object_id, write_frontier AS time
2842FROM mz_internal.mz_frontiers
2843WHERE write_frontier IS NOT NULL",
2844    access: vec![PUBLIC_SELECT],
2845    ontology: None,
2846});
2847
2848pub static MZ_WALLCLOCK_LAG_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
2849    name: "mz_wallclock_lag_history",
2850    schema: MZ_INTERNAL_SCHEMA,
2851    oid: oid::SOURCE_MZ_WALLCLOCK_LAG_HISTORY_OID,
2852    desc: WALLCLOCK_LAG_HISTORY_DESC.clone(),
2853    data_source: IntrospectionType::WallclockLagHistory.into(),
2854    column_comments: BTreeMap::from_iter([
2855        (
2856            "object_id",
2857            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
2858        ),
2859        (
2860            "replica_id",
2861            "The ID of a replica computing the object, or `NULL` for persistent objects. Corresponds to `mz_cluster_replicas.id`.",
2862        ),
2863        (
2864            "lag",
2865            "The amount of time the object's write frontier lags behind wallclock time.",
2866        ),
2867        (
2868            "occurred_at",
2869            "Wall-clock timestamp at which the event occurred.",
2870        ),
2871    ]),
2872    is_retained_metrics_object: false,
2873    access: vec![PUBLIC_SELECT],
2874    ontology: Some(Ontology {
2875        entity_name: "wallclock_lag_event",
2876        description: "Historical wallclock lag per object",
2877        links: &const {
2878            [
2879                OntologyLink {
2880                    name: "measures_lag_of",
2881                    target: "object",
2882                    properties: LinkProperties::measures_mapped(
2883                        "object_id",
2884                        "id",
2885                        "wallclock_lag",
2886                        mz_repr::SemanticType::GlobalId,
2887                        "mz_internal.mz_object_global_ids",
2888                    ),
2889                },
2890                OntologyLink {
2891                    name: "on_replica",
2892                    target: "replica",
2893                    properties: LinkProperties::fk_nullable(
2894                        "replica_id",
2895                        "id",
2896                        Cardinality::ManyToOne,
2897                    ),
2898                },
2899            ]
2900        },
2901        column_semantic_types: &const {
2902            [
2903                ("object_id", SemanticType::GlobalId),
2904                ("replica_id", SemanticType::ReplicaId),
2905                ("occurred_at", SemanticType::WallclockTimestamp),
2906            ]
2907        },
2908    }),
2909});
2910
2911pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2912    name: "mz_wallclock_global_lag_history",
2913    schema: MZ_INTERNAL_SCHEMA,
2914    oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_HISTORY_OID,
2915    desc: RelationDesc::builder()
2916        .with_column("object_id", SqlScalarType::String.nullable(false))
2917        .with_column("lag", SqlScalarType::Interval.nullable(true))
2918        .with_column(
2919            "occurred_at",
2920            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2921        )
2922        .with_key(vec![0, 2])
2923        .finish(),
2924    column_comments: BTreeMap::from_iter([
2925        (
2926            "object_id",
2927            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
2928        ),
2929        (
2930            "lag",
2931            "The minimum wallclock lag observed for the object during the minute.",
2932        ),
2933        (
2934            "occurred_at",
2935            "The minute-aligned timestamp of the observation.",
2936        ),
2937    ]),
2938    sql: "
2939WITH times_binned AS (
2940    SELECT
2941        object_id,
2942        lag,
2943        date_trunc('minute', occurred_at) AS occurred_at
2944    FROM mz_internal.mz_wallclock_lag_history
2945)
2946SELECT
2947    object_id,
2948    min(lag) AS lag,
2949    occurred_at
2950FROM times_binned
2951GROUP BY object_id, occurred_at
2952OPTIONS (AGGREGATE INPUT GROUP SIZE = 1)",
2953    access: vec![PUBLIC_SELECT],
2954    ontology: Some(Ontology {
2955        entity_name: "wallclock_global_lag_event",
2956        description: "Historical global wallclock lag",
2957        links: &const {
2958            [OntologyLink {
2959                name: "lag_of",
2960                target: "object_global_id",
2961                properties: LinkProperties::fk("object_id", "global_id", Cardinality::ManyToOne),
2962            }]
2963        },
2964        column_semantic_types: &const {
2965            [
2966                ("object_id", SemanticType::GlobalId),
2967                ("occurred_at", SemanticType::WallclockTimestamp),
2968            ]
2969        },
2970    }),
2971});
2972
2973pub static MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| {
2974    BuiltinView {
2975        name: "mz_wallclock_global_lag_recent_history",
2976        schema: MZ_INTERNAL_SCHEMA,
2977        oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_OID,
2978        desc: RelationDesc::builder()
2979            .with_column("object_id", SqlScalarType::String.nullable(false))
2980            .with_column("lag", SqlScalarType::Interval.nullable(true))
2981            .with_column(
2982                "occurred_at",
2983                SqlScalarType::TimestampTz { precision: None }.nullable(false),
2984            )
2985            .with_key(vec![0, 2])
2986            .finish(),
2987        column_comments: BTreeMap::from_iter([
2988            (
2989                "object_id",
2990                "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
2991            ),
2992            (
2993                "lag",
2994                "The minimum wallclock lag observed for the object during the minute.",
2995            ),
2996            (
2997                "occurred_at",
2998                "The minute-aligned timestamp of the observation.",
2999            ),
3000        ]),
3001        sql: "
3002SELECT object_id, lag, occurred_at
3003FROM mz_internal.mz_wallclock_global_lag_history
3004WHERE occurred_at + '1 day' > mz_now()",
3005        access: vec![PUBLIC_SELECT],
3006        ontology: None,
3007    }
3008});
3009
3010pub static MZ_WALLCLOCK_GLOBAL_LAG: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3011    name: "mz_wallclock_global_lag",
3012    schema: MZ_INTERNAL_SCHEMA,
3013    oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_OID,
3014    desc: RelationDesc::builder()
3015        .with_column("object_id", SqlScalarType::String.nullable(false))
3016        .with_column("lag", SqlScalarType::Interval.nullable(true))
3017        .with_key(vec![0])
3018        .finish(),
3019    column_comments: BTreeMap::from_iter([
3020        (
3021            "object_id",
3022            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
3023        ),
3024        (
3025            "lag",
3026            "The amount of time the object's write frontier lags behind wallclock time.",
3027        ),
3028    ]),
3029    sql: "
3030SELECT DISTINCT ON (object_id) object_id, lag
3031FROM mz_internal.mz_wallclock_global_lag_recent_history
3032WHERE occurred_at + '5 minutes' > mz_now()
3033ORDER BY object_id, occurred_at DESC",
3034    access: vec![PUBLIC_SELECT],
3035    ontology: Some(Ontology {
3036        entity_name: "wallclock_global_lag",
3037        description: "Current wallclock lag aggregated across replicas",
3038        links: &const {
3039            [OntologyLink {
3040                name: "measures_global_lag_of",
3041                target: "object",
3042                properties: LinkProperties::measures_mapped(
3043                    "object_id",
3044                    "id",
3045                    "wallclock_lag_global",
3046                    mz_repr::SemanticType::GlobalId,
3047                    "mz_internal.mz_object_global_ids",
3048                ),
3049            }]
3050        },
3051        column_semantic_types: &[("object_id", SemanticType::GlobalId)],
3052    }),
3053});
3054
3055pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW: LazyLock<BuiltinSource> =
3056    LazyLock::new(|| BuiltinSource {
3057        name: "mz_wallclock_global_lag_histogram_raw",
3058        schema: MZ_INTERNAL_SCHEMA,
3059        oid: oid::SOURCE_MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW_OID,
3060        desc: WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW_DESC.clone(),
3061        column_comments: BTreeMap::new(),
3062        data_source: IntrospectionType::WallclockLagHistogram.into(),
3063        is_retained_metrics_object: false,
3064        access: vec![PUBLIC_SELECT],
3065        ontology: None,
3066    });
3067
3068pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM: LazyLock<BuiltinView> =
3069    LazyLock::new(|| BuiltinView {
3070        name: "mz_wallclock_global_lag_histogram",
3071        schema: MZ_INTERNAL_SCHEMA,
3072        oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_OID,
3073        desc: RelationDesc::builder()
3074            .with_column(
3075                "period_start",
3076                SqlScalarType::TimestampTz { precision: None }.nullable(false),
3077            )
3078            .with_column(
3079                "period_end",
3080                SqlScalarType::TimestampTz { precision: None }.nullable(false),
3081            )
3082            .with_column("object_id", SqlScalarType::String.nullable(false))
3083            .with_column("lag_seconds", SqlScalarType::UInt64.nullable(true))
3084            .with_column("labels", SqlScalarType::Jsonb.nullable(false))
3085            .with_column("count", SqlScalarType::Int64.nullable(false))
3086            .with_key(vec![0, 1, 2, 3, 4])
3087            .finish(),
3088        column_comments: BTreeMap::new(),
3089        sql: "
3090SELECT *, count(*) AS count
3091FROM mz_internal.mz_wallclock_global_lag_histogram_raw
3092GROUP BY period_start, period_end, object_id, lag_seconds, labels",
3093        access: vec![PUBLIC_SELECT],
3094        ontology: None,
3095    });
3096
3097pub static MZ_MATERIALIZED_VIEW_REFRESHES: LazyLock<BuiltinSource> = LazyLock::new(|| {
3098    BuiltinSource {
3099        name: "mz_materialized_view_refreshes",
3100        schema: MZ_INTERNAL_SCHEMA,
3101        oid: oid::SOURCE_MZ_MATERIALIZED_VIEW_REFRESHES_OID,
3102        data_source: DataSourceDesc::Introspection(
3103            IntrospectionType::ComputeMaterializedViewRefreshes,
3104        ),
3105        desc: RelationDesc::builder()
3106            .with_column(
3107                "materialized_view_id",
3108                SqlScalarType::String.nullable(false),
3109            )
3110            .with_column(
3111                "last_completed_refresh",
3112                SqlScalarType::MzTimestamp.nullable(true),
3113            )
3114            .with_column("next_refresh", SqlScalarType::MzTimestamp.nullable(true))
3115            .finish(),
3116        column_comments: BTreeMap::from_iter([
3117            (
3118                "materialized_view_id",
3119                "The ID of the materialized view. Corresponds to `mz_catalog.mz_materialized_views.id`",
3120            ),
3121            (
3122                "last_completed_refresh",
3123                "The time of the last successfully completed refresh. `NULL` if the materialized view hasn't completed any refreshes yet.",
3124            ),
3125            (
3126                "next_refresh",
3127                "The time of the next scheduled refresh. `NULL` if the materialized view has no future scheduled refreshes.",
3128            ),
3129        ]),
3130        is_retained_metrics_object: false,
3131        access: vec![PUBLIC_SELECT],
3132        ontology: None,
3133    }
3134});
3135
3136pub static MZ_SUBSCRIPTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3137    name: "mz_subscriptions",
3138    schema: MZ_INTERNAL_SCHEMA,
3139    oid: oid::TABLE_MZ_SUBSCRIPTIONS_OID,
3140    desc: RelationDesc::builder()
3141        .with_column("id", SqlScalarType::String.nullable(false))
3142        .with_column("session_id", SqlScalarType::Uuid.nullable(false))
3143        .with_column("cluster_id", SqlScalarType::String.nullable(false))
3144        .with_column(
3145            "created_at",
3146            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3147        )
3148        .with_column(
3149            "referenced_object_ids",
3150            SqlScalarType::List {
3151                element_type: Box::new(SqlScalarType::String),
3152                custom_id: None,
3153            }
3154            .nullable(false),
3155        )
3156        .finish(),
3157    column_comments: BTreeMap::from_iter([
3158        ("id", "The ID of the subscription."),
3159        (
3160            "session_id",
3161            "The ID of the session that runs the subscription. Corresponds to `mz_sessions.id`.",
3162        ),
3163        (
3164            "cluster_id",
3165            "The ID of the cluster on which the subscription is running. Corresponds to `mz_clusters.id`.",
3166        ),
3167        (
3168            "created_at",
3169            "The time at which the subscription was created.",
3170        ),
3171        (
3172            "referenced_object_ids",
3173            "The IDs of objects referenced by the subscription. Corresponds to `mz_objects.id`",
3174        ),
3175    ]),
3176    is_retained_metrics_object: false,
3177    access: vec![PUBLIC_SELECT],
3178    ontology: Some(Ontology {
3179        entity_name: "subscription",
3180        description: "Active SUBSCRIBE operations",
3181        links: &const {
3182            [
3183                OntologyLink {
3184                    name: "uses_session",
3185                    target: "session",
3186                    properties: LinkProperties::fk("session_id", "id", Cardinality::ManyToOne),
3187                },
3188                OntologyLink {
3189                    name: "in_active_session",
3190                    target: "active_session",
3191                    properties: LinkProperties::fk_nullable(
3192                        "session_id",
3193                        "id",
3194                        Cardinality::ManyToOne,
3195                    ),
3196                },
3197                OntologyLink {
3198                    name: "belongs_to_cluster",
3199                    target: "cluster",
3200                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
3201                },
3202            ]
3203        },
3204        column_semantic_types: &const {
3205            [
3206                ("id", SemanticType::CatalogItemId),
3207                ("cluster_id", SemanticType::ClusterId),
3208            ]
3209        },
3210    }),
3211});
3212
3213pub static MZ_SESSIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3214    name: "mz_sessions",
3215    schema: MZ_INTERNAL_SCHEMA,
3216    oid: oid::TABLE_MZ_SESSIONS_OID,
3217    desc: RelationDesc::builder()
3218        .with_column("id", SqlScalarType::Uuid.nullable(false))
3219        .with_column("connection_id", SqlScalarType::UInt32.nullable(false))
3220        .with_column("role_id", SqlScalarType::String.nullable(false))
3221        .with_column("client_ip", SqlScalarType::String.nullable(true))
3222        .with_column(
3223            "connected_at",
3224            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3225        )
3226        .finish(),
3227    column_comments: BTreeMap::from_iter([
3228        ("id", "The globally unique ID of the session."),
3229        (
3230            "connection_id",
3231            "The connection ID of the session. Unique only for active sessions and can be recycled. Corresponds to `pg_backend_pid()`.",
3232        ),
3233        (
3234            "role_id",
3235            "The role ID of the role that the session is logged in as. Corresponds to `mz_catalog.mz_roles`.",
3236        ),
3237        (
3238            "client_ip",
3239            "The IP address of the client that initiated the session.",
3240        ),
3241        (
3242            "connected_at",
3243            "The time at which the session connected to the system.",
3244        ),
3245    ]),
3246    is_retained_metrics_object: false,
3247    access: vec![PUBLIC_SELECT],
3248    ontology: Some(Ontology {
3249        entity_name: "active_session",
3250        description: "Currently active sessions",
3251        links: &const {
3252            [OntologyLink {
3253                name: "logged_in_as",
3254                target: "role",
3255                properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
3256            }]
3257        },
3258        column_semantic_types: &[("role_id", SemanticType::RoleId)],
3259    }),
3260});
3261
3262pub static MZ_OVERRIDDEN_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3263    LazyLock::new(|| BuiltinMaterializedView {
3264        name: "mz_overridden_system_parameters",
3265        schema: MZ_INTERNAL_SCHEMA,
3266        oid: oid::MV_MZ_OVERRIDDEN_SYSTEM_PARAMETERS_OID,
3267        desc: RelationDesc::builder()
3268            .with_column("name", SqlScalarType::String.nullable(false))
3269            .with_column("value", SqlScalarType::String.nullable(false))
3270            .finish(),
3271        column_comments: BTreeMap::from_iter([
3272            ("name", "The name of the system parameter."),
3273            (
3274                "value",
3275                "The environment-wide value of the system parameter.",
3276            ),
3277        ]),
3278        // Projects the durable `system_configurations` collection (the
3279        // `ALTER SYSTEM` set) out of `mz_catalog_raw` (the durable catalog as
3280        // JSON): the key is `{name}` and the value is `{value}`. This surfaces
3281        // only parameters with an explicit environment-wide override, mirroring
3282        // the cluster- and replica-scoped views. Parameters left at their
3283        // default are absent.
3284        sql: "
3285IN CLUSTER mz_catalog_server
3286WITH (
3287    ASSERT NOT NULL name,
3288    ASSERT NOT NULL value
3289) AS
3290SELECT
3291    data->'key'->>'name' AS name,
3292    data->'value'->>'value' AS value
3293FROM mz_internal.mz_catalog_raw
3294WHERE data->>'kind' = 'ServerConfiguration'",
3295        is_retained_metrics_object: false,
3296        access: vec![PUBLIC_SELECT],
3297        ontology: Some(Ontology {
3298            entity_name: "system_parameter",
3299            description: "Environment-wide system parameter overrides",
3300            links: &const { [] },
3301            column_semantic_types: &[],
3302        }),
3303    });
3304
3305pub static MZ_CLUSTER_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3306    LazyLock::new(|| BuiltinMaterializedView {
3307        name: "mz_cluster_system_parameters",
3308        schema: MZ_INTERNAL_SCHEMA,
3309        oid: oid::MV_MZ_CLUSTER_SYSTEM_PARAMETERS_OID,
3310        desc: RelationDesc::builder()
3311            .with_column("cluster_id", SqlScalarType::String.nullable(false))
3312            .with_column("name", SqlScalarType::String.nullable(false))
3313            .with_column("value", SqlScalarType::String.nullable(false))
3314            .finish(),
3315        column_comments: BTreeMap::from_iter([
3316            (
3317                "cluster_id",
3318                "The ID of the cluster. Corresponds to `mz_clusters.id`.",
3319            ),
3320            ("name", "The name of the cluster-coherent system parameter."),
3321            ("value", "The cluster-scoped value of the system parameter."),
3322        ]),
3323        // Projects the durable `cluster_system_configurations` collection out of
3324        // `mz_catalog_raw` (the durable catalog as JSON): the key is
3325        // `{cluster_id, name}` and the value is `{value}`.
3326        sql: "
3327IN CLUSTER mz_catalog_server
3328WITH (
3329    ASSERT NOT NULL cluster_id,
3330    ASSERT NOT NULL name,
3331    ASSERT NOT NULL value
3332) AS
3333SELECT
3334    mz_internal.parse_catalog_id(data->'key'->'cluster_id') AS cluster_id,
3335    data->'key'->>'name' AS name,
3336    data->'value'->>'value' AS value
3337FROM mz_internal.mz_catalog_raw
3338WHERE data->>'kind' = 'ClusterSystemConfiguration'",
3339        is_retained_metrics_object: false,
3340        access: vec![PUBLIC_SELECT],
3341        ontology: Some(Ontology {
3342            entity_name: "cluster_system_parameter",
3343            description: "Cluster-coherent system parameter overrides",
3344            links: &const {
3345                [OntologyLink {
3346                    name: "scoped_to_cluster",
3347                    target: "cluster",
3348                    properties: LinkProperties::fk_typed(
3349                        "cluster_id",
3350                        "id",
3351                        Cardinality::ManyToOne,
3352                        mz_repr::SemanticType::ClusterId,
3353                    ),
3354                }]
3355            },
3356            column_semantic_types: &[("cluster_id", SemanticType::ClusterId)],
3357        }),
3358    });
3359
3360pub static MZ_REPLICA_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3361    LazyLock::new(|| BuiltinMaterializedView {
3362        name: "mz_replica_system_parameters",
3363        schema: MZ_INTERNAL_SCHEMA,
3364        oid: oid::MV_MZ_REPLICA_SYSTEM_PARAMETERS_OID,
3365        desc: RelationDesc::builder()
3366            .with_column("replica_id", SqlScalarType::String.nullable(false))
3367            .with_column("name", SqlScalarType::String.nullable(false))
3368            .with_column("value", SqlScalarType::String.nullable(false))
3369            .finish(),
3370        column_comments: BTreeMap::from_iter([
3371            (
3372                "replica_id",
3373                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
3374            ),
3375            ("name", "The name of the replica-local system parameter."),
3376            ("value", "The replica-scoped value of the system parameter."),
3377        ]),
3378        // Projects the durable `replica_system_configurations` collection out of
3379        // `mz_catalog_raw` (the durable catalog as JSON): the key is
3380        // `{replica_id, name}` and the value is `{value}`.
3381        sql: "
3382IN CLUSTER mz_catalog_server
3383WITH (
3384    ASSERT NOT NULL replica_id,
3385    ASSERT NOT NULL name,
3386    ASSERT NOT NULL value
3387) AS
3388SELECT
3389    mz_internal.parse_catalog_id(data->'key'->'replica_id') AS replica_id,
3390    data->'key'->>'name' AS name,
3391    data->'value'->>'value' AS value
3392FROM mz_internal.mz_catalog_raw
3393WHERE data->>'kind' = 'ReplicaSystemConfiguration'",
3394        is_retained_metrics_object: false,
3395        access: vec![PUBLIC_SELECT],
3396        ontology: Some(Ontology {
3397            entity_name: "replica_system_parameter",
3398            description: "Replica-local system parameter overrides",
3399            links: &const {
3400                [OntologyLink {
3401                    name: "scoped_to_replica",
3402                    target: "replica",
3403                    properties: LinkProperties::fk_typed(
3404                        "replica_id",
3405                        "id",
3406                        Cardinality::ManyToOne,
3407                        mz_repr::SemanticType::ReplicaId,
3408                    ),
3409                }]
3410            },
3411            column_semantic_types: &[("replica_id", SemanticType::ReplicaId)],
3412        }),
3413    });
3414
3415pub static MZ_COMMENTS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
3416    BuiltinMaterializedView {
3417        name: "mz_comments",
3418        schema: MZ_INTERNAL_SCHEMA,
3419        oid: oid::MV_MZ_COMMENTS_OID,
3420        desc: RelationDesc::builder()
3421            .with_column("id", SqlScalarType::String.nullable(false))
3422            .with_column("object_type", SqlScalarType::String.nullable(false))
3423            .with_column("object_sub_id", SqlScalarType::Int32.nullable(true))
3424            .with_column("comment", SqlScalarType::String.nullable(false))
3425            .finish(),
3426        column_comments: BTreeMap::from_iter([
3427            (
3428                "id",
3429                "The ID of the object. Corresponds to `mz_objects.id`.",
3430            ),
3431            (
3432                "object_type",
3433                "The type of object the comment is associated with.",
3434            ),
3435            (
3436                "object_sub_id",
3437                "For a comment on a column of a relation, the column number. `NULL` for other object types.",
3438            ),
3439            ("comment", "The comment itself."),
3440        ]),
3441        // Variant keys ('Table', 'View', etc.) are the serde JSON form of
3442        // `proto::CommentObject` (in `mz-catalog-protos`). `object_type`
3443        // values are the kebab-case `Display` of `audit_log::ObjectType`.
3444        //
3445        // Schema and ClusterReplica are nested structs in `mz_catalog_raw`.
3446        // We reach one level deeper for them: Schema picks `schema.Id` and
3447        // drops the database, ClusterReplica picks `replica_id` and drops
3448        // the cluster. That matches what `mz_objects.id` holds for those
3449        // rows.
3450        //
3451        // New variants on `proto::CommentObject` need branches in both CASE
3452        // expressions below.
3453        sql: "
3454IN CLUSTER mz_catalog_server
3455WITH (
3456    ASSERT NOT NULL id,
3457    ASSERT NOT NULL object_type,
3458    ASSERT NOT NULL comment
3459) AS
3460WITH commented AS (
3461    SELECT data->'key'->'object' AS obj,
3462           data->'key'->'sub_component' AS sub,
3463           data->'value'->>'comment' AS comment
3464    FROM mz_internal.mz_catalog_raw
3465    WHERE data->>'kind' = 'Comment'
3466)
3467SELECT
3468    CASE
3469        WHEN obj ? 'Table'            THEN mz_internal.parse_catalog_id(obj->'Table')
3470        WHEN obj ? 'View'             THEN mz_internal.parse_catalog_id(obj->'View')
3471        WHEN obj ? 'MaterializedView' THEN mz_internal.parse_catalog_id(obj->'MaterializedView')
3472        WHEN obj ? 'Source'           THEN mz_internal.parse_catalog_id(obj->'Source')
3473        WHEN obj ? 'Sink'             THEN mz_internal.parse_catalog_id(obj->'Sink')
3474        WHEN obj ? 'Index'            THEN mz_internal.parse_catalog_id(obj->'Index')
3475        WHEN obj ? 'Func'             THEN mz_internal.parse_catalog_id(obj->'Func')
3476        WHEN obj ? 'Connection'       THEN mz_internal.parse_catalog_id(obj->'Connection')
3477        WHEN obj ? 'Type'             THEN mz_internal.parse_catalog_id(obj->'Type')
3478        WHEN obj ? 'Secret'           THEN mz_internal.parse_catalog_id(obj->'Secret')
3479        WHEN obj ? 'Role'             THEN mz_internal.parse_catalog_id(obj->'Role')
3480        WHEN obj ? 'Database'         THEN mz_internal.parse_catalog_id(obj->'Database')
3481        WHEN obj ? 'Schema'           THEN mz_internal.parse_catalog_id(obj->'Schema'->'schema'->'Id')
3482        WHEN obj ? 'Cluster'          THEN mz_internal.parse_catalog_id(obj->'Cluster')
3483        WHEN obj ? 'ClusterReplica'   THEN mz_internal.parse_catalog_id(obj->'ClusterReplica'->'replica_id')
3484        WHEN obj ? 'NetworkPolicy'    THEN mz_internal.parse_catalog_id(obj->'NetworkPolicy')
3485    END                                                              AS id,
3486    CASE
3487        WHEN obj ? 'Table'            THEN 'table'
3488        WHEN obj ? 'View'             THEN 'view'
3489        WHEN obj ? 'MaterializedView' THEN 'materialized-view'
3490        WHEN obj ? 'Source'           THEN 'source'
3491        WHEN obj ? 'Sink'             THEN 'sink'
3492        WHEN obj ? 'Index'            THEN 'index'
3493        WHEN obj ? 'Func'             THEN 'func'
3494        WHEN obj ? 'Connection'       THEN 'connection'
3495        WHEN obj ? 'Type'             THEN 'type'
3496        WHEN obj ? 'Secret'           THEN 'secret'
3497        WHEN obj ? 'Role'             THEN 'role'
3498        WHEN obj ? 'Database'         THEN 'database'
3499        WHEN obj ? 'Schema'           THEN 'schema'
3500        WHEN obj ? 'Cluster'          THEN 'cluster'
3501        WHEN obj ? 'ClusterReplica'   THEN 'cluster-replica'
3502        WHEN obj ? 'NetworkPolicy'    THEN 'network-policy'
3503    END                                                              AS object_type,
3504    (sub->'ColumnPos')::int4                                          AS object_sub_id,
3505    comment
3506FROM commented",
3507        is_retained_metrics_object: false,
3508        access: vec![PUBLIC_SELECT],
3509        ontology: Some(Ontology {
3510            entity_name: "comment",
3511            description: "A COMMENT ON annotation for a catalog object or column",
3512            links: &const {
3513                [OntologyLink {
3514                    name: "comment_on",
3515                    target: "object",
3516                    properties: LinkProperties::fk_typed(
3517                        "id",
3518                        "id",
3519                        Cardinality::ManyToOne,
3520                        mz_repr::SemanticType::CatalogItemId,
3521                    ),
3522                }]
3523            },
3524            column_semantic_types: &const {
3525                [
3526                    ("id", SemanticType::CatalogItemId),
3527                    ("object_type", SemanticType::ObjectType),
3528                ]
3529            },
3530        }),
3531    }
3532});
3533
3534pub static MZ_SOURCE_REFERENCES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3535    name: "mz_source_references",
3536    schema: MZ_INTERNAL_SCHEMA,
3537    oid: oid::TABLE_MZ_SOURCE_REFERENCES_OID,
3538    desc: RelationDesc::builder()
3539        .with_column("source_id", SqlScalarType::String.nullable(false))
3540        .with_column("namespace", SqlScalarType::String.nullable(true))
3541        .with_column("name", SqlScalarType::String.nullable(false))
3542        .with_column(
3543            "updated_at",
3544            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3545        )
3546        .with_column(
3547            "columns",
3548            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
3549        )
3550        .finish(),
3551    column_comments: BTreeMap::new(),
3552    is_retained_metrics_object: false,
3553    access: vec![PUBLIC_SELECT],
3554    ontology: Some(Ontology {
3555        entity_name: "source_reference",
3556        description: "External references tracked by sources",
3557        links: &const {
3558            [OntologyLink {
3559                name: "references_source",
3560                target: "source",
3561                properties: LinkProperties::fk("source_id", "id", Cardinality::ManyToOne),
3562            }]
3563        },
3564        column_semantic_types: &[("source_id", SemanticType::CatalogItemId)],
3565    }),
3566});
3567
3568pub static MZ_WEBHOOKS_SOURCES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3569    name: "mz_webhook_sources",
3570    schema: MZ_INTERNAL_SCHEMA,
3571    oid: oid::TABLE_MZ_WEBHOOK_SOURCES_OID,
3572    desc: RelationDesc::builder()
3573        .with_column("id", SqlScalarType::String.nullable(false))
3574        .with_column("name", SqlScalarType::String.nullable(false))
3575        .with_column("url", SqlScalarType::String.nullable(false))
3576        .finish(),
3577    column_comments: BTreeMap::from_iter([
3578        (
3579            "id",
3580            "The ID of the webhook source. Corresponds to `mz_sources.id`.",
3581        ),
3582        ("name", "The name of the webhook source."),
3583        (
3584            "url",
3585            "The URL which can be used to send events to the source.",
3586        ),
3587    ]),
3588    is_retained_metrics_object: false,
3589    access: vec![PUBLIC_SELECT],
3590    ontology: Some(Ontology {
3591        entity_name: "webhook_source",
3592        description: "Webhook source configuration",
3593        links: &const {
3594            [OntologyLink {
3595                name: "details_of",
3596                target: "source",
3597                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
3598            }]
3599        },
3600        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3601    }),
3602});
3603
3604pub static MZ_HISTORY_RETENTION_STRATEGIES: LazyLock<BuiltinTable> = LazyLock::new(|| {
3605    BuiltinTable {
3606        name: "mz_history_retention_strategies",
3607        schema: MZ_INTERNAL_SCHEMA,
3608        oid: oid::TABLE_MZ_HISTORY_RETENTION_STRATEGIES_OID,
3609        desc: RelationDesc::builder()
3610            .with_column("id", SqlScalarType::String.nullable(false))
3611            .with_column("strategy", SqlScalarType::String.nullable(false))
3612            .with_column("value", SqlScalarType::Jsonb.nullable(false))
3613            .finish(),
3614        column_comments: BTreeMap::from_iter([
3615            ("id", "The ID of the object."),
3616            (
3617                "strategy",
3618                "The strategy. `FOR` is the only strategy, and means the object's compaction window is the duration of the `value` field.",
3619            ),
3620            (
3621                "value",
3622                "The value of the strategy. For `FOR`, is a number of milliseconds.",
3623            ),
3624        ]),
3625        is_retained_metrics_object: false,
3626        access: vec![PUBLIC_SELECT],
3627        ontology: Some(Ontology {
3628            entity_name: "history_retention",
3629            description: "History retention strategy for an object",
3630            links: &const { [] },
3631            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3632        }),
3633    }
3634});
3635
3636pub static MZ_LICENSE_KEYS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3637    name: "mz_license_keys",
3638    schema: MZ_INTERNAL_SCHEMA,
3639    oid: oid::TABLE_MZ_LICENSE_KEYS_OID,
3640    desc: RelationDesc::builder()
3641        .with_column("id", SqlScalarType::String.nullable(false))
3642        .with_column("organization", SqlScalarType::String.nullable(false))
3643        .with_column("environment_id", SqlScalarType::String.nullable(false))
3644        .with_column(
3645            "expiration",
3646            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3647        )
3648        .with_column(
3649            "not_before",
3650            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3651        )
3652        .finish(),
3653    column_comments: BTreeMap::from_iter([
3654        ("id", "The identifier of the license key."),
3655        (
3656            "organization",
3657            "The name of the organization that this license key was issued to.",
3658        ),
3659        (
3660            "environment_id",
3661            "The environment ID that this license key was issued for.",
3662        ),
3663        (
3664            "expiration",
3665            "The date and time when this license key expires.",
3666        ),
3667        (
3668            "not_before",
3669            "The start of the validity period for this license key.",
3670        ),
3671    ]),
3672    is_retained_metrics_object: false,
3673    access: vec![PUBLIC_SELECT],
3674    ontology: Some(Ontology {
3675        entity_name: "license_key",
3676        description: "License key metadata",
3677        links: &const { [] },
3678        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3679    }),
3680});
3681
3682pub static MZ_REPLACEMENTS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3683    name: "mz_replacements",
3684    schema: MZ_INTERNAL_SCHEMA,
3685    oid: oid::TABLE_MZ_REPLACEMENTS_OID,
3686    desc: RelationDesc::builder()
3687        .with_column("id", SqlScalarType::String.nullable(false))
3688        .with_column("target_id", SqlScalarType::String.nullable(false))
3689        .finish(),
3690    column_comments: BTreeMap::from_iter([
3691        (
3692            "id",
3693            "The ID of the replacement object. Corresponds to `mz_objects.id`.",
3694        ),
3695        (
3696            "target_id",
3697            "The ID of the replacement target. Corresponds to `mz_objects.id`.",
3698        ),
3699    ]),
3700    is_retained_metrics_object: false,
3701    access: vec![PUBLIC_SELECT],
3702    ontology: Some(Ontology {
3703        entity_name: "replacement",
3704        description: "A record of an object replacement (ALTER ... SWAP)",
3705        links: &const {
3706            [
3707                OntologyLink {
3708                    name: "replacement_object",
3709                    target: "object",
3710                    properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
3711                },
3712                OntologyLink {
3713                    name: "replacement_target",
3714                    target: "object",
3715                    properties: LinkProperties::fk("target_id", "id", Cardinality::ManyToOne),
3716                },
3717            ]
3718        },
3719        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3720    }),
3721});
3722
3723// These will be replaced with per-replica tables once source/sink multiplexing on
3724// a single cluster is supported.
3725pub static MZ_SOURCE_STATISTICS_RAW: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3726    name: "mz_source_statistics_raw",
3727    schema: MZ_INTERNAL_SCHEMA,
3728    oid: oid::SOURCE_MZ_SOURCE_STATISTICS_RAW_OID,
3729    data_source: IntrospectionType::StorageSourceStatistics.into(),
3730    desc: MZ_SOURCE_STATISTICS_RAW_DESC.clone(),
3731    column_comments: BTreeMap::new(),
3732    is_retained_metrics_object: true,
3733    access: vec![PUBLIC_SELECT],
3734    ontology: None,
3735});
3736pub static MZ_SINK_STATISTICS_RAW: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3737    name: "mz_sink_statistics_raw",
3738    schema: MZ_INTERNAL_SCHEMA,
3739    oid: oid::SOURCE_MZ_SINK_STATISTICS_RAW_OID,
3740    data_source: IntrospectionType::StorageSinkStatistics.into(),
3741    desc: MZ_SINK_STATISTICS_RAW_DESC.clone(),
3742    column_comments: BTreeMap::new(),
3743    is_retained_metrics_object: true,
3744    access: vec![PUBLIC_SELECT],
3745    ontology: None,
3746});
3747
3748pub static MZ_STORAGE_SHARDS: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3749    name: "mz_storage_shards",
3750    schema: MZ_INTERNAL_SCHEMA,
3751    oid: oid::SOURCE_MZ_STORAGE_SHARDS_OID,
3752    data_source: IntrospectionType::ShardMapping.into(),
3753    desc: RelationDesc::builder()
3754        .with_column("object_id", SqlScalarType::String.nullable(false))
3755        .with_column("shard_id", SqlScalarType::String.nullable(false))
3756        .finish(),
3757    column_comments: BTreeMap::new(),
3758    is_retained_metrics_object: false,
3759    access: vec![PUBLIC_SELECT],
3760    ontology: Some(Ontology {
3761        entity_name: "storage_shard",
3762        description: "Persist shards used by storage objects",
3763        links: &const {
3764            [OntologyLink {
3765                name: "shard_of",
3766                target: "object",
3767                properties: LinkProperties::fk_mapped(
3768                    "object_id",
3769                    "id",
3770                    Cardinality::ManyToOne,
3771                    mz_repr::SemanticType::GlobalId,
3772                    "mz_internal.mz_object_global_ids",
3773                ),
3774            }]
3775        },
3776        column_semantic_types: &const {
3777            [
3778                ("object_id", SemanticType::GlobalId),
3779                ("shard_id", SemanticType::ShardId),
3780            ]
3781        },
3782    }),
3783});
3784
3785pub static MZ_OBJECTS_ID_NAMESPACE_TYPES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3786    name: "mz_objects_id_namespace_types",
3787    schema: MZ_INTERNAL_SCHEMA,
3788    oid: oid::VIEW_MZ_OBJECTS_ID_NAMESPACE_TYPES_OID,
3789    desc: RelationDesc::builder()
3790        .with_column("object_type", SqlScalarType::String.nullable(false))
3791        .with_key(vec![0])
3792        .finish(),
3793    column_comments: BTreeMap::new(),
3794    sql: r#"SELECT *
3795    FROM (
3796        VALUES
3797            ('table'),
3798            ('view'),
3799            ('materialized-view'),
3800            ('source'),
3801            ('sink'),
3802            ('index'),
3803            ('connection'),
3804            ('type'),
3805            ('function'),
3806            ('secret')
3807    )
3808    AS _ (object_type)"#,
3809    access: vec![PUBLIC_SELECT],
3810    ontology: None,
3811});
3812
3813pub static MZ_OBJECT_OID_ALIAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3814    name: "mz_object_oid_alias",
3815    schema: MZ_INTERNAL_SCHEMA,
3816    oid: oid::VIEW_MZ_OBJECT_OID_ALIAS_OID,
3817    desc: RelationDesc::builder()
3818        .with_column("object_type", SqlScalarType::String.nullable(false))
3819        .with_column("oid_alias", SqlScalarType::String.nullable(false))
3820        .with_key(vec![0])
3821        .finish(),
3822    column_comments: BTreeMap::new(),
3823    sql: "SELECT object_type, oid_alias
3824    FROM (
3825        VALUES
3826            (
3827                'table'::pg_catalog.text,
3828                'regclass'::pg_catalog.text
3829            ),
3830            ('source', 'regclass'),
3831            ('view', 'regclass'),
3832            ('materialized-view', 'regclass'),
3833            ('index', 'regclass'),
3834            ('type', 'regtype'),
3835            ('function', 'regproc')
3836    )
3837    AS _ (object_type, oid_alias);",
3838    access: vec![PUBLIC_SELECT],
3839    ontology: None,
3840});
3841
3842pub static MZ_OBJECT_FULLY_QUALIFIED_NAMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3843    name: "mz_object_fully_qualified_names",
3844    schema: MZ_INTERNAL_SCHEMA,
3845    oid: oid::VIEW_MZ_OBJECT_FULLY_QUALIFIED_NAMES_OID,
3846    desc: RelationDesc::builder()
3847        .with_column("id", SqlScalarType::String.nullable(false))
3848        .with_column("name", SqlScalarType::String.nullable(false))
3849        .with_column("object_type", SqlScalarType::String.nullable(false))
3850        .with_column("schema_id", SqlScalarType::String.nullable(false))
3851        .with_column("schema_name", SqlScalarType::String.nullable(false))
3852        .with_column("database_id", SqlScalarType::String.nullable(true))
3853        .with_column("database_name", SqlScalarType::String.nullable(true))
3854        .with_column("cluster_id", SqlScalarType::String.nullable(true))
3855        .finish(),
3856    column_comments: BTreeMap::from_iter([
3857        ("id", "Materialize's unique ID for the object."),
3858        ("name", "The name of the object."),
3859        (
3860            "object_type",
3861            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
3862        ),
3863        (
3864            "schema_id",
3865            "The ID of the schema to which the object belongs. Corresponds to `mz_schemas.id`.",
3866        ),
3867        (
3868            "schema_name",
3869            "The name of the schema to which the object belongs. Corresponds to `mz_schemas.name`.",
3870        ),
3871        (
3872            "database_id",
3873            "The ID of the database to which the object belongs. Corresponds to `mz_databases.id`.",
3874        ),
3875        (
3876            "database_name",
3877            "The name of the database to which the object belongs. Corresponds to `mz_databases.name`.",
3878        ),
3879        (
3880            "cluster_id",
3881            "The ID of the cluster maintaining the source, materialized view, index, or sink. Corresponds to `mz_clusters.id`. `NULL` for other object types.",
3882        ),
3883    ]),
3884    sql: "
3885    SELECT o.id,
3886        o.name,
3887        o.type as object_type,
3888        sc.id as schema_id,
3889        sc.name as schema_name,
3890        db.id as database_id,
3891        db.name as database_name,
3892        o.cluster_id
3893    FROM mz_catalog.mz_objects o
3894    INNER JOIN mz_catalog.mz_schemas sc ON sc.id = o.schema_id
3895    -- LEFT JOIN accounts for objects in the ambient database.
3896    LEFT JOIN mz_catalog.mz_databases db ON db.id = sc.database_id",
3897    access: vec![PUBLIC_SELECT],
3898    ontology: Some(Ontology {
3899        entity_name: "object_fqn",
3900        description: "Fully qualified name (database.schema.name) for objects",
3901        links: &const {
3902            [
3903                OntologyLink {
3904                    name: "details_of",
3905                    target: "object",
3906                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
3907                },
3908                OntologyLink {
3909                    name: "in_schema",
3910                    target: "schema",
3911                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
3912                },
3913                OntologyLink {
3914                    name: "in_database",
3915                    target: "database",
3916                    properties: LinkProperties::fk("database_id", "id", Cardinality::ManyToOne),
3917                },
3918                OntologyLink {
3919                    name: "belongs_to_cluster",
3920                    target: "cluster",
3921                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
3922                },
3923            ]
3924        },
3925        column_semantic_types: &const {
3926            [
3927                ("id", SemanticType::CatalogItemId),
3928                ("object_type", SemanticType::ObjectType),
3929                ("schema_id", SemanticType::SchemaId),
3930                ("database_id", SemanticType::DatabaseId),
3931                ("cluster_id", SemanticType::ClusterId),
3932            ]
3933        },
3934    }),
3935});
3936
3937pub static MZ_OBJECT_GLOBAL_IDS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3938    name: "mz_object_global_ids",
3939    schema: MZ_INTERNAL_SCHEMA,
3940    oid: oid::VIEW_MZ_OBJECT_GLOBAL_IDS_OID,
3941    desc: RelationDesc::builder()
3942        .with_column("id", SqlScalarType::String.nullable(false))
3943        .with_column("global_id", SqlScalarType::String.nullable(false))
3944        .finish(),
3945    column_comments: BTreeMap::from_iter([
3946        (
3947            "id",
3948            "The ID of the object. Corresponds to `mz_objects.id`.",
3949        ),
3950        ("global_id", "The global ID of the object."),
3951    ]),
3952    is_retained_metrics_object: false,
3953    access: vec![PUBLIC_SELECT],
3954    ontology: Some(Ontology {
3955        entity_name: "object_global_id",
3956        description: "Mapping between CatalogItemId (SQL layer) and GlobalId (runtime layer)",
3957        links: &const {
3958            [OntologyLink {
3959                name: "id_references",
3960                target: "object",
3961                properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
3962            }]
3963        },
3964        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3965    }),
3966});
3967
3968// TODO (SangJunBak): Remove once mz_object_history is released and used in the Console https://github.com/MaterializeInc/console/issues/3342
3969pub static MZ_OBJECT_LIFETIMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3970    name: "mz_object_lifetimes",
3971    schema: MZ_INTERNAL_SCHEMA,
3972    oid: oid::VIEW_MZ_OBJECT_LIFETIMES_OID,
3973    desc: RelationDesc::builder()
3974        .with_column("id", SqlScalarType::String.nullable(true))
3975        .with_column("previous_id", SqlScalarType::String.nullable(true))
3976        .with_column("object_type", SqlScalarType::String.nullable(false))
3977        .with_column("event_type", SqlScalarType::String.nullable(false))
3978        .with_column(
3979            "occurred_at",
3980            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3981        )
3982        .finish(),
3983    column_comments: BTreeMap::from_iter([
3984        ("id", "Materialize's unique ID for the object."),
3985        ("previous_id", "The object's previous ID, if one exists."),
3986        (
3987            "object_type",
3988            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
3989        ),
3990        (
3991            "event_type",
3992            "The lifetime event, either `create` or `drop`.",
3993        ),
3994        (
3995            "occurred_at",
3996            "Wall-clock timestamp of when the event occurred.",
3997        ),
3998    ]),
3999    sql: "
4000    SELECT
4001        CASE
4002            WHEN a.object_type = 'cluster-replica' THEN a.details ->> 'replica_id'
4003            ELSE a.details ->> 'id'
4004        END id,
4005        a.details ->> 'previous_id' as previous_id,
4006        a.object_type,
4007        a.event_type,
4008        a.occurred_at
4009    FROM mz_catalog.mz_audit_events a
4010    WHERE a.event_type = 'create' OR a.event_type = 'drop'",
4011    access: vec![PUBLIC_SELECT],
4012    ontology: Some(Ontology {
4013        entity_name: "object_lifetime_event",
4014        description: "Create or drop lifecycle event for a catalog object",
4015        links: &const {
4016            [OntologyLink {
4017                name: "lifetime_event_of",
4018                target: "object",
4019                properties: LinkProperties::fk_typed(
4020                    "id",
4021                    "id",
4022                    Cardinality::ManyToOne,
4023                    mz_repr::SemanticType::CatalogItemId,
4024                ),
4025            }]
4026        },
4027        column_semantic_types: &const {
4028            [
4029                ("id", SemanticType::CatalogItemId),
4030                ("object_type", SemanticType::ObjectType),
4031            ]
4032        },
4033    }),
4034});
4035
4036pub static MZ_OBJECT_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4037    name: "mz_object_history",
4038    schema: MZ_INTERNAL_SCHEMA,
4039    oid: oid::VIEW_MZ_OBJECT_HISTORY_OID,
4040    desc: RelationDesc::builder()
4041        .with_column("id", SqlScalarType::String.nullable(true))
4042        .with_column("cluster_id", SqlScalarType::String.nullable(true))
4043        .with_column("object_type", SqlScalarType::String.nullable(false))
4044        .with_column(
4045            "created_at",
4046            SqlScalarType::TimestampTz { precision: None }.nullable(true),
4047        )
4048        .with_column(
4049            "dropped_at",
4050            SqlScalarType::TimestampTz { precision: None }.nullable(true),
4051        )
4052        .finish(),
4053    column_comments: BTreeMap::from_iter([
4054        ("id", "Materialize's unique ID for the object."),
4055        (
4056            "cluster_id",
4057            "The object's cluster ID. `NULL` if the object has no associated cluster.",
4058        ),
4059        (
4060            "object_type",
4061            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
4062        ),
4063        (
4064            "created_at",
4065            "Wall-clock timestamp of when the object was created. `NULL` for built in system objects.",
4066        ),
4067        (
4068            "dropped_at",
4069            "Wall-clock timestamp of when the object was dropped. `NULL` for built in system objects or if the object hasn't been dropped.",
4070        ),
4071    ]),
4072    sql: r#"
4073    WITH
4074        creates AS
4075        (
4076            SELECT
4077                details ->> 'id' AS id,
4078                -- We need to backfill cluster_id since older object create events don't include the cluster ID in the audit log
4079                COALESCE(details ->> 'cluster_id', objects.cluster_id) AS cluster_id,
4080                object_type,
4081                occurred_at
4082            FROM
4083                mz_catalog.mz_audit_events AS events
4084                    LEFT JOIN mz_catalog.mz_objects AS objects ON details ->> 'id' = objects.id
4085            WHERE event_type = 'create' AND object_type IN ( SELECT object_type FROM mz_internal.mz_objects_id_namespace_types )
4086        ),
4087        drops AS
4088        (
4089            SELECT details ->> 'id' AS id, occurred_at
4090            FROM mz_catalog.mz_audit_events
4091            WHERE event_type = 'drop' AND object_type IN ( SELECT object_type FROM mz_internal.mz_objects_id_namespace_types )
4092        ),
4093        user_object_history AS
4094        (
4095            SELECT
4096                creates.id,
4097                creates.cluster_id,
4098                creates.object_type,
4099                creates.occurred_at AS created_at,
4100                drops.occurred_at AS dropped_at
4101            FROM creates LEFT JOIN drops ON creates.id = drops.id
4102            WHERE creates.id LIKE 'u%'
4103        ),
4104        -- We need to union built in objects since they aren't in the audit log
4105        built_in_objects AS
4106        (
4107            -- Functions that accept different arguments have different oids but the same id. We deduplicate in this case.
4108            SELECT DISTINCT ON (objects.id)
4109                objects.id,
4110                objects.cluster_id,
4111                objects.type AS object_type,
4112                NULL::timestamptz AS created_at,
4113                NULL::timestamptz AS dropped_at
4114            FROM mz_catalog.mz_objects AS objects
4115            WHERE objects.id LIKE 's%'
4116        )
4117    SELECT * FROM user_object_history UNION ALL (SELECT * FROM built_in_objects)"#,
4118    access: vec![PUBLIC_SELECT],
4119    ontology: Some(Ontology {
4120        entity_name: "object_history",
4121        description: "Historical record of object creation and drops",
4122        links: &const {
4123            [OntologyLink {
4124                name: "history_of",
4125                target: "object",
4126                properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
4127            }]
4128        },
4129        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
4130    }),
4131});
4132
4133pub static MZ_OBJECT_TRANSITIVE_DEPENDENCIES: LazyLock<BuiltinView> = LazyLock::new(|| {
4134    BuiltinView {
4135        name: "mz_object_transitive_dependencies",
4136        schema: MZ_INTERNAL_SCHEMA,
4137        oid: oid::VIEW_MZ_OBJECT_TRANSITIVE_DEPENDENCIES_OID,
4138        desc: RelationDesc::builder()
4139            .with_column("object_id", SqlScalarType::String.nullable(false))
4140            .with_column(
4141                "referenced_object_id",
4142                SqlScalarType::String.nullable(false),
4143            )
4144            .with_key(vec![0, 1])
4145            .finish(),
4146        column_comments: BTreeMap::from_iter([
4147            (
4148                "object_id",
4149                "The ID of the dependent object. Corresponds to `mz_objects.id`.",
4150            ),
4151            (
4152                "referenced_object_id",
4153                "The ID of the (possibly transitively) referenced object. Corresponds to `mz_objects.id`.",
4154            ),
4155        ]),
4156        sql: "
4157WITH MUTUALLY RECURSIVE
4158  reach(object_id text, referenced_object_id text) AS (
4159    SELECT object_id, referenced_object_id FROM mz_internal.mz_object_dependencies
4160    UNION
4161    SELECT x, z FROM reach r1(x, y) JOIN reach r2(y, z) USING(y)
4162  )
4163SELECT object_id, referenced_object_id FROM reach;",
4164        access: vec![PUBLIC_SELECT],
4165        ontology: Some(Ontology {
4166            entity_name: "transitive_dependency",
4167            description: "Transitive closure of object dependencies — all direct and indirect dependencies",
4168            links: &const {
4169                [
4170                    OntologyLink {
4171                        name: "depends_on",
4172                        target: "object",
4173                        properties: LinkProperties::DependsOn {
4174                            source_column: "object_id",
4175                            target_column: "id",
4176                            source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
4177                            requires_mapping: None,
4178                        },
4179                    },
4180                    OntologyLink {
4181                        name: "dependency_is",
4182                        target: "object",
4183                        properties: LinkProperties::DependsOn {
4184                            source_column: "referenced_object_id",
4185                            target_column: "id",
4186                            source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
4187                            requires_mapping: None,
4188                        },
4189                    },
4190                ]
4191            },
4192            column_semantic_types: &const {
4193                [
4194                    ("object_id", SemanticType::CatalogItemId),
4195                    ("referenced_object_id", SemanticType::CatalogItemId),
4196                ]
4197            },
4198        }),
4199    }
4200});
4201
4202/// Peeled version of `PG_NAMESPACE`:
4203/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4204///   in order to make this view indexable.
4205/// - This has the database name as an extra column, so that downstream views can check it against
4206///  `current_database()`.
4207pub static PG_NAMESPACE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4208    name: "pg_namespace_all_databases",
4209    schema: MZ_INTERNAL_SCHEMA,
4210    oid: oid::VIEW_PG_NAMESPACE_ALL_DATABASES_OID,
4211    desc: RelationDesc::builder()
4212        .with_column("oid", SqlScalarType::Oid.nullable(false))
4213        .with_column("nspname", SqlScalarType::String.nullable(false))
4214        .with_column("nspowner", SqlScalarType::Oid.nullable(false))
4215        .with_column(
4216            "nspacl",
4217            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
4218        )
4219        .with_column("database_name", SqlScalarType::String.nullable(true))
4220        .finish(),
4221    column_comments: BTreeMap::new(),
4222    sql: "
4223SELECT
4224    s.oid AS oid,
4225    s.name AS nspname,
4226    role_owner.oid AS nspowner,
4227    NULL::pg_catalog.text[] AS nspacl,
4228    d.name as database_name
4229FROM mz_catalog.mz_schemas s
4230LEFT JOIN mz_catalog.mz_databases d ON d.id = s.database_id
4231JOIN mz_catalog.mz_roles role_owner ON role_owner.id = s.owner_id",
4232    access: vec![PUBLIC_SELECT],
4233    ontology: None,
4234});
4235
4236pub const PG_NAMESPACE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4237    name: "pg_namespace_all_databases_ind",
4238    schema: MZ_INTERNAL_SCHEMA,
4239    oid: oid::INDEX_PG_NAMESPACE_ALL_DATABASES_IND_OID,
4240    sql: "IN CLUSTER mz_catalog_server
4241ON mz_internal.pg_namespace_all_databases (nspname)",
4242    is_retained_metrics_object: false,
4243};
4244
4245/// Peeled version of `PG_CLASS`:
4246/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4247///   in order to make this view indexable.
4248/// - This has the database name as an extra column, so that downstream views can check it against
4249///  `current_database()`.
4250pub static PG_CLASS_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4251    BuiltinView {
4252        name: "pg_class_all_databases",
4253        schema: MZ_INTERNAL_SCHEMA,
4254        oid: oid::VIEW_PG_CLASS_ALL_DATABASES_OID,
4255        desc: RelationDesc::builder()
4256            .with_column("oid", SqlScalarType::Oid.nullable(false))
4257            .with_column("relname", SqlScalarType::String.nullable(false))
4258            .with_column("relnamespace", SqlScalarType::Oid.nullable(false))
4259            .with_column("reloftype", SqlScalarType::Oid.nullable(false))
4260            .with_column("relowner", SqlScalarType::Oid.nullable(false))
4261            .with_column("relam", SqlScalarType::Oid.nullable(false))
4262            .with_column("reltablespace", SqlScalarType::Oid.nullable(false))
4263            .with_column("reltuples", SqlScalarType::Float32.nullable(false))
4264            .with_column("reltoastrelid", SqlScalarType::Oid.nullable(false))
4265            .with_column("relhasindex", SqlScalarType::Bool.nullable(false))
4266            .with_column("relpersistence", SqlScalarType::PgLegacyChar.nullable(false))
4267            .with_column("relkind", SqlScalarType::String.nullable(true))
4268            .with_column("relnatts", SqlScalarType::Int16.nullable(false))
4269            .with_column("relchecks", SqlScalarType::Int16.nullable(false))
4270            .with_column("relhasrules", SqlScalarType::Bool.nullable(false))
4271            .with_column("relhastriggers", SqlScalarType::Bool.nullable(false))
4272            .with_column("relhassubclass", SqlScalarType::Bool.nullable(false))
4273            .with_column("relrowsecurity", SqlScalarType::Bool.nullable(false))
4274            .with_column("relforcerowsecurity", SqlScalarType::Bool.nullable(false))
4275            .with_column("relreplident", SqlScalarType::PgLegacyChar.nullable(false))
4276            .with_column("relispartition", SqlScalarType::Bool.nullable(false))
4277            .with_column("relhasoids", SqlScalarType::Bool.nullable(false))
4278            .with_column("reloptions", SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true))
4279            .with_column("database_name", SqlScalarType::String.nullable(true))
4280            .finish(),
4281        column_comments: BTreeMap::new(),
4282        sql: "
4283SELECT
4284    class_objects.oid,
4285    class_objects.name AS relname,
4286    mz_schemas.oid AS relnamespace,
4287    -- MZ doesn't support typed tables so reloftype is filled with 0
4288    0::pg_catalog.oid AS reloftype,
4289    role_owner.oid AS relowner,
4290    0::pg_catalog.oid AS relam,
4291    -- MZ doesn't have tablespaces so reltablespace is filled in with 0 implying the default tablespace
4292    0::pg_catalog.oid AS reltablespace,
4293    -- MZ doesn't support (estimated) row counts currently.
4294    -- Postgres defines a value of -1 as unknown.
4295    -1::float4 as reltuples,
4296    -- MZ doesn't use TOAST tables so reltoastrelid is filled with 0
4297    0::pg_catalog.oid AS reltoastrelid,
4298    EXISTS (SELECT id, oid, name, on_id, cluster_id FROM mz_catalog.mz_indexes where mz_indexes.on_id = class_objects.id) AS relhasindex,
4299    -- MZ doesn't have unlogged tables and because of (https://github.com/MaterializeInc/database-issues/issues/2689)
4300    -- temporary objects don't show up here, so relpersistence is filled with 'p' for permanent.
4301    -- TODO(jkosh44): update this column when issue is resolved.
4302    'p'::pg_catalog.\"char\" AS relpersistence,
4303    CASE
4304        WHEN class_objects.type = 'table' THEN 'r'
4305        WHEN class_objects.type = 'source' THEN 'r'
4306        WHEN class_objects.type = 'index' THEN 'i'
4307        WHEN class_objects.type = 'view' THEN 'v'
4308        WHEN class_objects.type = 'materialized-view' THEN 'm'
4309    END relkind,
4310    CASE
4311        WHEN class_objects.type = 'index' THEN COALESCE(
4312            (
4313                SELECT count(*)::pg_catalog.int2
4314                FROM mz_catalog.mz_index_columns
4315                WHERE mz_index_columns.index_id = class_objects.id
4316            ),
4317            0::pg_catalog.int2
4318        )
4319        ELSE COALESCE(
4320            (
4321                SELECT count(*)::pg_catalog.int2
4322                FROM mz_catalog.mz_columns
4323                WHERE mz_columns.id = class_objects.id
4324            ),
4325            0::pg_catalog.int2
4326        )
4327    END AS relnatts,
4328    -- MZ doesn't support CHECK constraints so relchecks is filled with 0
4329    0::pg_catalog.int2 AS relchecks,
4330    -- MZ doesn't support creating rules so relhasrules is filled with false
4331    false AS relhasrules,
4332    -- MZ doesn't support creating triggers so relhastriggers is filled with false
4333    false AS relhastriggers,
4334    -- MZ doesn't support table inheritance or partitions so relhassubclass is filled with false
4335    false AS relhassubclass,
4336    -- MZ doesn't have row level security so relrowsecurity and relforcerowsecurity is filled with false
4337    false AS relrowsecurity,
4338    false AS relforcerowsecurity,
4339    -- MZ doesn't support replication so relreplident is filled with 'd' for default
4340    'd'::pg_catalog.\"char\" AS relreplident,
4341    -- MZ doesn't support table partitioning so relispartition is filled with false
4342    false AS relispartition,
4343    -- PG removed relhasoids in v12 so it's filled with false
4344    false AS relhasoids,
4345    -- MZ doesn't support options for relations
4346    NULL::pg_catalog.text[] as reloptions,
4347    d.name as database_name
4348FROM (
4349    -- pg_class catalogs relations and indexes
4350    SELECT id, oid, schema_id, name, type, owner_id FROM mz_catalog.mz_relations
4351    UNION ALL
4352        SELECT mz_indexes.id, mz_indexes.oid, mz_relations.schema_id, mz_indexes.name, 'index' AS type, mz_indexes.owner_id
4353        FROM mz_catalog.mz_indexes
4354        JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
4355) AS class_objects
4356JOIN mz_catalog.mz_schemas ON mz_schemas.id = class_objects.schema_id
4357LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id
4358JOIN mz_catalog.mz_roles role_owner ON role_owner.id = class_objects.owner_id",
4359        access: vec![PUBLIC_SELECT],
4360        ontology: None,
4361    }
4362});
4363
4364pub const PG_CLASS_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4365    name: "pg_class_all_databases_ind",
4366    schema: MZ_INTERNAL_SCHEMA,
4367    oid: oid::INDEX_PG_CLASS_ALL_DATABASES_IND_OID,
4368    sql: "IN CLUSTER mz_catalog_server
4369ON mz_internal.pg_class_all_databases (relname)",
4370    is_retained_metrics_object: false,
4371};
4372
4373/// Peeled version of `PG_DESCRIPTION`:
4374/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4375///   in order to make this view indexable.
4376/// - This has 2 extra columns for the database names, so that downstream views can check them
4377///   against `current_database()`.
4378pub static PG_DESCRIPTION_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4379    BuiltinView {
4380        name: "pg_description_all_databases",
4381        schema: MZ_INTERNAL_SCHEMA,
4382        oid: oid::VIEW_PG_DESCRIPTION_ALL_DATABASES_OID,
4383        desc: RelationDesc::builder()
4384            .with_column("objoid", SqlScalarType::Oid.nullable(false))
4385            .with_column("classoid", SqlScalarType::Oid.nullable(true))
4386            .with_column("objsubid", SqlScalarType::Int32.nullable(false))
4387            .with_column("description", SqlScalarType::String.nullable(false))
4388            .with_column("oid_database_name", SqlScalarType::String.nullable(true))
4389            .with_column("class_database_name", SqlScalarType::String.nullable(true))
4390            .finish(),
4391        column_comments: BTreeMap::new(),
4392        sql: "
4393(
4394    -- The classoid of a comment is the oid of the pg_catalog system catalog
4395    -- that conceptually stores the commented object: pg_class for relations,
4396    -- pg_type for types, pg_namespace for schemas. We scope the lookup to the
4397    -- pg_catalog schema; otherwise a user-created object named e.g. `pg_class`
4398    -- makes the scalar subqueries below match multiple rows and the whole view
4399    -- errors for everyone. PostgreSQL's pg_description is a real catalog table
4400    -- and is unaffected by such user objects, and so are we.
4401    WITH pg_catalog_class AS (
4402        SELECT oid, relname, database_name
4403        FROM mz_internal.pg_class_all_databases
4404        WHERE relnamespace = (
4405            SELECT oid FROM mz_internal.pg_namespace_all_databases WHERE nspname = 'pg_catalog'
4406        )
4407    ),
4408    -- Gather all of the class oid's for objects that can have comments.
4409    pg_classoids AS (
4410        SELECT oid, database_name as oid_database_name,
4411          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_class') AS classoid,
4412          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_class') AS class_database_name
4413        FROM mz_internal.pg_class_all_databases
4414        UNION ALL
4415        SELECT oid, database_name as oid_database_name,
4416          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_type') AS classoid,
4417          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_type') AS class_database_name
4418        FROM mz_internal.pg_type_all_databases
4419        UNION ALL
4420        SELECT oid, database_name as oid_database_name,
4421          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_namespace') AS classoid,
4422          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_namespace') AS class_database_name
4423        FROM mz_internal.pg_namespace_all_databases
4424    ),
4425
4426    -- Gather all of the MZ ids for objects that can have comments.
4427    mz_objects AS (
4428        SELECT id, oid, type FROM mz_catalog.mz_objects
4429        UNION ALL
4430        SELECT id, oid, 'schema' AS type FROM mz_catalog.mz_schemas
4431    )
4432    SELECT
4433        pg_classoids.oid AS objoid,
4434        pg_classoids.classoid as classoid,
4435        COALESCE(cmt.object_sub_id, 0) AS objsubid,
4436        cmt.comment AS description,
4437        -- Columns added because of the peeling. (Note that there are 2 of these here.)
4438        oid_database_name,
4439        class_database_name
4440    FROM
4441        pg_classoids
4442    JOIN
4443        mz_objects ON pg_classoids.oid = mz_objects.oid
4444    JOIN
4445        mz_internal.mz_comments AS cmt ON mz_objects.id = cmt.id AND lower(mz_objects.type) = lower(cmt.object_type)
4446)",
4447        access: vec![PUBLIC_SELECT],
4448        ontology: None,
4449    }
4450});
4451
4452pub const PG_DESCRIPTION_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4453    name: "pg_description_all_databases_ind",
4454    schema: MZ_INTERNAL_SCHEMA,
4455    oid: oid::INDEX_PG_DESCRIPTION_ALL_DATABASES_IND_OID,
4456    sql: "IN CLUSTER mz_catalog_server
4457ON mz_internal.pg_description_all_databases (objoid, classoid, objsubid, description, oid_database_name, class_database_name)",
4458    is_retained_metrics_object: false,
4459};
4460
4461/// Peeled version of `PG_TYPE`:
4462/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4463///   in order to make this view indexable.
4464/// - This has the database name as an extra column, so that downstream views can check it against
4465///  `current_database()`.
4466pub static PG_TYPE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4467    BuiltinView {
4468        name: "pg_type_all_databases",
4469        schema: MZ_INTERNAL_SCHEMA,
4470        oid: oid::VIEW_PG_TYPE_ALL_DATABASES_OID,
4471        desc: RelationDesc::builder()
4472            .with_column("oid", SqlScalarType::Oid.nullable(false))
4473            .with_column("typname", SqlScalarType::String.nullable(false))
4474            .with_column("typnamespace", SqlScalarType::Oid.nullable(false))
4475            .with_column("typowner", SqlScalarType::Oid.nullable(false))
4476            .with_column("typlen", SqlScalarType::Int16.nullable(true))
4477            .with_column("typtype", SqlScalarType::PgLegacyChar.nullable(false))
4478            .with_column("typcategory", SqlScalarType::PgLegacyChar.nullable(true))
4479            .with_column("typdelim", SqlScalarType::PgLegacyChar.nullable(false))
4480            .with_column("typrelid", SqlScalarType::Oid.nullable(false))
4481            .with_column("typelem", SqlScalarType::Oid.nullable(false))
4482            .with_column("typarray", SqlScalarType::Oid.nullable(false))
4483            .with_column("typinput", SqlScalarType::RegProc.nullable(true))
4484            .with_column("typreceive", SqlScalarType::Oid.nullable(false))
4485            .with_column("typnotnull", SqlScalarType::Bool.nullable(false))
4486            .with_column("typbasetype", SqlScalarType::Oid.nullable(false))
4487            .with_column("typtypmod", SqlScalarType::Int32.nullable(false))
4488            .with_column("typcollation", SqlScalarType::Oid.nullable(false))
4489            .with_column("typdefault", SqlScalarType::String.nullable(true))
4490            .with_column("database_name", SqlScalarType::String.nullable(true))
4491            .finish(),
4492        column_comments: BTreeMap::new(),
4493        sql: "
4494SELECT
4495    mz_types.oid,
4496    mz_types.name AS typname,
4497    mz_schemas.oid AS typnamespace,
4498    role_owner.oid AS typowner,
4499    NULL::pg_catalog.int2 AS typlen,
4500    -- 'a' is used internally to denote an array type, but in postgres they show up
4501    -- as 'b'.
4502    (CASE mztype WHEN 'a' THEN 'b' ELSE mztype END)::pg_catalog.char AS typtype,
4503    (CASE category
4504        WHEN 'array' THEN 'A'
4505        WHEN 'bit-string' THEN 'V'
4506        WHEN 'boolean' THEN 'B'
4507        WHEN 'composite' THEN 'C'
4508        WHEN 'date-time' THEN 'D'
4509        WHEN 'enum' THEN 'E'
4510        WHEN 'geometric' THEN 'G'
4511        WHEN 'list' THEN 'U' -- List types are user-defined from PostgreSQL's perspective.
4512        WHEN 'network-address' THEN 'I'
4513        WHEN 'numeric' THEN 'N'
4514        WHEN 'pseudo' THEN 'P'
4515        WHEN 'string' THEN 'S'
4516        WHEN 'timespan' THEN 'T'
4517        WHEN 'user-defined' THEN 'U'
4518        WHEN 'unknown' THEN 'X'
4519    END)::pg_catalog.char AS typcategory,
4520    -- In pg only the 'box' type is not ','.
4521    ','::pg_catalog.char AS typdelim,
4522    0::pg_catalog.oid AS typrelid,
4523    coalesce(
4524        (
4525            SELECT t.oid
4526            FROM mz_catalog.mz_array_types a
4527            JOIN mz_catalog.mz_types t ON a.element_id = t.id
4528            WHERE a.id = mz_types.id
4529        ),
4530        (
4531            SELECT t.oid
4532            FROM mz_catalog.mz_list_types l
4533            JOIN mz_catalog.mz_types t ON l.element_id = t.id
4534            WHERE l.id = mz_types.id
4535        ),
4536        0
4537    ) AS typelem,
4538    coalesce(
4539        (
4540            SELECT
4541                t.oid
4542            FROM
4543                mz_catalog.mz_array_types AS a
4544                JOIN mz_catalog.mz_types AS t ON a.id = t.id
4545            WHERE
4546                a.element_id = mz_types.id
4547        ),
4548        0
4549    )
4550        AS typarray,
4551    mz_internal.mz_type_pg_metadata.typinput::pg_catalog.regproc AS typinput,
4552    COALESCE(mz_internal.mz_type_pg_metadata.typreceive, 0) AS typreceive,
4553    false::pg_catalog.bool AS typnotnull,
4554    0::pg_catalog.oid AS typbasetype,
4555    -1::pg_catalog.int4 AS typtypmod,
4556    -- MZ doesn't support COLLATE so typcollation is filled with 0
4557    0::pg_catalog.oid AS typcollation,
4558    NULL::pg_catalog.text AS typdefault,
4559    d.name as database_name
4560FROM
4561    mz_catalog.mz_types
4562    LEFT JOIN mz_internal.mz_type_pg_metadata ON mz_catalog.mz_types.id = mz_internal.mz_type_pg_metadata.id
4563    JOIN mz_catalog.mz_schemas ON mz_schemas.id = mz_types.schema_id
4564    JOIN (
4565            -- 'a' is not a supported typtype, but we use it to denote an array. It is
4566            -- converted to the correct value above.
4567            SELECT id, 'a' AS mztype FROM mz_catalog.mz_array_types
4568            UNION ALL SELECT id, 'b' FROM mz_catalog.mz_base_types
4569            UNION ALL SELECT id, 'l' FROM mz_catalog.mz_list_types
4570            UNION ALL SELECT id, 'm' FROM mz_catalog.mz_map_types
4571            UNION ALL SELECT id, 'p' FROM mz_catalog.mz_pseudo_types
4572        )
4573            AS t ON mz_types.id = t.id
4574    LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id
4575    JOIN mz_catalog.mz_roles role_owner ON role_owner.id = mz_types.owner_id",
4576        access: vec![PUBLIC_SELECT],
4577        ontology: None,
4578    }
4579});
4580
4581pub const PG_TYPE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4582    name: "pg_type_all_databases_ind",
4583    schema: MZ_INTERNAL_SCHEMA,
4584    oid: oid::INDEX_PG_TYPE_ALL_DATABASES_IND_OID,
4585    sql: "IN CLUSTER mz_catalog_server
4586ON mz_internal.pg_type_all_databases (oid)",
4587    is_retained_metrics_object: false,
4588};
4589
4590/// Peeled version of `PG_ATTRIBUTE`:
4591/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4592///   in order to make this view indexable.
4593/// - This has 2 extra columns for the database names, so that downstream views can check them
4594///   against `current_database()`.
4595pub static PG_ATTRIBUTE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4596    BuiltinView {
4597        name: "pg_attribute_all_databases",
4598        schema: MZ_INTERNAL_SCHEMA,
4599        oid: oid::VIEW_PG_ATTRIBUTE_ALL_DATABASES_OID,
4600        desc: RelationDesc::builder()
4601            .with_column("attrelid", SqlScalarType::Oid.nullable(false))
4602            .with_column("attname", SqlScalarType::String.nullable(false))
4603            .with_column("atttypid", SqlScalarType::Oid.nullable(false))
4604            .with_column("attlen", SqlScalarType::Int16.nullable(true))
4605            .with_column("attnum", SqlScalarType::Int16.nullable(false))
4606            .with_column("atttypmod", SqlScalarType::Int32.nullable(false))
4607            .with_column("attndims", SqlScalarType::Int16.nullable(false))
4608            .with_column("attnotnull", SqlScalarType::Bool.nullable(false))
4609            .with_column("atthasdef", SqlScalarType::Bool.nullable(false))
4610            .with_column("attidentity", SqlScalarType::PgLegacyChar.nullable(false))
4611            .with_column("attgenerated", SqlScalarType::PgLegacyChar.nullable(false))
4612            .with_column("attisdropped", SqlScalarType::Bool.nullable(false))
4613            .with_column("attcollation", SqlScalarType::Oid.nullable(false))
4614            .with_column("database_name", SqlScalarType::String.nullable(true))
4615            .with_column("pg_type_database_name", SqlScalarType::String.nullable(true))
4616            .finish(),
4617        column_comments: BTreeMap::new(),
4618        sql: "
4619SELECT
4620    class_objects.oid as attrelid,
4621    mz_columns.name as attname,
4622    mz_columns.type_oid AS atttypid,
4623    pg_type_all_databases.typlen AS attlen,
4624    position::int8::int2 as attnum,
4625    mz_columns.type_mod as atttypmod,
4626    -- dummy value, just to make go-jet's workaround work for now. Discussion:
4627    -- https://github.com/MaterializeInc/materialize/pull/34649#issuecomment-3714291409
4628    0::int2 as attndims,
4629    NOT nullable as attnotnull,
4630    mz_columns.default IS NOT NULL as atthasdef,
4631    ''::pg_catalog.\"char\" as attidentity,
4632    -- MZ doesn't support generated columns so attgenerated is filled with ''
4633    ''::pg_catalog.\"char\" as attgenerated,
4634    FALSE as attisdropped,
4635    -- MZ doesn't support COLLATE so attcollation is filled with 0
4636    0::pg_catalog.oid as attcollation,
4637    -- Columns added because of the peeling. (Note that there are 2 of these here.)
4638    d.name as database_name,
4639    pg_type_all_databases.database_name as pg_type_database_name
4640FROM (
4641    -- pg_attribute catalogs columns on relations and indexes
4642    SELECT id, oid, schema_id, name, type FROM mz_catalog.mz_relations
4643    UNION ALL
4644        SELECT mz_indexes.id, mz_indexes.oid, mz_relations.schema_id, mz_indexes.name, 'index' AS type
4645        FROM mz_catalog.mz_indexes
4646        JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
4647) AS class_objects
4648JOIN mz_catalog.mz_columns ON class_objects.id = mz_columns.id
4649JOIN mz_internal.pg_type_all_databases ON pg_type_all_databases.oid = mz_columns.type_oid
4650JOIN mz_catalog.mz_schemas ON mz_schemas.id = class_objects.schema_id
4651LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id",
4652        // Since this depends on pg_type, its id must be higher due to initialization
4653        // ordering.
4654        access: vec![PUBLIC_SELECT],
4655        ontology: None,
4656    }
4657});
4658
4659pub const PG_ATTRIBUTE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4660    name: "pg_attribute_all_databases_ind",
4661    schema: MZ_INTERNAL_SCHEMA,
4662    oid: oid::INDEX_PG_ATTRIBUTE_ALL_DATABASES_IND_OID,
4663    sql: "IN CLUSTER mz_catalog_server
4664ON mz_internal.pg_attribute_all_databases (
4665    attrelid, attname, atttypid, attlen, attnum, atttypmod, attnotnull, atthasdef, attidentity,
4666    attgenerated, attisdropped, attcollation, database_name, pg_type_database_name
4667)",
4668    is_retained_metrics_object: false,
4669};
4670
4671/// Peeled version of `PG_ATTRDEF`:
4672/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4673///   in order to make this view indexable.
4674pub static PG_ATTRDEF_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4675    name: "pg_attrdef_all_databases",
4676    schema: MZ_INTERNAL_SCHEMA,
4677    oid: oid::VIEW_PG_ATTRDEF_ALL_DATABASES_OID,
4678    desc: RelationDesc::builder()
4679        .with_column("oid", SqlScalarType::Oid.nullable(true))
4680        .with_column("adrelid", SqlScalarType::Oid.nullable(false))
4681        .with_column("adnum", SqlScalarType::Int64.nullable(false))
4682        .with_column("adbin", SqlScalarType::String.nullable(false))
4683        .with_column("adsrc", SqlScalarType::String.nullable(false))
4684        .finish(),
4685    column_comments: BTreeMap::new(),
4686    sql: "
4687SELECT
4688    NULL::pg_catalog.oid AS oid,
4689    mz_objects.oid AS adrelid,
4690    mz_columns.position::int8 AS adnum,
4691    mz_columns.default AS adbin,
4692    mz_columns.default AS adsrc
4693FROM mz_catalog.mz_columns
4694    JOIN mz_catalog.mz_objects ON mz_columns.id = mz_objects.id
4695WHERE default IS NOT NULL",
4696    access: vec![PUBLIC_SELECT],
4697    ontology: None,
4698});
4699
4700pub const PG_ATTRDEF_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4701    name: "pg_attrdef_all_databases_ind",
4702    schema: MZ_INTERNAL_SCHEMA,
4703    oid: oid::INDEX_PG_ATTRDEF_ALL_DATABASES_IND_OID,
4704    sql: "IN CLUSTER mz_catalog_server
4705ON mz_internal.pg_attrdef_all_databases (oid, adrelid, adnum, adbin, adsrc)",
4706    is_retained_metrics_object: false,
4707};
4708
4709pub static MZ_COMPUTE_ERROR_COUNTS_RAW_UNIFIED: LazyLock<BuiltinSource> =
4710    LazyLock::new(|| BuiltinSource {
4711        // TODO(database-issues#8173): Rename this source to `mz_compute_error_counts_raw`. Currently this causes a
4712        // naming conflict because the resolver stumbles over the source with the same name in
4713        // `mz_introspection` due to the automatic schema translation.
4714        name: "mz_compute_error_counts_raw_unified",
4715        schema: MZ_INTERNAL_SCHEMA,
4716        oid: oid::SOURCE_MZ_COMPUTE_ERROR_COUNTS_RAW_UNIFIED_OID,
4717        desc: RelationDesc::builder()
4718            .with_column("replica_id", SqlScalarType::String.nullable(false))
4719            .with_column("object_id", SqlScalarType::String.nullable(false))
4720            .with_column(
4721                "count",
4722                SqlScalarType::Numeric { max_scale: None }.nullable(false),
4723            )
4724            .finish(),
4725        data_source: IntrospectionType::ComputeErrorCounts.into(),
4726        column_comments: BTreeMap::new(),
4727        is_retained_metrics_object: false,
4728        access: vec![PUBLIC_SELECT],
4729        ontology: None,
4730    });
4731
4732pub static MZ_COMPUTE_HYDRATION_TIMES: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
4733    name: "mz_compute_hydration_times",
4734    schema: MZ_INTERNAL_SCHEMA,
4735    oid: oid::SOURCE_MZ_COMPUTE_HYDRATION_TIMES_OID,
4736    desc: RelationDesc::builder()
4737        .with_column("replica_id", SqlScalarType::String.nullable(false))
4738        .with_column("object_id", SqlScalarType::String.nullable(false))
4739        .with_column("time_ns", SqlScalarType::UInt64.nullable(true))
4740        .finish(),
4741    data_source: IntrospectionType::ComputeHydrationTimes.into(),
4742    column_comments: BTreeMap::new(),
4743    is_retained_metrics_object: true,
4744    access: vec![PUBLIC_SELECT],
4745    ontology: Some(Ontology {
4746        entity_name: "compute_hydration_time",
4747        description: "Time to hydrate compute objects",
4748        links: &const { [] },
4749        column_semantic_types: &const {
4750            [
4751                ("replica_id", SemanticType::ReplicaId),
4752                ("object_id", SemanticType::CatalogItemId),
4753            ]
4754        },
4755    }),
4756});
4757
4758pub static MZ_COMPUTE_HYDRATION_TIMES_IND: LazyLock<BuiltinIndex> =
4759    LazyLock::new(|| BuiltinIndex {
4760        name: "mz_compute_hydration_times_ind",
4761        schema: MZ_INTERNAL_SCHEMA,
4762        oid: oid::INDEX_MZ_COMPUTE_HYDRATION_TIMES_IND_OID,
4763        sql: "IN CLUSTER mz_catalog_server
4764    ON mz_internal.mz_compute_hydration_times (replica_id)",
4765        is_retained_metrics_object: true,
4766    });
4767
4768pub static MZ_OBJECT_ARRANGEMENT_SIZES_UNIFIED: LazyLock<BuiltinSource> = LazyLock::new(|| {
4769    BuiltinSource {
4770        name: "mz_object_arrangement_sizes",
4771        schema: MZ_INTERNAL_SCHEMA,
4772        oid: oid::SOURCE_MZ_OBJECT_ARRANGEMENT_SIZES_OID,
4773        desc: RelationDesc::builder()
4774            .with_column("replica_id", SqlScalarType::String.nullable(false))
4775            .with_column("object_id", SqlScalarType::String.nullable(false))
4776            .with_column("size", SqlScalarType::Int64.nullable(true))
4777            .finish(),
4778        data_source: IntrospectionType::ComputeObjectArrangementSizes.into(),
4779        column_comments: BTreeMap::from_iter([
4780            (
4781                "replica_id",
4782                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
4783            ),
4784            (
4785                "object_id",
4786                "The ID of the compute object (index or materialized view). Corresponds to `mz_objects.id`.",
4787            ),
4788            (
4789                "size",
4790                "The total arrangement heap and batcher size in bytes for this object on this replica, \
4791                 rounded to the nearest 10 MiB boundary to reduce per-byte churn in the differential \
4792                 collection. Objects with less than 5 MiB of arrangements report a size of 0.",
4793            ),
4794        ]),
4795        is_retained_metrics_object: true,
4796        access: vec![PUBLIC_SELECT],
4797        ontology: None,
4798    }
4799});
4800
4801pub static MZ_OBJECT_ARRANGEMENT_SIZES_IND: LazyLock<BuiltinIndex> =
4802    LazyLock::new(|| BuiltinIndex {
4803        name: "mz_object_arrangement_sizes_ind",
4804        schema: MZ_INTERNAL_SCHEMA,
4805        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZES_IND_OID,
4806        sql: "IN CLUSTER mz_catalog_server
4807    ON mz_internal.mz_object_arrangement_sizes (replica_id)",
4808        is_retained_metrics_object: true,
4809    });
4810
4811pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY: LazyLock<BuiltinTable> = LazyLock::new(|| {
4812    BuiltinTable {
4813        name: "mz_object_arrangement_size_history",
4814        schema: MZ_INTERNAL_SCHEMA,
4815        oid: oid::TABLE_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OID,
4816        desc: RelationDesc::builder()
4817            .with_column("replica_id", SqlScalarType::String.nullable(false))
4818            .with_column("object_id", SqlScalarType::String.nullable(false))
4819            .with_column("size", SqlScalarType::Int64.nullable(false))
4820            .with_column(
4821                "collection_timestamp",
4822                SqlScalarType::TimestampTz { precision: None }.nullable(false),
4823            )
4824            .with_column("hydration_complete", SqlScalarType::Bool.nullable(false))
4825            .finish(),
4826        column_comments: BTreeMap::from_iter([
4827            (
4828                "replica_id",
4829                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
4830            ),
4831            (
4832                "object_id",
4833                "The ID of the compute object (index or materialized view). Corresponds to `mz_objects.id`.",
4834            ),
4835            (
4836                "size",
4837                "The total arrangement heap and batcher size in bytes for this object on this replica \
4838                 at `collection_timestamp`, rounded to the nearest 10 MiB to reduce per-byte churn \
4839                 in the underlying differential collection. Objects with less than 5 MiB of \
4840                 arrangements are not recorded. May reflect a mid-build size if \
4841                 `hydration_complete` is `false`.",
4842            ),
4843            (
4844                "collection_timestamp",
4845                "The timestamp when this snapshot was collected.",
4846            ),
4847            (
4848                "hydration_complete",
4849                "Whether the arrangement had finished its initial hydration on this replica when \
4850                 the snapshot was collected. Filter for `true` to consider only stable, post-build \
4851                 sizes.",
4852            ),
4853        ]),
4854        is_retained_metrics_object: true,
4855        access: vec![PUBLIC_SELECT],
4856        ontology: None,
4857    }
4858});
4859
4860pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OBJECT_IND: LazyLock<BuiltinIndex> =
4861    LazyLock::new(|| BuiltinIndex {
4862        name: "mz_object_arrangement_size_history_object_ind",
4863        schema: MZ_INTERNAL_SCHEMA,
4864        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OBJECT_IND_OID,
4865        sql: "IN CLUSTER mz_catalog_server
4866    ON mz_internal.mz_object_arrangement_size_history (object_id)",
4867        is_retained_metrics_object: true,
4868    });
4869
4870pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_TS_IND: LazyLock<BuiltinIndex> =
4871    LazyLock::new(|| BuiltinIndex {
4872        name: "mz_object_arrangement_size_history_ts_ind",
4873        schema: MZ_INTERNAL_SCHEMA,
4874        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_TS_IND_OID,
4875        sql: "IN CLUSTER mz_catalog_server
4876    ON mz_internal.mz_object_arrangement_size_history (collection_timestamp)",
4877        is_retained_metrics_object: true,
4878    });
4879
4880pub static MZ_COMPUTE_HYDRATION_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4881    name: "mz_compute_hydration_statuses",
4882    schema: MZ_INTERNAL_SCHEMA,
4883    oid: oid::SOURCE_MZ_COMPUTE_HYDRATION_STATUSES_OID,
4884    desc: RelationDesc::builder()
4885        .with_column("object_id", SqlScalarType::String.nullable(false))
4886        .with_column("replica_id", SqlScalarType::String.nullable(false))
4887        .with_column("hydrated", SqlScalarType::Bool.nullable(false))
4888        .with_column("hydration_time", SqlScalarType::Interval.nullable(true))
4889        .finish(),
4890    column_comments: BTreeMap::from_iter([
4891        (
4892            "object_id",
4893            "The ID of a compute object. Corresponds to `mz_catalog.mz_indexes.id` or `mz_catalog.mz_materialized_views.id`",
4894        ),
4895        ("replica_id", "The ID of a cluster replica."),
4896        (
4897            "hydrated",
4898            "Whether the compute object is hydrated on the replica.",
4899        ),
4900        (
4901            "hydration_time",
4902            "The amount of time it took for the replica to hydrate the compute object.",
4903        ),
4904    ]),
4905    sql: "
4906WITH
4907    dataflows AS (
4908        SELECT
4909            object_id,
4910            replica_id,
4911            time_ns IS NOT NULL AS hydrated,
4912            ((time_ns / 1000) || 'microseconds')::interval AS hydration_time
4913        FROM mz_internal.mz_compute_hydration_times
4914    ),
4915    -- MVs that have advanced to the empty frontier don't have a dataflow installed anymore and
4916    -- therefore don't show up in `mz_compute_hydration_times`. We still want to show them here to
4917    -- avoid surprises for people joining `mz_materialized_views` against this relation (like the
4918    -- blue-green readiness query does), so we include them as 'hydrated'.
4919    complete_mvs AS (
4920        SELECT
4921            mv.id,
4922            f.replica_id,
4923            true AS hydrated,
4924            NULL::interval AS hydration_time
4925        FROM mz_materialized_views mv
4926        JOIN mz_catalog.mz_cluster_replica_frontiers f ON f.object_id = mv.id
4927        WHERE f.write_frontier IS NULL
4928    )
4929SELECT * FROM dataflows
4930UNION ALL
4931SELECT * FROM complete_mvs",
4932    access: vec![PUBLIC_SELECT],
4933    ontology: Some(Ontology {
4934        entity_name: "compute_hydration_status_view",
4935        description: "Computed hydration status per compute object",
4936        links: &const { [] },
4937        column_semantic_types: &const {
4938            [
4939                ("object_id", SemanticType::GlobalId),
4940                ("replica_id", SemanticType::ReplicaId),
4941            ]
4942        },
4943    }),
4944});
4945
4946pub static MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES: LazyLock<BuiltinSource> = LazyLock::new(|| {
4947    BuiltinSource {
4948        name: "mz_compute_operator_hydration_statuses",
4949        schema: MZ_INTERNAL_SCHEMA,
4950        oid: oid::SOURCE_MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES_OID,
4951        desc: RelationDesc::builder()
4952            .with_column("replica_id", SqlScalarType::String.nullable(false))
4953            .with_column("object_id", SqlScalarType::String.nullable(false))
4954            .with_column(
4955                "physical_plan_node_id",
4956                SqlScalarType::UInt64.nullable(false),
4957            )
4958            .with_column("hydrated", SqlScalarType::Bool.nullable(false))
4959            .with_key(vec![0, 1, 2])
4960            .finish(),
4961        data_source: IntrospectionType::ComputeOperatorHydrationStatus.into(),
4962        column_comments: BTreeMap::from_iter([
4963            ("replica_id", "The ID of a cluster replica."),
4964            (
4965                "object_id",
4966                "The ID of a compute object. Corresponds to `mz_catalog.mz_indexes.id` or `mz_catalog.mz_materialized_views.id`.",
4967            ),
4968            (
4969                "physical_plan_node_id",
4970                "The ID of a node in the physical plan of the compute object. Corresponds to a `node_id` displayed in the output of `EXPLAIN PHYSICAL PLAN WITH (node identifiers)`.",
4971            ),
4972            ("hydrated", "Whether the node is hydrated on the replica."),
4973        ]),
4974        is_retained_metrics_object: false,
4975        access: vec![PUBLIC_SELECT],
4976        ontology: Some(Ontology {
4977            entity_name: "compute_hydration_status",
4978            description: "Hydration status per compute operator",
4979            links: &const { [] },
4980            column_semantic_types: &const {
4981                [
4982                    ("replica_id", SemanticType::ReplicaId),
4983                    ("object_id", SemanticType::CatalogItemId),
4984                ]
4985            },
4986        }),
4987    }
4988});
4989
4990pub static MZ_CLUSTER_REPLICA_UTILIZATION: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4991    name: "mz_cluster_replica_utilization",
4992    schema: MZ_INTERNAL_SCHEMA,
4993    oid: oid::VIEW_MZ_CLUSTER_REPLICA_UTILIZATION_OID,
4994    desc: RelationDesc::builder()
4995        .with_column("replica_id", SqlScalarType::String.nullable(false))
4996        .with_column("process_id", SqlScalarType::UInt64.nullable(false))
4997        .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
4998        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
4999        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
5000        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
5001        .finish(),
5002    column_comments: BTreeMap::from_iter([
5003        ("replica_id", "The ID of a cluster replica."),
5004        ("process_id", "The ID of a process within the replica."),
5005        (
5006            "cpu_percent",
5007            "Approximate CPU usage, in percent of the total allocation.",
5008        ),
5009        (
5010            "memory_percent",
5011            "Approximate RAM usage, in percent of the total allocation.",
5012        ),
5013        (
5014            "disk_percent",
5015            "Approximate disk usage, in percent of the total allocation.",
5016        ),
5017        (
5018            "heap_percent",
5019            "Approximate heap (RAM + swap) usage, in percent of the total allocation.",
5020        ),
5021    ]),
5022    sql: "
5023SELECT
5024    r.id AS replica_id,
5025    m.process_id,
5026    m.cpu_nano_cores::float8 / NULLIF(s.cpu_nano_cores, 0) * 100 AS cpu_percent,
5027    m.memory_bytes::float8 / NULLIF(s.memory_bytes, 0) * 100 AS memory_percent,
5028    m.disk_bytes::float8 / NULLIF(s.disk_bytes, 0) * 100 AS disk_percent,
5029    m.heap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS heap_percent
5030FROM
5031    mz_catalog.mz_cluster_replicas AS r
5032        JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
5033        JOIN mz_internal.mz_cluster_replica_metrics AS m ON m.replica_id = r.id",
5034    access: vec![PUBLIC_SELECT],
5035    ontology: Some(Ontology {
5036        entity_name: "replica_utilization",
5037        description: "Computed utilization metrics per replica",
5038        links: &const {
5039            [OntologyLink {
5040                name: "utilization_of_replica",
5041                target: "replica",
5042                properties: LinkProperties::fk_typed(
5043                    "replica_id",
5044                    "id",
5045                    Cardinality::OneToOne,
5046                    mz_repr::SemanticType::CatalogItemId,
5047                ),
5048            }]
5049        },
5050        column_semantic_types: &[("replica_id", SemanticType::ReplicaId)],
5051    }),
5052});
5053
5054pub static MZ_CLUSTER_REPLICA_UTILIZATION_HISTORY: LazyLock<BuiltinView> =
5055    LazyLock::new(|| BuiltinView {
5056        name: "mz_cluster_replica_utilization_history",
5057        schema: MZ_INTERNAL_SCHEMA,
5058        oid: oid::VIEW_MZ_CLUSTER_REPLICA_UTILIZATION_HISTORY_OID,
5059        desc: RelationDesc::builder()
5060            .with_column("replica_id", SqlScalarType::String.nullable(false))
5061            .with_column("process_id", SqlScalarType::UInt64.nullable(false))
5062            .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
5063            .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
5064            .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
5065            .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
5066            .with_column(
5067                "occurred_at",
5068                SqlScalarType::TimestampTz { precision: None }.nullable(false),
5069            )
5070            .finish(),
5071        column_comments: BTreeMap::from_iter([
5072            ("replica_id", "The ID of a cluster replica."),
5073            ("process_id", "The ID of a process within the replica."),
5074            (
5075                "cpu_percent",
5076                "Approximate CPU usage, in percent of the total allocation.",
5077            ),
5078            (
5079                "memory_percent",
5080                "Approximate RAM usage, in percent of the total allocation.",
5081            ),
5082            (
5083                "disk_percent",
5084                "Approximate disk usage, in percent of the total allocation.",
5085            ),
5086            (
5087                "heap_percent",
5088                "Approximate heap (RAM + swap) usage, in percent of the total allocation.",
5089            ),
5090            (
5091                "occurred_at",
5092                "Wall-clock timestamp at which the event occurred.",
5093            ),
5094        ]),
5095        sql: "
5096SELECT
5097    r.id AS replica_id,
5098    m.process_id,
5099    m.cpu_nano_cores::float8 / NULLIF(s.cpu_nano_cores, 0) * 100 AS cpu_percent,
5100    m.memory_bytes::float8 / NULLIF(s.memory_bytes, 0) * 100 AS memory_percent,
5101    m.disk_bytes::float8 / NULLIF(s.disk_bytes, 0) * 100 AS disk_percent,
5102    m.heap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS heap_percent,
5103    m.occurred_at
5104FROM
5105    mz_catalog.mz_cluster_replicas AS r
5106        JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
5107        JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.id",
5108        access: vec![PUBLIC_SELECT],
5109        ontology: None,
5110    });
5111
5112pub static MZ_INDEX_ADVICE: LazyLock<BuiltinView> = LazyLock::new(|| {
5113    BuiltinView {
5114        name: "mz_index_advice",
5115        schema: MZ_INTERNAL_SCHEMA,
5116        oid: oid::VIEW_MZ_INDEX_ADVICE_OID,
5117        desc: RelationDesc::builder()
5118            .with_column("object_id", SqlScalarType::String.nullable(true))
5119            .with_column("hint", SqlScalarType::String.nullable(false))
5120            .with_column("details", SqlScalarType::String.nullable(false))
5121            .with_column("referenced_object_ids", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(true))
5122            .finish(),
5123        column_comments: BTreeMap::from_iter([
5124            ("object_id", "The ID of the object. Corresponds to mz_objects.id."),
5125            ("hint", "A suggestion to either change the object (e.g. create an index, turn a materialized view into an indexed view) or keep the object unchanged."),
5126            ("details", "Additional details on why the `hint` was proposed based on the dependencies of the object."),
5127            ("referenced_object_ids", "The IDs of objects referenced by `details`. Corresponds to mz_objects.id."),
5128        ]),
5129        sql: "
5130-- To avoid confusion with sources and sinks in the materialize sense,
5131-- the following uses the terms leafs (instead of sinks) and roots (instead of sources)
5132-- when referring to the object dependency graph.
5133--
5134-- The basic idea is to walk up the dependency graph to propagate the transitive dependencies
5135-- of maintained objected upwards. The leaves of the dependency graph are maintained objects
5136-- that are not depended on by other maintained objects and have a justification why they must
5137-- be maintained (e.g. a materialized view that is depended on by a sink).
5138-- Starting from these leaves, the dependencies are propagated upwards towards the roots according
5139-- to the object dependencies. Whenever there is a node that is being depended on by multiple
5140-- downstream objects, that node is marked to be converted into a maintained object and this
5141-- node is then propagated further up. Once completed, the list of objects that are marked as
5142-- maintained is checked against all objects to generate appropriate recommendations.
5143--
5144-- Note that the recommendations only incorporate dependencies between objects.
5145-- This can lead to bad recommendations, e.g. filters can no longer be pushed into (or close to)
5146-- a sink if an index is added in between the sink and the filter. For very selective filters,
5147-- this can lead to redundant work: the index is computing stuff only to discarded by the selective
5148-- filter later on. But these kind of aspects cannot be understood by merely looking at the
5149-- dependencies.
5150WITH MUTUALLY RECURSIVE
5151    -- for all objects, understand if they have an index on them and on which cluster they are running
5152    -- this avoids having different cases for views with an index and materialized views later on
5153    objects(id text, type text, cluster_id text, indexes text list) AS (
5154        -- views and materialized views without an index
5155        SELECT
5156            o.id,
5157            o.type,
5158            o.cluster_id,
5159            '{}'::text list AS indexes
5160        FROM mz_catalog.mz_objects o
5161        WHERE o.id LIKE 'u%' AND o.type IN ('materialized-view', 'view') AND NOT EXISTS (
5162            SELECT FROM mz_internal.mz_object_dependencies d
5163            JOIN mz_catalog.mz_objects AS i
5164                ON (i.id = d.object_id AND i.type = 'index')
5165            WHERE (o.id = d.referenced_object_id)
5166        )
5167
5168        UNION ALL
5169
5170        -- views and materialized views with an index
5171        SELECT
5172            o.id,
5173            o.type,
5174            -- o.cluster_id is always NULL for views, so use the cluster of the index instead
5175            COALESCE(o.cluster_id, i.cluster_id) AS cluster_id,
5176            list_agg(i.id) AS indexes
5177        FROM mz_catalog.mz_objects o
5178        JOIN mz_internal.mz_object_dependencies AS d
5179            ON (o.id = d.referenced_object_id)
5180        JOIN mz_catalog.mz_objects AS i
5181            ON (i.id = d.object_id AND i.type = 'index')
5182        WHERE o.id LIKE 'u%' AND o.type IN ('materialized-view', 'view', 'source')
5183        GROUP BY o.id, o.type, o.cluster_id, i.cluster_id
5184    ),
5185
5186    -- maintained objects that are at the leafs of the dependency graph with respect to a specific cluster
5187    maintained_leafs(id text, justification text) AS (
5188        -- materialized views that are connected to a sink
5189        SELECT
5190            m.id,
5191            s.id AS justification
5192        FROM objects AS m
5193        JOIN mz_internal.mz_object_dependencies AS d
5194            ON (m.id = d.referenced_object_id)
5195        JOIN mz_catalog.mz_objects AS s
5196            ON (s.id = d.object_id AND s.type = 'sink')
5197        WHERE m.type = 'materialized-view'
5198
5199        UNION ALL
5200
5201        -- (materialized) views with an index that are not transitively depend on by maintained objects on the same cluster
5202        SELECT
5203            v.id,
5204            unnest(v.indexes) AS justification
5205        FROM objects AS v
5206        WHERE v.type IN ('view', 'materialized-view', 'source') AND NOT EXISTS (
5207            SELECT FROM mz_internal.mz_object_transitive_dependencies AS d
5208            INNER JOIN mz_catalog.mz_objects AS child
5209                ON (d.object_id = child.id)
5210            WHERE d.referenced_object_id = v.id AND child.type IN ('materialized-view', 'index') AND v.cluster_id = child.cluster_id AND NOT v.indexes @> LIST[child.id]
5211        )
5212    ),
5213
5214    -- this is just a helper cte to union multiple lists as part of an aggregation, which is not directly possible in SQL
5215    agg_maintained_children(id text, maintained_children text list) AS (
5216        SELECT
5217            parent_id AS id,
5218            list_agg(maintained_child) AS maintained_leafs
5219        FROM (
5220            SELECT DISTINCT
5221                d.referenced_object_id AS parent_id,
5222                -- it's not possible to union lists in an aggregation, so we have to unnest the list first
5223                unnest(child.maintained_children) AS maintained_child
5224            FROM propagate_dependencies AS child
5225            INNER JOIN mz_internal.mz_object_dependencies AS d
5226                ON (child.id = d.object_id)
5227        )
5228        GROUP BY parent_id
5229    ),
5230
5231    -- propagate dependencies of maintained objects from the leafs to the roots of the dependency graph and
5232    -- record a justification when an object should be maintained, e.g. when it is depended on by more than one maintained object
5233    -- when an object should be maintained, maintained_children will just contain that object so that further upstream objects refer to it in their maintained_children
5234    propagate_dependencies(id text, maintained_children text list, justification text list) AS (
5235        -- base case: start with the leafs
5236        SELECT DISTINCT
5237            id,
5238            LIST[id] AS maintained_children,
5239            list_agg(justification) AS justification
5240        FROM maintained_leafs
5241        GROUP BY id
5242
5243        UNION
5244
5245        -- recursive case: if there is a child with the same dependencies as the parent,
5246        -- the parent is only reused by a single child
5247        SELECT
5248            parent.id,
5249            child.maintained_children,
5250            NULL::text list AS justification
5251        FROM agg_maintained_children AS parent
5252        INNER JOIN mz_internal.mz_object_dependencies AS d
5253            ON (parent.id = d.referenced_object_id)
5254        INNER JOIN propagate_dependencies AS child
5255            ON (d.object_id = child.id)
5256        WHERE parent.maintained_children = child.maintained_children
5257
5258        UNION
5259
5260        -- recursive case: if there is NO child with the same dependencies as the parent,
5261        -- different children are reusing the parent so maintaining the object is justified by itself
5262        SELECT DISTINCT
5263            parent.id,
5264            LIST[parent.id] AS maintained_children,
5265            parent.maintained_children AS justification
5266        FROM agg_maintained_children AS parent
5267        WHERE NOT EXISTS (
5268            SELECT FROM mz_internal.mz_object_dependencies AS d
5269            INNER JOIN propagate_dependencies AS child
5270                ON (d.object_id = child.id AND d.referenced_object_id = parent.id)
5271            WHERE parent.maintained_children = child.maintained_children
5272        )
5273    ),
5274
5275    objects_with_justification(id text, type text, cluster_id text, maintained_children text list, justification text list, indexes text list) AS (
5276        SELECT
5277            p.id,
5278            o.type,
5279            o.cluster_id,
5280            p.maintained_children,
5281            p.justification,
5282            o.indexes
5283        FROM propagate_dependencies p
5284        JOIN objects AS o
5285            ON (p.id = o.id)
5286    ),
5287
5288    hints(id text, hint text, details text, justification text list) AS (
5289        -- materialized views that are not required
5290        SELECT
5291            id,
5292            'convert to a view' AS hint,
5293            'no dependencies from sinks nor from objects on different clusters' AS details,
5294            justification
5295        FROM objects_with_justification
5296        WHERE type = 'materialized-view' AND justification IS NULL
5297
5298        UNION ALL
5299
5300        -- materialized views that are required because a sink or a maintained object from a different cluster depends on them
5301        SELECT
5302            id,
5303            'keep' AS hint,
5304            'dependencies from sinks or objects on different clusters: ' AS details,
5305            justification
5306        FROM objects_with_justification AS m
5307        WHERE type = 'materialized-view' AND justification IS NOT NULL AND EXISTS (
5308            SELECT FROM unnest(justification) AS dependency
5309            JOIN mz_catalog.mz_objects s ON (s.type = 'sink' AND s.id = dependency)
5310
5311            UNION ALL
5312
5313            SELECT FROM unnest(justification) AS dependency
5314            JOIN mz_catalog.mz_objects AS d ON (d.id = dependency)
5315            WHERE d.cluster_id != m.cluster_id
5316        )
5317
5318        UNION ALL
5319
5320        -- materialized views that can be converted to a view with or without an index because NO sink or a maintained object from a different cluster depends on them
5321        SELECT
5322            id,
5323            'convert to a view with an index' AS hint,
5324            'no dependencies from sinks nor from objects on different clusters, but maintained dependencies on the same cluster: ' AS details,
5325            justification
5326        FROM objects_with_justification AS m
5327        WHERE type = 'materialized-view' AND justification IS NOT NULL AND NOT EXISTS (
5328            SELECT FROM unnest(justification) AS dependency
5329            JOIN mz_catalog.mz_objects s ON (s.type = 'sink' AND s.id = dependency)
5330
5331            UNION ALL
5332
5333            SELECT FROM unnest(justification) AS dependency
5334            JOIN mz_catalog.mz_objects AS d ON (d.id = dependency)
5335            WHERE d.cluster_id != m.cluster_id
5336        )
5337
5338        UNION ALL
5339
5340        -- views that have indexes on different clusters should be a materialized view
5341        SELECT
5342            o.id,
5343            'convert to materialized view' AS hint,
5344            'dependencies on multiple clusters: ' AS details,
5345            o.justification
5346        FROM objects_with_justification o,
5347            LATERAL unnest(o.justification) j
5348        LEFT JOIN mz_catalog.mz_objects AS m
5349            ON (m.id = j AND m.type IN ('index', 'materialized-view'))
5350        WHERE o.type = 'view' AND o.justification IS NOT NULL
5351        GROUP BY o.id, o.justification
5352        HAVING count(DISTINCT m.cluster_id) >= 2
5353
5354        UNION ALL
5355
5356        -- views without an index that should be maintained
5357        SELECT
5358            id,
5359            'add index' AS hint,
5360            'multiple downstream dependencies: ' AS details,
5361            justification
5362        FROM objects_with_justification
5363        WHERE type = 'view' AND justification IS NOT NULL AND indexes = '{}'::text list
5364
5365        UNION ALL
5366
5367        -- index inside the dependency graph (not a leaf)
5368        SELECT
5369            unnest(indexes) AS id,
5370            'drop unless queried directly' AS hint,
5371            'fewer than two downstream dependencies: ' AS details,
5372            maintained_children AS justification
5373        FROM objects_with_justification
5374        WHERE type = 'view' AND NOT indexes = '{}'::text list AND justification IS NULL
5375
5376        UNION ALL
5377
5378        -- index on a leaf of the dependency graph
5379        SELECT
5380            unnest(indexes) AS id,
5381            'drop unless queried directly' AS hint,
5382            'associated object does not have any dependencies (maintained or not maintained)' AS details,
5383            NULL::text list AS justification
5384        FROM objects_with_justification
5385        -- indexes can only be part of justification for leaf nodes
5386        WHERE type IN ('view', 'materialized-view') AND NOT indexes = '{}'::text list AND justification @> indexes
5387
5388        UNION ALL
5389
5390        -- index on a source
5391        SELECT
5392            unnest(indexes) AS id,
5393            'drop unless queried directly' AS hint,
5394            'sources do not transform data and can expose data directly' AS details,
5395            NULL::text list AS justification
5396        FROM objects_with_justification
5397        -- indexes can only be part of justification for leaf nodes
5398        WHERE type = 'source' AND NOT indexes = '{}'::text list
5399
5400        UNION ALL
5401
5402        -- indexes on views inside the dependency graph
5403        SELECT
5404            unnest(indexes) AS id,
5405            'keep' AS hint,
5406            'multiple downstream dependencies: ' AS details,
5407            justification
5408        FROM objects_with_justification
5409        -- indexes can only be part of justification for leaf nodes
5410        WHERE type = 'view' AND justification IS NOT NULL AND NOT indexes = '{}'::text list AND NOT justification @> indexes
5411    ),
5412
5413    hints_resolved_ids(id text, hint text, details text, justification text list) AS (
5414        SELECT
5415            h.id,
5416            h.hint,
5417            h.details || list_agg(o.name)::text AS details,
5418            h.justification
5419        FROM hints AS h,
5420            LATERAL unnest(h.justification) j
5421        JOIN mz_catalog.mz_objects AS o
5422            ON (o.id = j)
5423        GROUP BY h.id, h.hint, h.details, h.justification
5424
5425        UNION ALL
5426
5427        SELECT
5428            id,
5429            hint,
5430            details,
5431            justification
5432        FROM hints
5433        WHERE justification IS NULL
5434    )
5435
5436SELECT
5437    h.id AS object_id,
5438    h.hint AS hint,
5439    h.details,
5440    h.justification AS referenced_object_ids
5441FROM hints_resolved_ids AS h",
5442        access: vec![PUBLIC_SELECT],
5443        ontology: None,
5444    }
5445});
5446
5447/// Peeled version of `PG_AUTHID`: Excludes the columns rolcreaterole and rolcreatedb, to make this
5448/// view indexable.
5449pub static PG_AUTHID_CORE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5450    name: "pg_authid_core",
5451    schema: MZ_INTERNAL_SCHEMA,
5452    oid: oid::VIEW_PG_AUTHID_CORE_OID,
5453    desc: RelationDesc::builder()
5454        .with_column("oid", SqlScalarType::Oid.nullable(false))
5455        .with_column("rolname", SqlScalarType::String.nullable(false))
5456        .with_column("rolsuper", SqlScalarType::Bool.nullable(true))
5457        .with_column("rolinherit", SqlScalarType::Bool.nullable(false))
5458        .with_column("rolcanlogin", SqlScalarType::Bool.nullable(false))
5459        .with_column("rolreplication", SqlScalarType::Bool.nullable(false))
5460        .with_column("rolbypassrls", SqlScalarType::Bool.nullable(false))
5461        .with_column("rolconnlimit", SqlScalarType::Int32.nullable(false))
5462        .with_column("rolpassword", SqlScalarType::String.nullable(true))
5463        .with_column(
5464            "rolvaliduntil",
5465            SqlScalarType::TimestampTz { precision: None }.nullable(true),
5466        )
5467        .finish(),
5468    column_comments: BTreeMap::new(),
5469    sql: r#"
5470SELECT
5471    r.oid AS oid,
5472    r.name AS rolname,
5473    rolsuper,
5474    inherit AS rolinherit,
5475    COALESCE(r.rolcanlogin, false) AS rolcanlogin,
5476    -- MZ doesn't support replication in the same way Postgres does
5477    false AS rolreplication,
5478    -- MZ doesn't how row level security
5479    false AS rolbypassrls,
5480    -- MZ doesn't have a connection limit
5481    -1 AS rolconnlimit,
5482    a.password_hash AS rolpassword,
5483    NULL::pg_catalog.timestamptz AS rolvaliduntil
5484FROM mz_catalog.mz_roles r
5485LEFT JOIN mz_catalog.mz_role_auth a ON r.oid = a.role_oid"#,
5486    access: vec![rbac::owner_privilege(ObjectType::Table, MZ_SYSTEM_ROLE_ID)],
5487    ontology: None,
5488});
5489
5490pub const PG_AUTHID_CORE_IND: BuiltinIndex = BuiltinIndex {
5491    name: "pg_authid_core_ind",
5492    schema: MZ_INTERNAL_SCHEMA,
5493    oid: oid::INDEX_PG_AUTHID_CORE_IND_OID,
5494    sql: "IN CLUSTER mz_catalog_server
5495ON mz_internal.pg_authid_core (rolname)",
5496    is_retained_metrics_object: false,
5497};
5498
5499pub static MZ_SHOW_ALL_OBJECTS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5500    name: "mz_show_all_objects",
5501    schema: MZ_INTERNAL_SCHEMA,
5502    oid: oid::VIEW_MZ_SHOW_ALL_OBJECTS_OID,
5503    desc: RelationDesc::builder()
5504        .with_column("schema_id", SqlScalarType::String.nullable(false))
5505        .with_column("name", SqlScalarType::String.nullable(false))
5506        .with_column("type", SqlScalarType::String.nullable(false))
5507        .with_column("comment", SqlScalarType::String.nullable(false))
5508        .finish(),
5509    column_comments: BTreeMap::new(),
5510    sql: "WITH comments AS (
5511        SELECT id, object_type, comment
5512        FROM mz_internal.mz_comments
5513        WHERE object_sub_id IS NULL
5514    )
5515    SELECT schema_id, name, type, COALESCE(comment, '') AS comment
5516    FROM mz_catalog.mz_objects AS objs
5517    LEFT JOIN comments ON objs.id = comments.id AND comments.object_type = objs.type",
5518    access: vec![PUBLIC_SELECT],
5519    ontology: None,
5520});
5521
5522pub static MZ_SHOW_CLUSTERS: LazyLock<BuiltinView> = LazyLock::new(|| {
5523    BuiltinView {
5524    name: "mz_show_clusters",
5525    schema: MZ_INTERNAL_SCHEMA,
5526    oid: oid::VIEW_MZ_SHOW_CLUSTERS_OID,
5527    desc: RelationDesc::builder()
5528        .with_column("name", SqlScalarType::String.nullable(false))
5529        .with_column("replicas", SqlScalarType::String.nullable(true))
5530        // One-line summary of any in-flight reconfiguration or burst, NULL
5531        // when the cluster is steady.
5532        .with_column("activity", SqlScalarType::String.nullable(true))
5533        .with_column("comment", SqlScalarType::String.nullable(false))
5534        .finish(),
5535    column_comments: BTreeMap::new(),
5536    // Settled reconfiguration records are retained, so match only
5537    // `in-progress`. A non-null auto-scaling `state` means a live burst.
5538    // The reconfiguration summary names only the dimensions the record
5539    // actually changes (from `changes`), with values where they read well.
5540    // NOTE: `||` with a NULL operand nulls the whole summary. `burst_size`
5541    // is a non-optional field of its record, keep it that way or COALESCE.
5542    // The NULLIF guards an empty diff (not expected in-progress), which
5543    // otherwise would render a dangling 'reconfiguring'.
5544    // Neither input needs `mz_now()`, keeping this indexed view non-temporal.
5545    sql: "
5546    WITH clusters AS (
5547        SELECT
5548            mc.id,
5549            mc.name,
5550            pg_catalog.string_agg(mcr.name || ' (' || mcr.size || ')', ', ' ORDER BY mcr.name) AS replicas
5551        FROM mz_catalog.mz_clusters mc
5552        LEFT JOIN mz_catalog.mz_cluster_replicas mcr
5553        ON mc.id = mcr.cluster_id
5554        GROUP BY mc.id, mc.name
5555    ),
5556    comments AS (
5557        SELECT id, comment
5558        FROM mz_internal.mz_comments
5559        WHERE object_type = 'cluster' AND object_sub_id IS NULL
5560    ),
5561    reconfigurations AS (
5562        SELECT
5563            cluster_id,
5564            'reconfiguring ' || NULLIF(array_to_string(ARRAY[
5565                'size to ' || (changes->>'size'),
5566                'replication factor to ' || (changes->>'replication_factor'),
5567                CASE WHEN changes->'availability_zones' IS NOT NULL THEN 'availability zones' END,
5568                CASE WHEN changes->'logging' IS NOT NULL THEN 'introspection settings' END
5569            ], ', '), '') AS summary
5570        FROM mz_internal.mz_cluster_reconfigurations
5571        WHERE status = 'in-progress'
5572    )
5573    SELECT
5574        name,
5575        replicas,
5576        CASE
5577            WHEN recon.summary IS NOT NULL AND scaling.state IS NOT NULL
5578                THEN recon.summary
5579                     || '; hydration burst at ' || (scaling.state->'burst'->>'burst_size')
5580            WHEN recon.summary IS NOT NULL
5581                THEN recon.summary
5582            WHEN scaling.state IS NOT NULL
5583                THEN 'hydration burst at ' || (scaling.state->'burst'->>'burst_size')
5584            ELSE NULL
5585        END AS activity,
5586        COALESCE(comment, '') as comment
5587    FROM clusters
5588    LEFT JOIN comments ON clusters.id = comments.id
5589    LEFT JOIN reconfigurations recon
5590        ON clusters.id = recon.cluster_id
5591    LEFT JOIN mz_internal.mz_cluster_auto_scaling_strategies scaling ON clusters.id = scaling.cluster_id",
5592    access: vec![PUBLIC_SELECT],
5593    ontology: None,
5594}
5595});
5596
5597pub static MZ_SHOW_SECRETS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5598    name: "mz_show_secrets",
5599    schema: MZ_INTERNAL_SCHEMA,
5600    oid: oid::VIEW_MZ_SHOW_SECRETS_OID,
5601    desc: RelationDesc::builder()
5602        .with_column("schema_id", SqlScalarType::String.nullable(false))
5603        .with_column("name", SqlScalarType::String.nullable(false))
5604        .with_column("comment", SqlScalarType::String.nullable(false))
5605        .finish(),
5606    column_comments: BTreeMap::new(),
5607    sql: "WITH comments AS (
5608        SELECT id, comment
5609        FROM mz_internal.mz_comments
5610        WHERE object_type = 'secret' AND object_sub_id IS NULL
5611    )
5612    SELECT schema_id, name, COALESCE(comment, '') as comment
5613    FROM mz_catalog.mz_secrets secrets
5614    LEFT JOIN comments ON secrets.id = comments.id",
5615    access: vec![PUBLIC_SELECT],
5616    ontology: None,
5617});
5618
5619pub static MZ_SHOW_COLUMNS: LazyLock<BuiltinView> = LazyLock::new(|| {
5620    BuiltinView {
5621    name: "mz_show_columns",
5622    schema: MZ_INTERNAL_SCHEMA,
5623    oid: oid::VIEW_MZ_SHOW_COLUMNS_OID,
5624    desc: RelationDesc::builder()
5625        .with_column("id", SqlScalarType::String.nullable(false))
5626        .with_column("name", SqlScalarType::String.nullable(false))
5627        .with_column("nullable", SqlScalarType::Bool.nullable(false))
5628        .with_column("type", SqlScalarType::String.nullable(false))
5629        .with_column("position", SqlScalarType::UInt64.nullable(false))
5630        .with_column("comment", SqlScalarType::String.nullable(false))
5631        .finish(),
5632    column_comments: BTreeMap::new(),
5633    // The `object_type` predicate on the comment join guards against
5634    // stale comment rows that can survive when a builtin's type changes
5635    // but its catalog id is preserved (e.g. a Table → MaterializedView
5636    // schema migration). Without it, a column would match both the old
5637    // and new object_type rows and each row would be emitted twice.
5638    sql: "
5639    SELECT columns.id, columns.name, columns.nullable, columns.type, columns.position, COALESCE(comment, '') as comment
5640    FROM mz_catalog.mz_columns columns
5641    LEFT JOIN mz_catalog.mz_objects obj ON obj.id = columns.id
5642    LEFT JOIN mz_internal.mz_comments comments
5643    ON columns.id = comments.id
5644       AND columns.position = comments.object_sub_id
5645       AND comments.object_type = obj.type",
5646    access: vec![PUBLIC_SELECT],
5647    ontology: None,
5648}
5649});
5650
5651pub static MZ_SHOW_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5652    name: "mz_show_databases",
5653    schema: MZ_INTERNAL_SCHEMA,
5654    oid: oid::VIEW_MZ_SHOW_DATABASES_OID,
5655    desc: RelationDesc::builder()
5656        .with_column("name", SqlScalarType::String.nullable(false))
5657        .with_column("comment", SqlScalarType::String.nullable(false))
5658        .finish(),
5659    column_comments: BTreeMap::new(),
5660    sql: "WITH comments AS (
5661        SELECT id, comment
5662        FROM mz_internal.mz_comments
5663        WHERE object_type = 'database' AND object_sub_id IS NULL
5664    )
5665    SELECT name, COALESCE(comment, '') as comment
5666    FROM mz_catalog.mz_databases databases
5667    LEFT JOIN comments ON databases.id = comments.id",
5668    access: vec![PUBLIC_SELECT],
5669    ontology: None,
5670});
5671
5672pub static MZ_SHOW_SCHEMAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5673    name: "mz_show_schemas",
5674    schema: MZ_INTERNAL_SCHEMA,
5675    oid: oid::VIEW_MZ_SHOW_SCHEMAS_OID,
5676    desc: RelationDesc::builder()
5677        .with_column("database_id", SqlScalarType::String.nullable(true))
5678        .with_column("name", SqlScalarType::String.nullable(false))
5679        .with_column("comment", SqlScalarType::String.nullable(false))
5680        .finish(),
5681    column_comments: BTreeMap::new(),
5682    sql: "WITH comments AS (
5683        SELECT id, comment
5684        FROM mz_internal.mz_comments
5685        WHERE object_type = 'schema' AND object_sub_id IS NULL
5686    )
5687    SELECT database_id, name, COALESCE(comment, '') as comment
5688    FROM mz_catalog.mz_schemas schemas
5689    LEFT JOIN comments ON schemas.id = comments.id",
5690    access: vec![PUBLIC_SELECT],
5691    ontology: None,
5692});
5693
5694pub static MZ_SHOW_ROLES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5695    name: "mz_show_roles",
5696    schema: MZ_INTERNAL_SCHEMA,
5697    oid: oid::VIEW_MZ_SHOW_ROLES_OID,
5698    desc: RelationDesc::builder()
5699        .with_column("name", SqlScalarType::String.nullable(false))
5700        .with_column("comment", SqlScalarType::String.nullable(false))
5701        .finish(),
5702    column_comments: BTreeMap::new(),
5703    sql: "WITH comments AS (
5704        SELECT id, comment
5705        FROM mz_internal.mz_comments
5706        WHERE object_type = 'role' AND object_sub_id IS NULL
5707    )
5708    SELECT name, COALESCE(comment, '') as comment
5709    FROM mz_catalog.mz_roles roles
5710    LEFT JOIN comments ON roles.id = comments.id
5711    WHERE roles.id NOT LIKE 's%'
5712      AND roles.id NOT LIKE 'g%'",
5713    access: vec![PUBLIC_SELECT],
5714    ontology: None,
5715});
5716
5717pub static MZ_SHOW_TABLES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5718    name: "mz_show_tables",
5719    schema: MZ_INTERNAL_SCHEMA,
5720    oid: oid::VIEW_MZ_SHOW_TABLES_OID,
5721    desc: RelationDesc::builder()
5722        .with_column("schema_id", SqlScalarType::String.nullable(false))
5723        .with_column("name", SqlScalarType::String.nullable(false))
5724        .with_column("comment", SqlScalarType::String.nullable(false))
5725        .with_column("source_id", SqlScalarType::String.nullable(true))
5726        .finish(),
5727    column_comments: BTreeMap::new(),
5728    sql: "WITH comments AS (
5729        SELECT id, comment
5730        FROM mz_internal.mz_comments
5731        WHERE object_type = 'table' AND object_sub_id IS NULL
5732    )
5733    SELECT schema_id, name, COALESCE(comment, '') as comment, source_id
5734    FROM mz_catalog.mz_tables tables
5735    LEFT JOIN comments ON tables.id = comments.id",
5736    access: vec![PUBLIC_SELECT],
5737    ontology: None,
5738});
5739
5740pub static MZ_SHOW_VIEWS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5741    name: "mz_show_views",
5742    schema: MZ_INTERNAL_SCHEMA,
5743    oid: oid::VIEW_MZ_SHOW_VIEWS_OID,
5744    desc: RelationDesc::builder()
5745        .with_column("schema_id", SqlScalarType::String.nullable(false))
5746        .with_column("name", SqlScalarType::String.nullable(false))
5747        .with_column("comment", SqlScalarType::String.nullable(false))
5748        .finish(),
5749    column_comments: BTreeMap::new(),
5750    sql: "WITH comments AS (
5751        SELECT id, comment
5752        FROM mz_internal.mz_comments
5753        WHERE object_type = 'view' AND object_sub_id IS NULL
5754    )
5755    SELECT schema_id, name, COALESCE(comment, '') as comment
5756    FROM mz_catalog.mz_views views
5757    LEFT JOIN comments ON views.id = comments.id",
5758    access: vec![PUBLIC_SELECT],
5759    ontology: None,
5760});
5761
5762pub static MZ_SHOW_TYPES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5763    name: "mz_show_types",
5764    schema: MZ_INTERNAL_SCHEMA,
5765    oid: oid::VIEW_MZ_SHOW_TYPES_OID,
5766    desc: RelationDesc::builder()
5767        .with_column("schema_id", SqlScalarType::String.nullable(false))
5768        .with_column("name", SqlScalarType::String.nullable(false))
5769        .with_column("comment", SqlScalarType::String.nullable(false))
5770        .finish(),
5771    column_comments: BTreeMap::new(),
5772    sql: "WITH comments AS (
5773        SELECT id, comment
5774        FROM mz_internal.mz_comments
5775        WHERE object_type = 'type' AND object_sub_id IS NULL
5776    )
5777    SELECT schema_id, name, COALESCE(comment, '') as comment
5778    FROM mz_catalog.mz_types types
5779    LEFT JOIN comments ON types.id = comments.id",
5780    access: vec![PUBLIC_SELECT],
5781    ontology: None,
5782});
5783
5784pub static MZ_SHOW_CONNECTIONS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5785    name: "mz_show_connections",
5786    schema: MZ_INTERNAL_SCHEMA,
5787    oid: oid::VIEW_MZ_SHOW_CONNECTIONS_OID,
5788    desc: RelationDesc::builder()
5789        .with_column("schema_id", SqlScalarType::String.nullable(false))
5790        .with_column("name", SqlScalarType::String.nullable(false))
5791        .with_column("type", SqlScalarType::String.nullable(false))
5792        .with_column("comment", SqlScalarType::String.nullable(false))
5793        .finish(),
5794    column_comments: BTreeMap::new(),
5795    sql: "WITH comments AS (
5796        SELECT id, comment
5797        FROM mz_internal.mz_comments
5798        WHERE object_type = 'connection' AND object_sub_id IS NULL
5799    )
5800    SELECT schema_id, name, type, COALESCE(comment, '') as comment
5801    FROM mz_catalog.mz_connections connections
5802    LEFT JOIN comments ON connections.id = comments.id",
5803    access: vec![PUBLIC_SELECT],
5804    ontology: None,
5805});
5806
5807pub static MZ_SHOW_SOURCES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5808    name: "mz_show_sources",
5809    schema: MZ_INTERNAL_SCHEMA,
5810    oid: oid::VIEW_MZ_SHOW_SOURCES_OID,
5811    desc: RelationDesc::builder()
5812        .with_column("id", SqlScalarType::String.nullable(false))
5813        .with_column("name", SqlScalarType::String.nullable(false))
5814        .with_column("type", SqlScalarType::String.nullable(false))
5815        .with_column("cluster", SqlScalarType::String.nullable(true))
5816        .with_column("schema_id", SqlScalarType::String.nullable(false))
5817        .with_column("cluster_id", SqlScalarType::String.nullable(true))
5818        .with_column("comment", SqlScalarType::String.nullable(false))
5819        .finish(),
5820    column_comments: BTreeMap::new(),
5821    sql: "
5822WITH comments AS (
5823    SELECT id, comment
5824    FROM mz_internal.mz_comments
5825    WHERE object_type = 'source' AND object_sub_id IS NULL
5826)
5827SELECT
5828    sources.id,
5829    sources.name,
5830    sources.type,
5831    clusters.name AS cluster,
5832    schema_id,
5833    cluster_id,
5834    COALESCE(comments.comment, '') as comment
5835FROM
5836    mz_catalog.mz_sources AS sources
5837        LEFT JOIN
5838            mz_catalog.mz_clusters AS clusters
5839            ON clusters.id = sources.cluster_id
5840        LEFT JOIN comments ON sources.id = comments.id",
5841    access: vec![PUBLIC_SELECT],
5842    ontology: None,
5843});
5844
5845pub static MZ_SHOW_SINKS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5846    name: "mz_show_sinks",
5847    schema: MZ_INTERNAL_SCHEMA,
5848    oid: oid::VIEW_MZ_SHOW_SINKS_OID,
5849    desc: RelationDesc::builder()
5850        .with_column("id", SqlScalarType::String.nullable(false))
5851        .with_column("name", SqlScalarType::String.nullable(false))
5852        .with_column("type", SqlScalarType::String.nullable(false))
5853        .with_column("cluster", SqlScalarType::String.nullable(false))
5854        .with_column("schema_id", SqlScalarType::String.nullable(false))
5855        .with_column("cluster_id", SqlScalarType::String.nullable(false))
5856        .with_column("comment", SqlScalarType::String.nullable(false))
5857        .finish(),
5858    column_comments: BTreeMap::new(),
5859    sql: "
5860WITH comments AS (
5861    SELECT id, comment
5862    FROM mz_internal.mz_comments
5863    WHERE object_type = 'sink' AND object_sub_id IS NULL
5864)
5865SELECT
5866    sinks.id,
5867    sinks.name,
5868    sinks.type,
5869    clusters.name AS cluster,
5870    schema_id,
5871    cluster_id,
5872    COALESCE(comments.comment, '') as comment
5873FROM
5874    mz_catalog.mz_sinks AS sinks
5875    JOIN
5876        mz_catalog.mz_clusters AS clusters
5877        ON clusters.id = sinks.cluster_id
5878    LEFT JOIN comments ON sinks.id = comments.id",
5879    access: vec![PUBLIC_SELECT],
5880    ontology: None,
5881});
5882
5883pub static MZ_SHOW_MATERIALIZED_VIEWS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5884    name: "mz_show_materialized_views",
5885    schema: MZ_INTERNAL_SCHEMA,
5886    oid: oid::VIEW_MZ_SHOW_MATERIALIZED_VIEWS_OID,
5887    desc: RelationDesc::builder()
5888        .with_column("id", SqlScalarType::String.nullable(false))
5889        .with_column("name", SqlScalarType::String.nullable(false))
5890        .with_column("cluster", SqlScalarType::String.nullable(false))
5891        .with_column("schema_id", SqlScalarType::String.nullable(false))
5892        .with_column("cluster_id", SqlScalarType::String.nullable(false))
5893        .with_column("comment", SqlScalarType::String.nullable(false))
5894        .finish(),
5895    column_comments: BTreeMap::new(),
5896    sql: "
5897WITH
5898    comments AS (
5899        SELECT id, comment
5900        FROM mz_internal.mz_comments
5901        WHERE object_type = 'materialized-view' AND object_sub_id IS NULL
5902    )
5903SELECT
5904    mviews.id as id,
5905    mviews.name,
5906    clusters.name AS cluster,
5907    schema_id,
5908    cluster_id,
5909    COALESCE(comments.comment, '') as comment
5910FROM
5911    mz_catalog.mz_materialized_views AS mviews
5912    JOIN mz_catalog.mz_clusters AS clusters ON clusters.id = mviews.cluster_id
5913    LEFT JOIN comments ON mviews.id = comments.id",
5914    access: vec![PUBLIC_SELECT],
5915    ontology: None,
5916});
5917
5918pub static MZ_SHOW_INDEXES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5919    name: "mz_show_indexes",
5920    schema: MZ_INTERNAL_SCHEMA,
5921    oid: oid::VIEW_MZ_SHOW_INDEXES_OID,
5922    desc: RelationDesc::builder()
5923        .with_column("id", SqlScalarType::String.nullable(false))
5924        .with_column("name", SqlScalarType::String.nullable(false))
5925        .with_column("on", SqlScalarType::String.nullable(false))
5926        .with_column("cluster", SqlScalarType::String.nullable(false))
5927        .with_column(
5928            "key",
5929            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
5930        )
5931        .with_column("on_id", SqlScalarType::String.nullable(false))
5932        .with_column("schema_id", SqlScalarType::String.nullable(false))
5933        .with_column("cluster_id", SqlScalarType::String.nullable(false))
5934        .with_column("comment", SqlScalarType::String.nullable(false))
5935        .finish(),
5936    column_comments: BTreeMap::new(),
5937    sql: "
5938WITH comments AS (
5939    SELECT id, comment
5940    FROM mz_internal.mz_comments
5941    WHERE object_type = 'index' AND object_sub_id IS NULL
5942)
5943SELECT
5944    idxs.id AS id,
5945    idxs.name AS name,
5946    objs.name AS on,
5947    clusters.name AS cluster,
5948    COALESCE(keys.key, '{}'::_text) AS key,
5949    idxs.on_id AS on_id,
5950    objs.schema_id AS schema_id,
5951    clusters.id AS cluster_id,
5952    COALESCE(comments.comment, '') as comment
5953FROM
5954    mz_catalog.mz_indexes AS idxs
5955    JOIN mz_catalog.mz_objects AS objs ON idxs.on_id = objs.id
5956    JOIN mz_catalog.mz_clusters AS clusters ON clusters.id = idxs.cluster_id
5957    LEFT JOIN
5958        (SELECT
5959            idxs.id,
5960            ARRAY_AGG(
5961                CASE
5962                    WHEN idx_cols.on_expression IS NULL THEN obj_cols.name
5963                    ELSE idx_cols.on_expression
5964                END
5965                ORDER BY idx_cols.index_position ASC
5966            ) AS key
5967        FROM
5968            mz_catalog.mz_indexes AS idxs
5969            JOIN mz_catalog.mz_index_columns idx_cols ON idxs.id = idx_cols.index_id
5970            LEFT JOIN mz_catalog.mz_columns obj_cols ON
5971                idxs.on_id = obj_cols.id AND idx_cols.on_position = obj_cols.position
5972        GROUP BY idxs.id) AS keys
5973    ON idxs.id = keys.id
5974    LEFT JOIN comments ON idxs.id = comments.id",
5975    access: vec![PUBLIC_SELECT],
5976    ontology: None,
5977});
5978
5979pub static MZ_SHOW_CLUSTER_REPLICAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5980    name: "mz_show_cluster_replicas",
5981    schema: MZ_INTERNAL_SCHEMA,
5982    oid: oid::VIEW_MZ_SHOW_CLUSTER_REPLICAS_OID,
5983    desc: RelationDesc::builder()
5984        .with_column("cluster", SqlScalarType::String.nullable(false))
5985        .with_column("replica", SqlScalarType::String.nullable(false))
5986        .with_column("replica_id", SqlScalarType::String.nullable(false))
5987        .with_column("size", SqlScalarType::String.nullable(true))
5988        .with_column("ready", SqlScalarType::Bool.nullable(false))
5989        .with_column("comment", SqlScalarType::String.nullable(false))
5990        .finish(),
5991    column_comments: BTreeMap::new(),
5992    sql: r#"SELECT
5993    mz_catalog.mz_clusters.name AS cluster,
5994    mz_catalog.mz_cluster_replicas.name AS replica,
5995    mz_catalog.mz_cluster_replicas.id as replica_id,
5996    mz_catalog.mz_cluster_replicas.size AS size,
5997    coalesce(statuses.ready, FALSE) AS ready,
5998    coalesce(comments.comment, '') as comment
5999FROM
6000    mz_catalog.mz_cluster_replicas
6001        JOIN mz_catalog.mz_clusters
6002            ON mz_catalog.mz_cluster_replicas.cluster_id = mz_catalog.mz_clusters.id
6003        LEFT JOIN
6004            (
6005                SELECT
6006                    replica_id,
6007                    bool_and(hydrated) AS ready
6008                FROM mz_internal.mz_hydration_statuses
6009                WHERE replica_id is not null
6010                GROUP BY replica_id
6011            ) AS statuses
6012            ON mz_catalog.mz_cluster_replicas.id = statuses.replica_id
6013        LEFT JOIN mz_internal.mz_comments comments
6014            ON mz_catalog.mz_cluster_replicas.id = comments.id
6015            AND comments.object_type = 'cluster-replica'
6016ORDER BY 1, 2"#,
6017    access: vec![PUBLIC_SELECT],
6018    ontology: None,
6019});
6020
6021/// Lightweight data product discovery for MCP (Model Context Protocol).
6022///
6023/// Lists materialized views and indexed views that the current user has
6024/// SELECT privileges on. Non-indexed regular views are excluded because
6025/// querying them would trigger a full recompute. Comments are optional
6026/// enrichment.
6027/// Used by the `get_data_products` and `read_data_product` MCP tools.
6028/// Does not include schema details: use `mz_mcp_data_product_details` for that.
6029pub static MZ_MCP_DATA_PRODUCTS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6030    name: "mz_mcp_data_products",
6031    schema: MZ_INTERNAL_SCHEMA,
6032    oid: oid::VIEW_MZ_MCP_DATA_PRODUCTS_OID,
6033    desc: RelationDesc::builder()
6034        .with_column("object_name", SqlScalarType::String.nullable(false))
6035        .with_column("cluster", SqlScalarType::String.nullable(true))
6036        .with_column("description", SqlScalarType::String.nullable(true))
6037        .with_key(vec![0, 1, 2])
6038        .finish(),
6039    column_comments: BTreeMap::from_iter([
6040        (
6041            "object_name",
6042            "Fully qualified object name (database.schema.name).",
6043        ),
6044        (
6045            "cluster",
6046            "Cluster hosting the object's index or compute. Reads still work from any cluster you can use, but only reads on this cluster benefit from the index. Shown only when your role has USAGE on it (otherwise null).",
6047        ),
6048        (
6049            "description",
6050            "Index comment if available, otherwise object comment. Used as data product description.",
6051        ),
6052    ]),
6053    // The `cluster` column is null unless the role has USAGE on the object's
6054    // index/compute cluster, so a data product never advertises a cluster the
6055    // role cannot actually run reads on (DEX-66). Materialized views stay
6056    // listed regardless because they serve from persist, so a read on any
6057    // cluster the role can use is safe. Plain indexed views require at least
6058    // one index cluster the role can use: without one, the default fallback
6059    // to the session cluster would recompute the view, which we deliberately
6060    // avoid (same reason non-indexed views are excluded above).
6061    sql: r#"
6062SELECT DISTINCT
6063    '"' || op.database || '"."' || op.schema || '"."' || op.name || '"' AS object_name,
6064    CASE WHEN cp.name IS NOT NULL THEN COALESCE(c_idx.name, c_obj.name) END AS cluster,
6065    COALESCE(cts_idx.comment, cts_obj.comment) AS description
6066FROM mz_internal.mz_show_my_object_privileges op
6067JOIN mz_objects o ON op.name = o.name AND op.object_type = o.type
6068JOIN mz_schemas s ON s.name = op.schema AND s.id = o.schema_id
6069JOIN mz_databases d ON d.name = op.database AND d.id = s.database_id
6070LEFT JOIN mz_indexes i ON i.on_id = o.id
6071LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6072LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6073LEFT JOIN mz_internal.mz_show_my_cluster_privileges cp
6074    ON cp.name = COALESCE(c_idx.name, c_obj.name) AND cp.privilege_type = 'USAGE'
6075LEFT JOIN mz_internal.mz_comments cts_idx ON cts_idx.id = i.id AND cts_idx.object_type = 'index' AND cts_idx.object_sub_id IS NULL
6076LEFT JOIN mz_internal.mz_comments cts_obj ON cts_obj.id = o.id AND cts_obj.object_type = o.type AND cts_obj.object_sub_id IS NULL
6077WHERE op.privilege_type = 'SELECT'
6078  AND (o.type = 'materialized-view'
6079       OR (o.type = 'view' AND i.id IS NOT NULL AND cp.name IS NOT NULL))
6080  AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6081"#,
6082    access: vec![PUBLIC_SELECT],
6083    ontology: None,
6084});
6085
6086/// Full data product details with JSON Schema for MCP agents.
6087///
6088/// Extends `mz_mcp_data_products` with column types, index keys (when
6089/// available), and column comments, formatted as a JSON Schema object.
6090/// Used by the `get_data_product_details` MCP tool. Lists materialized
6091/// views and indexed views; non-indexed regular views are excluded to
6092/// avoid triggering full recompute on query. Comments are optional
6093/// enrichment.
6094pub static MZ_MCP_DATA_PRODUCT_DETAILS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6095    name: "mz_mcp_data_product_details",
6096    schema: MZ_INTERNAL_SCHEMA,
6097    oid: oid::VIEW_MZ_MCP_DATA_PRODUCT_DETAILS_OID,
6098    // Note: no `.with_key` here. The view's row identity is semantically
6099    // (object_name, cluster, description) — same as the underlying details
6100    // CTE — but the planner can't prove key propagation through the
6101    // `LEFT JOIN ... ON ... IS NOT DISTINCT FROM` to the hydration CTE,
6102    // so declaring it here would diverge from the inferred RelationDesc
6103    // and fail `verify_builtin_descs`.
6104    desc: RelationDesc::builder()
6105        .with_column("object_name", SqlScalarType::String.nullable(false))
6106        .with_column("cluster", SqlScalarType::String.nullable(true))
6107        .with_column("description", SqlScalarType::String.nullable(true))
6108        .with_column("schema", SqlScalarType::Jsonb.nullable(false))
6109        .with_column("hydration", SqlScalarType::Jsonb.nullable(false))
6110        .finish(),
6111    column_comments: BTreeMap::from_iter([
6112        (
6113            "object_name",
6114            "Fully qualified object name (database.schema.name).",
6115        ),
6116        (
6117            "cluster",
6118            "Cluster hosting the object's index or compute. Reads still work from any cluster you can use, but only reads on this cluster benefit from the index. Shown only when your role has USAGE on it (otherwise null).",
6119        ),
6120        (
6121            "description",
6122            "Index comment if available, otherwise object comment. Used as data product description.",
6123        ),
6124        (
6125            "schema",
6126            "JSON Schema describing the object's columns and types.",
6127        ),
6128        (
6129            "hydration",
6130            "Readiness summary as a JSON object with `hydrated` (bool), `replica_count` (int), and `hydrated_replica_count` (int). `hydrated` is true only when the cluster has at least one replica and the dataflow is hydrated on every replica. Reads against a non-hydrated data product block until the dataflow catches up (they never return partial data). Check this before reading: if `hydrated` is false and `replica_count > 0`, wait and retry; if `replica_count` is 0, the cluster has no replicas and that needs operator action, not a retry.",
6131        ),
6132    ]),
6133    sql: r#"
6134WITH details_raw AS (
6135    SELECT
6136        '"' || op.database || '"."' || op.schema || '"."' || op.name || '"' AS object_name,
6137        COALESCE(c_idx.name, c_obj.name) AS cluster,
6138        COALESCE(cts_idx.comment, cts_obj.comment) AS description,
6139        COALESCE(jsonb_build_object(
6140        'type', 'object',
6141        'indexedColumns', jsonb_agg(distinct ccol.name) FILTER (WHERE ccol.position = ic.on_position),
6142        'properties', jsonb_strip_nulls(jsonb_object_agg(
6143            ccol.name,
6144            CASE
6145                WHEN ccol.type IN (
6146                    'uint2', 'uint4','uint8', 'int', 'integer', 'smallint',
6147                    'double', 'double precision', 'bigint', 'float',
6148                    'numeric', 'real'
6149                ) THEN jsonb_build_object(
6150                    'type', 'number',
6151                    'description', cts_col.comment
6152                )
6153                WHEN ccol.type = 'boolean' THEN jsonb_build_object(
6154                    'type', 'boolean',
6155                    'description', cts_col.comment
6156                )
6157                WHEN ccol.type = 'bytea' THEN jsonb_build_object(
6158                    'type', 'string',
6159                    'description', cts_col.comment,
6160                    'contentEncoding', 'base64',
6161                    'contentMediaType', 'application/octet-stream'
6162                )
6163                WHEN ccol.type = 'date' THEN jsonb_build_object(
6164                    'type', 'string',
6165                    'format', 'date',
6166                    'description', cts_col.comment
6167                )
6168                WHEN ccol.type = 'time' THEN jsonb_build_object(
6169                    'type', 'string',
6170                    'format', 'time',
6171                    'description', cts_col.comment
6172                )
6173                WHEN ccol.type ilike 'timestamp%%' THEN jsonb_build_object(
6174                    'type', 'string',
6175                    'format', 'date-time',
6176                    'description', cts_col.comment
6177                )
6178                WHEN ccol.type = 'jsonb' THEN jsonb_build_object(
6179                    'type', 'object',
6180                    'description', cts_col.comment
6181                )
6182                WHEN ccol.type = 'uuid' THEN jsonb_build_object(
6183                    'type', 'string',
6184                    'format', 'uuid',
6185                    'description', cts_col.comment
6186                )
6187                ELSE jsonb_build_object(
6188                    'type', 'string',
6189                    'description', cts_col.comment
6190                )
6191            END
6192        ))
6193    ), '{"type": "object", "properties": {}}'::jsonb) AS schema
6194FROM mz_internal.mz_show_my_object_privileges op
6195JOIN mz_objects o ON op.name = o.name AND op.object_type = o.type
6196JOIN mz_schemas s ON s.name = op.schema AND s.id = o.schema_id
6197JOIN mz_databases d ON d.name = op.database AND d.id = s.database_id
6198JOIN mz_columns ccol ON ccol.id = o.id
6199LEFT JOIN mz_indexes i ON i.on_id = o.id
6200LEFT JOIN mz_index_columns ic ON i.id = ic.index_id
6201LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6202LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6203LEFT JOIN mz_internal.mz_show_my_cluster_privileges cp
6204    ON cp.name = COALESCE(c_idx.name, c_obj.name) AND cp.privilege_type = 'USAGE'
6205LEFT JOIN mz_internal.mz_comments cts_idx ON cts_idx.id = i.id AND cts_idx.object_type = 'index' AND cts_idx.object_sub_id IS NULL
6206LEFT JOIN mz_internal.mz_comments cts_obj ON cts_obj.id = o.id AND cts_obj.object_type = o.type AND cts_obj.object_sub_id IS NULL
6207LEFT JOIN mz_internal.mz_comments cts_col ON cts_col.id = o.id AND cts_col.object_type = o.type AND cts_col.object_sub_id = ccol.position
6208WHERE op.privilege_type = 'SELECT'
6209  AND (o.type = 'materialized-view'
6210       OR (o.type = 'view' AND i.id IS NOT NULL AND cp.name IS NOT NULL))
6211  AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6212GROUP BY 1, 2, 3
6213),
6214-- Pick the right (object_id, cluster_id) for hydration: the index's id +
6215-- cluster when an index exists (its arrangement is what the data product
6216-- reads from), otherwise the materialized view's own id + cluster.
6217hydration_meta AS (
6218    SELECT DISTINCT
6219        '"' || db.name || '"."' || s.name || '"."' || o.name || '"' AS object_name,
6220        COALESCE(c_idx.name, c_obj.name) AS cluster,
6221        COALESCE(i.id, o.id) AS hydration_object_id,
6222        COALESCE(i.cluster_id, o.cluster_id) AS cluster_id
6223    FROM mz_objects o
6224    JOIN mz_schemas s ON s.id = o.schema_id
6225    JOIN mz_databases db ON db.id = s.database_id
6226    LEFT JOIN mz_indexes i ON i.on_id = o.id
6227    LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6228    LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6229    WHERE (o.type = 'materialized-view' OR (o.type = 'view' AND i.id IS NOT NULL))
6230      AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6231),
6232-- Dedupe by replica before counting: an MV with multiple indexes on the
6233-- same cluster has multiple rows in `hydration_meta`, and joining each
6234-- of them against `mz_cluster_replicas` would otherwise inflate the
6235-- counts by the number of indexes. A replica is "hydrated" only when
6236-- every index dataflow for this data product is hydrated on it.
6237hydration_per_replica AS (
6238    SELECT
6239        m.object_name,
6240        m.cluster,
6241        r.id AS replica_id,
6242        bool_and(COALESCE(h.hydrated, false)) AS replica_hydrated
6243    FROM hydration_meta m
6244    LEFT JOIN mz_catalog.mz_cluster_replicas r ON r.cluster_id = m.cluster_id
6245    LEFT JOIN mz_internal.mz_hydration_statuses h
6246        ON h.replica_id = r.id AND h.object_id = m.hydration_object_id
6247    GROUP BY m.object_name, m.cluster, r.id
6248),
6249hydration AS (
6250    SELECT
6251        object_name,
6252        cluster,
6253        COUNT(replica_id)::int AS replica_count,
6254        COUNT(replica_id) FILTER (WHERE replica_hydrated)::int AS hydrated_replica_count
6255    FROM hydration_per_replica
6256    GROUP BY object_name, cluster
6257)
6258SELECT
6259    d.object_name,
6260    -- Null the advertised cluster unless the role has USAGE on it (DEX-66),
6261    -- matching mz_mcp_data_products. Hydration below still joins on the real
6262    -- d.cluster, so readiness is reported accurately even when the name is
6263    -- hidden.
6264    CASE WHEN EXISTS (
6265        SELECT 1 FROM mz_internal.mz_show_my_cluster_privileges cp
6266        WHERE cp.name = d.cluster AND cp.privilege_type = 'USAGE'
6267    ) THEN d.cluster END AS cluster,
6268    d.description,
6269    d.schema,
6270    jsonb_build_object(
6271        'hydrated',
6272        COALESCE(h.replica_count > 0 AND h.hydrated_replica_count = h.replica_count, false),
6273        'replica_count', COALESCE(h.replica_count, 0),
6274        'hydrated_replica_count', COALESCE(h.hydrated_replica_count, 0)
6275    ) AS hydration
6276FROM details_raw d
6277LEFT JOIN hydration h
6278    ON h.object_name = d.object_name
6279   AND h.cluster IS NOT DISTINCT FROM d.cluster
6280"#,
6281    access: vec![PUBLIC_SELECT],
6282    ontology: None,
6283});
6284
6285pub static MZ_SHOW_ROLE_MEMBERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6286    name: "mz_show_role_members",
6287    schema: MZ_INTERNAL_SCHEMA,
6288    oid: oid::VIEW_MZ_SHOW_ROLE_MEMBERS_OID,
6289    desc: RelationDesc::builder()
6290        .with_column("role", SqlScalarType::String.nullable(false))
6291        .with_column("member", SqlScalarType::String.nullable(false))
6292        .with_column("grantor", SqlScalarType::String.nullable(false))
6293        .finish(),
6294    column_comments: BTreeMap::from_iter([
6295        ("role", "The role that `member` is a member of."),
6296        ("member", "The role that is a member of `role`."),
6297        (
6298            "grantor",
6299            "The role that granted membership of `member` to `role`.",
6300        ),
6301    ]),
6302    sql: r#"SELECT
6303    r1.name AS role,
6304    r2.name AS member,
6305    r3.name AS grantor
6306FROM mz_catalog.mz_role_members rm
6307JOIN mz_catalog.mz_roles r1 ON r1.id = rm.role_id
6308JOIN mz_catalog.mz_roles r2 ON r2.id = rm.member
6309JOIN mz_catalog.mz_roles r3 ON r3.id = rm.grantor
6310ORDER BY role"#,
6311    access: vec![PUBLIC_SELECT],
6312    ontology: None,
6313});
6314
6315pub static MZ_SHOW_MY_ROLE_MEMBERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6316    name: "mz_show_my_role_members",
6317    schema: MZ_INTERNAL_SCHEMA,
6318    oid: oid::VIEW_MZ_SHOW_MY_ROLE_MEMBERS_OID,
6319    desc: RelationDesc::builder()
6320        .with_column("role", SqlScalarType::String.nullable(false))
6321        .with_column("member", SqlScalarType::String.nullable(false))
6322        .with_column("grantor", SqlScalarType::String.nullable(false))
6323        .finish(),
6324    column_comments: BTreeMap::from_iter([
6325        ("role", "The role that `member` is a member of."),
6326        ("member", "The role that is a member of `role`."),
6327        (
6328            "grantor",
6329            "The role that granted membership of `member` to `role`.",
6330        ),
6331    ]),
6332    sql: r#"SELECT role, member, grantor
6333FROM mz_internal.mz_show_role_members
6334WHERE pg_has_role(member, 'USAGE')"#,
6335    access: vec![PUBLIC_SELECT],
6336    ontology: None,
6337});
6338
6339pub static MZ_SHOW_SYSTEM_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6340    name: "mz_show_system_privileges",
6341    schema: MZ_INTERNAL_SCHEMA,
6342    oid: oid::VIEW_MZ_SHOW_SYSTEM_PRIVILEGES_OID,
6343    desc: RelationDesc::builder()
6344        .with_column("grantor", SqlScalarType::String.nullable(true))
6345        .with_column("grantee", SqlScalarType::String.nullable(true))
6346        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6347        .finish(),
6348    column_comments: BTreeMap::from_iter([
6349        ("grantor", "The role that granted the privilege."),
6350        ("grantee", "The role that the privilege was granted to."),
6351        ("privilege_type", "They type of privilege granted."),
6352    ]),
6353    sql: r#"SELECT
6354    grantor.name AS grantor,
6355    CASE privileges.grantee
6356        WHEN 'p' THEN 'PUBLIC'
6357        ELSE grantee.name
6358    END AS grantee,
6359    privileges.privilege_type AS privilege_type
6360FROM
6361    (SELECT mz_internal.mz_aclexplode(ARRAY[privileges]).*
6362    FROM mz_catalog.mz_system_privileges) AS privileges
6363LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6364LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6365WHERE privileges.grantee NOT LIKE 's%'"#,
6366    access: vec![PUBLIC_SELECT],
6367    ontology: None,
6368});
6369
6370pub static MZ_SHOW_MY_SYSTEM_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6371    name: "mz_show_my_system_privileges",
6372    schema: MZ_INTERNAL_SCHEMA,
6373    oid: oid::VIEW_MZ_SHOW_MY_SYSTEM_PRIVILEGES_OID,
6374    desc: RelationDesc::builder()
6375        .with_column("grantor", SqlScalarType::String.nullable(true))
6376        .with_column("grantee", SqlScalarType::String.nullable(true))
6377        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6378        .finish(),
6379    column_comments: BTreeMap::from_iter([
6380        ("grantor", "The role that granted the privilege."),
6381        ("grantee", "The role that the privilege was granted to."),
6382        ("privilege_type", "They type of privilege granted."),
6383    ]),
6384    sql: r#"SELECT grantor, grantee, privilege_type
6385FROM mz_internal.mz_show_system_privileges
6386WHERE
6387    CASE
6388        WHEN grantee = 'PUBLIC' THEN true
6389        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6390        -- whether the current user holds role `grantee`. For a nonexistent grantee
6391        -- name, both return false. We use mz_session_role_memberships() instead
6392        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6393        -- loads the full system role graph and is blocked in restricted sessions.
6394        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6395    END"#,
6396    access: vec![PUBLIC_SELECT],
6397    ontology: None,
6398});
6399
6400pub static MZ_SHOW_CLUSTER_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6401    name: "mz_show_cluster_privileges",
6402    schema: MZ_INTERNAL_SCHEMA,
6403    oid: oid::VIEW_MZ_SHOW_CLUSTER_PRIVILEGES_OID,
6404    desc: RelationDesc::builder()
6405        .with_column("grantor", SqlScalarType::String.nullable(true))
6406        .with_column("grantee", SqlScalarType::String.nullable(true))
6407        .with_column("name", SqlScalarType::String.nullable(false))
6408        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6409        .finish(),
6410    column_comments: BTreeMap::from_iter([
6411        ("grantor", "The role that granted the privilege."),
6412        ("grantee", "The role that the privilege was granted to."),
6413        ("name", "The name of the cluster."),
6414        ("privilege_type", "They type of privilege granted."),
6415    ]),
6416    sql: r#"SELECT
6417    grantor.name AS grantor,
6418    CASE privileges.grantee
6419        WHEN 'p' THEN 'PUBLIC'
6420        ELSE grantee.name
6421    END AS grantee,
6422    privileges.name AS name,
6423    privileges.privilege_type AS privilege_type
6424FROM
6425    (SELECT mz_internal.mz_aclexplode(privileges).*, name
6426    FROM mz_catalog.mz_clusters
6427    WHERE id NOT LIKE 's%') AS privileges
6428LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6429LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6430WHERE privileges.grantee NOT LIKE 's%'"#,
6431    access: vec![PUBLIC_SELECT],
6432    ontology: None,
6433});
6434
6435pub static MZ_SHOW_MY_CLUSTER_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6436    name: "mz_show_my_cluster_privileges",
6437    schema: MZ_INTERNAL_SCHEMA,
6438    oid: oid::VIEW_MZ_SHOW_MY_CLUSTER_PRIVILEGES_OID,
6439    desc: RelationDesc::builder()
6440        .with_column("grantor", SqlScalarType::String.nullable(true))
6441        .with_column("grantee", SqlScalarType::String.nullable(true))
6442        .with_column("name", SqlScalarType::String.nullable(false))
6443        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6444        .finish(),
6445    column_comments: BTreeMap::from_iter([
6446        ("grantor", "The role that granted the privilege."),
6447        ("grantee", "The role that the privilege was granted to."),
6448        ("name", "The name of the cluster."),
6449        ("privilege_type", "They type of privilege granted."),
6450    ]),
6451    sql: r#"SELECT grantor, grantee, name, privilege_type
6452FROM mz_internal.mz_show_cluster_privileges
6453WHERE
6454    CASE
6455        WHEN grantee = 'PUBLIC' THEN true
6456        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6457        -- whether the current user holds role `grantee`. For a nonexistent grantee
6458        -- name, both return false. We use mz_session_role_memberships() instead
6459        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6460        -- loads the full system role graph and is blocked in restricted sessions.
6461        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6462    END"#,
6463    access: vec![PUBLIC_SELECT],
6464    ontology: None,
6465});
6466
6467pub static MZ_SHOW_DATABASE_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6468    name: "mz_show_database_privileges",
6469    schema: MZ_INTERNAL_SCHEMA,
6470    oid: oid::VIEW_MZ_SHOW_DATABASE_PRIVILEGES_OID,
6471    desc: RelationDesc::builder()
6472        .with_column("grantor", SqlScalarType::String.nullable(true))
6473        .with_column("grantee", SqlScalarType::String.nullable(true))
6474        .with_column("name", SqlScalarType::String.nullable(false))
6475        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6476        .finish(),
6477    column_comments: BTreeMap::from_iter([
6478        ("grantor", "The role that granted the privilege."),
6479        ("grantee", "The role that the privilege was granted to."),
6480        ("name", "The name of the database."),
6481        ("privilege_type", "They type of privilege granted."),
6482    ]),
6483    sql: r#"SELECT
6484    grantor.name AS grantor,
6485    CASE privileges.grantee
6486        WHEN 'p' THEN 'PUBLIC'
6487        ELSE grantee.name
6488    END AS grantee,
6489    privileges.name AS name,
6490    privileges.privilege_type AS privilege_type
6491FROM
6492    (SELECT mz_internal.mz_aclexplode(privileges).*, name
6493    FROM mz_catalog.mz_databases
6494    WHERE id NOT LIKE 's%') AS privileges
6495LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6496LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6497WHERE privileges.grantee NOT LIKE 's%'"#,
6498    access: vec![PUBLIC_SELECT],
6499    ontology: None,
6500});
6501
6502pub static MZ_SHOW_MY_DATABASE_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6503    name: "mz_show_my_database_privileges",
6504    schema: MZ_INTERNAL_SCHEMA,
6505    oid: oid::VIEW_MZ_SHOW_MY_DATABASE_PRIVILEGES_OID,
6506    desc: RelationDesc::builder()
6507        .with_column("grantor", SqlScalarType::String.nullable(true))
6508        .with_column("grantee", SqlScalarType::String.nullable(true))
6509        .with_column("name", SqlScalarType::String.nullable(false))
6510        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6511        .finish(),
6512    column_comments: BTreeMap::from_iter([
6513        ("grantor", "The role that granted the privilege."),
6514        ("grantee", "The role that the privilege was granted to."),
6515        ("name", "The name of the cluster."),
6516        ("privilege_type", "They type of privilege granted."),
6517    ]),
6518    sql: r#"SELECT grantor, grantee, name, privilege_type
6519FROM mz_internal.mz_show_database_privileges
6520WHERE
6521    CASE
6522        WHEN grantee = 'PUBLIC' THEN true
6523        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6524        -- whether the current user holds role `grantee`. For a nonexistent grantee
6525        -- name, both return false. We use mz_session_role_memberships() instead
6526        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6527        -- loads the full system role graph and is blocked in restricted sessions.
6528        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6529    END"#,
6530    access: vec![PUBLIC_SELECT],
6531    ontology: None,
6532});
6533
6534pub static MZ_SHOW_SCHEMA_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6535    name: "mz_show_schema_privileges",
6536    schema: MZ_INTERNAL_SCHEMA,
6537    oid: oid::VIEW_MZ_SHOW_SCHEMA_PRIVILEGES_OID,
6538    desc: RelationDesc::builder()
6539        .with_column("grantor", SqlScalarType::String.nullable(true))
6540        .with_column("grantee", SqlScalarType::String.nullable(true))
6541        .with_column("database", SqlScalarType::String.nullable(true))
6542        .with_column("name", SqlScalarType::String.nullable(false))
6543        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6544        .finish(),
6545    column_comments: BTreeMap::from_iter([
6546        ("grantor", "The role that granted the privilege."),
6547        ("grantee", "The role that the privilege was granted to."),
6548        (
6549            "database",
6550            "The name of the database containing the schema.",
6551        ),
6552        ("name", "The name of the schema."),
6553        ("privilege_type", "They type of privilege granted."),
6554    ]),
6555    sql: r#"SELECT
6556    grantor.name AS grantor,
6557    CASE privileges.grantee
6558        WHEN 'p' THEN 'PUBLIC'
6559        ELSE grantee.name
6560    END AS grantee,
6561    databases.name AS database,
6562    privileges.name AS name,
6563    privileges.privilege_type AS privilege_type
6564FROM
6565    (SELECT mz_internal.mz_aclexplode(privileges).*, database_id, name
6566    FROM mz_catalog.mz_schemas
6567    WHERE id NOT LIKE 's%') AS privileges
6568LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6569LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6570LEFT JOIN mz_catalog.mz_databases databases ON privileges.database_id = databases.id
6571WHERE privileges.grantee NOT LIKE 's%'"#,
6572    access: vec![PUBLIC_SELECT],
6573    ontology: None,
6574});
6575
6576pub static MZ_SHOW_MY_SCHEMA_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6577    name: "mz_show_my_schema_privileges",
6578    schema: MZ_INTERNAL_SCHEMA,
6579    oid: oid::VIEW_MZ_SHOW_MY_SCHEMA_PRIVILEGES_OID,
6580    desc: RelationDesc::builder()
6581        .with_column("grantor", SqlScalarType::String.nullable(true))
6582        .with_column("grantee", SqlScalarType::String.nullable(true))
6583        .with_column("database", SqlScalarType::String.nullable(true))
6584        .with_column("name", SqlScalarType::String.nullable(false))
6585        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6586        .finish(),
6587    column_comments: BTreeMap::from_iter([
6588        ("grantor", "The role that granted the privilege."),
6589        ("grantee", "The role that the privilege was granted to."),
6590        (
6591            "database",
6592            "The name of the database containing the schema.",
6593        ),
6594        ("name", "The name of the schema."),
6595        ("privilege_type", "They type of privilege granted."),
6596    ]),
6597    sql: r#"SELECT grantor, grantee, database, name, privilege_type
6598FROM mz_internal.mz_show_schema_privileges
6599WHERE
6600    CASE
6601        WHEN grantee = 'PUBLIC' THEN true
6602        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6603        -- whether the current user holds role `grantee`. For a nonexistent grantee
6604        -- name, both return false. We use mz_session_role_memberships() instead
6605        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6606        -- loads the full system role graph and is blocked in restricted sessions.
6607        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6608    END"#,
6609    access: vec![PUBLIC_SELECT],
6610    ontology: None,
6611});
6612
6613pub static MZ_SHOW_OBJECT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6614    name: "mz_show_object_privileges",
6615    schema: MZ_INTERNAL_SCHEMA,
6616    oid: oid::VIEW_MZ_SHOW_OBJECT_PRIVILEGES_OID,
6617    desc: RelationDesc::builder()
6618        .with_column("grantor", SqlScalarType::String.nullable(true))
6619        .with_column("grantee", SqlScalarType::String.nullable(true))
6620        .with_column("database", SqlScalarType::String.nullable(true))
6621        .with_column("schema", SqlScalarType::String.nullable(true))
6622        .with_column("name", SqlScalarType::String.nullable(false))
6623        .with_column("object_type", SqlScalarType::String.nullable(false))
6624        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6625        .finish(),
6626    column_comments: BTreeMap::from_iter([
6627        ("grantor", "The role that granted the privilege."),
6628        ("grantee", "The role that the privilege was granted to."),
6629        (
6630            "database",
6631            "The name of the database containing the object.",
6632        ),
6633        ("schema", "The name of the schema containing the object."),
6634        ("name", "The name of the object."),
6635        (
6636            "object_type",
6637            "The type of object the privilege is granted on.",
6638        ),
6639        ("privilege_type", "They type of privilege granted."),
6640    ]),
6641    sql: r#"SELECT
6642    grantor.name AS grantor,
6643    CASE privileges.grantee
6644            WHEN 'p' THEN 'PUBLIC'
6645            ELSE grantee.name
6646        END AS grantee,
6647    databases.name AS database,
6648    schemas.name AS schema,
6649    privileges.name AS name,
6650    privileges.type AS object_type,
6651    privileges.privilege_type AS privilege_type
6652FROM
6653    (SELECT mz_internal.mz_aclexplode(privileges).*, schema_id, name, type
6654    FROM mz_catalog.mz_objects
6655    WHERE id NOT LIKE 's%') AS privileges
6656LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6657LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6658LEFT JOIN mz_catalog.mz_schemas schemas ON privileges.schema_id = schemas.id
6659LEFT JOIN mz_catalog.mz_databases databases ON schemas.database_id = databases.id
6660WHERE privileges.grantee NOT LIKE 's%'"#,
6661    access: vec![PUBLIC_SELECT],
6662    ontology: None,
6663});
6664
6665pub static MZ_SHOW_MY_OBJECT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6666    name: "mz_show_my_object_privileges",
6667    schema: MZ_INTERNAL_SCHEMA,
6668    oid: oid::VIEW_MZ_SHOW_MY_OBJECT_PRIVILEGES_OID,
6669    desc: RelationDesc::builder()
6670        .with_column("grantor", SqlScalarType::String.nullable(true))
6671        .with_column("grantee", SqlScalarType::String.nullable(true))
6672        .with_column("database", SqlScalarType::String.nullable(true))
6673        .with_column("schema", SqlScalarType::String.nullable(true))
6674        .with_column("name", SqlScalarType::String.nullable(false))
6675        .with_column("object_type", SqlScalarType::String.nullable(false))
6676        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6677        .finish(),
6678    column_comments: BTreeMap::from_iter([
6679        ("grantor", "The role that granted the privilege."),
6680        ("grantee", "The role that the privilege was granted to."),
6681        (
6682            "database",
6683            "The name of the database containing the object.",
6684        ),
6685        ("schema", "The name of the schema containing the object."),
6686        ("name", "The name of the object."),
6687        (
6688            "object_type",
6689            "The type of object the privilege is granted on.",
6690        ),
6691        ("privilege_type", "They type of privilege granted."),
6692    ]),
6693    sql: r#"SELECT grantor, grantee, database, schema, name, object_type, privilege_type
6694FROM mz_internal.mz_show_object_privileges
6695WHERE
6696    CASE
6697        WHEN grantee = 'PUBLIC' THEN true
6698        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6699        -- whether the current user holds role `grantee`. For a nonexistent grantee
6700        -- name, both return false. We use mz_session_role_memberships() instead
6701        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6702        -- loads the full system role graph and is blocked in restricted sessions.
6703        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6704    END"#,
6705    access: vec![PUBLIC_SELECT],
6706    ontology: None,
6707});
6708
6709pub static MZ_SHOW_ALL_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6710    name: "mz_show_all_privileges",
6711    schema: MZ_INTERNAL_SCHEMA,
6712    oid: oid::VIEW_MZ_SHOW_ALL_PRIVILEGES_OID,
6713    desc: RelationDesc::builder()
6714        .with_column("grantor", SqlScalarType::String.nullable(true))
6715        .with_column("grantee", SqlScalarType::String.nullable(true))
6716        .with_column("database", SqlScalarType::String.nullable(true))
6717        .with_column("schema", SqlScalarType::String.nullable(true))
6718        .with_column("name", SqlScalarType::String.nullable(true))
6719        .with_column("object_type", SqlScalarType::String.nullable(false))
6720        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6721        .finish(),
6722    column_comments: BTreeMap::from_iter([
6723        ("grantor", "The role that granted the privilege."),
6724        ("grantee", "The role that the privilege was granted to."),
6725        (
6726            "database",
6727            "The name of the database containing the object.",
6728        ),
6729        ("schema", "The name of the schema containing the object."),
6730        ("name", "The name of the privilege target."),
6731        (
6732            "object_type",
6733            "The type of object the privilege is granted on.",
6734        ),
6735        ("privilege_type", "They type of privilege granted."),
6736    ]),
6737    sql: r#"SELECT grantor, grantee, NULL AS database, NULL AS schema, NULL AS name, 'system' AS object_type, privilege_type
6738FROM mz_internal.mz_show_system_privileges
6739UNION ALL
6740SELECT grantor, grantee, NULL AS database, NULL AS schema, name, 'cluster' AS object_type, privilege_type
6741FROM mz_internal.mz_show_cluster_privileges
6742UNION ALL
6743SELECT grantor, grantee, NULL AS database, NULL AS schema, name, 'database' AS object_type, privilege_type
6744FROM mz_internal.mz_show_database_privileges
6745UNION ALL
6746SELECT grantor, grantee, database, NULL AS schema, name, 'schema' AS object_type, privilege_type
6747FROM mz_internal.mz_show_schema_privileges
6748UNION ALL
6749SELECT grantor, grantee, database, schema, name, object_type, privilege_type
6750FROM mz_internal.mz_show_object_privileges"#,
6751    access: vec![PUBLIC_SELECT],
6752    ontology: None,
6753});
6754
6755pub static MZ_SHOW_ALL_MY_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6756    name: "mz_show_all_my_privileges",
6757    schema: MZ_INTERNAL_SCHEMA,
6758    oid: oid::VIEW_MZ_SHOW_ALL_MY_PRIVILEGES_OID,
6759    desc: RelationDesc::builder()
6760        .with_column("grantor", SqlScalarType::String.nullable(true))
6761        .with_column("grantee", SqlScalarType::String.nullable(true))
6762        .with_column("database", SqlScalarType::String.nullable(true))
6763        .with_column("schema", SqlScalarType::String.nullable(true))
6764        .with_column("name", SqlScalarType::String.nullable(true))
6765        .with_column("object_type", SqlScalarType::String.nullable(false))
6766        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6767        .finish(),
6768    column_comments: BTreeMap::from_iter([
6769        ("grantor", "The role that granted the privilege."),
6770        ("grantee", "The role that the privilege was granted to."),
6771        (
6772            "database",
6773            "The name of the database containing the object.",
6774        ),
6775        ("schema", "The name of the schema containing the object."),
6776        ("name", "The name of the privilege target."),
6777        (
6778            "object_type",
6779            "The type of object the privilege is granted on.",
6780        ),
6781        ("privilege_type", "They type of privilege granted."),
6782    ]),
6783    sql: r#"SELECT grantor, grantee, database, schema, name, object_type, privilege_type
6784FROM mz_internal.mz_show_all_privileges
6785WHERE
6786    CASE
6787        WHEN grantee = 'PUBLIC' THEN true
6788        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6789        -- whether the current user holds role `grantee`. For a nonexistent grantee
6790        -- name, both return false. We use mz_session_role_memberships() instead
6791        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6792        -- loads the full system role graph and is blocked in restricted sessions.
6793        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6794    END"#,
6795    access: vec![PUBLIC_SELECT],
6796    ontology: None,
6797});
6798
6799pub static MZ_SHOW_DEFAULT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6800    name: "mz_show_default_privileges",
6801    schema: MZ_INTERNAL_SCHEMA,
6802    oid: oid::VIEW_MZ_SHOW_DEFAULT_PRIVILEGES_OID,
6803    desc: RelationDesc::builder()
6804        .with_column("object_owner", SqlScalarType::String.nullable(true))
6805        .with_column("database", SqlScalarType::String.nullable(true))
6806        .with_column("schema", SqlScalarType::String.nullable(true))
6807        .with_column("object_type", SqlScalarType::String.nullable(false))
6808        .with_column("grantee", SqlScalarType::String.nullable(true))
6809        .with_column("privilege_type", SqlScalarType::String.nullable(true))
6810        .finish(),
6811    column_comments: BTreeMap::from_iter([
6812        (
6813            "object_owner",
6814            "Privileges described in this row will be granted on objects created by `object_owner`.",
6815        ),
6816        (
6817            "database",
6818            "Privileges described in this row will be granted only on objects created in `database` if non-null.",
6819        ),
6820        (
6821            "schema",
6822            "Privileges described in this row will be granted only on objects created in `schema` if non-null.",
6823        ),
6824        (
6825            "object_type",
6826            "Privileges described in this row will be granted only on objects of type `object_type`.",
6827        ),
6828        (
6829            "grantee",
6830            "Privileges described in this row will be granted to `grantee`.",
6831        ),
6832        ("privilege_type", "They type of privilege to be granted."),
6833    ]),
6834    sql: r#"SELECT
6835    CASE defaults.role_id
6836        WHEN 'p' THEN 'PUBLIC'
6837        ELSE object_owner.name
6838    END AS object_owner,
6839    databases.name AS database,
6840    schemas.name AS schema,
6841    object_type,
6842    CASE defaults.grantee
6843        WHEN 'p' THEN 'PUBLIC'
6844        ELSE grantee.name
6845    END AS grantee,
6846    unnest(mz_internal.mz_format_privileges(defaults.privileges)) AS privilege_type
6847FROM mz_catalog.mz_default_privileges defaults
6848LEFT JOIN mz_catalog.mz_roles AS object_owner ON defaults.role_id = object_owner.id
6849LEFT JOIN mz_catalog.mz_roles AS grantee ON defaults.grantee = grantee.id
6850LEFT JOIN mz_catalog.mz_databases AS databases ON defaults.database_id = databases.id
6851LEFT JOIN mz_catalog.mz_schemas AS schemas ON defaults.schema_id = schemas.id
6852WHERE defaults.grantee NOT LIKE 's%'
6853    AND defaults.database_id IS NULL OR defaults.database_id NOT LIKE 's%'
6854    AND defaults.schema_id IS NULL OR defaults.schema_id NOT LIKE 's%'"#,
6855    access: vec![PUBLIC_SELECT],
6856    ontology: None,
6857});
6858
6859pub static MZ_SHOW_MY_DEFAULT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6860    name: "mz_show_my_default_privileges",
6861    schema: MZ_INTERNAL_SCHEMA,
6862    oid: oid::VIEW_MZ_SHOW_MY_DEFAULT_PRIVILEGES_OID,
6863    desc: RelationDesc::builder()
6864        .with_column("object_owner", SqlScalarType::String.nullable(true))
6865        .with_column("database", SqlScalarType::String.nullable(true))
6866        .with_column("schema", SqlScalarType::String.nullable(true))
6867        .with_column("object_type", SqlScalarType::String.nullable(false))
6868        .with_column("grantee", SqlScalarType::String.nullable(true))
6869        .with_column("privilege_type", SqlScalarType::String.nullable(true))
6870        .finish(),
6871    column_comments: BTreeMap::from_iter([
6872        (
6873            "object_owner",
6874            "Privileges described in this row will be granted on objects created by `object_owner`.",
6875        ),
6876        (
6877            "database",
6878            "Privileges described in this row will be granted only on objects created in `database` if non-null.",
6879        ),
6880        (
6881            "schema",
6882            "Privileges described in this row will be granted only on objects created in `schema` if non-null.",
6883        ),
6884        (
6885            "object_type",
6886            "Privileges described in this row will be granted only on objects of type `object_type`.",
6887        ),
6888        (
6889            "grantee",
6890            "Privileges described in this row will be granted to `grantee`.",
6891        ),
6892        ("privilege_type", "They type of privilege to be granted."),
6893    ]),
6894    sql: r#"SELECT object_owner, database, schema, object_type, grantee, privilege_type
6895FROM mz_internal.mz_show_default_privileges
6896WHERE
6897    CASE
6898        WHEN grantee = 'PUBLIC' THEN true
6899        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6900        -- whether the current user holds role `grantee`. For a nonexistent grantee
6901        -- name, both return false. We use mz_session_role_memberships() instead
6902        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6903        -- loads the full system role graph and is blocked in restricted sessions.
6904        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6905    END"#,
6906    access: vec![PUBLIC_SELECT],
6907    ontology: None,
6908});
6909
6910pub static MZ_SHOW_NETWORK_POLICIES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6911    name: "mz_show_network_policies",
6912    schema: MZ_INTERNAL_SCHEMA,
6913    oid: oid::VIEW_MZ_SHOW_NETWORK_POLICIES_OID,
6914    desc: RelationDesc::builder()
6915        .with_column("name", SqlScalarType::String.nullable(false))
6916        .with_column("rules", SqlScalarType::String.nullable(true))
6917        .with_column("comment", SqlScalarType::String.nullable(false))
6918        .finish(),
6919    column_comments: BTreeMap::new(),
6920    sql: "
6921WITH comments AS (
6922    SELECT id, comment
6923    FROM mz_internal.mz_comments
6924    WHERE object_type = 'network-policy' AND object_sub_id IS NULL
6925)
6926SELECT
6927    policy.name,
6928    pg_catalog.string_agg(rule.name,',' ORDER BY rule.name) as rules,
6929    COALESCE(comment, '') as comment
6930FROM
6931    mz_internal.mz_network_policies as policy
6932LEFT JOIN
6933    mz_internal.mz_network_policy_rules as rule ON policy.id = rule.policy_id
6934LEFT JOIN
6935    comments ON policy.id = comments.id
6936WHERE
6937    policy.id NOT LIKE 's%'
6938AND
6939    policy.id NOT LIKE 'g%'
6940GROUP BY policy.name, comments.comment;",
6941    access: vec![PUBLIC_SELECT],
6942    ontology: None,
6943});
6944
6945pub static MZ_CLUSTER_REPLICA_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6946    name: "mz_cluster_replica_history",
6947    schema: MZ_INTERNAL_SCHEMA,
6948    oid: oid::VIEW_MZ_CLUSTER_REPLICA_HISTORY_OID,
6949    desc: RelationDesc::builder()
6950        .with_column("replica_id", SqlScalarType::String.nullable(true))
6951        .with_column("size", SqlScalarType::String.nullable(true))
6952        .with_column("cluster_id", SqlScalarType::String.nullable(true))
6953        .with_column("cluster_name", SqlScalarType::String.nullable(true))
6954        .with_column("replica_name", SqlScalarType::String.nullable(true))
6955        .with_column(
6956            "created_at",
6957            SqlScalarType::TimestampTz { precision: None }.nullable(false),
6958        )
6959        .with_column(
6960            "dropped_at",
6961            SqlScalarType::TimestampTz { precision: None }.nullable(true),
6962        )
6963        .with_column(
6964            "credits_per_hour",
6965            SqlScalarType::Numeric { max_scale: None }.nullable(true),
6966        )
6967        .finish(),
6968    column_comments: BTreeMap::from_iter([
6969        ("replica_id", "The ID of a cluster replica."),
6970        (
6971            "size",
6972            "The size of the cluster replica. Corresponds to `mz_cluster_replica_sizes.size`.",
6973        ),
6974        (
6975            "cluster_id",
6976            "The ID of the cluster associated with the replica.",
6977        ),
6978        (
6979            "cluster_name",
6980            "The name of the cluster associated with the replica.",
6981        ),
6982        ("replica_name", "The name of the replica."),
6983        ("created_at", "The time at which the replica was created."),
6984        (
6985            "dropped_at",
6986            "The time at which the replica was dropped, or `NULL` if it still exists.",
6987        ),
6988        (
6989            "credits_per_hour",
6990            "The number of compute credits consumed per hour. Corresponds to `mz_cluster_replica_sizes.credits_per_hour`.",
6991        ),
6992    ]),
6993    sql: r#"
6994        WITH
6995            creates AS
6996            (
6997                SELECT
6998                    details ->> 'logical_size' AS size,
6999                    details ->> 'replica_id' AS replica_id,
7000                    details ->> 'replica_name' AS replica_name,
7001                    details ->> 'cluster_name' AS cluster_name,
7002                    details ->> 'cluster_id' AS cluster_id,
7003                    occurred_at
7004                FROM mz_catalog.mz_audit_events
7005                WHERE
7006                    object_type = 'cluster-replica' AND event_type = 'create'
7007                        AND
7008                    details ->> 'replica_id' IS NOT NULL
7009                        AND
7010                    details ->> 'cluster_id' !~~ 's%'
7011            ),
7012            drops AS
7013            (
7014                SELECT details ->> 'replica_id' AS replica_id, occurred_at
7015                FROM mz_catalog.mz_audit_events
7016                WHERE object_type = 'cluster-replica' AND event_type = 'drop'
7017            )
7018        SELECT
7019            creates.replica_id,
7020            creates.size,
7021            creates.cluster_id,
7022            creates.cluster_name,
7023            creates.replica_name,
7024            creates.occurred_at AS created_at,
7025            drops.occurred_at AS dropped_at,
7026            mz_cluster_replica_sizes.credits_per_hour as credits_per_hour
7027        FROM
7028            creates
7029                LEFT JOIN drops ON creates.replica_id = drops.replica_id
7030                LEFT JOIN
7031                    mz_catalog.mz_cluster_replica_sizes
7032                    ON mz_cluster_replica_sizes.size = creates.size"#,
7033    access: vec![PUBLIC_SELECT],
7034    ontology: Some(Ontology {
7035        entity_name: "replica_history",
7036        description: "Historical record of replica creation/drops",
7037        links: &const { [] },
7038        column_semantic_types: &[],
7039    }),
7040});
7041
7042pub static MZ_CLUSTER_REPLICA_NAME_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7043    name: "mz_cluster_replica_name_history",
7044    schema: MZ_INTERNAL_SCHEMA,
7045    oid: oid::VIEW_MZ_CLUSTER_REPLICA_NAME_HISTORY_OID,
7046    desc: RelationDesc::builder()
7047        .with_column(
7048            "occurred_at",
7049            SqlScalarType::TimestampTz { precision: None }.nullable(true),
7050        )
7051        .with_column("id", SqlScalarType::String.nullable(true))
7052        .with_column("previous_name", SqlScalarType::String.nullable(true))
7053        .with_column("new_name", SqlScalarType::String.nullable(true))
7054        .finish(),
7055    column_comments: BTreeMap::from_iter([
7056        (
7057            "occurred_at",
7058            "The time at which the cluster replica was created or renamed. `NULL` if it's a built in system cluster replica.",
7059        ),
7060        ("id", "The ID of the cluster replica."),
7061        (
7062            "previous_name",
7063            "The previous name of the cluster replica. `NULL` if there was no previous name.",
7064        ),
7065        ("new_name", "The new name of the cluster replica."),
7066    ]),
7067    sql: r#"WITH user_replica_alter_history AS (
7068  SELECT occurred_at,
7069    audit_events.details->>'replica_id' AS id,
7070    audit_events.details->>'old_name' AS previous_name,
7071    audit_events.details->>'new_name' AS new_name
7072  FROM mz_catalog.mz_audit_events AS audit_events
7073  WHERE object_type = 'cluster-replica'
7074    AND audit_events.event_type = 'alter'
7075    AND audit_events.details->>'replica_id' like 'u%'
7076),
7077user_replica_create_history AS (
7078  SELECT occurred_at,
7079    audit_events.details->>'replica_id' AS id,
7080    NULL AS previous_name,
7081    audit_events.details->>'replica_name' AS new_name
7082  FROM mz_catalog.mz_audit_events AS audit_events
7083  WHERE object_type = 'cluster-replica'
7084    AND audit_events.event_type = 'create'
7085    AND audit_events.details->>'replica_id' like 'u%'
7086),
7087-- Because built in system cluster replicas don't have audit events, we need to manually add them
7088system_replicas AS (
7089  -- We assume that the system cluster replicas were created at the beginning of time
7090  SELECT NULL::timestamptz AS occurred_at,
7091    id,
7092    NULL AS previous_name,
7093    name AS new_name
7094  FROM mz_catalog.mz_cluster_replicas
7095  WHERE id LIKE 's%'
7096)
7097SELECT *
7098FROM user_replica_alter_history
7099UNION ALL
7100SELECT *
7101FROM user_replica_create_history
7102UNION ALL
7103SELECT *
7104FROM system_replicas"#,
7105    access: vec![PUBLIC_SELECT],
7106    ontology: Some(Ontology {
7107        entity_name: "replica_name_history",
7108        description: "Historical replica names",
7109        links: &const { [] },
7110        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
7111    }),
7112});
7113
7114pub static MZ_HYDRATION_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7115    name: "mz_hydration_statuses",
7116    schema: MZ_INTERNAL_SCHEMA,
7117    oid: oid::VIEW_MZ_HYDRATION_STATUSES_OID,
7118    desc: RelationDesc::builder()
7119        .with_column("object_id", SqlScalarType::String.nullable(false))
7120        .with_column("replica_id", SqlScalarType::String.nullable(true))
7121        .with_column("hydrated", SqlScalarType::Bool.nullable(true))
7122        .finish(),
7123    column_comments: BTreeMap::from_iter([
7124        (
7125            "object_id",
7126            "The ID of a dataflow-powered object. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, `mz_internal.mz_subscriptions`, `mz_catalog.mz_sources.id`, or `mz_catalog.mz_sinks.id`.",
7127        ),
7128        ("replica_id", "The ID of a cluster replica."),
7129        ("hydrated", "Whether the object is hydrated on the replica."),
7130    ]),
7131    sql: r#"WITH
7132-- Joining against the linearizable catalog tables ensures that this view
7133-- always contains the set of installed objects, even when it depends
7134-- on introspection relations that may received delayed updates.
7135--
7136-- Note that this view only includes objects that are maintained by dataflows.
7137-- In particular, some source types (webhook, introspection, ...) are not and
7138-- are therefore omitted.
7139indexes AS (
7140    SELECT
7141        i.id AS object_id,
7142        h.replica_id,
7143        COALESCE(h.hydrated, false) AS hydrated
7144    FROM mz_catalog.mz_indexes i
7145    LEFT JOIN mz_internal.mz_compute_hydration_statuses h
7146        ON (h.object_id = i.id)
7147),
7148materialized_views AS (
7149    SELECT
7150        i.id AS object_id,
7151        h.replica_id,
7152        COALESCE(h.hydrated, false) AS hydrated
7153    FROM mz_catalog.mz_materialized_views i
7154    LEFT JOIN mz_internal.mz_compute_hydration_statuses h
7155        ON (h.object_id = i.id)
7156),
7157-- Hydration is a dataflow concept and not all sources are maintained by
7158-- dataflows, so we need to find the ones that are. Generally, sources that
7159-- have a cluster ID are maintained by a dataflow running on that cluster.
7160-- Webhook sources are an exception to this rule.
7161sources_with_clusters AS (
7162    SELECT id, cluster_id
7163    FROM mz_catalog.mz_sources
7164    WHERE cluster_id IS NOT NULL AND type != 'webhook'
7165),
7166sources AS (
7167    SELECT
7168        s.id AS object_id,
7169        ss.replica_id AS replica_id,
7170        ss.rehydration_latency IS NOT NULL AS hydrated
7171    FROM sources_with_clusters s
7172    LEFT JOIN mz_internal.mz_source_statistics ss USING (id)
7173),
7174-- We don't yet report sink hydration status (database-issues#8331), so we do a best effort attempt here and
7175-- define a sink as hydrated when it's both "running" and has a frontier greater than the minimum.
7176-- There is likely still a possibility of FPs.
7177sinks AS (
7178    SELECT
7179        s.id AS object_id,
7180        r.id AS replica_id,
7181        ss.status = 'running' AND COALESCE(f.write_frontier, 0) > 0 AS hydrated
7182    FROM mz_catalog.mz_sinks s
7183    LEFT JOIN mz_internal.mz_sink_statuses ss USING (id)
7184    JOIN mz_catalog.mz_cluster_replicas r
7185        ON (r.cluster_id = s.cluster_id)
7186    LEFT JOIN mz_catalog.mz_cluster_replica_frontiers f
7187        ON (f.object_id = s.id AND f.replica_id = r.id)
7188)
7189SELECT * FROM indexes
7190UNION ALL
7191SELECT * FROM materialized_views
7192UNION ALL
7193SELECT * FROM sources
7194UNION ALL
7195SELECT * FROM sinks"#,
7196    access: vec![PUBLIC_SELECT],
7197    ontology: Some(Ontology {
7198        entity_name: "hydration_status",
7199        description: "Overall hydration status per object",
7200        links: &const {
7201            [
7202                OntologyLink {
7203                    name: "hydration_of",
7204                    target: "object",
7205                    properties: LinkProperties::fk_typed(
7206                        "object_id",
7207                        "id",
7208                        Cardinality::OneToOne,
7209                        mz_repr::SemanticType::CatalogItemId,
7210                    ),
7211                },
7212                OntologyLink {
7213                    name: "hydration_on_replica",
7214                    target: "replica",
7215                    properties: LinkProperties::fk("replica_id", "id", Cardinality::ManyToOne),
7216                },
7217            ]
7218        },
7219        column_semantic_types: &const {
7220            [
7221                ("object_id", SemanticType::CatalogItemId),
7222                ("replica_id", SemanticType::ReplicaId),
7223            ]
7224        },
7225    }),
7226});
7227
7228pub const MZ_HYDRATION_STATUSES_IND: BuiltinIndex = BuiltinIndex {
7229    name: "mz_hydration_statuses_ind",
7230    schema: MZ_INTERNAL_SCHEMA,
7231    oid: oid::INDEX_MZ_HYDRATION_STATUSES_IND_OID,
7232    sql: "IN CLUSTER mz_catalog_server
7233ON mz_internal.mz_hydration_statuses (object_id, replica_id)",
7234    is_retained_metrics_object: false,
7235};
7236
7237pub static MZ_MATERIALIZATION_DEPENDENCIES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7238    name: "mz_materialization_dependencies",
7239    schema: MZ_INTERNAL_SCHEMA,
7240    oid: oid::VIEW_MZ_MATERIALIZATION_DEPENDENCIES_OID,
7241    desc: RelationDesc::builder()
7242        .with_column("object_id", SqlScalarType::String.nullable(false))
7243        .with_column("dependency_id", SqlScalarType::String.nullable(false))
7244        .finish(),
7245    column_comments: BTreeMap::from_iter([
7246        (
7247            "object_id",
7248            "The ID of a materialization. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, or `mz_catalog.mz_sinks.id`.",
7249        ),
7250        (
7251            "dependency_id",
7252            "The ID of a dataflow dependency. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, `mz_catalog.mz_sources.id`, or `mz_catalog.mz_tables.id`.",
7253        ),
7254    ]),
7255    sql: "
7256SELECT object_id, dependency_id
7257FROM mz_internal.mz_compute_dependencies
7258UNION ALL
7259SELECT s.id, d.referenced_object_id AS dependency_id
7260FROM mz_internal.mz_object_dependencies d
7261JOIN mz_catalog.mz_sinks s ON (s.id = d.object_id)
7262JOIN mz_catalog.mz_relations r ON (r.id = d.referenced_object_id)",
7263    access: vec![PUBLIC_SELECT],
7264    ontology: Some(Ontology {
7265        entity_name: "materialization_dep",
7266        description: "Dependencies between materializations",
7267        links: &const {
7268            [
7269                OntologyLink {
7270                    name: "depends_on",
7271                    target: "object",
7272                    properties: LinkProperties::DependsOn {
7273                        source_column: "object_id",
7274                        target_column: "id",
7275                        source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
7276                        requires_mapping: None,
7277                    },
7278                },
7279                OntologyLink {
7280                    name: "dependency_is",
7281                    target: "object",
7282                    properties: LinkProperties::fk("dependency_id", "id", Cardinality::ManyToOne),
7283                },
7284            ]
7285        },
7286        column_semantic_types: &const {
7287            [
7288                ("object_id", SemanticType::CatalogItemId),
7289                ("dependency_id", SemanticType::CatalogItemId),
7290            ]
7291        },
7292    }),
7293});
7294
7295pub static MZ_MATERIALIZATION_LAG: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7296    name: "mz_materialization_lag",
7297    schema: MZ_INTERNAL_SCHEMA,
7298    oid: oid::VIEW_MZ_MATERIALIZATION_LAG_OID,
7299    desc: RelationDesc::builder()
7300        .with_column("object_id", SqlScalarType::String.nullable(false))
7301        .with_column("local_lag", SqlScalarType::Interval.nullable(true))
7302        .with_column("global_lag", SqlScalarType::Interval.nullable(true))
7303        .with_column(
7304            "slowest_local_input_id",
7305            SqlScalarType::String.nullable(false),
7306        )
7307        .with_column(
7308            "slowest_global_input_id",
7309            SqlScalarType::String.nullable(false),
7310        )
7311        .finish(),
7312    column_comments: BTreeMap::from_iter([
7313        (
7314            "object_id",
7315            "The ID of the materialized view, index, or sink.",
7316        ),
7317        (
7318            "local_lag",
7319            "The amount of time the materialization lags behind its direct inputs.",
7320        ),
7321        (
7322            "global_lag",
7323            "The amount of time the materialization lags behind its root inputs (sources and tables).",
7324        ),
7325        (
7326            "slowest_local_input_id",
7327            "The ID of the slowest direct input.",
7328        ),
7329        (
7330            "slowest_global_input_id",
7331            "The ID of the slowest root input.",
7332        ),
7333    ]),
7334    sql: "
7335WITH MUTUALLY RECURSIVE
7336    -- IDs of objects for which we want to know the lag.
7337    materializations (id text) AS (
7338        SELECT id FROM mz_catalog.mz_indexes
7339        UNION ALL
7340        SELECT id FROM mz_catalog.mz_materialized_views
7341        UNION ALL
7342        SELECT id FROM mz_catalog.mz_sinks
7343    ),
7344    -- Direct dependencies of materializations.
7345    direct_dependencies (id text, dep_id text) AS (
7346        SELECT m.id, d.dependency_id
7347        FROM materializations m
7348        JOIN mz_internal.mz_materialization_dependencies d ON (m.id = d.object_id)
7349    ),
7350    -- All transitive dependencies of materializations.
7351    transitive_dependencies (id text, dep_id text) AS (
7352        SELECT id, dep_id FROM direct_dependencies
7353        UNION
7354        SELECT td.id, dd.dep_id
7355        FROM transitive_dependencies td
7356        JOIN direct_dependencies dd ON (dd.id = td.dep_id)
7357    ),
7358    -- Root dependencies of materializations (sources and tables).
7359    root_dependencies (id text, dep_id text) AS (
7360        SELECT *
7361        FROM transitive_dependencies td
7362        WHERE NOT EXISTS (
7363            SELECT 1
7364            FROM direct_dependencies dd
7365            WHERE dd.id = td.dep_id
7366        )
7367    ),
7368    -- Write progress times of materializations.
7369    materialization_times (id text, time timestamptz) AS (
7370        SELECT m.id, to_timestamp(f.write_frontier::text::double / 1000)
7371        FROM materializations m
7372        JOIN mz_internal.mz_frontiers f ON (m.id = f.object_id)
7373    ),
7374    -- Write progress times of direct dependencies of materializations.
7375    input_times (id text, slowest_dep text, time timestamptz) AS (
7376        SELECT DISTINCT ON (d.id)
7377            d.id,
7378            d.dep_id,
7379            to_timestamp(f.write_frontier::text::double / 1000)
7380        FROM direct_dependencies d
7381        JOIN mz_internal.mz_frontiers f ON (d.dep_id = f.object_id)
7382        ORDER BY d.id, f.write_frontier ASC
7383    ),
7384    -- Write progress times of root dependencies of materializations.
7385    root_times (id text, slowest_dep text, time timestamptz) AS (
7386        SELECT DISTINCT ON (d.id)
7387            d.id,
7388            d.dep_id,
7389            to_timestamp(f.write_frontier::text::double / 1000)
7390        FROM root_dependencies d
7391        JOIN mz_internal.mz_frontiers f ON (d.dep_id = f.object_id)
7392        ORDER BY d.id, f.write_frontier ASC
7393    )
7394SELECT
7395    id AS object_id,
7396    -- Ensure that lag values are always NULL for materializations that have reached the empty
7397    -- frontier, as those have processed all their input data.
7398    -- Also make sure that lag values are never negative, even when input frontiers are before
7399    -- output frontiers (as can happen during hydration).
7400    CASE
7401        WHEN m.time IS NULL THEN INTERVAL '0'
7402        WHEN i.time IS NULL THEN NULL
7403        ELSE greatest(i.time - m.time, INTERVAL '0')
7404    END AS local_lag,
7405    CASE
7406        WHEN m.time IS NULL THEN INTERVAL '0'
7407        WHEN r.time IS NULL THEN NULL
7408        ELSE greatest(r.time - m.time, INTERVAL '0')
7409    END AS global_lag,
7410    i.slowest_dep AS slowest_local_input_id,
7411    r.slowest_dep AS slowest_global_input_id
7412FROM materialization_times m
7413JOIN input_times i USING (id)
7414JOIN root_times r USING (id)",
7415    access: vec![PUBLIC_SELECT],
7416    ontology: Some(Ontology {
7417        entity_name: "materialization_lag",
7418        description: "Lag between a materialization and its inputs",
7419        links: &const {
7420            [
7421                OntologyLink {
7422                    name: "measures_materialization_lag",
7423                    target: "object",
7424                    properties: LinkProperties::measures("object_id", "id", "materialization_lag"),
7425                },
7426                OntologyLink {
7427                    name: "slowest_local_input",
7428                    target: "object",
7429                    properties: LinkProperties::fk(
7430                        "slowest_local_input_id",
7431                        "id",
7432                        Cardinality::ManyToOne,
7433                    ),
7434                },
7435                OntologyLink {
7436                    name: "slowest_global_input",
7437                    target: "object",
7438                    properties: LinkProperties::fk(
7439                        "slowest_global_input_id",
7440                        "id",
7441                        Cardinality::ManyToOne,
7442                    ),
7443                },
7444            ]
7445        },
7446        column_semantic_types: &const {
7447            [
7448                ("object_id", SemanticType::CatalogItemId),
7449                ("slowest_local_input_id", SemanticType::CatalogItemId),
7450                ("slowest_global_input_id", SemanticType::CatalogItemId),
7451            ]
7452        },
7453    }),
7454});
7455/// The output relation shared by all `mz_console_cluster_utilization_overview*`
7456/// views. Every (bucket size, retention) variant produces the same columns so
7457/// the Console can swap between them based on the selected time range.
7458fn console_cluster_utilization_overview_desc() -> RelationDesc {
7459    RelationDesc::builder()
7460        .with_column(
7461            "bucket_start",
7462            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7463        )
7464        .with_column("replica_id", SqlScalarType::String.nullable(false))
7465        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
7466        .with_column(
7467            "max_memory_at",
7468            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7469        )
7470        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
7471        .with_column(
7472            "max_disk_at",
7473            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7474        )
7475        .with_column(
7476            "memory_and_disk_percent",
7477            SqlScalarType::Float64.nullable(true),
7478        )
7479        .with_column(
7480            "max_memory_and_disk_memory_percent",
7481            SqlScalarType::Float64.nullable(true),
7482        )
7483        .with_column(
7484            "max_memory_and_disk_disk_percent",
7485            SqlScalarType::Float64.nullable(true),
7486        )
7487        .with_column(
7488            "max_memory_and_disk_at",
7489            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7490        )
7491        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
7492        .with_column(
7493            "max_heap_at",
7494            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7495        )
7496        .with_column("max_cpu_percent", SqlScalarType::Float64.nullable(true))
7497        .with_column(
7498            "max_cpu_at",
7499            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7500        )
7501        .with_column("offline_events", SqlScalarType::Jsonb.nullable(true))
7502        .with_column(
7503            "bucket_end",
7504            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7505        )
7506        .with_column("name", SqlScalarType::String.nullable(true))
7507        .with_column("cluster_id", SqlScalarType::String.nullable(true))
7508        .with_column("size", SqlScalarType::String.nullable(true))
7509        .finish()
7510}
7511
7512/// Builds the SQL body shared by the `mz_console_cluster_utilization_overview*`
7513/// views, which power the Console's cluster utilization graphs.
7514///
7515/// There is one view per (bucket width, retention window) pair so the Console
7516/// can read a pre-materialized, indexed rollup for each time range it offers
7517/// instead of recomputing this (expensive) query on every page load. The bodies
7518/// must be kept in sync with the equivalent ad-hoc query in the Console
7519/// (`buildReplicaUtilizationHistoryQuery` in
7520/// `console/src/api/materialize/cluster/replicaUtilizationHistory.ts`).
7521///
7522/// * `bin`: the `date_bin` bucket width, e.g. `1 MINUTE`.
7523/// * `retention`: how much history the view retains, e.g. `3 HOURS`, enforced
7524///   with a temporal `mz_now()` filter so the maintained arrangement stays
7525///   bounded.
7526/// * `group_size`: the expected number of metric samples per (replica, bucket),
7527///   used for the `DISTINCT ON INPUT GROUP SIZE` top-k hint. Replica metrics are
7528///   scraped roughly once per minute, so this is the bucket width in minutes.
7529fn console_cluster_utilization_overview_sql(bin: &str, retention: &str, group_size: u32) -> String {
7530    format!(
7531        r#"WITH replica_history AS (
7532  SELECT replica_id, size, cluster_id
7533  FROM mz_internal.mz_cluster_replica_history
7534  UNION
7535  -- We union the current set of cluster replicas since mz_cluster_replica_history doesn't include system clusters.
7536  SELECT id AS replica_id, size, cluster_id
7537  FROM mz_catalog.mz_cluster_replicas
7538),
7539replica_metrics_history AS (
7540  SELECT
7541    m.occurred_at,
7542    m.replica_id,
7543    r.size,
7544    (SUM(m.cpu_nano_cores::float8) / NULLIF(s.cpu_nano_cores, 0) / NULLIF(s.processes, 0)) AS cpu_percent,
7545    (SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)) AS memory_percent,
7546    (SUM(m.disk_bytes::float8) / NULLIF(s.disk_bytes, 0) / NULLIF(s.processes, 0)) AS disk_percent,
7547    SUM(m.disk_bytes::float8) AS disk_bytes,
7548    SUM(m.memory_bytes::float8) AS memory_bytes,
7549    s.disk_bytes::float8 * s.processes AS total_disk_bytes,
7550    s.memory_bytes::float8 * s.processes AS total_memory_bytes,
7551    MAX(m.heap_bytes::float8) AS heap_bytes,
7552    MAX(m.heap_limit) AS heap_limit,
7553    -- heap_limit is NULL when clusterd isn't launched with --heap-limit (e.g.
7554    -- the emulator's process orchestrator). Fall back to the size-based memory
7555    -- percent so the chart still renders.
7556    COALESCE(
7557      MAX(m.heap_bytes::float8 / NULLIF(m.heap_limit, 0)),
7558      SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)
7559    ) AS heap_percent
7560  FROM
7561    replica_history AS r
7562    INNER JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
7563    INNER JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.replica_id
7564  GROUP BY
7565    m.occurred_at,
7566    m.replica_id,
7567    r.size,
7568    s.cpu_nano_cores,
7569    s.memory_bytes,
7570    s.disk_bytes,
7571    s.processes
7572),
7573replica_utilization_history_binned AS (
7574  -- NOTE: we read directly from replica_metrics_history rather than re-joining
7575  -- replica_history; every replica_id here already came from replica_history,
7576  -- so the join was redundant (and could fan out a replica that changed size).
7577  SELECT
7578    m.occurred_at,
7579    m.replica_id,
7580    m.cpu_percent,
7581    m.memory_percent,
7582    m.memory_bytes,
7583    m.disk_percent,
7584    m.disk_bytes,
7585    m.total_disk_bytes,
7586    m.total_memory_bytes,
7587    m.heap_bytes,
7588    m.heap_percent,
7589    m.size,
7590    date_bin('{bin}', m.occurred_at, '1970-01-01'::timestamp) AS bucket_start
7591  FROM replica_metrics_history AS m
7592  WHERE mz_now() <= date_bin('{bin}', m.occurred_at, '1970-01-01'::timestamp) + INTERVAL '{retention}'
7593),
7594-- For each (replica, bucket), take the sample with the highest memory.
7595max_memory AS (
7596  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, memory_percent, occurred_at
7597  FROM replica_utilization_history_binned
7598  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
7599  ORDER BY bucket_start, replica_id, COALESCE(memory_bytes, 0) DESC
7600),
7601-- For each (replica, bucket), take the sample with the highest disk.
7602max_disk AS (
7603  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, disk_percent, occurred_at
7604  FROM replica_utilization_history_binned
7605  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
7606  ORDER BY bucket_start, replica_id, COALESCE(disk_bytes, 0) DESC
7607),
7608-- For each (replica, bucket), take the sample with the highest cpu.
7609max_cpu AS (
7610  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, cpu_percent, occurred_at
7611  FROM replica_utilization_history_binned
7612  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
7613  ORDER BY bucket_start, replica_id, COALESCE(cpu_percent, 0) DESC
7614),
7615/*
7616  For each (replica, bucket), take the sample with the highest combined memory
7617  and disk. This is different from adding max_memory and max_disk per bucket
7618  because both values may not occur at the same time if the bucket interval is
7619  large.
7620*/
7621max_memory_and_disk AS (
7622  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, memory_percent, disk_percent, memory_and_disk_percent, occurred_at
7623  FROM (
7624    SELECT *,
7625      CASE
7626        WHEN disk_bytes IS NULL AND memory_bytes IS NULL THEN NULL
7627        ELSE (COALESCE(memory_bytes, 0) + COALESCE(disk_bytes, 0)) / NULLIF((total_memory_bytes + total_disk_bytes), 0)
7628      END AS memory_and_disk_percent
7629    FROM replica_utilization_history_binned
7630  ) AS max_memory_and_disk_inner
7631  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
7632  ORDER BY bucket_start, replica_id, COALESCE(memory_and_disk_percent, 0) DESC
7633),
7634-- For each (replica, bucket), take the sample with the highest heap.
7635max_heap AS (
7636  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, heap_percent, occurred_at
7637  FROM replica_utilization_history_binned
7638  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
7639  ORDER BY bucket_start, replica_id, COALESCE(heap_bytes, 0) DESC
7640),
7641-- For each (replica, bucket), collect its offline events at that time.
7642replica_offline_event_history AS (
7643  SELECT
7644    date_bin('{bin}', occurred_at, '1970-01-01'::timestamp) AS bucket_start,
7645    replica_id,
7646    jsonb_agg(
7647      jsonb_build_object(
7648        'replicaId', rsh.replica_id,
7649        'occurredAt', rsh.occurred_at,
7650        'status', rsh.status,
7651        'reason', rsh.reason
7652      )
7653    ) AS offline_events
7654  FROM mz_internal.mz_cluster_replica_status_history AS rsh
7655  -- We assume the statuses for process 0 are the same as all processes.
7656  WHERE process_id = '0'
7657    AND status = 'offline'
7658    AND mz_now() <= date_bin('{bin}', occurred_at, '1970-01-01'::timestamp) + INTERVAL '{retention}'
7659  GROUP BY bucket_start, replica_id
7660)
7661SELECT
7662  bucket_start,
7663  replica_id,
7664  max_memory.memory_percent,
7665  max_memory.occurred_at AS max_memory_at,
7666  max_disk.disk_percent,
7667  max_disk.occurred_at AS max_disk_at,
7668  max_memory_and_disk.memory_and_disk_percent AS memory_and_disk_percent,
7669  max_memory_and_disk.memory_percent AS max_memory_and_disk_memory_percent,
7670  max_memory_and_disk.disk_percent AS max_memory_and_disk_disk_percent,
7671  max_memory_and_disk.occurred_at AS max_memory_and_disk_at,
7672  max_heap.heap_percent,
7673  max_heap.occurred_at AS max_heap_at,
7674  max_cpu.cpu_percent AS max_cpu_percent,
7675  max_cpu.occurred_at AS max_cpu_at,
7676  replica_offline_event_history.offline_events,
7677  bucket_start + INTERVAL '{bin}' AS bucket_end,
7678  replica_name_history.new_name AS name,
7679  replica_history.cluster_id,
7680  replica_history.size
7681FROM max_memory
7682JOIN max_disk USING (bucket_start, replica_id)
7683JOIN max_cpu USING (bucket_start, replica_id)
7684JOIN max_memory_and_disk USING (bucket_start, replica_id)
7685JOIN max_heap USING (bucket_start, replica_id)
7686JOIN replica_history USING (replica_id)
7687/*
7688  TOP k=1 over the name history via a LATERAL subquery + LIMIT: for each bucket,
7689  get the most recent replica name as of the end of the bucket.
7690*/
7691CROSS JOIN LATERAL (
7692  SELECT new_name
7693  FROM mz_internal.mz_cluster_replica_name_history AS replica_name_history
7694  WHERE replica_id = replica_name_history.id
7695    -- We treat NULLs as the beginning of time.
7696    AND bucket_start + INTERVAL '{bin}' >= COALESCE(replica_name_history.occurred_at, '1970-01-01'::timestamp)
7697  ORDER BY replica_name_history.occurred_at DESC
7698  LIMIT 1
7699) AS replica_name_history
7700LEFT JOIN replica_offline_event_history USING (bucket_start, replica_id)"#,
7701        bin = bin,
7702        retention = retention,
7703        group_size = group_size,
7704    )
7705}
7706
7707/// Schema for the un-binned 3-hour console cluster utilization base. Unlike the
7708/// binned `_overview*` views, this exposes raw per-(replica, sample) metrics so
7709/// the Console can bin client-side.
7710fn console_cluster_utilization_unbinned_3h_desc() -> RelationDesc {
7711    RelationDesc::builder()
7712        .with_column("replica_id", SqlScalarType::String.nullable(false))
7713        .with_column("cluster_id", SqlScalarType::String.nullable(true))
7714        .with_column("size", SqlScalarType::String.nullable(false))
7715        .with_column("name", SqlScalarType::String.nullable(true))
7716        .with_column(
7717            "occurred_at",
7718            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7719        )
7720        .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
7721        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
7722        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
7723        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
7724        .with_column(
7725            "memory_and_disk_percent",
7726            SqlScalarType::Float64.nullable(true),
7727        )
7728        .finish()
7729}
7730
7731/// Builds the SQL for the un-binned 3-hour console cluster utilization base: one
7732/// row per (replica, metric sample) over `retention`, with no `date_bin`/top-k,
7733/// so the Console bins it client-side. The binned `_overview*` views handle the
7734/// longer windows. A temporal `mz_now()` filter bounds the maintained
7735/// arrangement. Kept in sync with the Console
7736/// (`buildConsoleClusterUtilizationUnbinned3hQuery` in
7737/// `replicaUtilizationHistory.ts`).
7738fn console_cluster_utilization_unbinned_3h_sql(retention: &str) -> String {
7739    format!(
7740        r#"WITH replica_history AS (
7741  -- Dedup to one row per replica (prefer the current size). Size is fixed per
7742  -- replica so this is normally a no-op, but a stray duplicate size in history
7743  -- would fan out the metrics join; with no Top-1 dedup here that would emit two
7744  -- rows per (replica_id, occurred_at) and break the Console SUBSCRIBE upsert key.
7745  SELECT DISTINCT ON (replica_id) replica_id, size, cluster_id
7746  FROM (
7747    -- We union the current set of cluster replicas since mz_cluster_replica_history doesn't include system clusters.
7748    SELECT id AS replica_id, size, cluster_id, 0 AS source_rank
7749    FROM mz_catalog.mz_cluster_replicas
7750    UNION ALL
7751    SELECT replica_id, size, cluster_id, 1 AS source_rank
7752    FROM mz_internal.mz_cluster_replica_history
7753  ) all_replicas
7754  ORDER BY replica_id, source_rank
7755),
7756replica_metrics AS (
7757  SELECT
7758    m.occurred_at,
7759    m.replica_id,
7760    r.cluster_id,
7761    r.size,
7762    (SUM(m.cpu_nano_cores::float8) / NULLIF(s.cpu_nano_cores, 0) / NULLIF(s.processes, 0)) AS cpu_percent,
7763    (SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)) AS memory_percent,
7764    (SUM(m.disk_bytes::float8) / NULLIF(s.disk_bytes, 0) / NULLIF(s.processes, 0)) AS disk_percent,
7765    COALESCE(
7766      MAX(m.heap_bytes::float8 / NULLIF(m.heap_limit, 0)),
7767      SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)
7768    ) AS heap_percent,
7769    CASE
7770      WHEN SUM(m.disk_bytes::float8) IS NULL AND SUM(m.memory_bytes::float8) IS NULL THEN NULL
7771      ELSE (COALESCE(SUM(m.memory_bytes::float8), 0) + COALESCE(SUM(m.disk_bytes::float8), 0))
7772           / NULLIF((s.memory_bytes::float8 + s.disk_bytes::float8) * s.processes, 0)
7773    END AS memory_and_disk_percent
7774  FROM replica_history AS r
7775    INNER JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
7776    INNER JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.replica_id
7777  -- No aggregation over time: one row per (replica, sample) so the Console bins
7778  -- client-side. The temporal mz_now() filter keeps the maintained arrangement
7779  -- bounded to the retention window.
7780  WHERE mz_now() <= m.occurred_at + INTERVAL '{retention}'
7781  GROUP BY
7782    m.occurred_at,
7783    m.replica_id,
7784    r.cluster_id,
7785    r.size,
7786    s.cpu_nano_cores,
7787    s.memory_bytes,
7788    s.disk_bytes,
7789    s.processes
7790)
7791SELECT
7792  m.replica_id,
7793  m.cluster_id,
7794  m.size,
7795  replica_name_history.new_name AS name,
7796  m.occurred_at,
7797  m.cpu_percent,
7798  m.memory_percent,
7799  m.disk_percent,
7800  m.heap_percent,
7801  m.memory_and_disk_percent
7802FROM replica_metrics AS m
7803/* Most recent replica name as of the sample time. */
7804CROSS JOIN LATERAL (
7805  SELECT new_name
7806  FROM mz_internal.mz_cluster_replica_name_history AS replica_name_history
7807  WHERE m.replica_id = replica_name_history.id
7808    -- We treat NULLs as the beginning of time.
7809    AND m.occurred_at >= COALESCE(replica_name_history.occurred_at, '1970-01-01'::timestamp)
7810  ORDER BY replica_name_history.occurred_at DESC
7811  LIMIT 1
7812) AS replica_name_history"#,
7813        retention = retention,
7814    )
7815}
7816
7817/**
7818 * Displays cluster utilization over 14 days bucketed by 1 hour, for the
7819 * Console's environment overview and cluster pages, to speed up load times.
7820 * This view (and its `_3h`/`_24h` siblings) is kept in sync with
7821 * MaterializeInc/console/src/api/materialize/cluster/replicaUtilizationHistory.ts
7822 */
7823pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW: LazyLock<BuiltinView> =
7824    LazyLock::new(|| BuiltinView {
7825        name: "mz_console_cluster_utilization_overview",
7826        schema: MZ_INTERNAL_SCHEMA,
7827        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_OID,
7828        desc: console_cluster_utilization_overview_desc(),
7829        column_comments: BTreeMap::new(),
7830        sql: Box::leak(
7831            console_cluster_utilization_overview_sql("1 HOUR", "14 DAYS", 60).into_boxed_str(),
7832        ),
7833        access: vec![PUBLIC_SELECT],
7834        ontology: None,
7835    });
7836
7837/**
7838 * Un-binned cluster utilization over the last 3 hours, for the Console's "Last
7839 * hour" / "Last 3 hours" graphs. Unlike the binned `_overview*` views, this
7840 * exposes raw per-(replica, sample) metrics and the Console bins client-side.
7841 * See `console_cluster_utilization_unbinned_3h_sql` for details.
7842 */
7843pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H: LazyLock<BuiltinView> =
7844    LazyLock::new(|| BuiltinView {
7845        name: "mz_console_cluster_utilization_overview_3h",
7846        schema: MZ_INTERNAL_SCHEMA,
7847        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_OID,
7848        desc: console_cluster_utilization_unbinned_3h_desc(),
7849        column_comments: BTreeMap::new(),
7850        sql: Box::leak(console_cluster_utilization_unbinned_3h_sql("3 HOURS").into_boxed_str()),
7851        access: vec![PUBLIC_SELECT],
7852        ontology: None,
7853    });
7854
7855/**
7856 * Cluster utilization over the last 24 hours bucketed by 5 minutes, for the
7857 * Console's "Last 6 hours" / "Last 24 hours" cluster utilization graphs. See
7858 * `console_cluster_utilization_overview_sql` for details.
7859 */
7860pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H: LazyLock<BuiltinView> =
7861    LazyLock::new(|| BuiltinView {
7862        name: "mz_console_cluster_utilization_overview_24h",
7863        schema: MZ_INTERNAL_SCHEMA,
7864        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_OID,
7865        desc: console_cluster_utilization_overview_desc(),
7866        column_comments: BTreeMap::new(),
7867        sql: Box::leak(
7868            console_cluster_utilization_overview_sql("5 MINUTES", "24 HOURS", 5).into_boxed_str(),
7869        ),
7870        access: vec![PUBLIC_SELECT],
7871        ontology: None,
7872    });
7873/**
7874 * Traces the blue/green deployment lineage in the audit log to determine all cluster
7875 * IDs that are logically the same cluster.
7876 * cluster_id: The ID of a cluster.
7877 * current_deployment_cluster_id: The cluster ID of the last cluster in
7878 *   cluster_id's blue/green lineage.
7879 * cluster_name: The name of the cluster.
7880 * The approach taken is as follows. First, find all extant clusters and add them
7881 * to the result set. Per cluster, we do the following:
7882 * 1. Find the most recent create or rename event. This moment represents when the
7883 *    cluster took on its final logical identity.
7884 * 2. Look for a cluster that had the same name (or the same name with `_dbt_deploy`
7885 *    appended) that was dropped within one minute of that moment. That cluster is
7886 *    almost certainly the logical predecessor of the current cluster. Add the cluster
7887 *    to the result set.
7888 * 3. Repeat the procedure until a cluster with no logical predecessor is discovered.
7889 * Limiting the search for a dropped cluster to a window of one minute is a heuristic,
7890 * but one that's likely to be pretty good one. If a name is reused after more
7891 * than one minute, that's a good sign that it wasn't an automatic blue/green
7892 * process, but someone turning on a new use case that happens to have the same
7893 * name as a previous but logically distinct use case.
7894 */
7895pub static MZ_CLUSTER_DEPLOYMENT_LINEAGE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7896    name: "mz_cluster_deployment_lineage",
7897    schema: MZ_INTERNAL_SCHEMA,
7898    oid: oid::VIEW_MZ_CLUSTER_DEPLOYMENT_LINEAGE_OID,
7899    desc: RelationDesc::builder()
7900        .with_column("cluster_id", SqlScalarType::String.nullable(true))
7901        .with_column(
7902            "current_deployment_cluster_id",
7903            SqlScalarType::String.nullable(false),
7904        )
7905        .with_column("cluster_name", SqlScalarType::String.nullable(false))
7906        .with_key(vec![0, 1, 2])
7907        .finish(),
7908    column_comments: BTreeMap::from_iter([
7909        (
7910            "cluster_id",
7911            "The ID of the cluster. Corresponds to `mz_clusters.id` (though the cluster may no longer exist).",
7912        ),
7913        (
7914            "current_deployment_cluster_id",
7915            "The cluster ID of the last cluster in `cluster_id`'s blue/green lineage (the cluster is guaranteed to exist).",
7916        ),
7917        ("cluster_name", "The name of the cluster"),
7918    ]),
7919    sql: r#"WITH MUTUALLY RECURSIVE cluster_events (
7920  cluster_id text,
7921  cluster_name text,
7922  event_type text,
7923  occurred_at timestamptz
7924) AS (
7925  SELECT coalesce(details->>'id', details->>'cluster_id') AS cluster_id,
7926    coalesce(details->>'name', details->>'new_name') AS cluster_name,
7927    event_type,
7928    occurred_at
7929  FROM mz_audit_events
7930  WHERE (
7931      event_type IN ('create', 'drop')
7932      OR (
7933        event_type = 'alter'
7934        AND details ? 'new_name'
7935      )
7936    )
7937    AND object_type = 'cluster'
7938    AND mz_now() < occurred_at + INTERVAL '30 days'
7939),
7940mz_cluster_deployment_lineage (
7941  cluster_id text,
7942  current_deployment_cluster_id text,
7943  cluster_name text
7944) AS (
7945  SELECT c.id,
7946    c.id,
7947    c.name
7948  FROM mz_clusters c
7949  WHERE c.id LIKE 'u%'
7950  UNION
7951  SELECT *
7952  FROM dropped_clusters
7953),
7954-- Closest create or rename event based on the current clusters in the result set
7955most_recent_create_or_rename (
7956  cluster_id text,
7957  current_deployment_cluster_id text,
7958  cluster_name text,
7959  occurred_at timestamptz
7960) AS (
7961  SELECT DISTINCT ON (e.cluster_id) e.cluster_id,
7962    c.current_deployment_cluster_id,
7963    e.cluster_name,
7964    e.occurred_at
7965  FROM mz_cluster_deployment_lineage c
7966    JOIN cluster_events e ON c.cluster_id = e.cluster_id
7967    AND c.cluster_name = e.cluster_name
7968  WHERE e.event_type <> 'drop'
7969  ORDER BY e.cluster_id,
7970    e.occurred_at DESC
7971),
7972-- Clusters that were dropped most recently within 1 minute of most_recent_create_or_rename
7973dropped_clusters (
7974  cluster_id text,
7975  current_deployment_cluster_id text,
7976  cluster_name text
7977) AS (
7978  SELECT DISTINCT ON (cr.cluster_id) e.cluster_id,
7979    cr.current_deployment_cluster_id,
7980    cr.cluster_name
7981  FROM most_recent_create_or_rename cr
7982    JOIN cluster_events e ON e.occurred_at BETWEEN cr.occurred_at - interval '1 minute'
7983    AND cr.occurred_at + interval '1 minute'
7984    AND (
7985      e.cluster_name = cr.cluster_name
7986      OR e.cluster_name = cr.cluster_name || '_dbt_deploy'
7987    )
7988  WHERE e.event_type = 'drop'
7989  ORDER BY cr.cluster_id,
7990    abs(
7991      extract(
7992        epoch
7993        FROM cr.occurred_at - e.occurred_at
7994      )
7995    )
7996)
7997SELECT *
7998FROM mz_cluster_deployment_lineage"#,
7999    access: vec![PUBLIC_SELECT],
8000    ontology: Some(Ontology {
8001        entity_name: "cluster_deployment",
8002        description: "Cluster deployment lineage information",
8003        links: &const {
8004            [
8005                OntologyLink {
8006                    name: "deployment_of",
8007                    target: "cluster",
8008                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
8009                },
8010                OntologyLink {
8011                    name: "current_deployment",
8012                    target: "cluster",
8013                    properties: LinkProperties::fk(
8014                        "current_deployment_cluster_id",
8015                        "id",
8016                        Cardinality::ManyToOne,
8017                    ),
8018                },
8019            ]
8020        },
8021        column_semantic_types: &[],
8022    }),
8023});
8024
8025pub const MZ_SHOW_DATABASES_IND: BuiltinIndex = BuiltinIndex {
8026    name: "mz_show_databases_ind",
8027    schema: MZ_INTERNAL_SCHEMA,
8028    oid: oid::INDEX_MZ_SHOW_DATABASES_IND_OID,
8029    sql: "IN CLUSTER mz_catalog_server
8030ON mz_internal.mz_show_databases (name)",
8031    is_retained_metrics_object: false,
8032};
8033
8034pub const MZ_SHOW_SCHEMAS_IND: BuiltinIndex = BuiltinIndex {
8035    name: "mz_show_schemas_ind",
8036    schema: MZ_INTERNAL_SCHEMA,
8037    oid: oid::INDEX_MZ_SHOW_SCHEMAS_IND_OID,
8038    sql: "IN CLUSTER mz_catalog_server
8039ON mz_internal.mz_show_schemas (database_id)",
8040    is_retained_metrics_object: false,
8041};
8042
8043pub const MZ_SHOW_CONNECTIONS_IND: BuiltinIndex = BuiltinIndex {
8044    name: "mz_show_connections_ind",
8045    schema: MZ_INTERNAL_SCHEMA,
8046    oid: oid::INDEX_MZ_SHOW_CONNECTIONS_IND_OID,
8047    sql: "IN CLUSTER mz_catalog_server
8048ON mz_internal.mz_show_connections (schema_id)",
8049    is_retained_metrics_object: false,
8050};
8051
8052pub const MZ_SHOW_TABLES_IND: BuiltinIndex = BuiltinIndex {
8053    name: "mz_show_tables_ind",
8054    schema: MZ_INTERNAL_SCHEMA,
8055    oid: oid::INDEX_MZ_SHOW_TABLES_IND_OID,
8056    sql: "IN CLUSTER mz_catalog_server
8057ON mz_internal.mz_show_tables (schema_id)",
8058    is_retained_metrics_object: false,
8059};
8060
8061pub const MZ_SHOW_SOURCES_IND: BuiltinIndex = BuiltinIndex {
8062    name: "mz_show_sources_ind",
8063    schema: MZ_INTERNAL_SCHEMA,
8064    oid: oid::INDEX_MZ_SHOW_SOURCES_IND_OID,
8065    sql: "IN CLUSTER mz_catalog_server
8066ON mz_internal.mz_show_sources (schema_id)",
8067    is_retained_metrics_object: false,
8068};
8069
8070pub const MZ_SHOW_VIEWS_IND: BuiltinIndex = BuiltinIndex {
8071    name: "mz_show_views_ind",
8072    schema: MZ_INTERNAL_SCHEMA,
8073    oid: oid::INDEX_MZ_SHOW_VIEWS_IND_OID,
8074    sql: "IN CLUSTER mz_catalog_server
8075ON mz_internal.mz_show_views (schema_id)",
8076    is_retained_metrics_object: false,
8077};
8078
8079pub const MZ_SHOW_MATERIALIZED_VIEWS_IND: BuiltinIndex = BuiltinIndex {
8080    name: "mz_show_materialized_views_ind",
8081    schema: MZ_INTERNAL_SCHEMA,
8082    oid: oid::INDEX_MZ_SHOW_MATERIALIZED_VIEWS_IND_OID,
8083    sql: "IN CLUSTER mz_catalog_server
8084ON mz_internal.mz_show_materialized_views (schema_id)",
8085    is_retained_metrics_object: false,
8086};
8087
8088pub const MZ_SHOW_SINKS_IND: BuiltinIndex = BuiltinIndex {
8089    name: "mz_show_sinks_ind",
8090    schema: MZ_INTERNAL_SCHEMA,
8091    oid: oid::INDEX_MZ_SHOW_SINKS_IND_OID,
8092    sql: "IN CLUSTER mz_catalog_server
8093ON mz_internal.mz_show_sinks (schema_id)",
8094    is_retained_metrics_object: false,
8095};
8096
8097pub const MZ_SHOW_TYPES_IND: BuiltinIndex = BuiltinIndex {
8098    name: "mz_show_types_ind",
8099    schema: MZ_INTERNAL_SCHEMA,
8100    oid: oid::INDEX_MZ_SHOW_TYPES_IND_OID,
8101    sql: "IN CLUSTER mz_catalog_server
8102ON mz_internal.mz_show_types (schema_id)",
8103    is_retained_metrics_object: false,
8104};
8105
8106pub const MZ_SHOW_ROLES_IND: BuiltinIndex = BuiltinIndex {
8107    name: "mz_show_roles_ind",
8108    schema: MZ_INTERNAL_SCHEMA,
8109    oid: oid::INDEX_MZ_SHOW_ROLES_IND_OID,
8110    sql: "IN CLUSTER mz_catalog_server
8111ON mz_internal.mz_show_roles (name)",
8112    is_retained_metrics_object: false,
8113};
8114
8115pub const MZ_SHOW_ALL_OBJECTS_IND: BuiltinIndex = BuiltinIndex {
8116    name: "mz_show_all_objects_ind",
8117    schema: MZ_INTERNAL_SCHEMA,
8118    oid: oid::INDEX_MZ_SHOW_ALL_OBJECTS_IND_OID,
8119    sql: "IN CLUSTER mz_catalog_server
8120ON mz_internal.mz_show_all_objects (schema_id)",
8121    is_retained_metrics_object: false,
8122};
8123
8124pub const MZ_SHOW_INDEXES_IND: BuiltinIndex = BuiltinIndex {
8125    name: "mz_show_indexes_ind",
8126    schema: MZ_INTERNAL_SCHEMA,
8127    oid: oid::INDEX_MZ_SHOW_INDEXES_IND_OID,
8128    sql: "IN CLUSTER mz_catalog_server
8129ON mz_internal.mz_show_indexes (schema_id)",
8130    is_retained_metrics_object: false,
8131};
8132
8133pub const MZ_SHOW_COLUMNS_IND: BuiltinIndex = BuiltinIndex {
8134    name: "mz_show_columns_ind",
8135    schema: MZ_INTERNAL_SCHEMA,
8136    oid: oid::INDEX_MZ_SHOW_COLUMNS_IND_OID,
8137    sql: "IN CLUSTER mz_catalog_server
8138ON mz_internal.mz_show_columns (id)",
8139    is_retained_metrics_object: false,
8140};
8141
8142pub const MZ_SHOW_CLUSTERS_IND: BuiltinIndex = BuiltinIndex {
8143    name: "mz_show_clusters_ind",
8144    schema: MZ_INTERNAL_SCHEMA,
8145    oid: oid::INDEX_MZ_SHOW_CLUSTERS_IND_OID,
8146    sql: "IN CLUSTER mz_catalog_server
8147ON mz_internal.mz_show_clusters (name)",
8148    is_retained_metrics_object: false,
8149};
8150
8151pub const MZ_SHOW_CLUSTER_REPLICAS_IND: BuiltinIndex = BuiltinIndex {
8152    name: "mz_show_cluster_replicas_ind",
8153    schema: MZ_INTERNAL_SCHEMA,
8154    oid: oid::INDEX_MZ_SHOW_CLUSTER_REPLICAS_IND_OID,
8155    sql: "IN CLUSTER mz_catalog_server
8156ON mz_internal.mz_show_cluster_replicas (cluster)",
8157    is_retained_metrics_object: false,
8158};
8159
8160pub const MZ_SHOW_SECRETS_IND: BuiltinIndex = BuiltinIndex {
8161    name: "mz_show_secrets_ind",
8162    schema: MZ_INTERNAL_SCHEMA,
8163    oid: oid::INDEX_MZ_SHOW_SECRETS_IND_OID,
8164    sql: "IN CLUSTER mz_catalog_server
8165ON mz_internal.mz_show_secrets (schema_id)",
8166    is_retained_metrics_object: false,
8167};
8168
8169pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_IND: BuiltinIndex = BuiltinIndex {
8170    name: "mz_console_cluster_utilization_overview_ind",
8171    schema: MZ_INTERNAL_SCHEMA,
8172    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_IND_OID,
8173    sql: "IN CLUSTER mz_catalog_server
8174ON mz_internal.mz_console_cluster_utilization_overview (cluster_id)",
8175    is_retained_metrics_object: false,
8176};
8177
8178pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_IND: BuiltinIndex = BuiltinIndex {
8179    name: "mz_console_cluster_utilization_overview_3h_ind",
8180    schema: MZ_INTERNAL_SCHEMA,
8181    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_IND_OID,
8182    sql: "IN CLUSTER mz_catalog_server
8183ON mz_internal.mz_console_cluster_utilization_overview_3h (cluster_id)",
8184    is_retained_metrics_object: false,
8185};
8186
8187pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_IND: BuiltinIndex = BuiltinIndex {
8188    name: "mz_console_cluster_utilization_overview_24h_ind",
8189    schema: MZ_INTERNAL_SCHEMA,
8190    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_IND_OID,
8191    sql: "IN CLUSTER mz_catalog_server
8192ON mz_internal.mz_console_cluster_utilization_overview_24h (cluster_id)",
8193    is_retained_metrics_object: false,
8194};
8195
8196pub const MZ_CLUSTER_DEPLOYMENT_LINEAGE_IND: BuiltinIndex = BuiltinIndex {
8197    name: "mz_cluster_deployment_lineage_ind",
8198    schema: MZ_INTERNAL_SCHEMA,
8199    oid: oid::INDEX_MZ_CLUSTER_DEPLOYMENT_LINEAGE_IND_OID,
8200    sql: "IN CLUSTER mz_catalog_server
8201ON mz_internal.mz_cluster_deployment_lineage (cluster_id)",
8202    is_retained_metrics_object: false,
8203};
8204
8205pub const MZ_SOURCE_STATUSES_IND: BuiltinIndex = BuiltinIndex {
8206    name: "mz_source_statuses_ind",
8207    schema: MZ_INTERNAL_SCHEMA,
8208    oid: oid::INDEX_MZ_SOURCE_STATUSES_IND_OID,
8209    sql: "IN CLUSTER mz_catalog_server
8210ON mz_internal.mz_source_statuses (id)",
8211    is_retained_metrics_object: false,
8212};
8213
8214pub const MZ_SINK_STATUSES_IND: BuiltinIndex = BuiltinIndex {
8215    name: "mz_sink_statuses_ind",
8216    schema: MZ_INTERNAL_SCHEMA,
8217    oid: oid::INDEX_MZ_SINK_STATUSES_IND_OID,
8218    sql: "IN CLUSTER mz_catalog_server
8219ON mz_internal.mz_sink_statuses (id)",
8220    is_retained_metrics_object: false,
8221};
8222
8223pub const MZ_SOURCE_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8224    name: "mz_source_status_history_ind",
8225    schema: MZ_INTERNAL_SCHEMA,
8226    oid: oid::INDEX_MZ_SOURCE_STATUS_HISTORY_IND_OID,
8227    sql: "IN CLUSTER mz_catalog_server
8228ON mz_internal.mz_source_status_history (source_id)",
8229    is_retained_metrics_object: false,
8230};
8231
8232pub const MZ_SINK_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8233    name: "mz_sink_status_history_ind",
8234    schema: MZ_INTERNAL_SCHEMA,
8235    oid: oid::INDEX_MZ_SINK_STATUS_HISTORY_IND_OID,
8236    sql: "IN CLUSTER mz_catalog_server
8237ON mz_internal.mz_sink_status_history (sink_id)",
8238    is_retained_metrics_object: false,
8239};
8240
8241// In both `mz_source_statistics` and `mz_sink_statistics` we cast the `SUM` of
8242// uint8's to `uint8` instead of leaving them as `numeric`. This is because we want to
8243// save index space, and we don't expect the sum to be > 2^63
8244// (even if a source with 2000 workers, that each produce 400 terabytes in a month ~ 2^61).
8245//
8246//
8247// These aggregations are just to make `GROUP BY` happy. Each id has a single row in the
8248// underlying relation.
8249//
8250// We append WITH_HISTORY because we want to build a separate view + index that doesn't
8251// retain history. This is because retaining its history causes MZ_SOURCE_STATISTICS_WITH_HISTORY_IND
8252// to hold all records/updates, which causes CPU and latency of querying it to spike.
8253pub static MZ_SOURCE_STATISTICS_WITH_HISTORY: LazyLock<BuiltinView> =
8254    LazyLock::new(|| BuiltinView {
8255        name: "mz_source_statistics_with_history",
8256        schema: MZ_INTERNAL_SCHEMA,
8257        oid: oid::VIEW_MZ_SOURCE_STATISTICS_WITH_HISTORY_OID,
8258        desc: RelationDesc::builder()
8259            .with_column("id", SqlScalarType::String.nullable(false))
8260            .with_column("replica_id", SqlScalarType::String.nullable(true))
8261            .with_column("messages_received", SqlScalarType::UInt64.nullable(false))
8262            .with_column("bytes_received", SqlScalarType::UInt64.nullable(false))
8263            .with_column("updates_staged", SqlScalarType::UInt64.nullable(false))
8264            .with_column("updates_committed", SqlScalarType::UInt64.nullable(false))
8265            .with_column("records_indexed", SqlScalarType::UInt64.nullable(false))
8266            .with_column("bytes_indexed", SqlScalarType::UInt64.nullable(false))
8267            .with_column(
8268                "rehydration_latency",
8269                SqlScalarType::Interval.nullable(true),
8270            )
8271            .with_column(
8272                "snapshot_records_known",
8273                SqlScalarType::UInt64.nullable(true),
8274            )
8275            .with_column(
8276                "snapshot_records_staged",
8277                SqlScalarType::UInt64.nullable(true),
8278            )
8279            .with_column("snapshot_committed", SqlScalarType::Bool.nullable(false))
8280            .with_column("offset_known", SqlScalarType::UInt64.nullable(true))
8281            .with_column("offset_committed", SqlScalarType::UInt64.nullable(true))
8282            .with_key(vec![0, 1])
8283            .finish(),
8284        column_comments: BTreeMap::new(),
8285        sql: "
8286WITH
8287    -- For each subsource, statistics are reported as its parent source
8288    subsource_to_parent AS
8289    (
8290        SELECT subsource.id AS id, parent.id AS report_id
8291        FROM mz_catalog.mz_sources AS subsource
8292            JOIN mz_internal.mz_object_dependencies AS dep ON subsource.id = dep.object_id
8293            JOIN mz_catalog.mz_sources AS parent ON parent.id = dep.referenced_object_id
8294        WHERE subsource.type = 'subsource'
8295    ),
8296    -- For each table from source, statistics are reported as its parent source
8297    table_to_parent AS
8298    (
8299        SELECT id, source_id AS report_id
8300        FROM mz_catalog.mz_tables
8301        WHERE source_id IS NOT NULL
8302    ),
8303    -- For each source and subsource, statistics are reported as itself
8304    source_refl AS
8305    (
8306        SELECT id, id AS report_id
8307        FROM mz_catalog.mz_sources
8308        WHERE type NOT IN ('progress', 'log')
8309    ),
8310    -- For each table from source, statistics are reported as itself
8311    table_refl AS
8312    (
8313        SELECT id, id AS report_id
8314        FROM mz_catalog.mz_tables
8315        WHERE source_id IS NOT NULL
8316    ),
8317    report_paths AS
8318    (
8319        SELECT id, report_id FROM subsource_to_parent
8320        UNION ALL SELECT id, report_id FROM table_to_parent
8321        UNION ALL SELECT id, report_id FROM source_refl
8322        UNION ALL SELECT id, report_id FROM table_refl
8323    )
8324SELECT
8325    report_paths.report_id AS id,
8326    replica_id,
8327    -- Counters
8328    SUM(messages_received)::uint8 AS messages_received,
8329    SUM(bytes_received)::uint8 AS bytes_received,
8330    SUM(updates_staged)::uint8 AS updates_staged,
8331    SUM(updates_committed)::uint8 AS updates_committed,
8332    -- Resetting Gauges
8333    SUM(records_indexed)::uint8 AS records_indexed,
8334    SUM(bytes_indexed)::uint8 AS bytes_indexed,
8335    -- Ensure we aggregate to NULL when not all workers are done rehydrating.
8336    CASE
8337        WHEN bool_or(rehydration_latency IS NULL) THEN NULL
8338        ELSE MAX(rehydration_latency)::interval
8339    END AS rehydration_latency,
8340    SUM(snapshot_records_known)::uint8 AS snapshot_records_known,
8341    SUM(snapshot_records_staged)::uint8 AS snapshot_records_staged,
8342    bool_and(snapshot_committed) as snapshot_committed,
8343    -- Gauges
8344    MAX(offset_known)::uint8 AS offset_known,
8345    MIN(offset_committed)::uint8 AS offset_committed
8346FROM mz_internal.mz_source_statistics_raw
8347    JOIN report_paths USING (id)
8348GROUP BY report_paths.report_id, replica_id",
8349        access: vec![PUBLIC_SELECT],
8350        ontology: None,
8351    });
8352
8353pub const MZ_SOURCE_STATISTICS_WITH_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8354    name: "mz_source_statistics_with_history_ind",
8355    schema: MZ_INTERNAL_SCHEMA,
8356    oid: oid::INDEX_MZ_SOURCE_STATISTICS_WITH_HISTORY_IND_OID,
8357    sql: "IN CLUSTER mz_catalog_server
8358ON mz_internal.mz_source_statistics_with_history (id, replica_id)",
8359    is_retained_metrics_object: true,
8360};
8361
8362// The non historical version of MZ_SOURCE_STATISTICS_WITH_HISTORY.
8363// Used to query MZ_SOURCE_STATISTICS at the current time.
8364pub static MZ_SOURCE_STATISTICS: LazyLock<BuiltinView> = LazyLock::new(|| {
8365    BuiltinView {
8366        name: "mz_source_statistics",
8367        schema: MZ_INTERNAL_SCHEMA,
8368        oid: oid::VIEW_MZ_SOURCE_STATISTICS_OID,
8369        // We need to add a redundant where clause for a new dataflow to be created.
8370        desc: RelationDesc::builder()
8371            .with_column("id", SqlScalarType::String.nullable(false))
8372            .with_column("replica_id", SqlScalarType::String.nullable(true))
8373            .with_column("messages_received", SqlScalarType::UInt64.nullable(false))
8374            .with_column("bytes_received", SqlScalarType::UInt64.nullable(false))
8375            .with_column("updates_staged", SqlScalarType::UInt64.nullable(false))
8376            .with_column("updates_committed", SqlScalarType::UInt64.nullable(false))
8377            .with_column("records_indexed", SqlScalarType::UInt64.nullable(false))
8378            .with_column("bytes_indexed", SqlScalarType::UInt64.nullable(false))
8379            .with_column(
8380                "rehydration_latency",
8381                SqlScalarType::Interval.nullable(true),
8382            )
8383            .with_column(
8384                "snapshot_records_known",
8385                SqlScalarType::UInt64.nullable(true),
8386            )
8387            .with_column(
8388                "snapshot_records_staged",
8389                SqlScalarType::UInt64.nullable(true),
8390            )
8391            .with_column("snapshot_committed", SqlScalarType::Bool.nullable(false))
8392            .with_column("offset_known", SqlScalarType::UInt64.nullable(true))
8393            .with_column("offset_committed", SqlScalarType::UInt64.nullable(true))
8394            .with_key(vec![0, 1])
8395            .finish(),
8396        column_comments: BTreeMap::from_iter([
8397            (
8398                "id",
8399                "The ID of the source. Corresponds to `mz_catalog.mz_sources.id`.",
8400            ),
8401            (
8402                "replica_id",
8403                "The ID of a replica running the source. Corresponds to `mz_catalog.mz_cluster_replicas.id`.",
8404            ),
8405            (
8406                "messages_received",
8407                "The number of messages the source has received from the external system. Messages are counted in a source type-specific manner. Messages do not correspond directly to updates: some messages produce multiple updates, while other messages may be coalesced into a single update.",
8408            ),
8409            (
8410                "bytes_received",
8411                "The number of bytes the source has read from the external system. Bytes are counted in a source type-specific manner and may or may not include protocol overhead.",
8412            ),
8413            (
8414                "updates_staged",
8415                "The number of updates (insertions plus deletions) the source has written but not yet committed to the storage layer.",
8416            ),
8417            (
8418                "updates_committed",
8419                "The number of updates (insertions plus deletions) the source has committed to the storage layer.",
8420            ),
8421            (
8422                "records_indexed",
8423                "The number of individual records indexed in the source envelope state.",
8424            ),
8425            (
8426                "bytes_indexed",
8427                "The number of bytes stored in the source's internal index, if any.",
8428            ),
8429            (
8430                "rehydration_latency",
8431                "The amount of time it took for the source to rehydrate its internal index, if any, after the source last restarted.",
8432            ),
8433            (
8434                "snapshot_records_known",
8435                "The size of the source's snapshot, measured in number of records. See below to learn what constitutes a record.",
8436            ),
8437            (
8438                "snapshot_records_staged",
8439                "The number of records in the source's snapshot that Materialize has read. See below to learn what constitutes a record.",
8440            ),
8441            (
8442                "snapshot_committed",
8443                "Whether the source has committed the initial snapshot for a source.",
8444            ),
8445            (
8446                "offset_known",
8447                "The offset of the most recent data in the source's upstream service that Materialize knows about. See below to learn what constitutes an offset.",
8448            ),
8449            (
8450                "offset_committed",
8451                "The offset of the the data that Materialize has durably ingested. See below to learn what constitutes an offset.",
8452            ),
8453        ]),
8454        sql: "SELECT * FROM mz_internal.mz_source_statistics_with_history WHERE length(id) > 0",
8455        access: vec![PUBLIC_SELECT],
8456        ontology: Some(Ontology {
8457            entity_name: "source_statistics",
8458            description: "Aggregated source ingestion statistics",
8459            links: &const {
8460                [OntologyLink {
8461                    name: "statistics_of_source",
8462                    target: "source",
8463                    properties: LinkProperties::measures("id", "id", "ingestion_statistics"),
8464                }]
8465            },
8466            column_semantic_types: &const {
8467                [
8468                    ("id", SemanticType::CatalogItemId),
8469                    ("replica_id", SemanticType::ReplicaId),
8470                    ("messages_received", SemanticType::RecordCount),
8471                    ("bytes_received", SemanticType::ByteCount),
8472                    ("updates_staged", SemanticType::RecordCount),
8473                    ("updates_committed", SemanticType::RecordCount),
8474                    ("records_indexed", SemanticType::RecordCount),
8475                    ("bytes_indexed", SemanticType::ByteCount),
8476                    ("snapshot_records_known", SemanticType::RecordCount),
8477                    ("snapshot_records_staged", SemanticType::RecordCount),
8478                ]
8479            },
8480        }),
8481    }
8482});
8483
8484pub const MZ_SOURCE_STATISTICS_IND: BuiltinIndex = BuiltinIndex {
8485    name: "mz_source_statistics_ind",
8486    schema: MZ_INTERNAL_SCHEMA,
8487    oid: oid::INDEX_MZ_SOURCE_STATISTICS_IND_OID,
8488    sql: "IN CLUSTER mz_catalog_server
8489ON mz_internal.mz_source_statistics (id, replica_id)",
8490    is_retained_metrics_object: false,
8491};
8492
8493pub static MZ_SINK_STATISTICS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
8494    name: "mz_sink_statistics",
8495    schema: MZ_INTERNAL_SCHEMA,
8496    oid: oid::VIEW_MZ_SINK_STATISTICS_OID,
8497    desc: RelationDesc::builder()
8498        .with_column("id", SqlScalarType::String.nullable(false))
8499        .with_column("replica_id", SqlScalarType::String.nullable(true))
8500        .with_column("messages_staged", SqlScalarType::UInt64.nullable(false))
8501        .with_column("messages_committed", SqlScalarType::UInt64.nullable(false))
8502        .with_column("bytes_staged", SqlScalarType::UInt64.nullable(false))
8503        .with_column("bytes_committed", SqlScalarType::UInt64.nullable(false))
8504        .with_key(vec![0, 1])
8505        .finish(),
8506    column_comments: BTreeMap::from_iter([
8507        (
8508            "id",
8509            "The ID of the sink. Corresponds to `mz_catalog.mz_sinks.id`.",
8510        ),
8511        (
8512            "replica_id",
8513            "The ID of a replica running the sink. Corresponds to `mz_catalog.mz_cluster_replicas.id`.",
8514        ),
8515        (
8516            "messages_staged",
8517            "The number of messages staged but possibly not committed to the sink.",
8518        ),
8519        (
8520            "messages_committed",
8521            "The number of messages committed to the sink.",
8522        ),
8523        (
8524            "bytes_staged",
8525            "The number of bytes staged but possibly not committed to the sink. This counts both keys and values, if applicable.",
8526        ),
8527        (
8528            "bytes_committed",
8529            "The number of bytes committed to the sink. This counts both keys and values, if applicable.",
8530        ),
8531    ]),
8532    sql: "
8533SELECT
8534    id,
8535    replica_id,
8536    SUM(messages_staged)::uint8 AS messages_staged,
8537    SUM(messages_committed)::uint8 AS messages_committed,
8538    SUM(bytes_staged)::uint8 AS bytes_staged,
8539    SUM(bytes_committed)::uint8 AS bytes_committed
8540FROM mz_internal.mz_sink_statistics_raw
8541GROUP BY id, replica_id",
8542    access: vec![PUBLIC_SELECT],
8543    ontology: Some(Ontology {
8544        entity_name: "sink_statistics",
8545        description: "Aggregated sink export statistics",
8546        links: &const {
8547            [OntologyLink {
8548                name: "statistics_of_sink",
8549                target: "sink",
8550                properties: LinkProperties::measures("id", "id", "export_statistics"),
8551            }]
8552        },
8553        column_semantic_types: &const {
8554            [
8555                ("id", SemanticType::CatalogItemId),
8556                ("replica_id", SemanticType::ReplicaId),
8557                ("messages_staged", SemanticType::RecordCount),
8558                ("messages_committed", SemanticType::RecordCount),
8559                ("bytes_staged", SemanticType::ByteCount),
8560                ("bytes_committed", SemanticType::ByteCount),
8561            ]
8562        },
8563    }),
8564});
8565
8566pub const MZ_SINK_STATISTICS_IND: BuiltinIndex = BuiltinIndex {
8567    name: "mz_sink_statistics_ind",
8568    schema: MZ_INTERNAL_SCHEMA,
8569    oid: oid::INDEX_MZ_SINK_STATISTICS_IND_OID,
8570    sql: "IN CLUSTER mz_catalog_server
8571ON mz_internal.mz_sink_statistics (id, replica_id)",
8572    is_retained_metrics_object: true,
8573};
8574
8575pub const MZ_CLUSTER_REPLICA_STATUSES_IND: BuiltinIndex = BuiltinIndex {
8576    name: "mz_cluster_replica_statuses_ind",
8577    schema: MZ_INTERNAL_SCHEMA,
8578    oid: oid::INDEX_MZ_CLUSTER_REPLICA_STATUSES_IND_OID,
8579    sql: "IN CLUSTER mz_catalog_server
8580ON mz_internal.mz_cluster_replica_statuses (replica_id)",
8581    is_retained_metrics_object: false,
8582};
8583
8584pub const MZ_CLUSTER_REPLICA_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8585    name: "mz_cluster_replica_status_history_ind",
8586    schema: MZ_INTERNAL_SCHEMA,
8587    oid: oid::INDEX_MZ_CLUSTER_REPLICA_STATUS_HISTORY_IND_OID,
8588    sql: "IN CLUSTER mz_catalog_server
8589ON mz_internal.mz_cluster_replica_status_history (replica_id)",
8590    is_retained_metrics_object: false,
8591};
8592
8593pub const MZ_CLUSTER_REPLICA_METRICS_IND: BuiltinIndex = BuiltinIndex {
8594    name: "mz_cluster_replica_metrics_ind",
8595    schema: MZ_INTERNAL_SCHEMA,
8596    oid: oid::INDEX_MZ_CLUSTER_REPLICA_METRICS_IND_OID,
8597    sql: "IN CLUSTER mz_catalog_server
8598ON mz_internal.mz_cluster_replica_metrics (replica_id)",
8599    is_retained_metrics_object: false,
8600};
8601
8602pub const MZ_CLUSTER_REPLICA_METRICS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8603    name: "mz_cluster_replica_metrics_history_ind",
8604    schema: MZ_INTERNAL_SCHEMA,
8605    oid: oid::INDEX_MZ_CLUSTER_REPLICA_METRICS_HISTORY_IND_OID,
8606    sql: "IN CLUSTER mz_catalog_server
8607ON mz_internal.mz_cluster_replica_metrics_history (replica_id)",
8608    is_retained_metrics_object: false,
8609};
8610
8611pub const MZ_CLUSTER_REPLICA_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8612    name: "mz_cluster_replica_history_ind",
8613    schema: MZ_INTERNAL_SCHEMA,
8614    oid: oid::INDEX_MZ_CLUSTER_REPLICA_HISTORY_IND_OID,
8615    sql: "IN CLUSTER mz_catalog_server
8616ON mz_internal.mz_cluster_replica_history (dropped_at)",
8617    is_retained_metrics_object: true,
8618};
8619
8620pub const MZ_CLUSTER_REPLICA_NAME_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8621    name: "mz_cluster_replica_name_history_ind",
8622    schema: MZ_INTERNAL_SCHEMA,
8623    oid: oid::INDEX_MZ_CLUSTER_REPLICA_NAME_HISTORY_IND_OID,
8624    sql: "IN CLUSTER mz_catalog_server
8625ON mz_internal.mz_cluster_replica_name_history (id)",
8626    is_retained_metrics_object: false,
8627};
8628
8629pub const MZ_OBJECT_LIFETIMES_IND: BuiltinIndex = BuiltinIndex {
8630    name: "mz_object_lifetimes_ind",
8631    schema: MZ_INTERNAL_SCHEMA,
8632    oid: oid::INDEX_MZ_OBJECT_LIFETIMES_IND_OID,
8633    sql: "IN CLUSTER mz_catalog_server
8634ON mz_internal.mz_object_lifetimes (id)",
8635    is_retained_metrics_object: false,
8636};
8637
8638pub const MZ_OBJECT_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8639    name: "mz_object_history_ind",
8640    schema: MZ_INTERNAL_SCHEMA,
8641    oid: oid::INDEX_MZ_OBJECT_HISTORY_IND_OID,
8642    sql: "IN CLUSTER mz_catalog_server
8643ON mz_internal.mz_object_history (id)",
8644    is_retained_metrics_object: false,
8645};
8646
8647pub const MZ_OBJECT_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
8648    name: "mz_object_dependencies_ind",
8649    schema: MZ_INTERNAL_SCHEMA,
8650    oid: oid::INDEX_MZ_OBJECT_DEPENDENCIES_IND_OID,
8651    sql: "IN CLUSTER mz_catalog_server
8652ON mz_internal.mz_object_dependencies (object_id)",
8653    is_retained_metrics_object: true,
8654};
8655
8656pub const MZ_COMPUTE_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
8657    name: "mz_compute_dependencies_ind",
8658    schema: MZ_INTERNAL_SCHEMA,
8659    oid: oid::INDEX_MZ_COMPUTE_DEPENDENCIES_IND_OID,
8660    sql: "IN CLUSTER mz_catalog_server
8661ON mz_internal.mz_compute_dependencies (dependency_id)",
8662    is_retained_metrics_object: false,
8663};
8664
8665pub const MZ_OBJECT_TRANSITIVE_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
8666    name: "mz_object_transitive_dependencies_ind",
8667    schema: MZ_INTERNAL_SCHEMA,
8668    oid: oid::INDEX_MZ_OBJECT_TRANSITIVE_DEPENDENCIES_IND_OID,
8669    sql: "IN CLUSTER mz_catalog_server
8670ON mz_internal.mz_object_transitive_dependencies (object_id)",
8671    is_retained_metrics_object: false,
8672};
8673
8674pub const MZ_FRONTIERS_IND: BuiltinIndex = BuiltinIndex {
8675    name: "mz_frontiers_ind",
8676    schema: MZ_INTERNAL_SCHEMA,
8677    oid: oid::INDEX_MZ_FRONTIERS_IND_OID,
8678    sql: "IN CLUSTER mz_catalog_server
8679ON mz_internal.mz_frontiers (object_id)",
8680    is_retained_metrics_object: false,
8681};
8682
8683pub const MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8684    name: "mz_wallclock_global_lag_recent_history_ind",
8685    schema: MZ_INTERNAL_SCHEMA,
8686    oid: oid::INDEX_MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_IND_OID,
8687    sql: "IN CLUSTER mz_catalog_server
8688ON mz_internal.mz_wallclock_global_lag_recent_history (object_id)",
8689    is_retained_metrics_object: false,
8690};
8691
8692pub const MZ_RECENT_ACTIVITY_LOG_THINNED_IND: BuiltinIndex = BuiltinIndex {
8693    name: "mz_recent_activity_log_thinned_ind",
8694    schema: MZ_INTERNAL_SCHEMA,
8695    oid: oid::INDEX_MZ_RECENT_ACTIVITY_LOG_THINNED_IND_OID,
8696    sql: "IN CLUSTER mz_catalog_server
8697-- sql_hash because we plan to join
8698-- this against mz_internal.mz_sql_text
8699ON mz_internal.mz_recent_activity_log_thinned (sql_hash)",
8700    is_retained_metrics_object: false,
8701};
8702
8703pub const MZ_WEBHOOK_SOURCES_IND: BuiltinIndex = BuiltinIndex {
8704    name: "mz_webhook_sources_ind",
8705    schema: MZ_INTERNAL_SCHEMA,
8706    oid: oid::INDEX_MZ_WEBHOOK_SOURCES_IND_OID,
8707    sql: "IN CLUSTER mz_catalog_server
8708ON mz_internal.mz_webhook_sources (id)",
8709    is_retained_metrics_object: true,
8710};
8711
8712pub const MZ_COMMENTS_IND: BuiltinIndex = BuiltinIndex {
8713    name: "mz_comments_ind",
8714    schema: MZ_INTERNAL_SCHEMA,
8715    oid: oid::INDEX_MZ_COMMENTS_IND_OID,
8716    sql: "IN CLUSTER mz_catalog_server
8717ON mz_internal.mz_comments (id)",
8718    is_retained_metrics_object: true,
8719};
8720
8721pub static MZ_ANALYTICS: BuiltinConnection = BuiltinConnection {
8722    name: "mz_analytics",
8723    schema: MZ_INTERNAL_SCHEMA,
8724    oid: oid::CONNECTION_MZ_ANALYTICS_OID,
8725    sql: "CREATE CONNECTION mz_internal.mz_analytics TO AWS (ASSUME ROLE ARN = '')",
8726    access: &[MzAclItem {
8727        grantee: MZ_SYSTEM_ROLE_ID,
8728        grantor: MZ_ANALYTICS_ROLE_ID,
8729        acl_mode: rbac::all_object_privileges(SystemObjectType::Object(ObjectType::Connection)),
8730    }],
8731    owner_id: &MZ_ANALYTICS_ROLE_ID,
8732    runtime_alterable: true,
8733};