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