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            ("swap_bytes", "Approximate swap usage, in bytes."),
2738        ]),
2739        is_retained_metrics_object: false,
2740        access: vec![PUBLIC_SELECT],
2741        ontology: None,
2742    });
2743
2744pub static MZ_CLUSTER_REPLICA_METRICS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2745    name: "mz_cluster_replica_metrics",
2746    schema: MZ_INTERNAL_SCHEMA,
2747    oid: oid::VIEW_MZ_CLUSTER_REPLICA_METRICS_OID,
2748    desc: RelationDesc::builder()
2749        .with_column("replica_id", SqlScalarType::String.nullable(false))
2750        .with_column("process_id", SqlScalarType::UInt64.nullable(false))
2751        .with_column("cpu_nano_cores", SqlScalarType::UInt64.nullable(true))
2752        .with_column("memory_bytes", SqlScalarType::UInt64.nullable(true))
2753        .with_column("disk_bytes", SqlScalarType::UInt64.nullable(true))
2754        .with_column("heap_bytes", SqlScalarType::UInt64.nullable(true))
2755        .with_column("heap_limit", SqlScalarType::UInt64.nullable(true))
2756        .with_column("swap_bytes", SqlScalarType::UInt64.nullable(true))
2757        .with_key(vec![0, 1])
2758        .finish(),
2759    column_comments: BTreeMap::from_iter([
2760        ("replica_id", "The ID of a cluster replica."),
2761        ("process_id", "The ID of a process within the replica."),
2762        (
2763            "cpu_nano_cores",
2764            "Approximate CPU usage, in billionths of a vCPU core.",
2765        ),
2766        ("memory_bytes", "Approximate RAM usage, in bytes."),
2767        ("disk_bytes", "Approximate disk usage, in bytes."),
2768        (
2769            "heap_bytes",
2770            "Approximate heap (RAM + swap) usage, in bytes.",
2771        ),
2772        ("heap_limit", "Available heap (RAM + swap) space, in bytes."),
2773        ("swap_bytes", "Approximate swap usage, in bytes."),
2774    ]),
2775    sql: "
2776SELECT
2777    DISTINCT ON (replica_id, process_id)
2778    replica_id,
2779    process_id,
2780    cpu_nano_cores,
2781    memory_bytes,
2782    disk_bytes,
2783    heap_bytes,
2784    heap_limit,
2785    swap_bytes
2786FROM mz_internal.mz_cluster_replica_metrics_history
2787JOIN mz_cluster_replicas r ON r.id = replica_id
2788ORDER BY replica_id, process_id, occurred_at DESC",
2789    access: vec![PUBLIC_SELECT],
2790    ontology: Some(Ontology {
2791        entity_name: "replica_metrics",
2792        description: "CPU and memory metrics per replica",
2793        links: &const {
2794            [OntologyLink {
2795                name: "metrics_of_replica",
2796                target: "replica",
2797                properties: LinkProperties::fk_typed(
2798                    "replica_id",
2799                    "id",
2800                    Cardinality::OneToOne,
2801                    mz_repr::SemanticType::CatalogItemId,
2802                ),
2803            }]
2804        },
2805        column_semantic_types: &const {
2806            [
2807                ("replica_id", SemanticType::ReplicaId),
2808                ("memory_bytes", SemanticType::ByteCount),
2809                ("disk_bytes", SemanticType::ByteCount),
2810                ("heap_bytes", SemanticType::ByteCount),
2811                ("heap_limit", SemanticType::ByteCount),
2812                ("swap_bytes", SemanticType::ByteCount),
2813            ]
2814        },
2815    }),
2816});
2817
2818pub static MZ_FRONTIERS: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
2819    name: "mz_frontiers",
2820    schema: MZ_INTERNAL_SCHEMA,
2821    oid: oid::SOURCE_MZ_FRONTIERS_OID,
2822    data_source: IntrospectionType::Frontiers.into(),
2823    desc: RelationDesc::builder()
2824        .with_column("object_id", SqlScalarType::String.nullable(false))
2825        .with_column("read_frontier", SqlScalarType::MzTimestamp.nullable(true))
2826        .with_column("write_frontier", SqlScalarType::MzTimestamp.nullable(true))
2827        .finish(),
2828    column_comments: BTreeMap::from_iter([
2829        (
2830            "object_id",
2831            "The ID of the source, sink, table, index, materialized view, or subscription.",
2832        ),
2833        (
2834            "read_frontier",
2835            "The earliest timestamp at which the output is still readable.",
2836        ),
2837        (
2838            "write_frontier",
2839            "The next timestamp at which the output may change.",
2840        ),
2841    ]),
2842    is_retained_metrics_object: false,
2843    access: vec![PUBLIC_SELECT],
2844    ontology: Some(Ontology {
2845        entity_name: "frontier",
2846        description: "Current read/write frontiers for sources, sinks, tables, materialized views, indexes, and subscriptions",
2847        links: &const {
2848            [OntologyLink {
2849                name: "frontier_of",
2850                target: "object",
2851                properties: LinkProperties::fk_mapped(
2852                    "object_id",
2853                    "id",
2854                    Cardinality::ManyToOne,
2855                    mz_repr::SemanticType::GlobalId,
2856                    "mz_internal.mz_object_global_ids",
2857                ),
2858            }]
2859        },
2860        column_semantic_types: &const {
2861            [
2862                ("object_id", SemanticType::GlobalId),
2863                ("read_frontier", SemanticType::MzTimestamp),
2864                ("write_frontier", SemanticType::MzTimestamp),
2865            ]
2866        },
2867    }),
2868});
2869
2870/// DEPRECATED and scheduled for removal! Use `mz_frontiers` instead.
2871pub static MZ_GLOBAL_FRONTIERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2872    name: "mz_global_frontiers",
2873    schema: MZ_INTERNAL_SCHEMA,
2874    oid: oid::VIEW_MZ_GLOBAL_FRONTIERS_OID,
2875    desc: RelationDesc::builder()
2876        .with_column("object_id", SqlScalarType::String.nullable(false))
2877        .with_column("time", SqlScalarType::MzTimestamp.nullable(false))
2878        .finish(),
2879    column_comments: BTreeMap::new(),
2880    sql: "
2881SELECT object_id, write_frontier AS time
2882FROM mz_internal.mz_frontiers
2883WHERE write_frontier IS NOT NULL",
2884    access: vec![PUBLIC_SELECT],
2885    ontology: None,
2886});
2887
2888pub static MZ_WALLCLOCK_LAG_HISTORY: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
2889    name: "mz_wallclock_lag_history",
2890    schema: MZ_INTERNAL_SCHEMA,
2891    oid: oid::SOURCE_MZ_WALLCLOCK_LAG_HISTORY_OID,
2892    desc: WALLCLOCK_LAG_HISTORY_DESC.clone(),
2893    data_source: IntrospectionType::WallclockLagHistory.into(),
2894    column_comments: BTreeMap::from_iter([
2895        (
2896            "object_id",
2897            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
2898        ),
2899        (
2900            "replica_id",
2901            "The ID of a replica computing the object, or `NULL` for persistent objects. Corresponds to `mz_cluster_replicas.id`.",
2902        ),
2903        (
2904            "lag",
2905            "The amount of time the object's write frontier lags behind wallclock time.",
2906        ),
2907        (
2908            "occurred_at",
2909            "Wall-clock timestamp at which the event occurred.",
2910        ),
2911    ]),
2912    is_retained_metrics_object: false,
2913    access: vec![PUBLIC_SELECT],
2914    ontology: Some(Ontology {
2915        entity_name: "wallclock_lag_event",
2916        description: "Historical wallclock lag per object",
2917        links: &const {
2918            [
2919                OntologyLink {
2920                    name: "measures_lag_of",
2921                    target: "object",
2922                    properties: LinkProperties::measures_mapped(
2923                        "object_id",
2924                        "id",
2925                        "wallclock_lag",
2926                        mz_repr::SemanticType::GlobalId,
2927                        "mz_internal.mz_object_global_ids",
2928                    ),
2929                },
2930                OntologyLink {
2931                    name: "on_replica",
2932                    target: "replica",
2933                    properties: LinkProperties::fk_nullable(
2934                        "replica_id",
2935                        "id",
2936                        Cardinality::ManyToOne,
2937                    ),
2938                },
2939            ]
2940        },
2941        column_semantic_types: &const {
2942            [
2943                ("object_id", SemanticType::GlobalId),
2944                ("replica_id", SemanticType::ReplicaId),
2945                ("occurred_at", SemanticType::WallclockTimestamp),
2946            ]
2947        },
2948    }),
2949});
2950
2951pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
2952    name: "mz_wallclock_global_lag_history",
2953    schema: MZ_INTERNAL_SCHEMA,
2954    oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_HISTORY_OID,
2955    desc: RelationDesc::builder()
2956        .with_column("object_id", SqlScalarType::String.nullable(false))
2957        .with_column("lag", SqlScalarType::Interval.nullable(true))
2958        .with_column(
2959            "occurred_at",
2960            SqlScalarType::TimestampTz { precision: None }.nullable(false),
2961        )
2962        .with_key(vec![0, 2])
2963        .finish(),
2964    column_comments: BTreeMap::from_iter([
2965        (
2966            "object_id",
2967            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
2968        ),
2969        (
2970            "lag",
2971            "The minimum wallclock lag observed for the object during the minute.",
2972        ),
2973        (
2974            "occurred_at",
2975            "The minute-aligned timestamp of the observation.",
2976        ),
2977    ]),
2978    sql: "
2979WITH times_binned AS (
2980    SELECT
2981        object_id,
2982        lag,
2983        date_trunc('minute', occurred_at) AS occurred_at
2984    FROM mz_internal.mz_wallclock_lag_history
2985)
2986SELECT
2987    object_id,
2988    min(lag) AS lag,
2989    occurred_at
2990FROM times_binned
2991GROUP BY object_id, occurred_at
2992OPTIONS (AGGREGATE INPUT GROUP SIZE = 1)",
2993    access: vec![PUBLIC_SELECT],
2994    ontology: Some(Ontology {
2995        entity_name: "wallclock_global_lag_event",
2996        description: "Historical global wallclock lag",
2997        links: &const {
2998            [OntologyLink {
2999                name: "lag_of",
3000                target: "object_global_id",
3001                properties: LinkProperties::fk("object_id", "global_id", Cardinality::ManyToOne),
3002            }]
3003        },
3004        column_semantic_types: &const {
3005            [
3006                ("object_id", SemanticType::GlobalId),
3007                ("occurred_at", SemanticType::WallclockTimestamp),
3008            ]
3009        },
3010    }),
3011});
3012
3013pub static MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| {
3014    BuiltinView {
3015        name: "mz_wallclock_global_lag_recent_history",
3016        schema: MZ_INTERNAL_SCHEMA,
3017        oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_OID,
3018        desc: RelationDesc::builder()
3019            .with_column("object_id", SqlScalarType::String.nullable(false))
3020            .with_column("lag", SqlScalarType::Interval.nullable(true))
3021            .with_column(
3022                "occurred_at",
3023                SqlScalarType::TimestampTz { precision: None }.nullable(false),
3024            )
3025            .with_key(vec![0, 2])
3026            .finish(),
3027        column_comments: BTreeMap::from_iter([
3028            (
3029                "object_id",
3030                "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
3031            ),
3032            (
3033                "lag",
3034                "The minimum wallclock lag observed for the object during the minute.",
3035            ),
3036            (
3037                "occurred_at",
3038                "The minute-aligned timestamp of the observation.",
3039            ),
3040        ]),
3041        sql: "
3042SELECT object_id, lag, occurred_at
3043FROM mz_internal.mz_wallclock_global_lag_history
3044WHERE occurred_at + '1 day' > mz_now()",
3045        access: vec![PUBLIC_SELECT],
3046        ontology: None,
3047    }
3048});
3049
3050pub static MZ_WALLCLOCK_GLOBAL_LAG: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3051    name: "mz_wallclock_global_lag",
3052    schema: MZ_INTERNAL_SCHEMA,
3053    oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_OID,
3054    desc: RelationDesc::builder()
3055        .with_column("object_id", SqlScalarType::String.nullable(false))
3056        .with_column("lag", SqlScalarType::Interval.nullable(true))
3057        .with_key(vec![0])
3058        .finish(),
3059    column_comments: BTreeMap::from_iter([
3060        (
3061            "object_id",
3062            "The ID of the table, source, materialized view, index, or sink. Corresponds to `mz_objects.id`.",
3063        ),
3064        (
3065            "lag",
3066            "The amount of time the object's write frontier lags behind wallclock time.",
3067        ),
3068    ]),
3069    sql: "
3070SELECT DISTINCT ON (object_id) object_id, lag
3071FROM mz_internal.mz_wallclock_global_lag_recent_history
3072WHERE occurred_at + '5 minutes' > mz_now()
3073ORDER BY object_id, occurred_at DESC",
3074    access: vec![PUBLIC_SELECT],
3075    ontology: Some(Ontology {
3076        entity_name: "wallclock_global_lag",
3077        description: "Current wallclock lag aggregated across replicas",
3078        links: &const {
3079            [OntologyLink {
3080                name: "measures_global_lag_of",
3081                target: "object",
3082                properties: LinkProperties::measures_mapped(
3083                    "object_id",
3084                    "id",
3085                    "wallclock_lag_global",
3086                    mz_repr::SemanticType::GlobalId,
3087                    "mz_internal.mz_object_global_ids",
3088                ),
3089            }]
3090        },
3091        column_semantic_types: &[("object_id", SemanticType::GlobalId)],
3092    }),
3093});
3094
3095pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW: LazyLock<BuiltinSource> =
3096    LazyLock::new(|| BuiltinSource {
3097        name: "mz_wallclock_global_lag_histogram_raw",
3098        schema: MZ_INTERNAL_SCHEMA,
3099        oid: oid::SOURCE_MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW_OID,
3100        desc: WALLCLOCK_GLOBAL_LAG_HISTOGRAM_RAW_DESC.clone(),
3101        column_comments: BTreeMap::new(),
3102        data_source: IntrospectionType::WallclockLagHistogram.into(),
3103        is_retained_metrics_object: false,
3104        access: vec![PUBLIC_SELECT],
3105        ontology: None,
3106    });
3107
3108pub static MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM: LazyLock<BuiltinView> =
3109    LazyLock::new(|| BuiltinView {
3110        name: "mz_wallclock_global_lag_histogram",
3111        schema: MZ_INTERNAL_SCHEMA,
3112        oid: oid::VIEW_MZ_WALLCLOCK_GLOBAL_LAG_HISTOGRAM_OID,
3113        desc: RelationDesc::builder()
3114            .with_column(
3115                "period_start",
3116                SqlScalarType::TimestampTz { precision: None }.nullable(false),
3117            )
3118            .with_column(
3119                "period_end",
3120                SqlScalarType::TimestampTz { precision: None }.nullable(false),
3121            )
3122            .with_column("object_id", SqlScalarType::String.nullable(false))
3123            .with_column("lag_seconds", SqlScalarType::UInt64.nullable(true))
3124            .with_column("labels", SqlScalarType::Jsonb.nullable(false))
3125            .with_column("count", SqlScalarType::Int64.nullable(false))
3126            .with_key(vec![0, 1, 2, 3, 4])
3127            .finish(),
3128        column_comments: BTreeMap::new(),
3129        sql: "
3130SELECT *, count(*) AS count
3131FROM mz_internal.mz_wallclock_global_lag_histogram_raw
3132GROUP BY period_start, period_end, object_id, lag_seconds, labels",
3133        access: vec![PUBLIC_SELECT],
3134        ontology: None,
3135    });
3136
3137pub static MZ_MATERIALIZED_VIEW_REFRESHES: LazyLock<BuiltinSource> = LazyLock::new(|| {
3138    BuiltinSource {
3139        name: "mz_materialized_view_refreshes",
3140        schema: MZ_INTERNAL_SCHEMA,
3141        oid: oid::SOURCE_MZ_MATERIALIZED_VIEW_REFRESHES_OID,
3142        data_source: DataSourceDesc::Introspection(
3143            IntrospectionType::ComputeMaterializedViewRefreshes,
3144        ),
3145        desc: RelationDesc::builder()
3146            .with_column(
3147                "materialized_view_id",
3148                SqlScalarType::String.nullable(false),
3149            )
3150            .with_column(
3151                "last_completed_refresh",
3152                SqlScalarType::MzTimestamp.nullable(true),
3153            )
3154            .with_column("next_refresh", SqlScalarType::MzTimestamp.nullable(true))
3155            .finish(),
3156        column_comments: BTreeMap::from_iter([
3157            (
3158                "materialized_view_id",
3159                "The ID of the materialized view. Corresponds to `mz_catalog.mz_materialized_views.id`",
3160            ),
3161            (
3162                "last_completed_refresh",
3163                "The time of the last successfully completed refresh. `NULL` if the materialized view hasn't completed any refreshes yet.",
3164            ),
3165            (
3166                "next_refresh",
3167                "The time of the next scheduled refresh. `NULL` if the materialized view has no future scheduled refreshes.",
3168            ),
3169        ]),
3170        is_retained_metrics_object: false,
3171        access: vec![PUBLIC_SELECT],
3172        ontology: None,
3173    }
3174});
3175
3176pub static MZ_SUBSCRIPTIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3177    name: "mz_subscriptions",
3178    schema: MZ_INTERNAL_SCHEMA,
3179    oid: oid::TABLE_MZ_SUBSCRIPTIONS_OID,
3180    desc: RelationDesc::builder()
3181        .with_column("id", SqlScalarType::String.nullable(false))
3182        .with_column("session_id", SqlScalarType::Uuid.nullable(false))
3183        .with_column("cluster_id", SqlScalarType::String.nullable(false))
3184        .with_column(
3185            "created_at",
3186            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3187        )
3188        .with_column(
3189            "referenced_object_ids",
3190            SqlScalarType::List {
3191                element_type: Box::new(SqlScalarType::String),
3192                custom_id: None,
3193            }
3194            .nullable(false),
3195        )
3196        .finish(),
3197    column_comments: BTreeMap::from_iter([
3198        ("id", "The ID of the subscription."),
3199        (
3200            "session_id",
3201            "The ID of the session that runs the subscription. Corresponds to `mz_sessions.id`.",
3202        ),
3203        (
3204            "cluster_id",
3205            "The ID of the cluster on which the subscription is running. Corresponds to `mz_clusters.id`.",
3206        ),
3207        (
3208            "created_at",
3209            "The time at which the subscription was created.",
3210        ),
3211        (
3212            "referenced_object_ids",
3213            "The IDs of objects referenced by the subscription. Corresponds to `mz_objects.id`",
3214        ),
3215    ]),
3216    is_retained_metrics_object: false,
3217    access: vec![PUBLIC_SELECT],
3218    ontology: Some(Ontology {
3219        entity_name: "subscription",
3220        description: "Active SUBSCRIBE operations",
3221        links: &const {
3222            [
3223                OntologyLink {
3224                    name: "uses_session",
3225                    target: "session",
3226                    properties: LinkProperties::fk("session_id", "id", Cardinality::ManyToOne),
3227                },
3228                OntologyLink {
3229                    name: "in_active_session",
3230                    target: "active_session",
3231                    properties: LinkProperties::fk_nullable(
3232                        "session_id",
3233                        "id",
3234                        Cardinality::ManyToOne,
3235                    ),
3236                },
3237                OntologyLink {
3238                    name: "belongs_to_cluster",
3239                    target: "cluster",
3240                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
3241                },
3242            ]
3243        },
3244        column_semantic_types: &const {
3245            [
3246                ("id", SemanticType::CatalogItemId),
3247                ("cluster_id", SemanticType::ClusterId),
3248            ]
3249        },
3250    }),
3251});
3252
3253pub static MZ_SESSIONS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3254    name: "mz_sessions",
3255    schema: MZ_INTERNAL_SCHEMA,
3256    oid: oid::TABLE_MZ_SESSIONS_OID,
3257    desc: RelationDesc::builder()
3258        .with_column("id", SqlScalarType::Uuid.nullable(false))
3259        .with_column("connection_id", SqlScalarType::UInt32.nullable(false))
3260        .with_column("role_id", SqlScalarType::String.nullable(false))
3261        .with_column("client_ip", SqlScalarType::String.nullable(true))
3262        .with_column(
3263            "connected_at",
3264            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3265        )
3266        .finish(),
3267    column_comments: BTreeMap::from_iter([
3268        ("id", "The globally unique ID of the session."),
3269        (
3270            "connection_id",
3271            "The connection ID of the session. Unique only for active sessions and can be recycled. Corresponds to `pg_backend_pid()`.",
3272        ),
3273        (
3274            "role_id",
3275            "The role ID of the role that the session is logged in as. Corresponds to `mz_catalog.mz_roles`.",
3276        ),
3277        (
3278            "client_ip",
3279            "The IP address of the client that initiated the session.",
3280        ),
3281        (
3282            "connected_at",
3283            "The time at which the session connected to the system.",
3284        ),
3285    ]),
3286    is_retained_metrics_object: false,
3287    access: vec![PUBLIC_SELECT],
3288    ontology: Some(Ontology {
3289        entity_name: "active_session",
3290        description: "Currently active sessions",
3291        links: &const {
3292            [OntologyLink {
3293                name: "logged_in_as",
3294                target: "role",
3295                properties: LinkProperties::fk("role_id", "id", Cardinality::ManyToOne),
3296            }]
3297        },
3298        column_semantic_types: &[("role_id", SemanticType::RoleId)],
3299    }),
3300});
3301
3302pub static MZ_OVERRIDDEN_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3303    LazyLock::new(|| BuiltinMaterializedView {
3304        name: "mz_overridden_system_parameters",
3305        schema: MZ_INTERNAL_SCHEMA,
3306        oid: oid::MV_MZ_OVERRIDDEN_SYSTEM_PARAMETERS_OID,
3307        desc: RelationDesc::builder()
3308            .with_column("name", SqlScalarType::String.nullable(false))
3309            .with_column("value", SqlScalarType::String.nullable(false))
3310            .finish(),
3311        column_comments: BTreeMap::from_iter([
3312            ("name", "The name of the system parameter."),
3313            (
3314                "value",
3315                "The environment-wide value of the system parameter.",
3316            ),
3317        ]),
3318        // Projects the durable `system_configurations` collection (the
3319        // `ALTER SYSTEM` set) out of `mz_catalog_raw` (the durable catalog as
3320        // JSON): the key is `{name}` and the value is `{value}`. This surfaces
3321        // only parameters with an explicit environment-wide override, mirroring
3322        // the cluster- and replica-scoped views. Parameters left at their
3323        // default are absent.
3324        sql: "
3325IN CLUSTER mz_catalog_server
3326WITH (
3327    ASSERT NOT NULL name,
3328    ASSERT NOT NULL value
3329) AS
3330SELECT
3331    data->'key'->>'name' AS name,
3332    data->'value'->>'value' AS value
3333FROM mz_internal.mz_catalog_raw
3334WHERE data->>'kind' = 'ServerConfiguration'",
3335        is_retained_metrics_object: false,
3336        access: vec![PUBLIC_SELECT],
3337        ontology: Some(Ontology {
3338            entity_name: "system_parameter",
3339            description: "Environment-wide system parameter overrides",
3340            links: &const { [] },
3341            column_semantic_types: &[],
3342        }),
3343    });
3344
3345pub static MZ_CLUSTER_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3346    LazyLock::new(|| BuiltinMaterializedView {
3347        name: "mz_cluster_system_parameters",
3348        schema: MZ_INTERNAL_SCHEMA,
3349        oid: oid::MV_MZ_CLUSTER_SYSTEM_PARAMETERS_OID,
3350        desc: RelationDesc::builder()
3351            .with_column("cluster_id", SqlScalarType::String.nullable(false))
3352            .with_column("name", SqlScalarType::String.nullable(false))
3353            .with_column("value", SqlScalarType::String.nullable(false))
3354            .finish(),
3355        column_comments: BTreeMap::from_iter([
3356            (
3357                "cluster_id",
3358                "The ID of the cluster. Corresponds to `mz_clusters.id`.",
3359            ),
3360            ("name", "The name of the cluster-coherent system parameter."),
3361            ("value", "The cluster-scoped value of the system parameter."),
3362        ]),
3363        // Projects the durable `cluster_system_configurations` collection out of
3364        // `mz_catalog_raw` (the durable catalog as JSON): the key is
3365        // `{cluster_id, name}` and the value is `{value}`.
3366        sql: "
3367IN CLUSTER mz_catalog_server
3368WITH (
3369    ASSERT NOT NULL cluster_id,
3370    ASSERT NOT NULL name,
3371    ASSERT NOT NULL value
3372) AS
3373SELECT
3374    mz_internal.parse_catalog_id(data->'key'->'cluster_id') AS cluster_id,
3375    data->'key'->>'name' AS name,
3376    data->'value'->>'value' AS value
3377FROM mz_internal.mz_catalog_raw
3378WHERE data->>'kind' = 'ClusterSystemConfiguration'",
3379        is_retained_metrics_object: false,
3380        access: vec![PUBLIC_SELECT],
3381        ontology: Some(Ontology {
3382            entity_name: "cluster_system_parameter",
3383            description: "Cluster-coherent system parameter overrides",
3384            links: &const {
3385                [OntologyLink {
3386                    name: "scoped_to_cluster",
3387                    target: "cluster",
3388                    properties: LinkProperties::fk_typed(
3389                        "cluster_id",
3390                        "id",
3391                        Cardinality::ManyToOne,
3392                        mz_repr::SemanticType::ClusterId,
3393                    ),
3394                }]
3395            },
3396            column_semantic_types: &[("cluster_id", SemanticType::ClusterId)],
3397        }),
3398    });
3399
3400pub static MZ_REPLICA_SYSTEM_PARAMETERS: LazyLock<BuiltinMaterializedView> =
3401    LazyLock::new(|| BuiltinMaterializedView {
3402        name: "mz_replica_system_parameters",
3403        schema: MZ_INTERNAL_SCHEMA,
3404        oid: oid::MV_MZ_REPLICA_SYSTEM_PARAMETERS_OID,
3405        desc: RelationDesc::builder()
3406            .with_column("replica_id", SqlScalarType::String.nullable(false))
3407            .with_column("name", SqlScalarType::String.nullable(false))
3408            .with_column("value", SqlScalarType::String.nullable(false))
3409            .finish(),
3410        column_comments: BTreeMap::from_iter([
3411            (
3412                "replica_id",
3413                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
3414            ),
3415            ("name", "The name of the replica-local system parameter."),
3416            ("value", "The replica-scoped value of the system parameter."),
3417        ]),
3418        // Projects the durable `replica_system_configurations` collection out of
3419        // `mz_catalog_raw` (the durable catalog as JSON): the key is
3420        // `{replica_id, name}` and the value is `{value}`.
3421        sql: "
3422IN CLUSTER mz_catalog_server
3423WITH (
3424    ASSERT NOT NULL replica_id,
3425    ASSERT NOT NULL name,
3426    ASSERT NOT NULL value
3427) AS
3428SELECT
3429    mz_internal.parse_catalog_id(data->'key'->'replica_id') AS replica_id,
3430    data->'key'->>'name' AS name,
3431    data->'value'->>'value' AS value
3432FROM mz_internal.mz_catalog_raw
3433WHERE data->>'kind' = 'ReplicaSystemConfiguration'",
3434        is_retained_metrics_object: false,
3435        access: vec![PUBLIC_SELECT],
3436        ontology: Some(Ontology {
3437            entity_name: "replica_system_parameter",
3438            description: "Replica-local system parameter overrides",
3439            links: &const {
3440                [OntologyLink {
3441                    name: "scoped_to_replica",
3442                    target: "replica",
3443                    properties: LinkProperties::fk_typed(
3444                        "replica_id",
3445                        "id",
3446                        Cardinality::ManyToOne,
3447                        mz_repr::SemanticType::ReplicaId,
3448                    ),
3449                }]
3450            },
3451            column_semantic_types: &[("replica_id", SemanticType::ReplicaId)],
3452        }),
3453    });
3454
3455pub static MZ_COMMENTS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
3456    BuiltinMaterializedView {
3457        name: "mz_comments",
3458        schema: MZ_INTERNAL_SCHEMA,
3459        oid: oid::MV_MZ_COMMENTS_OID,
3460        desc: RelationDesc::builder()
3461            .with_column("id", SqlScalarType::String.nullable(false))
3462            .with_column("object_type", SqlScalarType::String.nullable(false))
3463            .with_column("object_sub_id", SqlScalarType::Int32.nullable(true))
3464            .with_column("comment", SqlScalarType::String.nullable(false))
3465            .finish(),
3466        column_comments: BTreeMap::from_iter([
3467            (
3468                "id",
3469                "The ID of the object. Corresponds to `mz_objects.id`.",
3470            ),
3471            (
3472                "object_type",
3473                "The type of object the comment is associated with.",
3474            ),
3475            (
3476                "object_sub_id",
3477                "For a comment on a column of a relation, the column number. `NULL` for other object types.",
3478            ),
3479            ("comment", "The comment itself."),
3480        ]),
3481        // Variant keys ('Table', 'View', etc.) are the serde JSON form of
3482        // `proto::CommentObject` (in `mz-catalog-protos`). `object_type`
3483        // values are the kebab-case `Display` of `audit_log::ObjectType`.
3484        //
3485        // Schema and ClusterReplica are nested structs in `mz_catalog_raw`.
3486        // We reach one level deeper for them: Schema picks `schema.Id` and
3487        // drops the database, ClusterReplica picks `replica_id` and drops
3488        // the cluster. That matches what `mz_objects.id` holds for those
3489        // rows.
3490        //
3491        // New variants on `proto::CommentObject` need branches in both CASE
3492        // expressions below.
3493        sql: "
3494IN CLUSTER mz_catalog_server
3495WITH (
3496    ASSERT NOT NULL id,
3497    ASSERT NOT NULL object_type,
3498    ASSERT NOT NULL comment
3499) AS
3500WITH commented AS (
3501    SELECT data->'key'->'object' AS obj,
3502           data->'key'->'sub_component' AS sub,
3503           data->'value'->>'comment' AS comment
3504    FROM mz_internal.mz_catalog_raw
3505    WHERE data->>'kind' = 'Comment'
3506)
3507SELECT
3508    CASE
3509        WHEN obj ? 'Table'            THEN mz_internal.parse_catalog_id(obj->'Table')
3510        WHEN obj ? 'View'             THEN mz_internal.parse_catalog_id(obj->'View')
3511        WHEN obj ? 'MaterializedView' THEN mz_internal.parse_catalog_id(obj->'MaterializedView')
3512        WHEN obj ? 'Source'           THEN mz_internal.parse_catalog_id(obj->'Source')
3513        WHEN obj ? 'Sink'             THEN mz_internal.parse_catalog_id(obj->'Sink')
3514        WHEN obj ? 'Index'            THEN mz_internal.parse_catalog_id(obj->'Index')
3515        WHEN obj ? 'Func'             THEN mz_internal.parse_catalog_id(obj->'Func')
3516        WHEN obj ? 'Connection'       THEN mz_internal.parse_catalog_id(obj->'Connection')
3517        WHEN obj ? 'Type'             THEN mz_internal.parse_catalog_id(obj->'Type')
3518        WHEN obj ? 'Secret'           THEN mz_internal.parse_catalog_id(obj->'Secret')
3519        WHEN obj ? 'Role'             THEN mz_internal.parse_catalog_id(obj->'Role')
3520        WHEN obj ? 'Database'         THEN mz_internal.parse_catalog_id(obj->'Database')
3521        WHEN obj ? 'Schema'           THEN mz_internal.parse_catalog_id(obj->'Schema'->'schema'->'Id')
3522        WHEN obj ? 'Cluster'          THEN mz_internal.parse_catalog_id(obj->'Cluster')
3523        WHEN obj ? 'ClusterReplica'   THEN mz_internal.parse_catalog_id(obj->'ClusterReplica'->'replica_id')
3524        WHEN obj ? 'NetworkPolicy'    THEN mz_internal.parse_catalog_id(obj->'NetworkPolicy')
3525    END                                                              AS id,
3526    CASE
3527        WHEN obj ? 'Table'            THEN 'table'
3528        WHEN obj ? 'View'             THEN 'view'
3529        WHEN obj ? 'MaterializedView' THEN 'materialized-view'
3530        WHEN obj ? 'Source'           THEN 'source'
3531        WHEN obj ? 'Sink'             THEN 'sink'
3532        WHEN obj ? 'Index'            THEN 'index'
3533        WHEN obj ? 'Func'             THEN 'func'
3534        WHEN obj ? 'Connection'       THEN 'connection'
3535        WHEN obj ? 'Type'             THEN 'type'
3536        WHEN obj ? 'Secret'           THEN 'secret'
3537        WHEN obj ? 'Role'             THEN 'role'
3538        WHEN obj ? 'Database'         THEN 'database'
3539        WHEN obj ? 'Schema'           THEN 'schema'
3540        WHEN obj ? 'Cluster'          THEN 'cluster'
3541        WHEN obj ? 'ClusterReplica'   THEN 'cluster-replica'
3542        WHEN obj ? 'NetworkPolicy'    THEN 'network-policy'
3543    END                                                              AS object_type,
3544    (sub->'ColumnPos')::int4                                          AS object_sub_id,
3545    comment
3546FROM commented",
3547        is_retained_metrics_object: false,
3548        access: vec![PUBLIC_SELECT],
3549        ontology: Some(Ontology {
3550            entity_name: "comment",
3551            description: "A COMMENT ON annotation for a catalog object or column",
3552            links: &const {
3553                [OntologyLink {
3554                    name: "comment_on",
3555                    target: "object",
3556                    properties: LinkProperties::fk_typed(
3557                        "id",
3558                        "id",
3559                        Cardinality::ManyToOne,
3560                        mz_repr::SemanticType::CatalogItemId,
3561                    ),
3562                }]
3563            },
3564            column_semantic_types: &const {
3565                [
3566                    ("id", SemanticType::CatalogItemId),
3567                    ("object_type", SemanticType::ObjectType),
3568                ]
3569            },
3570        }),
3571    }
3572});
3573
3574pub static MZ_SOURCE_REFERENCES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3575    name: "mz_source_references",
3576    schema: MZ_INTERNAL_SCHEMA,
3577    oid: oid::TABLE_MZ_SOURCE_REFERENCES_OID,
3578    desc: RelationDesc::builder()
3579        .with_column("source_id", SqlScalarType::String.nullable(false))
3580        .with_column("namespace", SqlScalarType::String.nullable(true))
3581        .with_column("name", SqlScalarType::String.nullable(false))
3582        .with_column(
3583            "updated_at",
3584            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3585        )
3586        .with_column(
3587            "columns",
3588            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
3589        )
3590        .finish(),
3591    column_comments: BTreeMap::new(),
3592    is_retained_metrics_object: false,
3593    access: vec![PUBLIC_SELECT],
3594    ontology: Some(Ontology {
3595        entity_name: "source_reference",
3596        description: "External references tracked by sources",
3597        links: &const {
3598            [OntologyLink {
3599                name: "references_source",
3600                target: "source",
3601                properties: LinkProperties::fk("source_id", "id", Cardinality::ManyToOne),
3602            }]
3603        },
3604        column_semantic_types: &[("source_id", SemanticType::CatalogItemId)],
3605    }),
3606});
3607
3608pub static MZ_WEBHOOKS_SOURCES: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3609    name: "mz_webhook_sources",
3610    schema: MZ_INTERNAL_SCHEMA,
3611    oid: oid::TABLE_MZ_WEBHOOK_SOURCES_OID,
3612    desc: RelationDesc::builder()
3613        .with_column("id", SqlScalarType::String.nullable(false))
3614        .with_column("name", SqlScalarType::String.nullable(false))
3615        .with_column("url", SqlScalarType::String.nullable(false))
3616        .finish(),
3617    column_comments: BTreeMap::from_iter([
3618        (
3619            "id",
3620            "The ID of the webhook source. Corresponds to `mz_sources.id`.",
3621        ),
3622        ("name", "The name of the webhook source."),
3623        (
3624            "url",
3625            "The URL which can be used to send events to the source.",
3626        ),
3627    ]),
3628    is_retained_metrics_object: false,
3629    access: vec![PUBLIC_SELECT],
3630    ontology: Some(Ontology {
3631        entity_name: "webhook_source",
3632        description: "Webhook source configuration",
3633        links: &const {
3634            [OntologyLink {
3635                name: "details_of",
3636                target: "source",
3637                properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
3638            }]
3639        },
3640        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3641    }),
3642});
3643
3644pub static MZ_METRIC_SINKS: LazyLock<BuiltinMaterializedView> = LazyLock::new(|| {
3645    BuiltinMaterializedView {
3646        name: "mz_metric_sinks",
3647        schema: MZ_INTERNAL_SCHEMA,
3648        oid: oid::MV_MZ_METRIC_SINKS_OID,
3649        desc: RelationDesc::builder()
3650            .with_column("id", SqlScalarType::String.nullable(false))
3651            .with_column("oid", SqlScalarType::Oid.nullable(false))
3652            .with_column("schema_id", SqlScalarType::String.nullable(false))
3653            .with_column("name", SqlScalarType::String.nullable(false))
3654            .with_column("from_id", SqlScalarType::String.nullable(false))
3655            .with_column("cluster_id", SqlScalarType::String.nullable(false))
3656            .with_column("owner_id", SqlScalarType::String.nullable(false))
3657            .with_key(vec![0])
3658            .with_key(vec![1])
3659            .finish(),
3660        column_comments: BTreeMap::from_iter([
3661            ("id", "Materialize's unique ID for the metric sink."),
3662            ("oid", "A PostgreSQL-compatible OID for the metric sink."),
3663            (
3664                "schema_id",
3665                "The ID of the schema to which the metric sink belongs. Corresponds to `mz_schemas.id`.",
3666            ),
3667            ("name", "The name of the metric sink."),
3668            (
3669                "from_id",
3670                "The ID of the relation the metric sink reads. Corresponds to `mz_objects.id`.",
3671            ),
3672            (
3673                "cluster_id",
3674                "The ID of the cluster maintaining the metric sink. Corresponds to `mz_clusters.id`.",
3675            ),
3676            (
3677                "owner_id",
3678                "The role ID of the owner of the metric sink. Corresponds to `mz_roles.id`.",
3679            ),
3680        ]),
3681        sql: "
3682IN CLUSTER mz_catalog_server
3683WITH (
3684    ASSERT NOT NULL id,
3685    ASSERT NOT NULL oid,
3686    ASSERT NOT NULL schema_id,
3687    ASSERT NOT NULL name,
3688    ASSERT NOT NULL from_id,
3689    ASSERT NOT NULL cluster_id,
3690    ASSERT NOT NULL owner_id
3691) AS
3692SELECT
3693    mz_internal.parse_catalog_id(data->'key'->'gid') AS id,
3694    (data->'value'->>'oid')::oid AS oid,
3695    mz_internal.parse_catalog_id(data->'value'->'schema_id') AS schema_id,
3696    data->'value'->>'name' AS name,
3697    parsed->>'from_id' AS from_id,
3698    parsed->>'cluster_id' AS cluster_id,
3699    mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id
3700FROM
3701    mz_internal.mz_catalog_raw
3702    CROSS JOIN LATERAL (
3703        SELECT mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')
3704    ) AS l(parsed)
3705WHERE
3706    data->>'kind' = 'Item' AND
3707    parsed->>'type' = 'metric-sink'",
3708        is_retained_metrics_object: false,
3709        access: vec![PUBLIC_SELECT],
3710        ontology: Some(Ontology {
3711            entity_name: "metric-sink",
3712            description: "A sink that exports metrics about a relation",
3713            links: &const {
3714                [
3715                    OntologyLink {
3716                        name: "in_schema",
3717                        target: "schema",
3718                        properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
3719                    },
3720                    OntologyLink {
3721                        name: "reads_relation",
3722                        target: "relation",
3723                        properties: LinkProperties::fk("from_id", "id", Cardinality::ManyToOne),
3724                    },
3725                    OntologyLink {
3726                        name: "runs_on_cluster",
3727                        target: "cluster",
3728                        properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
3729                    },
3730                    OntologyLink {
3731                        name: "owned_by",
3732                        target: "role",
3733                        properties: LinkProperties::fk("owner_id", "id", Cardinality::ManyToOne),
3734                    },
3735                ]
3736            },
3737            column_semantic_types: &const {
3738                [
3739                    ("id", SemanticType::CatalogItemId),
3740                    ("oid", SemanticType::OID),
3741                    ("schema_id", SemanticType::SchemaId),
3742                    ("from_id", SemanticType::CatalogItemId),
3743                    ("cluster_id", SemanticType::ClusterId),
3744                    ("owner_id", SemanticType::RoleId),
3745                ]
3746            },
3747        }),
3748    }
3749});
3750
3751pub const MZ_METRIC_SINKS_IND: BuiltinIndex = BuiltinIndex {
3752    name: "mz_metric_sinks_ind",
3753    schema: MZ_INTERNAL_SCHEMA,
3754    oid: oid::INDEX_MZ_METRIC_SINKS_IND_OID,
3755    sql: "IN CLUSTER mz_catalog_server
3756ON mz_internal.mz_metric_sinks (id)",
3757    is_retained_metrics_object: false,
3758};
3759
3760pub static MZ_HISTORY_RETENTION_STRATEGIES: LazyLock<BuiltinTable> = LazyLock::new(|| {
3761    BuiltinTable {
3762        name: "mz_history_retention_strategies",
3763        schema: MZ_INTERNAL_SCHEMA,
3764        oid: oid::TABLE_MZ_HISTORY_RETENTION_STRATEGIES_OID,
3765        desc: RelationDesc::builder()
3766            .with_column("id", SqlScalarType::String.nullable(false))
3767            .with_column("strategy", SqlScalarType::String.nullable(false))
3768            .with_column("value", SqlScalarType::Jsonb.nullable(false))
3769            .finish(),
3770        column_comments: BTreeMap::from_iter([
3771            ("id", "The ID of the object."),
3772            (
3773                "strategy",
3774                "The strategy. `FOR` is the only strategy, and means the object's compaction window is the duration of the `value` field.",
3775            ),
3776            (
3777                "value",
3778                "The value of the strategy. For `FOR`, is a number of milliseconds.",
3779            ),
3780        ]),
3781        is_retained_metrics_object: false,
3782        access: vec![PUBLIC_SELECT],
3783        ontology: Some(Ontology {
3784            entity_name: "history_retention",
3785            description: "History retention strategy for an object",
3786            links: &const { [] },
3787            column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3788        }),
3789    }
3790});
3791
3792pub static MZ_LICENSE_KEYS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3793    name: "mz_license_keys",
3794    schema: MZ_INTERNAL_SCHEMA,
3795    oid: oid::TABLE_MZ_LICENSE_KEYS_OID,
3796    desc: RelationDesc::builder()
3797        .with_column("id", SqlScalarType::String.nullable(false))
3798        .with_column("organization", SqlScalarType::String.nullable(false))
3799        .with_column("environment_id", SqlScalarType::String.nullable(false))
3800        .with_column(
3801            "expiration",
3802            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3803        )
3804        .with_column(
3805            "not_before",
3806            SqlScalarType::TimestampTz { precision: None }.nullable(false),
3807        )
3808        .finish(),
3809    column_comments: BTreeMap::from_iter([
3810        ("id", "The identifier of the license key."),
3811        (
3812            "organization",
3813            "The name of the organization that this license key was issued to.",
3814        ),
3815        (
3816            "environment_id",
3817            "The environment ID that this license key was issued for.",
3818        ),
3819        (
3820            "expiration",
3821            "The date and time when this license key expires.",
3822        ),
3823        (
3824            "not_before",
3825            "The start of the validity period for this license key.",
3826        ),
3827    ]),
3828    is_retained_metrics_object: false,
3829    access: vec![PUBLIC_SELECT],
3830    ontology: Some(Ontology {
3831        entity_name: "license_key",
3832        description: "License key metadata",
3833        links: &const { [] },
3834        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3835    }),
3836});
3837
3838pub static MZ_REPLACEMENTS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
3839    name: "mz_replacements",
3840    schema: MZ_INTERNAL_SCHEMA,
3841    oid: oid::TABLE_MZ_REPLACEMENTS_OID,
3842    desc: RelationDesc::builder()
3843        .with_column("id", SqlScalarType::String.nullable(false))
3844        .with_column("target_id", SqlScalarType::String.nullable(false))
3845        .finish(),
3846    column_comments: BTreeMap::from_iter([
3847        (
3848            "id",
3849            "The ID of the replacement object. Corresponds to `mz_objects.id`.",
3850        ),
3851        (
3852            "target_id",
3853            "The ID of the replacement target. Corresponds to `mz_objects.id`.",
3854        ),
3855    ]),
3856    is_retained_metrics_object: false,
3857    access: vec![PUBLIC_SELECT],
3858    ontology: Some(Ontology {
3859        entity_name: "replacement",
3860        description: "A record of an object replacement (ALTER ... SWAP)",
3861        links: &const {
3862            [
3863                OntologyLink {
3864                    name: "replacement_object",
3865                    target: "object",
3866                    properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
3867                },
3868                OntologyLink {
3869                    name: "replacement_target",
3870                    target: "object",
3871                    properties: LinkProperties::fk("target_id", "id", Cardinality::ManyToOne),
3872                },
3873            ]
3874        },
3875        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
3876    }),
3877});
3878
3879// These will be replaced with per-replica tables once source/sink multiplexing on
3880// a single cluster is supported.
3881pub static MZ_SOURCE_STATISTICS_RAW: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3882    name: "mz_source_statistics_raw",
3883    schema: MZ_INTERNAL_SCHEMA,
3884    oid: oid::SOURCE_MZ_SOURCE_STATISTICS_RAW_OID,
3885    data_source: IntrospectionType::StorageSourceStatistics.into(),
3886    desc: MZ_SOURCE_STATISTICS_RAW_DESC.clone(),
3887    column_comments: BTreeMap::new(),
3888    is_retained_metrics_object: true,
3889    access: vec![PUBLIC_SELECT],
3890    ontology: None,
3891});
3892pub static MZ_SINK_STATISTICS_RAW: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3893    name: "mz_sink_statistics_raw",
3894    schema: MZ_INTERNAL_SCHEMA,
3895    oid: oid::SOURCE_MZ_SINK_STATISTICS_RAW_OID,
3896    data_source: IntrospectionType::StorageSinkStatistics.into(),
3897    desc: MZ_SINK_STATISTICS_RAW_DESC.clone(),
3898    column_comments: BTreeMap::new(),
3899    is_retained_metrics_object: true,
3900    access: vec![PUBLIC_SELECT],
3901    ontology: None,
3902});
3903
3904pub static MZ_STORAGE_SHARDS: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
3905    name: "mz_storage_shards",
3906    schema: MZ_INTERNAL_SCHEMA,
3907    oid: oid::SOURCE_MZ_STORAGE_SHARDS_OID,
3908    data_source: IntrospectionType::ShardMapping.into(),
3909    desc: RelationDesc::builder()
3910        .with_column("object_id", SqlScalarType::String.nullable(false))
3911        .with_column("shard_id", SqlScalarType::String.nullable(false))
3912        .finish(),
3913    column_comments: BTreeMap::new(),
3914    is_retained_metrics_object: false,
3915    access: vec![PUBLIC_SELECT],
3916    ontology: Some(Ontology {
3917        entity_name: "storage_shard",
3918        description: "Persist shards used by storage objects",
3919        links: &const {
3920            [OntologyLink {
3921                name: "shard_of",
3922                target: "object",
3923                properties: LinkProperties::fk_mapped(
3924                    "object_id",
3925                    "id",
3926                    Cardinality::ManyToOne,
3927                    mz_repr::SemanticType::GlobalId,
3928                    "mz_internal.mz_object_global_ids",
3929                ),
3930            }]
3931        },
3932        column_semantic_types: &const {
3933            [
3934                ("object_id", SemanticType::GlobalId),
3935                ("shard_id", SemanticType::ShardId),
3936            ]
3937        },
3938    }),
3939});
3940
3941pub static MZ_OBJECTS_ID_NAMESPACE_TYPES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3942    name: "mz_objects_id_namespace_types",
3943    schema: MZ_INTERNAL_SCHEMA,
3944    oid: oid::VIEW_MZ_OBJECTS_ID_NAMESPACE_TYPES_OID,
3945    desc: RelationDesc::builder()
3946        .with_column("object_type", SqlScalarType::String.nullable(false))
3947        .with_key(vec![0])
3948        .finish(),
3949    column_comments: BTreeMap::new(),
3950    sql: r#"SELECT *
3951    FROM (
3952        VALUES
3953            ('table'),
3954            ('view'),
3955            ('materialized-view'),
3956            ('source'),
3957            ('sink'),
3958            ('metric-sink'),
3959            ('index'),
3960            ('connection'),
3961            ('type'),
3962            ('function'),
3963            ('secret')
3964    )
3965    AS _ (object_type)"#,
3966    access: vec![PUBLIC_SELECT],
3967    ontology: None,
3968});
3969
3970/// Object dependency edges. Each row `(object_id, dependency_id)` means
3971/// `object_id` depends on `dependency_id`.
3972///
3973/// Unions the dataflow dependencies between maintained objects (index,
3974/// materialized view, sink, source, table) with the source-to-subsource and
3975/// source-to-table edges that connect a source to the children carrying its
3976/// data. Indexed on `mz_catalog_server` so the console surfaces that walk the
3977/// dependency graph read one maintained arrangement instead of recomputing the
3978/// union per request: the object workflow graph, critical-path freshness
3979/// analysis, and impact/dependents views.
3980pub static MZ_OBJECT_GRAPH_EDGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
3981    name: "mz_object_graph_edges",
3982    schema: MZ_INTERNAL_SCHEMA,
3983    oid: oid::VIEW_MZ_OBJECT_GRAPH_EDGES_OID,
3984    desc: RelationDesc::builder()
3985        .with_column("object_id", SqlScalarType::String.nullable(false))
3986        .with_column("dependency_id", SqlScalarType::String.nullable(false))
3987        .with_key(vec![0, 1])
3988        .finish(),
3989    column_comments: BTreeMap::from_iter([
3990        (
3991            "object_id",
3992            "The ID of the dependent object. Corresponds to `mz_objects.id`.",
3993        ),
3994        (
3995            "dependency_id",
3996            "The ID of the object it depends on. Corresponds to `mz_objects.id`.",
3997        ),
3998    ]),
3999    sql: "
4000SELECT md.object_id, md.dependency_id
4001FROM mz_internal.mz_materialization_dependencies md
4002JOIN mz_catalog.mz_objects po ON po.id = md.dependency_id
4003    AND po.type IN ('index', 'materialized-view', 'sink', 'source', 'table')
4004JOIN mz_catalog.mz_objects co ON co.id = md.object_id
4005    AND co.type IN ('index', 'materialized-view', 'sink', 'source', 'table')
4006UNION
4007-- Subsource -> parent-source edges: a subsource depends on the (user) source it
4008-- belongs to, an edge mz_materialization_dependencies doesn't carry.
4009SELECT od.object_id, od.referenced_object_id
4010FROM mz_internal.mz_object_dependencies od
4011JOIN mz_catalog.mz_sources ps ON ps.id = od.referenced_object_id
4012JOIN mz_catalog.mz_sources cs ON cs.id = od.object_id
4013-- Progress collections are deliberately left out: their dependency edge points
4014-- source -> progress, and they only exist for old-syntax sources, which the
4015-- source-table migration is removing.
4016WHERE ps.id LIKE 'u%' AND cs.type = 'subsource'
4017UNION
4018-- Select the (non-null) source id from the join rather than the nullable
4019-- mz_tables.source_id, so dependency_id is non-null across all branches.
4020SELECT t.id, ps.id
4021FROM mz_catalog.mz_tables t
4022JOIN mz_catalog.mz_sources ps ON ps.id = t.source_id",
4023    access: vec![PUBLIC_SELECT],
4024    // No ontology entity: these edges are already in the ontology via the
4025    // DependsOn links of mz_object_dependencies and
4026    // mz_materialization_dependencies. An entity here would duplicate them.
4027    ontology: None,
4028});
4029
4030pub static MZ_OBJECT_OID_ALIAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4031    name: "mz_object_oid_alias",
4032    schema: MZ_INTERNAL_SCHEMA,
4033    oid: oid::VIEW_MZ_OBJECT_OID_ALIAS_OID,
4034    desc: RelationDesc::builder()
4035        .with_column("object_type", SqlScalarType::String.nullable(false))
4036        .with_column("oid_alias", SqlScalarType::String.nullable(false))
4037        .with_key(vec![0])
4038        .finish(),
4039    column_comments: BTreeMap::new(),
4040    sql: "SELECT object_type, oid_alias
4041    FROM (
4042        VALUES
4043            (
4044                'table'::pg_catalog.text,
4045                'regclass'::pg_catalog.text
4046            ),
4047            ('source', 'regclass'),
4048            ('view', 'regclass'),
4049            ('materialized-view', 'regclass'),
4050            ('index', 'regclass'),
4051            ('type', 'regtype'),
4052            ('function', 'regproc')
4053    )
4054    AS _ (object_type, oid_alias);",
4055    access: vec![PUBLIC_SELECT],
4056    ontology: None,
4057});
4058
4059pub static MZ_OBJECT_FULLY_QUALIFIED_NAMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4060    name: "mz_object_fully_qualified_names",
4061    schema: MZ_INTERNAL_SCHEMA,
4062    oid: oid::VIEW_MZ_OBJECT_FULLY_QUALIFIED_NAMES_OID,
4063    desc: RelationDesc::builder()
4064        .with_column("id", SqlScalarType::String.nullable(false))
4065        .with_column("name", SqlScalarType::String.nullable(false))
4066        .with_column("object_type", SqlScalarType::String.nullable(false))
4067        .with_column("schema_id", SqlScalarType::String.nullable(false))
4068        .with_column("schema_name", SqlScalarType::String.nullable(false))
4069        .with_column("database_id", SqlScalarType::String.nullable(true))
4070        .with_column("database_name", SqlScalarType::String.nullable(true))
4071        .with_column("cluster_id", SqlScalarType::String.nullable(true))
4072        .finish(),
4073    column_comments: BTreeMap::from_iter([
4074        ("id", "Materialize's unique ID for the object."),
4075        ("name", "The name of the object."),
4076        (
4077            "object_type",
4078            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
4079        ),
4080        (
4081            "schema_id",
4082            "The ID of the schema to which the object belongs. Corresponds to `mz_schemas.id`.",
4083        ),
4084        (
4085            "schema_name",
4086            "The name of the schema to which the object belongs. Corresponds to `mz_schemas.name`.",
4087        ),
4088        (
4089            "database_id",
4090            "The ID of the database to which the object belongs. Corresponds to `mz_databases.id`.",
4091        ),
4092        (
4093            "database_name",
4094            "The name of the database to which the object belongs. Corresponds to `mz_databases.name`.",
4095        ),
4096        (
4097            "cluster_id",
4098            "The ID of the cluster maintaining the source, materialized view, index, or sink. Corresponds to `mz_clusters.id`. `NULL` for other object types.",
4099        ),
4100    ]),
4101    sql: "
4102    SELECT o.id,
4103        o.name,
4104        o.type as object_type,
4105        sc.id as schema_id,
4106        sc.name as schema_name,
4107        db.id as database_id,
4108        db.name as database_name,
4109        o.cluster_id
4110    FROM mz_catalog.mz_objects o
4111    INNER JOIN mz_catalog.mz_schemas sc ON sc.id = o.schema_id
4112    -- LEFT JOIN accounts for objects in the ambient database.
4113    LEFT JOIN mz_catalog.mz_databases db ON db.id = sc.database_id",
4114    access: vec![PUBLIC_SELECT],
4115    ontology: Some(Ontology {
4116        entity_name: "object_fqn",
4117        description: "Fully qualified name (database.schema.name) for objects",
4118        links: &const {
4119            [
4120                OntologyLink {
4121                    name: "details_of",
4122                    target: "object",
4123                    properties: LinkProperties::fk("id", "id", Cardinality::OneToOne),
4124                },
4125                OntologyLink {
4126                    name: "in_schema",
4127                    target: "schema",
4128                    properties: LinkProperties::fk("schema_id", "id", Cardinality::ManyToOne),
4129                },
4130                OntologyLink {
4131                    name: "in_database",
4132                    target: "database",
4133                    properties: LinkProperties::fk("database_id", "id", Cardinality::ManyToOne),
4134                },
4135                OntologyLink {
4136                    name: "belongs_to_cluster",
4137                    target: "cluster",
4138                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
4139                },
4140            ]
4141        },
4142        column_semantic_types: &const {
4143            [
4144                ("id", SemanticType::CatalogItemId),
4145                ("object_type", SemanticType::ObjectType),
4146                ("schema_id", SemanticType::SchemaId),
4147                ("database_id", SemanticType::DatabaseId),
4148                ("cluster_id", SemanticType::ClusterId),
4149            ]
4150        },
4151    }),
4152});
4153
4154pub static MZ_OBJECT_GLOBAL_IDS: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
4155    name: "mz_object_global_ids",
4156    schema: MZ_INTERNAL_SCHEMA,
4157    oid: oid::VIEW_MZ_OBJECT_GLOBAL_IDS_OID,
4158    desc: RelationDesc::builder()
4159        .with_column("id", SqlScalarType::String.nullable(false))
4160        .with_column("global_id", SqlScalarType::String.nullable(false))
4161        .finish(),
4162    column_comments: BTreeMap::from_iter([
4163        (
4164            "id",
4165            "The ID of the object. Corresponds to `mz_objects.id`.",
4166        ),
4167        ("global_id", "The global ID of the object."),
4168    ]),
4169    is_retained_metrics_object: false,
4170    access: vec![PUBLIC_SELECT],
4171    ontology: Some(Ontology {
4172        entity_name: "object_global_id",
4173        description: "Mapping between CatalogItemId (SQL layer) and GlobalId (runtime layer)",
4174        links: &const {
4175            [OntologyLink {
4176                name: "id_references",
4177                target: "object",
4178                properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
4179            }]
4180        },
4181        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
4182    }),
4183});
4184
4185// TODO (SangJunBak): Remove once mz_object_history is released and used in the Console https://github.com/MaterializeInc/console/issues/3342
4186pub static MZ_OBJECT_LIFETIMES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4187    name: "mz_object_lifetimes",
4188    schema: MZ_INTERNAL_SCHEMA,
4189    oid: oid::VIEW_MZ_OBJECT_LIFETIMES_OID,
4190    desc: RelationDesc::builder()
4191        .with_column("id", SqlScalarType::String.nullable(true))
4192        .with_column("previous_id", SqlScalarType::String.nullable(true))
4193        .with_column("object_type", SqlScalarType::String.nullable(false))
4194        .with_column("event_type", SqlScalarType::String.nullable(false))
4195        .with_column(
4196            "occurred_at",
4197            SqlScalarType::TimestampTz { precision: None }.nullable(false),
4198        )
4199        .finish(),
4200    column_comments: BTreeMap::from_iter([
4201        ("id", "Materialize's unique ID for the object."),
4202        ("previous_id", "The object's previous ID, if one exists."),
4203        (
4204            "object_type",
4205            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
4206        ),
4207        (
4208            "event_type",
4209            "The lifetime event, either `create` or `drop`.",
4210        ),
4211        (
4212            "occurred_at",
4213            "Wall-clock timestamp of when the event occurred.",
4214        ),
4215    ]),
4216    sql: "
4217    SELECT
4218        CASE
4219            WHEN a.object_type = 'cluster-replica' THEN a.details ->> 'replica_id'
4220            ELSE a.details ->> 'id'
4221        END id,
4222        a.details ->> 'previous_id' as previous_id,
4223        a.object_type,
4224        a.event_type,
4225        a.occurred_at
4226    FROM mz_catalog.mz_audit_events a
4227    WHERE a.event_type = 'create' OR a.event_type = 'drop'",
4228    access: vec![PUBLIC_SELECT],
4229    ontology: Some(Ontology {
4230        entity_name: "object_lifetime_event",
4231        description: "Create or drop lifecycle event for a catalog object",
4232        links: &const {
4233            [OntologyLink {
4234                name: "lifetime_event_of",
4235                target: "object",
4236                properties: LinkProperties::fk_typed(
4237                    "id",
4238                    "id",
4239                    Cardinality::ManyToOne,
4240                    mz_repr::SemanticType::CatalogItemId,
4241                ),
4242            }]
4243        },
4244        column_semantic_types: &const {
4245            [
4246                ("id", SemanticType::CatalogItemId),
4247                ("object_type", SemanticType::ObjectType),
4248            ]
4249        },
4250    }),
4251});
4252
4253pub static MZ_OBJECT_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4254    name: "mz_object_history",
4255    schema: MZ_INTERNAL_SCHEMA,
4256    oid: oid::VIEW_MZ_OBJECT_HISTORY_OID,
4257    desc: RelationDesc::builder()
4258        .with_column("id", SqlScalarType::String.nullable(true))
4259        .with_column("cluster_id", SqlScalarType::String.nullable(true))
4260        .with_column("object_type", SqlScalarType::String.nullable(false))
4261        .with_column(
4262            "created_at",
4263            SqlScalarType::TimestampTz { precision: None }.nullable(true),
4264        )
4265        .with_column(
4266            "dropped_at",
4267            SqlScalarType::TimestampTz { precision: None }.nullable(true),
4268        )
4269        .finish(),
4270    column_comments: BTreeMap::from_iter([
4271        ("id", "Materialize's unique ID for the object."),
4272        (
4273            "cluster_id",
4274            "The object's cluster ID. `NULL` if the object has no associated cluster.",
4275        ),
4276        (
4277            "object_type",
4278            "The type of the object: one of `table`, `source`, `view`, `materialized-view`, `sink`, `index`, `connection`, `secret`, `type`, or `function`.",
4279        ),
4280        (
4281            "created_at",
4282            "Wall-clock timestamp of when the object was created. `NULL` for built in system objects.",
4283        ),
4284        (
4285            "dropped_at",
4286            "Wall-clock timestamp of when the object was dropped. `NULL` for built in system objects or if the object hasn't been dropped.",
4287        ),
4288    ]),
4289    sql: r#"
4290    WITH
4291        creates AS
4292        (
4293            SELECT
4294                details ->> 'id' AS id,
4295                -- We need to backfill cluster_id since older object create events don't include the cluster ID in the audit log
4296                COALESCE(details ->> 'cluster_id', objects.cluster_id) AS cluster_id,
4297                object_type,
4298                occurred_at
4299            FROM
4300                mz_catalog.mz_audit_events AS events
4301                    LEFT JOIN mz_catalog.mz_objects AS objects ON details ->> 'id' = objects.id
4302            WHERE event_type = 'create' AND object_type IN ( SELECT object_type FROM mz_internal.mz_objects_id_namespace_types )
4303        ),
4304        drops AS
4305        (
4306            SELECT details ->> 'id' AS id, occurred_at
4307            FROM mz_catalog.mz_audit_events
4308            WHERE event_type = 'drop' AND object_type IN ( SELECT object_type FROM mz_internal.mz_objects_id_namespace_types )
4309        ),
4310        user_object_history AS
4311        (
4312            SELECT
4313                creates.id,
4314                creates.cluster_id,
4315                creates.object_type,
4316                creates.occurred_at AS created_at,
4317                drops.occurred_at AS dropped_at
4318            FROM creates LEFT JOIN drops ON creates.id = drops.id
4319            WHERE creates.id LIKE 'u%'
4320        ),
4321        -- We need to union built in objects since they aren't in the audit log
4322        built_in_objects AS
4323        (
4324            -- Functions that accept different arguments have different oids but the same id. We deduplicate in this case.
4325            SELECT DISTINCT ON (objects.id)
4326                objects.id,
4327                objects.cluster_id,
4328                objects.type AS object_type,
4329                NULL::timestamptz AS created_at,
4330                NULL::timestamptz AS dropped_at
4331            FROM mz_catalog.mz_objects AS objects
4332            WHERE objects.id LIKE 's%'
4333        )
4334    SELECT * FROM user_object_history UNION ALL (SELECT * FROM built_in_objects)"#,
4335    access: vec![PUBLIC_SELECT],
4336    ontology: Some(Ontology {
4337        entity_name: "object_history",
4338        description: "Historical record of object creation and drops",
4339        links: &const {
4340            [OntologyLink {
4341                name: "history_of",
4342                target: "object",
4343                properties: LinkProperties::fk("id", "id", Cardinality::ManyToOne),
4344            }]
4345        },
4346        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
4347    }),
4348});
4349
4350pub static MZ_OBJECT_TRANSITIVE_DEPENDENCIES: LazyLock<BuiltinView> = LazyLock::new(|| {
4351    BuiltinView {
4352        name: "mz_object_transitive_dependencies",
4353        schema: MZ_INTERNAL_SCHEMA,
4354        oid: oid::VIEW_MZ_OBJECT_TRANSITIVE_DEPENDENCIES_OID,
4355        desc: RelationDesc::builder()
4356            .with_column("object_id", SqlScalarType::String.nullable(false))
4357            .with_column(
4358                "referenced_object_id",
4359                SqlScalarType::String.nullable(false),
4360            )
4361            .with_key(vec![0, 1])
4362            .finish(),
4363        column_comments: BTreeMap::from_iter([
4364            (
4365                "object_id",
4366                "The ID of the dependent object. Corresponds to `mz_objects.id`.",
4367            ),
4368            (
4369                "referenced_object_id",
4370                "The ID of the (possibly transitively) referenced object. Corresponds to `mz_objects.id`.",
4371            ),
4372        ]),
4373        sql: "
4374WITH MUTUALLY RECURSIVE
4375  reach(object_id text, referenced_object_id text) AS (
4376    SELECT object_id, referenced_object_id FROM mz_internal.mz_object_dependencies
4377    UNION
4378    SELECT x, z FROM reach r1(x, y) JOIN reach r2(y, z) USING(y)
4379  )
4380SELECT object_id, referenced_object_id FROM reach;",
4381        access: vec![PUBLIC_SELECT],
4382        ontology: Some(Ontology {
4383            entity_name: "transitive_dependency",
4384            description: "Transitive closure of object dependencies — all direct and indirect dependencies",
4385            links: &const {
4386                [
4387                    OntologyLink {
4388                        name: "depends_on",
4389                        target: "object",
4390                        properties: LinkProperties::DependsOn {
4391                            source_column: "object_id",
4392                            target_column: "id",
4393                            source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
4394                            requires_mapping: None,
4395                        },
4396                    },
4397                    OntologyLink {
4398                        name: "dependency_is",
4399                        target: "object",
4400                        properties: LinkProperties::DependsOn {
4401                            source_column: "referenced_object_id",
4402                            target_column: "id",
4403                            source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
4404                            requires_mapping: None,
4405                        },
4406                    },
4407                ]
4408            },
4409            column_semantic_types: &const {
4410                [
4411                    ("object_id", SemanticType::CatalogItemId),
4412                    ("referenced_object_id", SemanticType::CatalogItemId),
4413                ]
4414            },
4415        }),
4416    }
4417});
4418
4419/// Peeled version of `PG_NAMESPACE`:
4420/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4421///   in order to make this view indexable.
4422/// - This has the database name as an extra column, so that downstream views can check it against
4423///  `current_database()`.
4424pub static PG_NAMESPACE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4425    name: "pg_namespace_all_databases",
4426    schema: MZ_INTERNAL_SCHEMA,
4427    oid: oid::VIEW_PG_NAMESPACE_ALL_DATABASES_OID,
4428    desc: RelationDesc::builder()
4429        .with_column("oid", SqlScalarType::Oid.nullable(false))
4430        .with_column("nspname", SqlScalarType::String.nullable(false))
4431        .with_column("nspowner", SqlScalarType::Oid.nullable(false))
4432        .with_column(
4433            "nspacl",
4434            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
4435        )
4436        .with_column("database_name", SqlScalarType::String.nullable(true))
4437        .finish(),
4438    column_comments: BTreeMap::new(),
4439    sql: "
4440SELECT
4441    s.oid AS oid,
4442    s.name AS nspname,
4443    role_owner.oid AS nspowner,
4444    NULL::pg_catalog.text[] AS nspacl,
4445    d.name as database_name
4446FROM mz_catalog.mz_schemas s
4447LEFT JOIN mz_catalog.mz_databases d ON d.id = s.database_id
4448JOIN mz_catalog.mz_roles role_owner ON role_owner.id = s.owner_id",
4449    access: vec![PUBLIC_SELECT],
4450    ontology: None,
4451});
4452
4453pub const PG_NAMESPACE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4454    name: "pg_namespace_all_databases_ind",
4455    schema: MZ_INTERNAL_SCHEMA,
4456    oid: oid::INDEX_PG_NAMESPACE_ALL_DATABASES_IND_OID,
4457    sql: "IN CLUSTER mz_catalog_server
4458ON mz_internal.pg_namespace_all_databases (nspname)",
4459    is_retained_metrics_object: false,
4460};
4461
4462/// Peeled version of `PG_CLASS`:
4463/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4464///   in order to make this view indexable.
4465/// - This has the database name as an extra column, so that downstream views can check it against
4466///  `current_database()`.
4467pub static PG_CLASS_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4468    BuiltinView {
4469        name: "pg_class_all_databases",
4470        schema: MZ_INTERNAL_SCHEMA,
4471        oid: oid::VIEW_PG_CLASS_ALL_DATABASES_OID,
4472        desc: RelationDesc::builder()
4473            .with_column("oid", SqlScalarType::Oid.nullable(false))
4474            .with_column("relname", SqlScalarType::String.nullable(false))
4475            .with_column("relnamespace", SqlScalarType::Oid.nullable(false))
4476            .with_column("reloftype", SqlScalarType::Oid.nullable(false))
4477            .with_column("relowner", SqlScalarType::Oid.nullable(false))
4478            .with_column("relam", SqlScalarType::Oid.nullable(false))
4479            .with_column("reltablespace", SqlScalarType::Oid.nullable(false))
4480            .with_column("reltuples", SqlScalarType::Float32.nullable(false))
4481            .with_column("reltoastrelid", SqlScalarType::Oid.nullable(false))
4482            .with_column("relhasindex", SqlScalarType::Bool.nullable(false))
4483            .with_column("relpersistence", SqlScalarType::PgLegacyChar.nullable(false))
4484            .with_column("relkind", SqlScalarType::String.nullable(true))
4485            .with_column("relnatts", SqlScalarType::Int16.nullable(false))
4486            .with_column("relchecks", SqlScalarType::Int16.nullable(false))
4487            .with_column("relhasrules", SqlScalarType::Bool.nullable(false))
4488            .with_column("relhastriggers", SqlScalarType::Bool.nullable(false))
4489            .with_column("relhassubclass", SqlScalarType::Bool.nullable(false))
4490            .with_column("relrowsecurity", SqlScalarType::Bool.nullable(false))
4491            .with_column("relforcerowsecurity", SqlScalarType::Bool.nullable(false))
4492            .with_column("relreplident", SqlScalarType::PgLegacyChar.nullable(false))
4493            .with_column("relispartition", SqlScalarType::Bool.nullable(false))
4494            .with_column("relhasoids", SqlScalarType::Bool.nullable(false))
4495            .with_column("reloptions", SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true))
4496            .with_column("database_name", SqlScalarType::String.nullable(true))
4497            .finish(),
4498        column_comments: BTreeMap::new(),
4499        sql: "
4500SELECT
4501    class_objects.oid,
4502    class_objects.name AS relname,
4503    mz_schemas.oid AS relnamespace,
4504    -- MZ doesn't support typed tables so reloftype is filled with 0
4505    0::pg_catalog.oid AS reloftype,
4506    role_owner.oid AS relowner,
4507    0::pg_catalog.oid AS relam,
4508    -- MZ doesn't have tablespaces so reltablespace is filled in with 0 implying the default tablespace
4509    0::pg_catalog.oid AS reltablespace,
4510    -- MZ doesn't support (estimated) row counts currently.
4511    -- Postgres defines a value of -1 as unknown.
4512    -1::float4 as reltuples,
4513    -- MZ doesn't use TOAST tables so reltoastrelid is filled with 0
4514    0::pg_catalog.oid AS reltoastrelid,
4515    EXISTS (SELECT id, oid, name, on_id, cluster_id FROM mz_catalog.mz_indexes where mz_indexes.on_id = class_objects.id) AS relhasindex,
4516    -- MZ doesn't have unlogged tables and because of (https://github.com/MaterializeInc/database-issues/issues/2689)
4517    -- temporary objects don't show up here, so relpersistence is filled with 'p' for permanent.
4518    -- TODO(jkosh44): update this column when issue is resolved.
4519    'p'::pg_catalog.\"char\" AS relpersistence,
4520    CASE
4521        WHEN class_objects.type = 'table' THEN 'r'
4522        WHEN class_objects.type = 'source' THEN 'r'
4523        WHEN class_objects.type = 'index' THEN 'i'
4524        WHEN class_objects.type = 'view' THEN 'v'
4525        WHEN class_objects.type = 'materialized-view' THEN 'm'
4526    END relkind,
4527    CASE
4528        WHEN class_objects.type = 'index' THEN COALESCE(
4529            (
4530                SELECT count(*)::pg_catalog.int2
4531                FROM mz_catalog.mz_index_columns
4532                WHERE mz_index_columns.index_id = class_objects.id
4533            ),
4534            0::pg_catalog.int2
4535        )
4536        ELSE COALESCE(
4537            (
4538                SELECT count(*)::pg_catalog.int2
4539                FROM mz_catalog.mz_columns
4540                WHERE mz_columns.id = class_objects.id
4541            ),
4542            0::pg_catalog.int2
4543        )
4544    END AS relnatts,
4545    -- MZ doesn't support CHECK constraints so relchecks is filled with 0
4546    0::pg_catalog.int2 AS relchecks,
4547    -- MZ doesn't support creating rules so relhasrules is filled with false
4548    false AS relhasrules,
4549    -- MZ doesn't support creating triggers so relhastriggers is filled with false
4550    false AS relhastriggers,
4551    -- MZ doesn't support table inheritance or partitions so relhassubclass is filled with false
4552    false AS relhassubclass,
4553    -- MZ doesn't have row level security so relrowsecurity and relforcerowsecurity is filled with false
4554    false AS relrowsecurity,
4555    false AS relforcerowsecurity,
4556    -- MZ doesn't support replication so relreplident is filled with 'd' for default
4557    'd'::pg_catalog.\"char\" AS relreplident,
4558    -- MZ doesn't support table partitioning so relispartition is filled with false
4559    false AS relispartition,
4560    -- PG removed relhasoids in v12 so it's filled with false
4561    false AS relhasoids,
4562    -- MZ doesn't support options for relations
4563    NULL::pg_catalog.text[] as reloptions,
4564    d.name as database_name
4565FROM (
4566    -- pg_class catalogs relations and indexes
4567    SELECT id, oid, schema_id, name, type, owner_id FROM mz_catalog.mz_relations
4568    UNION ALL
4569        SELECT mz_indexes.id, mz_indexes.oid, mz_relations.schema_id, mz_indexes.name, 'index' AS type, mz_indexes.owner_id
4570        FROM mz_catalog.mz_indexes
4571        JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
4572) AS class_objects
4573JOIN mz_catalog.mz_schemas ON mz_schemas.id = class_objects.schema_id
4574LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id
4575JOIN mz_catalog.mz_roles role_owner ON role_owner.id = class_objects.owner_id",
4576        access: vec![PUBLIC_SELECT],
4577        ontology: None,
4578    }
4579});
4580
4581pub const PG_CLASS_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4582    name: "pg_class_all_databases_ind",
4583    schema: MZ_INTERNAL_SCHEMA,
4584    oid: oid::INDEX_PG_CLASS_ALL_DATABASES_IND_OID,
4585    sql: "IN CLUSTER mz_catalog_server
4586ON mz_internal.pg_class_all_databases (relname)",
4587    is_retained_metrics_object: false,
4588};
4589
4590/// Peeled version of `PG_DESCRIPTION`:
4591/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4592///   in order to make this view indexable.
4593/// - This has 2 extra columns for the database names, so that downstream views can check them
4594///   against `current_database()`.
4595pub static PG_DESCRIPTION_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4596    BuiltinView {
4597        name: "pg_description_all_databases",
4598        schema: MZ_INTERNAL_SCHEMA,
4599        oid: oid::VIEW_PG_DESCRIPTION_ALL_DATABASES_OID,
4600        desc: RelationDesc::builder()
4601            .with_column("objoid", SqlScalarType::Oid.nullable(false))
4602            .with_column("classoid", SqlScalarType::Oid.nullable(true))
4603            .with_column("objsubid", SqlScalarType::Int32.nullable(false))
4604            .with_column("description", SqlScalarType::String.nullable(false))
4605            .with_column("oid_database_name", SqlScalarType::String.nullable(true))
4606            .with_column("class_database_name", SqlScalarType::String.nullable(true))
4607            .finish(),
4608        column_comments: BTreeMap::new(),
4609        sql: "
4610(
4611    -- The classoid of a comment is the oid of the pg_catalog system catalog
4612    -- that conceptually stores the commented object: pg_class for relations,
4613    -- pg_type for types, pg_namespace for schemas. We scope the lookup to the
4614    -- pg_catalog schema; otherwise a user-created object named e.g. `pg_class`
4615    -- makes the scalar subqueries below match multiple rows and the whole view
4616    -- errors for everyone. PostgreSQL's pg_description is a real catalog table
4617    -- and is unaffected by such user objects, and so are we.
4618    WITH pg_catalog_class AS (
4619        SELECT oid, relname, database_name
4620        FROM mz_internal.pg_class_all_databases
4621        WHERE relnamespace = (
4622            SELECT oid FROM mz_internal.pg_namespace_all_databases WHERE nspname = 'pg_catalog'
4623        )
4624    ),
4625    -- Gather all of the class oid's for objects that can have comments.
4626    pg_classoids AS (
4627        SELECT oid, database_name as oid_database_name,
4628          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_class') AS classoid,
4629          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_class') AS class_database_name
4630        FROM mz_internal.pg_class_all_databases
4631        UNION ALL
4632        SELECT oid, database_name as oid_database_name,
4633          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_type') AS classoid,
4634          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_type') AS class_database_name
4635        FROM mz_internal.pg_type_all_databases
4636        UNION ALL
4637        SELECT oid, database_name as oid_database_name,
4638          (SELECT oid FROM pg_catalog_class WHERE relname = 'pg_namespace') AS classoid,
4639          (SELECT database_name FROM pg_catalog_class WHERE relname = 'pg_namespace') AS class_database_name
4640        FROM mz_internal.pg_namespace_all_databases
4641    ),
4642
4643    -- Gather all of the MZ ids for objects that can have comments.
4644    mz_objects AS (
4645        SELECT id, oid, type FROM mz_catalog.mz_objects
4646        UNION ALL
4647        SELECT id, oid, 'schema' AS type FROM mz_catalog.mz_schemas
4648    )
4649    SELECT
4650        pg_classoids.oid AS objoid,
4651        pg_classoids.classoid as classoid,
4652        COALESCE(cmt.object_sub_id, 0) AS objsubid,
4653        cmt.comment AS description,
4654        -- Columns added because of the peeling. (Note that there are 2 of these here.)
4655        oid_database_name,
4656        class_database_name
4657    FROM
4658        pg_classoids
4659    JOIN
4660        mz_objects ON pg_classoids.oid = mz_objects.oid
4661    JOIN
4662        mz_internal.mz_comments AS cmt ON mz_objects.id = cmt.id AND lower(mz_objects.type) = lower(cmt.object_type)
4663)",
4664        access: vec![PUBLIC_SELECT],
4665        ontology: None,
4666    }
4667});
4668
4669pub const PG_DESCRIPTION_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4670    name: "pg_description_all_databases_ind",
4671    schema: MZ_INTERNAL_SCHEMA,
4672    oid: oid::INDEX_PG_DESCRIPTION_ALL_DATABASES_IND_OID,
4673    sql: "IN CLUSTER mz_catalog_server
4674ON mz_internal.pg_description_all_databases (objoid, classoid, objsubid, description, oid_database_name, class_database_name)",
4675    is_retained_metrics_object: false,
4676};
4677
4678/// Peeled version of `PG_TYPE`:
4679/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4680///   in order to make this view indexable.
4681/// - This has the database name as an extra column, so that downstream views can check it against
4682///  `current_database()`.
4683pub static PG_TYPE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4684    BuiltinView {
4685        name: "pg_type_all_databases",
4686        schema: MZ_INTERNAL_SCHEMA,
4687        oid: oid::VIEW_PG_TYPE_ALL_DATABASES_OID,
4688        desc: RelationDesc::builder()
4689            .with_column("oid", SqlScalarType::Oid.nullable(false))
4690            .with_column("typname", SqlScalarType::String.nullable(false))
4691            .with_column("typnamespace", SqlScalarType::Oid.nullable(false))
4692            .with_column("typowner", SqlScalarType::Oid.nullable(false))
4693            .with_column("typlen", SqlScalarType::Int16.nullable(true))
4694            .with_column("typtype", SqlScalarType::PgLegacyChar.nullable(false))
4695            .with_column("typcategory", SqlScalarType::PgLegacyChar.nullable(true))
4696            .with_column("typdelim", SqlScalarType::PgLegacyChar.nullable(false))
4697            .with_column("typrelid", SqlScalarType::Oid.nullable(false))
4698            .with_column("typelem", SqlScalarType::Oid.nullable(false))
4699            .with_column("typarray", SqlScalarType::Oid.nullable(false))
4700            .with_column("typinput", SqlScalarType::RegProc.nullable(true))
4701            .with_column("typreceive", SqlScalarType::Oid.nullable(false))
4702            .with_column("typnotnull", SqlScalarType::Bool.nullable(false))
4703            .with_column("typbasetype", SqlScalarType::Oid.nullable(false))
4704            .with_column("typtypmod", SqlScalarType::Int32.nullable(false))
4705            .with_column("typcollation", SqlScalarType::Oid.nullable(false))
4706            .with_column("typdefault", SqlScalarType::String.nullable(true))
4707            .with_column("database_name", SqlScalarType::String.nullable(true))
4708            .with_column("typsend", SqlScalarType::RegProc.nullable(false))
4709            .finish(),
4710        column_comments: BTreeMap::new(),
4711        sql: "
4712SELECT
4713    mz_types.oid,
4714    mz_types.name AS typname,
4715    mz_schemas.oid AS typnamespace,
4716    role_owner.oid AS typowner,
4717    NULL::pg_catalog.int2 AS typlen,
4718    -- 'a' is used internally to denote an array type, but in postgres they show up
4719    -- as 'b'.
4720    (CASE mztype WHEN 'a' THEN 'b' ELSE mztype END)::pg_catalog.char AS typtype,
4721    (CASE category
4722        WHEN 'array' THEN 'A'
4723        WHEN 'bit-string' THEN 'V'
4724        WHEN 'boolean' THEN 'B'
4725        WHEN 'composite' THEN 'C'
4726        WHEN 'date-time' THEN 'D'
4727        WHEN 'enum' THEN 'E'
4728        WHEN 'geometric' THEN 'G'
4729        WHEN 'list' THEN 'U' -- List types are user-defined from PostgreSQL's perspective.
4730        WHEN 'network-address' THEN 'I'
4731        WHEN 'numeric' THEN 'N'
4732        WHEN 'pseudo' THEN 'P'
4733        WHEN 'string' THEN 'S'
4734        WHEN 'timespan' THEN 'T'
4735        WHEN 'user-defined' THEN 'U'
4736        WHEN 'unknown' THEN 'X'
4737    END)::pg_catalog.char AS typcategory,
4738    -- In pg only the 'box' type is not ','.
4739    ','::pg_catalog.char AS typdelim,
4740    0::pg_catalog.oid AS typrelid,
4741    coalesce(
4742        (
4743            SELECT t.oid
4744            FROM mz_catalog.mz_array_types a
4745            JOIN mz_catalog.mz_types t ON a.element_id = t.id
4746            WHERE a.id = mz_types.id
4747        ),
4748        (
4749            SELECT t.oid
4750            FROM mz_catalog.mz_list_types l
4751            JOIN mz_catalog.mz_types t ON l.element_id = t.id
4752            WHERE l.id = mz_types.id
4753        ),
4754        0
4755    ) AS typelem,
4756    coalesce(
4757        (
4758            SELECT
4759                t.oid
4760            FROM
4761                mz_catalog.mz_array_types AS a
4762                JOIN mz_catalog.mz_types AS t ON a.id = t.id
4763            WHERE
4764                a.element_id = mz_types.id
4765        ),
4766        0
4767    )
4768        AS typarray,
4769    mz_internal.mz_type_pg_metadata.typinput::pg_catalog.regproc AS typinput,
4770    COALESCE(mz_internal.mz_type_pg_metadata.typreceive, 0) AS typreceive,
4771    false::pg_catalog.bool AS typnotnull,
4772    0::pg_catalog.oid AS typbasetype,
4773    -1::pg_catalog.int4 AS typtypmod,
4774    -- MZ doesn't support COLLATE so typcollation is filled with 0
4775    0::pg_catalog.oid AS typcollation,
4776    NULL::pg_catalog.text AS typdefault,
4777    d.name as database_name,
4778    COALESCE(mz_internal.mz_type_pg_metadata.typsend, 0)::pg_catalog.regproc AS typsend
4779FROM
4780    mz_catalog.mz_types
4781    LEFT JOIN mz_internal.mz_type_pg_metadata ON mz_catalog.mz_types.id = mz_internal.mz_type_pg_metadata.id
4782    JOIN mz_catalog.mz_schemas ON mz_schemas.id = mz_types.schema_id
4783    JOIN (
4784            -- 'a' is not a supported typtype, but we use it to denote an array. It is
4785            -- converted to the correct value above.
4786            SELECT id, 'a' AS mztype FROM mz_catalog.mz_array_types
4787            UNION ALL SELECT id, 'b' FROM mz_catalog.mz_base_types
4788            UNION ALL SELECT id, 'l' FROM mz_catalog.mz_list_types
4789            UNION ALL SELECT id, 'm' FROM mz_catalog.mz_map_types
4790            UNION ALL SELECT id, 'p' FROM mz_catalog.mz_pseudo_types
4791        )
4792            AS t ON mz_types.id = t.id
4793    LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id
4794    JOIN mz_catalog.mz_roles role_owner ON role_owner.id = mz_types.owner_id",
4795        access: vec![PUBLIC_SELECT],
4796        ontology: None,
4797    }
4798});
4799
4800pub const PG_TYPE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4801    name: "pg_type_all_databases_ind",
4802    schema: MZ_INTERNAL_SCHEMA,
4803    oid: oid::INDEX_PG_TYPE_ALL_DATABASES_IND_OID,
4804    sql: "IN CLUSTER mz_catalog_server
4805ON mz_internal.pg_type_all_databases (oid)",
4806    is_retained_metrics_object: false,
4807};
4808
4809/// Peeled version of `PG_ATTRIBUTE`:
4810/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4811///   in order to make this view indexable.
4812/// - This has 2 extra columns for the database names, so that downstream views can check them
4813///   against `current_database()`.
4814pub static PG_ATTRIBUTE_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| {
4815    BuiltinView {
4816        name: "pg_attribute_all_databases",
4817        schema: MZ_INTERNAL_SCHEMA,
4818        oid: oid::VIEW_PG_ATTRIBUTE_ALL_DATABASES_OID,
4819        desc: RelationDesc::builder()
4820            .with_column("attrelid", SqlScalarType::Oid.nullable(false))
4821            .with_column("attname", SqlScalarType::String.nullable(false))
4822            .with_column("atttypid", SqlScalarType::Oid.nullable(false))
4823            .with_column("attlen", SqlScalarType::Int16.nullable(true))
4824            .with_column("attnum", SqlScalarType::Int16.nullable(false))
4825            .with_column("atttypmod", SqlScalarType::Int32.nullable(false))
4826            .with_column("attndims", SqlScalarType::Int16.nullable(false))
4827            .with_column("attnotnull", SqlScalarType::Bool.nullable(false))
4828            .with_column("atthasdef", SqlScalarType::Bool.nullable(false))
4829            .with_column("attidentity", SqlScalarType::PgLegacyChar.nullable(false))
4830            .with_column("attgenerated", SqlScalarType::PgLegacyChar.nullable(false))
4831            .with_column("attisdropped", SqlScalarType::Bool.nullable(false))
4832            .with_column("attcollation", SqlScalarType::Oid.nullable(false))
4833            .with_column("database_name", SqlScalarType::String.nullable(true))
4834            .with_column("pg_type_database_name", SqlScalarType::String.nullable(true))
4835            .finish(),
4836        column_comments: BTreeMap::new(),
4837        sql: "
4838SELECT
4839    class_objects.oid as attrelid,
4840    mz_columns.name as attname,
4841    mz_columns.type_oid AS atttypid,
4842    pg_type_all_databases.typlen AS attlen,
4843    position::int8::int2 as attnum,
4844    mz_columns.type_mod as atttypmod,
4845    -- dummy value, just to make go-jet's workaround work for now. Discussion:
4846    -- https://github.com/MaterializeInc/materialize/pull/34649#issuecomment-3714291409
4847    0::int2 as attndims,
4848    NOT nullable as attnotnull,
4849    mz_columns.default IS NOT NULL as atthasdef,
4850    ''::pg_catalog.\"char\" as attidentity,
4851    -- MZ doesn't support generated columns so attgenerated is filled with ''
4852    ''::pg_catalog.\"char\" as attgenerated,
4853    FALSE as attisdropped,
4854    -- MZ doesn't support COLLATE so attcollation is filled with 0
4855    0::pg_catalog.oid as attcollation,
4856    -- Columns added because of the peeling. (Note that there are 2 of these here.)
4857    d.name as database_name,
4858    pg_type_all_databases.database_name as pg_type_database_name
4859FROM (
4860    -- pg_attribute catalogs columns on relations and indexes
4861    SELECT id, oid, schema_id, name, type FROM mz_catalog.mz_relations
4862    UNION ALL
4863        SELECT mz_indexes.id, mz_indexes.oid, mz_relations.schema_id, mz_indexes.name, 'index' AS type
4864        FROM mz_catalog.mz_indexes
4865        JOIN mz_catalog.mz_relations ON mz_indexes.on_id = mz_relations.id
4866) AS class_objects
4867JOIN mz_catalog.mz_columns ON class_objects.id = mz_columns.id
4868JOIN mz_internal.pg_type_all_databases ON pg_type_all_databases.oid = mz_columns.type_oid
4869JOIN mz_catalog.mz_schemas ON mz_schemas.id = class_objects.schema_id
4870LEFT JOIN mz_catalog.mz_databases d ON d.id = mz_schemas.database_id",
4871        // Since this depends on pg_type, its id must be higher due to initialization
4872        // ordering.
4873        access: vec![PUBLIC_SELECT],
4874        ontology: None,
4875    }
4876});
4877
4878pub const PG_ATTRIBUTE_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4879    name: "pg_attribute_all_databases_ind",
4880    schema: MZ_INTERNAL_SCHEMA,
4881    oid: oid::INDEX_PG_ATTRIBUTE_ALL_DATABASES_IND_OID,
4882    sql: "IN CLUSTER mz_catalog_server
4883ON mz_internal.pg_attribute_all_databases (
4884    attrelid, attname, atttypid, attlen, attnum, atttypmod, attnotnull, atthasdef, attidentity,
4885    attgenerated, attisdropped, attcollation, database_name, pg_type_database_name
4886)",
4887    is_retained_metrics_object: false,
4888};
4889
4890/// Peeled version of `PG_ATTRDEF`:
4891/// - This doesn't check `mz_schemas.database_id IS NULL OR d.name = pg_catalog.current_database()`,
4892///   in order to make this view indexable.
4893pub static PG_ATTRDEF_ALL_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
4894    name: "pg_attrdef_all_databases",
4895    schema: MZ_INTERNAL_SCHEMA,
4896    oid: oid::VIEW_PG_ATTRDEF_ALL_DATABASES_OID,
4897    desc: RelationDesc::builder()
4898        .with_column("oid", SqlScalarType::Oid.nullable(true))
4899        .with_column("adrelid", SqlScalarType::Oid.nullable(false))
4900        .with_column("adnum", SqlScalarType::Int64.nullable(false))
4901        .with_column("adbin", SqlScalarType::String.nullable(false))
4902        .with_column("adsrc", SqlScalarType::String.nullable(false))
4903        .finish(),
4904    column_comments: BTreeMap::new(),
4905    sql: "
4906SELECT
4907    NULL::pg_catalog.oid AS oid,
4908    mz_objects.oid AS adrelid,
4909    mz_columns.position::int8 AS adnum,
4910    mz_columns.default AS adbin,
4911    mz_columns.default AS adsrc
4912FROM mz_catalog.mz_columns
4913    JOIN mz_catalog.mz_objects ON mz_columns.id = mz_objects.id
4914WHERE default IS NOT NULL",
4915    access: vec![PUBLIC_SELECT],
4916    ontology: None,
4917});
4918
4919pub const PG_ATTRDEF_ALL_DATABASES_IND: BuiltinIndex = BuiltinIndex {
4920    name: "pg_attrdef_all_databases_ind",
4921    schema: MZ_INTERNAL_SCHEMA,
4922    oid: oid::INDEX_PG_ATTRDEF_ALL_DATABASES_IND_OID,
4923    sql: "IN CLUSTER mz_catalog_server
4924ON mz_internal.pg_attrdef_all_databases (oid, adrelid, adnum, adbin, adsrc)",
4925    is_retained_metrics_object: false,
4926};
4927
4928pub static MZ_COMPUTE_ERROR_COUNTS_RAW_UNIFIED: LazyLock<BuiltinSource> =
4929    LazyLock::new(|| BuiltinSource {
4930        // TODO(database-issues#8173): Rename this source to `mz_compute_error_counts_raw`.
4931        // Currently this causes a naming conflict because the resolver stumbles over the
4932        // source with the same name in `mz_introspection` due to the automatic schema
4933        // translation.
4934        name: "mz_compute_error_counts_raw_unified",
4935        schema: MZ_INTERNAL_SCHEMA,
4936        oid: oid::SOURCE_MZ_COMPUTE_ERROR_COUNTS_RAW_UNIFIED_OID,
4937        desc: RelationDesc::builder()
4938            .with_column("replica_id", SqlScalarType::String.nullable(false))
4939            .with_column("object_id", SqlScalarType::String.nullable(false))
4940            .with_column(
4941                "count",
4942                SqlScalarType::Numeric { max_scale: None }.nullable(false),
4943            )
4944            .finish(),
4945        data_source: IntrospectionType::ComputeErrorCounts.into(),
4946        column_comments: BTreeMap::new(),
4947        is_retained_metrics_object: false,
4948        access: vec![PUBLIC_SELECT],
4949        ontology: None,
4950    });
4951
4952pub static MZ_COMPUTE_HYDRATION_TIMES: LazyLock<BuiltinSource> = LazyLock::new(|| BuiltinSource {
4953    name: "mz_compute_hydration_times",
4954    schema: MZ_INTERNAL_SCHEMA,
4955    oid: oid::SOURCE_MZ_COMPUTE_HYDRATION_TIMES_OID,
4956    desc: RelationDesc::builder()
4957        .with_column("replica_id", SqlScalarType::String.nullable(false))
4958        .with_column("object_id", SqlScalarType::String.nullable(false))
4959        .with_column("time_ns", SqlScalarType::UInt64.nullable(true))
4960        .finish(),
4961    data_source: IntrospectionType::ComputeHydrationTimes.into(),
4962    column_comments: BTreeMap::new(),
4963    is_retained_metrics_object: true,
4964    access: vec![PUBLIC_SELECT],
4965    ontology: Some(Ontology {
4966        entity_name: "compute_hydration_time",
4967        description: "Time to hydrate compute objects",
4968        links: &const { [] },
4969        column_semantic_types: &const {
4970            [
4971                ("replica_id", SemanticType::ReplicaId),
4972                ("object_id", SemanticType::CatalogItemId),
4973            ]
4974        },
4975    }),
4976});
4977
4978pub static MZ_COMPUTE_HYDRATION_TIMES_IND: LazyLock<BuiltinIndex> =
4979    LazyLock::new(|| BuiltinIndex {
4980        name: "mz_compute_hydration_times_ind",
4981        schema: MZ_INTERNAL_SCHEMA,
4982        oid: oid::INDEX_MZ_COMPUTE_HYDRATION_TIMES_IND_OID,
4983        sql: "IN CLUSTER mz_catalog_server
4984    ON mz_internal.mz_compute_hydration_times (replica_id)",
4985        is_retained_metrics_object: true,
4986    });
4987
4988pub static MZ_OBJECT_ARRANGEMENT_SIZES_UNIFIED: LazyLock<BuiltinSource> = LazyLock::new(|| {
4989    BuiltinSource {
4990        name: "mz_object_arrangement_sizes",
4991        schema: MZ_INTERNAL_SCHEMA,
4992        oid: oid::SOURCE_MZ_OBJECT_ARRANGEMENT_SIZES_OID,
4993        desc: RelationDesc::builder()
4994            .with_column("replica_id", SqlScalarType::String.nullable(false))
4995            .with_column("object_id", SqlScalarType::String.nullable(false))
4996            .with_column("size", SqlScalarType::Int64.nullable(true))
4997            .finish(),
4998        data_source: IntrospectionType::ComputeObjectArrangementSizes.into(),
4999        column_comments: BTreeMap::from_iter([
5000            (
5001                "replica_id",
5002                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
5003            ),
5004            (
5005                "object_id",
5006                "The ID of the compute object (index or materialized view). Corresponds to `mz_objects.id`.",
5007            ),
5008            (
5009                "size",
5010                "The total arrangement heap and batcher size in bytes for this object on this replica, \
5011                 rounded to the nearest 10 MiB boundary to reduce per-byte churn in the differential \
5012                 collection. Objects with less than 5 MiB of arrangements report a size of 0.",
5013            ),
5014        ]),
5015        is_retained_metrics_object: true,
5016        access: vec![PUBLIC_SELECT],
5017        ontology: None,
5018    }
5019});
5020
5021pub static MZ_OBJECT_ARRANGEMENT_SIZES_IND: LazyLock<BuiltinIndex> =
5022    LazyLock::new(|| BuiltinIndex {
5023        name: "mz_object_arrangement_sizes_ind",
5024        schema: MZ_INTERNAL_SCHEMA,
5025        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZES_IND_OID,
5026        sql: "IN CLUSTER mz_catalog_server
5027    ON mz_internal.mz_object_arrangement_sizes (replica_id)",
5028        is_retained_metrics_object: true,
5029    });
5030
5031pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY: LazyLock<BuiltinTable> = LazyLock::new(|| {
5032    BuiltinTable {
5033        name: "mz_object_arrangement_size_history",
5034        schema: MZ_INTERNAL_SCHEMA,
5035        oid: oid::TABLE_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OID,
5036        desc: RelationDesc::builder()
5037            .with_column("replica_id", SqlScalarType::String.nullable(false))
5038            .with_column("object_id", SqlScalarType::String.nullable(false))
5039            .with_column("size", SqlScalarType::Int64.nullable(false))
5040            .with_column(
5041                "collection_timestamp",
5042                SqlScalarType::TimestampTz { precision: None }.nullable(false),
5043            )
5044            .with_column("hydration_complete", SqlScalarType::Bool.nullable(false))
5045            .finish(),
5046        column_comments: BTreeMap::from_iter([
5047            (
5048                "replica_id",
5049                "The ID of the cluster replica. Corresponds to `mz_cluster_replicas.id`.",
5050            ),
5051            (
5052                "object_id",
5053                "The ID of the compute object (index or materialized view). Corresponds to `mz_objects.id`.",
5054            ),
5055            (
5056                "size",
5057                "The total arrangement heap and batcher size in bytes for this object on this replica \
5058                 at `collection_timestamp`, rounded to the nearest 10 MiB to reduce per-byte churn \
5059                 in the underlying differential collection. Objects with less than 5 MiB of \
5060                 arrangements are not recorded. May reflect a mid-build size if \
5061                 `hydration_complete` is `false`.",
5062            ),
5063            (
5064                "collection_timestamp",
5065                "The timestamp when this snapshot was collected.",
5066            ),
5067            (
5068                "hydration_complete",
5069                "Whether the arrangement had finished its initial hydration on this replica when \
5070                 the snapshot was collected. Filter for `true` to consider only stable, post-build \
5071                 sizes.",
5072            ),
5073        ]),
5074        is_retained_metrics_object: true,
5075        access: vec![PUBLIC_SELECT],
5076        ontology: None,
5077    }
5078});
5079
5080pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OBJECT_IND: LazyLock<BuiltinIndex> =
5081    LazyLock::new(|| BuiltinIndex {
5082        name: "mz_object_arrangement_size_history_object_ind",
5083        schema: MZ_INTERNAL_SCHEMA,
5084        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_OBJECT_IND_OID,
5085        sql: "IN CLUSTER mz_catalog_server
5086    ON mz_internal.mz_object_arrangement_size_history (object_id)",
5087        is_retained_metrics_object: true,
5088    });
5089
5090pub static MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_TS_IND: LazyLock<BuiltinIndex> =
5091    LazyLock::new(|| BuiltinIndex {
5092        name: "mz_object_arrangement_size_history_ts_ind",
5093        schema: MZ_INTERNAL_SCHEMA,
5094        oid: oid::INDEX_MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY_TS_IND_OID,
5095        sql: "IN CLUSTER mz_catalog_server
5096    ON mz_internal.mz_object_arrangement_size_history (collection_timestamp)",
5097        is_retained_metrics_object: true,
5098    });
5099
5100/// Completed hydration episodes, one row per object, replica, and installation.
5101///
5102/// Exempt from the bootstrap reset and from forced shard replacement, since the
5103/// contents cannot be rebuilt from anything else. Schema evolution keeps them and
5104/// applies normally. Clearing them for a schema change is still allowed, see the
5105/// tripwire in `validate_migration_steps`.
5106pub static MZ_OBJECT_HYDRATION_HISTORY: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
5107    name: "mz_object_hydration_history",
5108    schema: MZ_INTERNAL_SCHEMA,
5109    oid: oid::TABLE_MZ_OBJECT_HYDRATION_HISTORY_OID,
5110    desc: RelationDesc::builder()
5111        .with_column("object_id", SqlScalarType::String.nullable(false))
5112        .with_column("cluster_id", SqlScalarType::String.nullable(false))
5113        .with_column("replica_id", SqlScalarType::String.nullable(false))
5114        .with_column(
5115            "installed_at",
5116            SqlScalarType::TimestampTz { precision: None }.nullable(false),
5117        )
5118        .with_column(
5119            "started_at",
5120            SqlScalarType::TimestampTz { precision: None }.nullable(true),
5121        )
5122        .with_column(
5123            "hydrated_at",
5124            SqlScalarType::TimestampTz { precision: None }.nullable(true),
5125        )
5126        .with_column("status", SqlScalarType::String.nullable(false))
5127        .finish(),
5128    column_comments: BTreeMap::from_iter([
5129        (
5130            "object_id",
5131            "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.",
5132        ),
5133        ("cluster_id", "The ID of the object's cluster."),
5134        (
5135            "replica_id",
5136            "The ID of the cluster replica. May name a replica that no longer exists.",
5137        ),
5138        (
5139            "installed_at",
5140            "When the object's dataflow was installed on the replica.",
5141        ),
5142        (
5143            "started_at",
5144            "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.",
5145        ),
5146        ("hydrated_at", "When hydration finished."),
5147        (
5148            "status",
5149            "The terminal status. Currently always `hydrated`.",
5150        ),
5151    ]),
5152    // Not a retained-metrics object: that would pin a 30 day compaction window,
5153    // and our history lives in the rows, which the retention sweep retracts on
5154    // its own schedule. Nothing reads this table at an old timestamp.
5155    is_retained_metrics_object: false,
5156    access: vec![PUBLIC_SELECT],
5157    ontology: Some(Ontology {
5158        entity_name: "object_hydration_event",
5159        description: "Completed hydration of an index or materialized view on a replica",
5160        // NOTE: These references outlive what they point at. A row deliberately
5161        // survives the object and the replica it describes, so resolving one
5162        // against the catalog can come up empty.
5163        links: &const {
5164            [
5165                OntologyLink {
5166                    name: "hydration_of_dataflow",
5167                    target: "object_global_id",
5168                    properties: LinkProperties::fk_typed(
5169                        "object_id",
5170                        "global_id",
5171                        Cardinality::ManyToOne,
5172                        mz_repr::SemanticType::GlobalId,
5173                    ),
5174                },
5175                OntologyLink {
5176                    name: "hydrated_on_cluster",
5177                    target: "cluster",
5178                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
5179                },
5180                OntologyLink {
5181                    name: "hydrated_on_replica",
5182                    target: "replica",
5183                    properties: LinkProperties::fk_typed(
5184                        "replica_id",
5185                        "id",
5186                        Cardinality::ManyToOne,
5187                        mz_repr::SemanticType::ReplicaId,
5188                    ),
5189                },
5190            ]
5191        },
5192        column_semantic_types: &[
5193            ("object_id", SemanticType::GlobalId),
5194            ("cluster_id", SemanticType::ClusterId),
5195            ("replica_id", SemanticType::ReplicaId),
5196        ],
5197    }),
5198});
5199
5200/// Successful hydration episodes for cluster replicas.
5201///
5202/// Exempt from the bootstrap reset and from forced shard replacement, since the
5203/// contents cannot be rebuilt from anything else. Schema evolution keeps them and
5204/// applies normally. Clearing them for a schema change is still allowed, see the
5205/// tripwire in `validate_migration_steps`.
5206pub static MZ_REPLICA_HYDRATION_HISTORY: LazyLock<BuiltinTable> = LazyLock::new(|| BuiltinTable {
5207    name: "mz_replica_hydration_history",
5208    schema: MZ_INTERNAL_SCHEMA,
5209    oid: oid::TABLE_MZ_REPLICA_HYDRATION_HISTORY_OID,
5210    desc: RelationDesc::builder()
5211        .with_column("replica_id", SqlScalarType::String.nullable(false))
5212        .with_column("cluster_id", SqlScalarType::String.nullable(false))
5213        .with_column(
5214            "started_at",
5215            SqlScalarType::TimestampTz { precision: None }.nullable(false),
5216        )
5217        .with_column(
5218            "finished_at",
5219            SqlScalarType::TimestampTz { precision: None }.nullable(true),
5220        )
5221        .with_column("object_count", SqlScalarType::UInt64.nullable(false))
5222        .with_column("peak_memory_bytes", SqlScalarType::UInt64.nullable(true))
5223        .with_column("peak_disk_bytes", SqlScalarType::UInt64.nullable(true))
5224        .with_column("status", SqlScalarType::String.nullable(false))
5225        .finish(),
5226    column_comments: BTreeMap::from_iter([
5227        (
5228            "replica_id",
5229            "The ID of the cluster replica. May name a replica that no longer exists.",
5230        ),
5231        ("cluster_id", "The ID of the replica's cluster."),
5232        (
5233            "started_at",
5234            "The earliest maintained compute dataflow installation in the hydration episode.",
5235        ),
5236        (
5237            "finished_at",
5238            "The latest maintained compute dataflow hydration in the hydration episode.",
5239        ),
5240        (
5241            "object_count",
5242            "The number of maintained compute dataflows in the hydration episode.",
5243        ),
5244        (
5245            "peak_memory_bytes",
5246            "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.",
5247        ),
5248        (
5249            "peak_disk_bytes",
5250            "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.",
5251        ),
5252        (
5253            "status",
5254            "The hydration episode's status. Currently always `hydrated`.",
5255        ),
5256    ]),
5257    // Not a retained-metrics object: that would pin a 30 day compaction window,
5258    // and our history lives in the rows, which the retention sweep retracts on
5259    // its own schedule. Nothing reads this table at an old timestamp.
5260    is_retained_metrics_object: false,
5261    access: vec![PUBLIC_SELECT],
5262    ontology: Some(Ontology {
5263        entity_name: "replica_hydration_episode",
5264        description: "Successful hydration episode on a cluster replica",
5265        links: &const {
5266            [
5267                OntologyLink {
5268                    name: "hydrated_on_cluster",
5269                    target: "cluster",
5270                    properties: LinkProperties::ForeignKey {
5271                        source_column: "cluster_id",
5272                        target_column: "id",
5273                        cardinality: Cardinality::ManyToOne,
5274                        source_id_type: None,
5275                        requires_mapping: None,
5276                        nullable: false,
5277                        note: Some(
5278                            "Hydration samples can outlive their cluster, so this reference may not resolve.",
5279                        ),
5280                        extra_key_columns: None,
5281                    },
5282                },
5283                OntologyLink {
5284                    name: "hydrated_on_replica",
5285                    target: "replica",
5286                    properties: LinkProperties::ForeignKey {
5287                        source_column: "replica_id",
5288                        target_column: "id",
5289                        cardinality: Cardinality::ManyToOne,
5290                        source_id_type: Some(mz_repr::SemanticType::ReplicaId),
5291                        requires_mapping: None,
5292                        nullable: false,
5293                        note: Some(
5294                            "Hydration samples can outlive their replica, so this reference may not resolve.",
5295                        ),
5296                        extra_key_columns: None,
5297                    },
5298                },
5299            ]
5300        },
5301        column_semantic_types: &[
5302            ("replica_id", SemanticType::ReplicaId),
5303            ("cluster_id", SemanticType::ClusterId),
5304        ],
5305    }),
5306});
5307
5308pub static MZ_COMPUTE_HYDRATION_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5309    name: "mz_compute_hydration_statuses",
5310    schema: MZ_INTERNAL_SCHEMA,
5311    oid: oid::SOURCE_MZ_COMPUTE_HYDRATION_STATUSES_OID,
5312    desc: RelationDesc::builder()
5313        .with_column("object_id", SqlScalarType::String.nullable(false))
5314        .with_column("replica_id", SqlScalarType::String.nullable(false))
5315        .with_column("hydrated", SqlScalarType::Bool.nullable(false))
5316        .with_column("hydration_time", SqlScalarType::Interval.nullable(true))
5317        .finish(),
5318    column_comments: BTreeMap::from_iter([
5319        (
5320            "object_id",
5321            "The ID of a compute object. Corresponds to `mz_catalog.mz_indexes.id` or `mz_catalog.mz_materialized_views.id`",
5322        ),
5323        ("replica_id", "The ID of a cluster replica."),
5324        (
5325            "hydrated",
5326            "Whether the compute object is hydrated on the replica.",
5327        ),
5328        (
5329            "hydration_time",
5330            "The amount of time it took for the replica to hydrate the compute object.",
5331        ),
5332    ]),
5333    sql: "
5334WITH
5335    dataflows AS (
5336        SELECT
5337            object_id,
5338            replica_id,
5339            time_ns IS NOT NULL AS hydrated,
5340            ((time_ns / 1000) || 'microseconds')::interval AS hydration_time
5341        FROM mz_internal.mz_compute_hydration_times
5342    ),
5343    -- MVs that have advanced to the empty frontier don't have a dataflow installed anymore and
5344    -- therefore don't show up in `mz_compute_hydration_times`. We still want to show them here to
5345    -- avoid surprises for people joining `mz_materialized_views` against this relation (like the
5346    -- blue-green readiness query does), so we include them as 'hydrated'.
5347    complete_mvs AS (
5348        SELECT
5349            mv.id,
5350            f.replica_id,
5351            true AS hydrated,
5352            NULL::interval AS hydration_time
5353        FROM mz_materialized_views mv
5354        JOIN mz_catalog.mz_cluster_replica_frontiers f ON f.object_id = mv.id
5355        WHERE f.write_frontier IS NULL
5356    )
5357SELECT * FROM dataflows
5358UNION ALL
5359SELECT * FROM complete_mvs",
5360    access: vec![PUBLIC_SELECT],
5361    ontology: Some(Ontology {
5362        entity_name: "compute_hydration_status_view",
5363        description: "Computed hydration status per compute object",
5364        links: &const { [] },
5365        column_semantic_types: &const {
5366            [
5367                ("object_id", SemanticType::GlobalId),
5368                ("replica_id", SemanticType::ReplicaId),
5369            ]
5370        },
5371    }),
5372});
5373
5374pub static MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES: LazyLock<BuiltinSource> = LazyLock::new(|| {
5375    BuiltinSource {
5376        name: "mz_compute_operator_hydration_statuses",
5377        schema: MZ_INTERNAL_SCHEMA,
5378        oid: oid::SOURCE_MZ_COMPUTE_OPERATOR_HYDRATION_STATUSES_OID,
5379        desc: RelationDesc::builder()
5380            .with_column("replica_id", SqlScalarType::String.nullable(false))
5381            .with_column("object_id", SqlScalarType::String.nullable(false))
5382            .with_column(
5383                "physical_plan_node_id",
5384                SqlScalarType::UInt64.nullable(false),
5385            )
5386            .with_column("hydrated", SqlScalarType::Bool.nullable(false))
5387            .with_key(vec![0, 1, 2])
5388            .finish(),
5389        data_source: IntrospectionType::ComputeOperatorHydrationStatus.into(),
5390        column_comments: BTreeMap::from_iter([
5391            ("replica_id", "The ID of a cluster replica."),
5392            (
5393                "object_id",
5394                "The ID of a compute object. Corresponds to `mz_catalog.mz_indexes.id` or `mz_catalog.mz_materialized_views.id`.",
5395            ),
5396            (
5397                "physical_plan_node_id",
5398                "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)`.",
5399            ),
5400            ("hydrated", "Whether the node is hydrated on the replica."),
5401        ]),
5402        is_retained_metrics_object: false,
5403        access: vec![PUBLIC_SELECT],
5404        ontology: Some(Ontology {
5405            entity_name: "compute_hydration_status",
5406            description: "Hydration status per compute operator",
5407            links: &const { [] },
5408            column_semantic_types: &const {
5409                [
5410                    ("replica_id", SemanticType::ReplicaId),
5411                    ("object_id", SemanticType::CatalogItemId),
5412                ]
5413            },
5414        }),
5415    }
5416});
5417
5418pub static MZ_CLUSTER_REPLICA_UTILIZATION: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5419    name: "mz_cluster_replica_utilization",
5420    schema: MZ_INTERNAL_SCHEMA,
5421    oid: oid::VIEW_MZ_CLUSTER_REPLICA_UTILIZATION_OID,
5422    desc: RelationDesc::builder()
5423        .with_column("replica_id", SqlScalarType::String.nullable(false))
5424        .with_column("process_id", SqlScalarType::UInt64.nullable(false))
5425        .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
5426        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
5427        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
5428        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
5429        .with_column("swap_percent", SqlScalarType::Float64.nullable(true))
5430        .finish(),
5431    column_comments: BTreeMap::from_iter([
5432        ("replica_id", "The ID of a cluster replica."),
5433        ("process_id", "The ID of a process within the replica."),
5434        (
5435            "cpu_percent",
5436            "Approximate CPU usage, in percent of the total allocation.",
5437        ),
5438        (
5439            "memory_percent",
5440            "Approximate RAM usage, in percent of the total allocation.",
5441        ),
5442        (
5443            "disk_percent",
5444            "Approximate disk usage, in percent of the total allocation.",
5445        ),
5446        (
5447            "heap_percent",
5448            "Approximate heap (RAM + swap) usage, in percent of the total allocation.",
5449        ),
5450        (
5451            "swap_percent",
5452            "Approximate swap usage, in percent of the total heap allocation.",
5453        ),
5454    ]),
5455    sql: "
5456SELECT
5457    r.id AS replica_id,
5458    m.process_id,
5459    m.cpu_nano_cores::float8 / NULLIF(s.cpu_nano_cores, 0) * 100 AS cpu_percent,
5460    m.memory_bytes::float8 / NULLIF(s.memory_bytes, 0) * 100 AS memory_percent,
5461    m.disk_bytes::float8 / NULLIF(s.disk_bytes, 0) * 100 AS disk_percent,
5462    m.heap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS heap_percent,
5463    m.swap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS swap_percent
5464FROM
5465    mz_catalog.mz_cluster_replicas AS r
5466        JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
5467        JOIN mz_internal.mz_cluster_replica_metrics AS m ON m.replica_id = r.id",
5468    access: vec![PUBLIC_SELECT],
5469    ontology: Some(Ontology {
5470        entity_name: "replica_utilization",
5471        description: "Computed utilization metrics per replica",
5472        links: &const {
5473            [OntologyLink {
5474                name: "utilization_of_replica",
5475                target: "replica",
5476                properties: LinkProperties::fk_typed(
5477                    "replica_id",
5478                    "id",
5479                    Cardinality::OneToOne,
5480                    mz_repr::SemanticType::CatalogItemId,
5481                ),
5482            }]
5483        },
5484        column_semantic_types: &[("replica_id", SemanticType::ReplicaId)],
5485    }),
5486});
5487
5488pub static MZ_CLUSTER_REPLICA_UTILIZATION_HISTORY: LazyLock<BuiltinView> =
5489    LazyLock::new(|| BuiltinView {
5490        name: "mz_cluster_replica_utilization_history",
5491        schema: MZ_INTERNAL_SCHEMA,
5492        oid: oid::VIEW_MZ_CLUSTER_REPLICA_UTILIZATION_HISTORY_OID,
5493        desc: RelationDesc::builder()
5494            .with_column("replica_id", SqlScalarType::String.nullable(false))
5495            .with_column("process_id", SqlScalarType::UInt64.nullable(false))
5496            .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
5497            .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
5498            .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
5499            .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
5500            .with_column("swap_percent", SqlScalarType::Float64.nullable(true))
5501            .with_column(
5502                "occurred_at",
5503                SqlScalarType::TimestampTz { precision: None }.nullable(false),
5504            )
5505            .finish(),
5506        column_comments: BTreeMap::from_iter([
5507            ("replica_id", "The ID of a cluster replica."),
5508            ("process_id", "The ID of a process within the replica."),
5509            (
5510                "cpu_percent",
5511                "Approximate CPU usage, in percent of the total allocation.",
5512            ),
5513            (
5514                "memory_percent",
5515                "Approximate RAM usage, in percent of the total allocation.",
5516            ),
5517            (
5518                "disk_percent",
5519                "Approximate disk usage, in percent of the total allocation.",
5520            ),
5521            (
5522                "heap_percent",
5523                "Approximate heap (RAM + swap) usage, in percent of the total allocation.",
5524            ),
5525            (
5526                "swap_percent",
5527                "Approximate swap usage, in percent of the total heap allocation.",
5528            ),
5529            (
5530                "occurred_at",
5531                "Wall-clock timestamp at which the event occurred.",
5532            ),
5533        ]),
5534        sql: "
5535SELECT
5536    r.id AS replica_id,
5537    m.process_id,
5538    m.cpu_nano_cores::float8 / NULLIF(s.cpu_nano_cores, 0) * 100 AS cpu_percent,
5539    m.memory_bytes::float8 / NULLIF(s.memory_bytes, 0) * 100 AS memory_percent,
5540    m.disk_bytes::float8 / NULLIF(s.disk_bytes, 0) * 100 AS disk_percent,
5541    m.heap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS heap_percent,
5542    m.swap_bytes::float8 / NULLIF(m.heap_limit, 0) * 100 AS swap_percent,
5543    m.occurred_at
5544FROM
5545    mz_catalog.mz_cluster_replicas AS r
5546        JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
5547        JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.id",
5548        access: vec![PUBLIC_SELECT],
5549        ontology: None,
5550    });
5551
5552pub static MZ_INDEX_ADVICE: LazyLock<BuiltinView> = LazyLock::new(|| {
5553    BuiltinView {
5554        name: "mz_index_advice",
5555        schema: MZ_INTERNAL_SCHEMA,
5556        oid: oid::VIEW_MZ_INDEX_ADVICE_OID,
5557        desc: RelationDesc::builder()
5558            .with_column("object_id", SqlScalarType::String.nullable(true))
5559            .with_column("hint", SqlScalarType::String.nullable(false))
5560            .with_column("details", SqlScalarType::String.nullable(false))
5561            .with_column("referenced_object_ids", SqlScalarType::List { element_type: Box::new(SqlScalarType::String), custom_id: None }.nullable(true))
5562            .finish(),
5563        column_comments: BTreeMap::from_iter([
5564            ("object_id", "The ID of the object. Corresponds to mz_objects.id."),
5565            ("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."),
5566            ("details", "Additional details on why the `hint` was proposed based on the dependencies of the object."),
5567            ("referenced_object_ids", "The IDs of objects referenced by `details`. Corresponds to mz_objects.id."),
5568        ]),
5569        sql: "
5570-- To avoid confusion with sources and sinks in the materialize sense,
5571-- the following uses the terms leafs (instead of sinks) and roots (instead of sources)
5572-- when referring to the object dependency graph.
5573--
5574-- The basic idea is to walk up the dependency graph to propagate the transitive dependencies
5575-- of maintained objected upwards. The leaves of the dependency graph are maintained objects
5576-- that are not depended on by other maintained objects and have a justification why they must
5577-- be maintained (e.g. a materialized view that is depended on by a sink).
5578-- Starting from these leaves, the dependencies are propagated upwards towards the roots according
5579-- to the object dependencies. Whenever there is a node that is being depended on by multiple
5580-- downstream objects, that node is marked to be converted into a maintained object and this
5581-- node is then propagated further up. Once completed, the list of objects that are marked as
5582-- maintained is checked against all objects to generate appropriate recommendations.
5583--
5584-- Note that the recommendations only incorporate dependencies between objects.
5585-- This can lead to bad recommendations, e.g. filters can no longer be pushed into (or close to)
5586-- a sink if an index is added in between the sink and the filter. For very selective filters,
5587-- this can lead to redundant work: the index is computing stuff only to discarded by the selective
5588-- filter later on. But these kind of aspects cannot be understood by merely looking at the
5589-- dependencies.
5590WITH MUTUALLY RECURSIVE
5591    -- for all objects, understand if they have an index on them and on which cluster they are running
5592    -- this avoids having different cases for views with an index and materialized views later on
5593    objects(id text, type text, cluster_id text, indexes text list) AS (
5594        -- views and materialized views without an index
5595        SELECT
5596            o.id,
5597            o.type,
5598            o.cluster_id,
5599            '{}'::text list AS indexes
5600        FROM mz_catalog.mz_objects o
5601        WHERE o.id LIKE 'u%' AND o.type IN ('materialized-view', 'view') AND NOT EXISTS (
5602            SELECT FROM mz_internal.mz_object_dependencies d
5603            JOIN mz_catalog.mz_objects AS i
5604                ON (i.id = d.object_id AND i.type = 'index')
5605            WHERE (o.id = d.referenced_object_id)
5606        )
5607
5608        UNION ALL
5609
5610        -- views and materialized views with an index
5611        SELECT
5612            o.id,
5613            o.type,
5614            -- o.cluster_id is always NULL for views, so use the cluster of the index instead
5615            COALESCE(o.cluster_id, i.cluster_id) AS cluster_id,
5616            list_agg(i.id) AS indexes
5617        FROM mz_catalog.mz_objects o
5618        JOIN mz_internal.mz_object_dependencies AS d
5619            ON (o.id = d.referenced_object_id)
5620        JOIN mz_catalog.mz_objects AS i
5621            ON (i.id = d.object_id AND i.type = 'index')
5622        WHERE o.id LIKE 'u%' AND o.type IN ('materialized-view', 'view', 'source')
5623        GROUP BY o.id, o.type, o.cluster_id, i.cluster_id
5624    ),
5625
5626    -- maintained objects that are at the leafs of the dependency graph with respect to a specific cluster
5627    maintained_leafs(id text, justification text) AS (
5628        -- materialized views that are connected to a sink
5629        SELECT
5630            m.id,
5631            s.id AS justification
5632        FROM objects AS m
5633        JOIN mz_internal.mz_object_dependencies AS d
5634            ON (m.id = d.referenced_object_id)
5635        JOIN mz_catalog.mz_objects AS s
5636            ON (s.id = d.object_id AND s.type = 'sink')
5637        WHERE m.type = 'materialized-view'
5638
5639        UNION ALL
5640
5641        -- (materialized) views with an index that are not transitively depend on by maintained objects on the same cluster
5642        SELECT
5643            v.id,
5644            unnest(v.indexes) AS justification
5645        FROM objects AS v
5646        WHERE v.type IN ('view', 'materialized-view', 'source') AND NOT EXISTS (
5647            SELECT FROM mz_internal.mz_object_transitive_dependencies AS d
5648            INNER JOIN mz_catalog.mz_objects AS child
5649                ON (d.object_id = child.id)
5650            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]
5651        )
5652    ),
5653
5654    -- this is just a helper cte to union multiple lists as part of an aggregation, which is not directly possible in SQL
5655    agg_maintained_children(id text, maintained_children text list) AS (
5656        SELECT
5657            parent_id AS id,
5658            list_agg(maintained_child) AS maintained_leafs
5659        FROM (
5660            SELECT DISTINCT
5661                d.referenced_object_id AS parent_id,
5662                -- it's not possible to union lists in an aggregation, so we have to unnest the list first
5663                unnest(child.maintained_children) AS maintained_child
5664            FROM propagate_dependencies AS child
5665            INNER JOIN mz_internal.mz_object_dependencies AS d
5666                ON (child.id = d.object_id)
5667        )
5668        GROUP BY parent_id
5669    ),
5670
5671    -- propagate dependencies of maintained objects from the leafs to the roots of the dependency graph and
5672    -- record a justification when an object should be maintained, e.g. when it is depended on by more than one maintained object
5673    -- 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
5674    propagate_dependencies(id text, maintained_children text list, justification text list) AS (
5675        -- base case: start with the leafs
5676        SELECT DISTINCT
5677            id,
5678            LIST[id] AS maintained_children,
5679            list_agg(justification) AS justification
5680        FROM maintained_leafs
5681        GROUP BY id
5682
5683        UNION
5684
5685        -- recursive case: if there is a child with the same dependencies as the parent,
5686        -- the parent is only reused by a single child
5687        SELECT
5688            parent.id,
5689            child.maintained_children,
5690            NULL::text list AS justification
5691        FROM agg_maintained_children AS parent
5692        INNER JOIN mz_internal.mz_object_dependencies AS d
5693            ON (parent.id = d.referenced_object_id)
5694        INNER JOIN propagate_dependencies AS child
5695            ON (d.object_id = child.id)
5696        WHERE parent.maintained_children = child.maintained_children
5697
5698        UNION
5699
5700        -- recursive case: if there is NO child with the same dependencies as the parent,
5701        -- different children are reusing the parent so maintaining the object is justified by itself
5702        SELECT DISTINCT
5703            parent.id,
5704            LIST[parent.id] AS maintained_children,
5705            parent.maintained_children AS justification
5706        FROM agg_maintained_children AS parent
5707        WHERE NOT EXISTS (
5708            SELECT FROM mz_internal.mz_object_dependencies AS d
5709            INNER JOIN propagate_dependencies AS child
5710                ON (d.object_id = child.id AND d.referenced_object_id = parent.id)
5711            WHERE parent.maintained_children = child.maintained_children
5712        )
5713    ),
5714
5715    objects_with_justification(id text, type text, cluster_id text, maintained_children text list, justification text list, indexes text list) AS (
5716        SELECT
5717            p.id,
5718            o.type,
5719            o.cluster_id,
5720            p.maintained_children,
5721            p.justification,
5722            o.indexes
5723        FROM propagate_dependencies p
5724        JOIN objects AS o
5725            ON (p.id = o.id)
5726    ),
5727
5728    hints(id text, hint text, details text, justification text list) AS (
5729        -- materialized views that are not required
5730        SELECT
5731            id,
5732            'convert to a view' AS hint,
5733            'no dependencies from sinks nor from objects on different clusters' AS details,
5734            justification
5735        FROM objects_with_justification
5736        WHERE type = 'materialized-view' AND justification IS NULL
5737
5738        UNION ALL
5739
5740        -- materialized views that are required because a sink or a maintained object from a different cluster depends on them
5741        SELECT
5742            id,
5743            'keep' AS hint,
5744            'dependencies from sinks or objects on different clusters: ' AS details,
5745            justification
5746        FROM objects_with_justification AS m
5747        WHERE type = 'materialized-view' AND justification IS NOT NULL AND EXISTS (
5748            SELECT FROM unnest(justification) AS dependency
5749            JOIN mz_catalog.mz_objects s ON (s.type = 'sink' AND s.id = dependency)
5750
5751            UNION ALL
5752
5753            SELECT FROM unnest(justification) AS dependency
5754            JOIN mz_catalog.mz_objects AS d ON (d.id = dependency)
5755            WHERE d.cluster_id != m.cluster_id
5756        )
5757
5758        UNION ALL
5759
5760        -- 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
5761        SELECT
5762            id,
5763            'convert to a view with an index' AS hint,
5764            'no dependencies from sinks nor from objects on different clusters, but maintained dependencies on the same cluster: ' AS details,
5765            justification
5766        FROM objects_with_justification AS m
5767        WHERE type = 'materialized-view' AND justification IS NOT NULL AND NOT EXISTS (
5768            SELECT FROM unnest(justification) AS dependency
5769            JOIN mz_catalog.mz_objects s ON (s.type = 'sink' AND s.id = dependency)
5770
5771            UNION ALL
5772
5773            SELECT FROM unnest(justification) AS dependency
5774            JOIN mz_catalog.mz_objects AS d ON (d.id = dependency)
5775            WHERE d.cluster_id != m.cluster_id
5776        )
5777
5778        UNION ALL
5779
5780        -- views that have indexes on different clusters should be a materialized view
5781        SELECT
5782            o.id,
5783            'convert to materialized view' AS hint,
5784            'dependencies on multiple clusters: ' AS details,
5785            o.justification
5786        FROM objects_with_justification o,
5787            LATERAL unnest(o.justification) j
5788        LEFT JOIN mz_catalog.mz_objects AS m
5789            ON (m.id = j AND m.type IN ('index', 'materialized-view'))
5790        WHERE o.type = 'view' AND o.justification IS NOT NULL
5791        GROUP BY o.id, o.justification
5792        HAVING count(DISTINCT m.cluster_id) >= 2
5793
5794        UNION ALL
5795
5796        -- views without an index that should be maintained
5797        SELECT
5798            id,
5799            'add index' AS hint,
5800            'multiple downstream dependencies: ' AS details,
5801            justification
5802        FROM objects_with_justification
5803        WHERE type = 'view' AND justification IS NOT NULL AND indexes = '{}'::text list
5804
5805        UNION ALL
5806
5807        -- index inside the dependency graph (not a leaf)
5808        SELECT
5809            unnest(indexes) AS id,
5810            'drop unless queried directly' AS hint,
5811            'fewer than two downstream dependencies: ' AS details,
5812            maintained_children AS justification
5813        FROM objects_with_justification
5814        WHERE type = 'view' AND NOT indexes = '{}'::text list AND justification IS NULL
5815
5816        UNION ALL
5817
5818        -- index on a leaf of the dependency graph
5819        SELECT
5820            unnest(indexes) AS id,
5821            'drop unless queried directly' AS hint,
5822            'associated object does not have any dependencies (maintained or not maintained)' AS details,
5823            NULL::text list AS justification
5824        FROM objects_with_justification
5825        -- indexes can only be part of justification for leaf nodes
5826        WHERE type IN ('view', 'materialized-view') AND NOT indexes = '{}'::text list AND justification @> indexes
5827
5828        UNION ALL
5829
5830        -- index on a source
5831        SELECT
5832            unnest(indexes) AS id,
5833            'drop unless queried directly' AS hint,
5834            'sources do not transform data and can expose data directly' AS details,
5835            NULL::text list AS justification
5836        FROM objects_with_justification
5837        -- indexes can only be part of justification for leaf nodes
5838        WHERE type = 'source' AND NOT indexes = '{}'::text list
5839
5840        UNION ALL
5841
5842        -- indexes on views inside the dependency graph
5843        SELECT
5844            unnest(indexes) AS id,
5845            'keep' AS hint,
5846            'multiple downstream dependencies: ' AS details,
5847            justification
5848        FROM objects_with_justification
5849        -- indexes can only be part of justification for leaf nodes
5850        WHERE type = 'view' AND justification IS NOT NULL AND NOT indexes = '{}'::text list AND NOT justification @> indexes
5851    ),
5852
5853    hints_resolved_ids(id text, hint text, details text, justification text list) AS (
5854        SELECT
5855            h.id,
5856            h.hint,
5857            h.details || list_agg(o.name)::text AS details,
5858            h.justification
5859        FROM hints AS h,
5860            LATERAL unnest(h.justification) j
5861        JOIN mz_catalog.mz_objects AS o
5862            ON (o.id = j)
5863        GROUP BY h.id, h.hint, h.details, h.justification
5864
5865        UNION ALL
5866
5867        SELECT
5868            id,
5869            hint,
5870            details,
5871            justification
5872        FROM hints
5873        WHERE justification IS NULL
5874    )
5875
5876SELECT
5877    h.id AS object_id,
5878    h.hint AS hint,
5879    h.details,
5880    h.justification AS referenced_object_ids
5881FROM hints_resolved_ids AS h",
5882        access: vec![PUBLIC_SELECT],
5883        ontology: None,
5884    }
5885});
5886
5887/// Peeled version of `PG_AUTHID`: Excludes the columns rolcreaterole and rolcreatedb, to make this
5888/// view indexable.
5889pub static PG_AUTHID_CORE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5890    name: "pg_authid_core",
5891    schema: MZ_INTERNAL_SCHEMA,
5892    oid: oid::VIEW_PG_AUTHID_CORE_OID,
5893    desc: RelationDesc::builder()
5894        .with_column("oid", SqlScalarType::Oid.nullable(false))
5895        .with_column("rolname", SqlScalarType::String.nullable(false))
5896        .with_column("rolsuper", SqlScalarType::Bool.nullable(true))
5897        .with_column("rolinherit", SqlScalarType::Bool.nullable(false))
5898        .with_column("rolcanlogin", SqlScalarType::Bool.nullable(false))
5899        .with_column("rolreplication", SqlScalarType::Bool.nullable(false))
5900        .with_column("rolbypassrls", SqlScalarType::Bool.nullable(false))
5901        .with_column("rolconnlimit", SqlScalarType::Int32.nullable(false))
5902        .with_column("rolpassword", SqlScalarType::String.nullable(true))
5903        .with_column(
5904            "rolvaliduntil",
5905            SqlScalarType::TimestampTz { precision: None }.nullable(true),
5906        )
5907        .finish(),
5908    column_comments: BTreeMap::new(),
5909    sql: r#"
5910SELECT
5911    r.oid AS oid,
5912    r.name AS rolname,
5913    rolsuper,
5914    inherit AS rolinherit,
5915    COALESCE(r.rolcanlogin, false) AS rolcanlogin,
5916    -- MZ doesn't support replication in the same way Postgres does
5917    false AS rolreplication,
5918    -- MZ doesn't how row level security
5919    false AS rolbypassrls,
5920    -- MZ doesn't have a connection limit
5921    -1 AS rolconnlimit,
5922    a.password_hash AS rolpassword,
5923    NULL::pg_catalog.timestamptz AS rolvaliduntil
5924FROM mz_catalog.mz_roles r
5925LEFT JOIN mz_catalog.mz_role_auth a ON r.oid = a.role_oid"#,
5926    access: vec![rbac::owner_privilege(ObjectType::Table, MZ_SYSTEM_ROLE_ID)],
5927    ontology: None,
5928});
5929
5930pub const PG_AUTHID_CORE_IND: BuiltinIndex = BuiltinIndex {
5931    name: "pg_authid_core_ind",
5932    schema: MZ_INTERNAL_SCHEMA,
5933    oid: oid::INDEX_PG_AUTHID_CORE_IND_OID,
5934    sql: "IN CLUSTER mz_catalog_server
5935ON mz_internal.pg_authid_core (rolname)",
5936    is_retained_metrics_object: false,
5937};
5938
5939pub static MZ_SHOW_ALL_OBJECTS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
5940    name: "mz_show_all_objects",
5941    schema: MZ_INTERNAL_SCHEMA,
5942    oid: oid::VIEW_MZ_SHOW_ALL_OBJECTS_OID,
5943    desc: RelationDesc::builder()
5944        .with_column("schema_id", SqlScalarType::String.nullable(false))
5945        .with_column("name", SqlScalarType::String.nullable(false))
5946        .with_column("type", SqlScalarType::String.nullable(false))
5947        .with_column("comment", SqlScalarType::String.nullable(false))
5948        .finish(),
5949    column_comments: BTreeMap::new(),
5950    sql: "WITH comments AS (
5951        SELECT id, object_type, comment
5952        FROM mz_internal.mz_comments
5953        WHERE object_sub_id IS NULL
5954    )
5955    SELECT schema_id, name, type, COALESCE(comment, '') AS comment
5956    FROM mz_catalog.mz_objects AS objs
5957    LEFT JOIN comments ON objs.id = comments.id AND comments.object_type = objs.type",
5958    access: vec![PUBLIC_SELECT],
5959    ontology: None,
5960});
5961
5962pub static MZ_SHOW_CLUSTERS: LazyLock<BuiltinView> = LazyLock::new(|| {
5963    BuiltinView {
5964    name: "mz_show_clusters",
5965    schema: MZ_INTERNAL_SCHEMA,
5966    oid: oid::VIEW_MZ_SHOW_CLUSTERS_OID,
5967    desc: RelationDesc::builder()
5968        .with_column("name", SqlScalarType::String.nullable(false))
5969        .with_column("replicas", SqlScalarType::String.nullable(true))
5970        // One-line summary of any in-flight reconfiguration or burst, NULL
5971        // when the cluster is steady.
5972        .with_column("activity", SqlScalarType::String.nullable(true))
5973        .with_column("comment", SqlScalarType::String.nullable(false))
5974        .finish(),
5975    column_comments: BTreeMap::new(),
5976    // Settled reconfiguration records are retained, so match only
5977    // `in-progress`. A non-null auto-scaling `state` means a live burst.
5978    // The reconfiguration summary names only the dimensions the record
5979    // actually changes (from `changes`), with values where they read well.
5980    // NOTE: `||` with a NULL operand nulls the whole summary. `burst_size`
5981    // is a non-optional field of its record, keep it that way or COALESCE.
5982    // The NULLIF guards an empty diff (not expected in-progress), which
5983    // otherwise would render a dangling 'reconfiguring'.
5984    // Neither input needs `mz_now()`, keeping this indexed view non-temporal.
5985    sql: "
5986    WITH clusters AS (
5987        SELECT
5988            mc.id,
5989            mc.name,
5990            pg_catalog.string_agg(mcr.name || ' (' || mcr.size || ')', ', ' ORDER BY mcr.name) AS replicas
5991        FROM mz_catalog.mz_clusters mc
5992        LEFT JOIN mz_catalog.mz_cluster_replicas mcr
5993        ON mc.id = mcr.cluster_id
5994        GROUP BY mc.id, mc.name
5995    ),
5996    comments AS (
5997        SELECT id, comment
5998        FROM mz_internal.mz_comments
5999        WHERE object_type = 'cluster' AND object_sub_id IS NULL
6000    ),
6001    reconfigurations AS (
6002        SELECT
6003            cluster_id,
6004            'reconfiguring ' || NULLIF(array_to_string(ARRAY[
6005                'size to ' || (changes->>'size'),
6006                'replication factor to ' || (changes->>'replication_factor'),
6007                CASE WHEN changes->'availability_zones' IS NOT NULL THEN 'availability zones' END,
6008                CASE WHEN changes->'logging' IS NOT NULL THEN 'introspection settings' END,
6009                CASE WHEN changes->'arrangement_compression' IS NOT NULL THEN 'arrangement compression' END
6010            ], ', '), '') AS summary
6011        FROM mz_internal.mz_cluster_reconfigurations
6012        WHERE status = 'in-progress'
6013    )
6014    SELECT
6015        name,
6016        replicas,
6017        CASE
6018            WHEN recon.summary IS NOT NULL AND scaling.state IS NOT NULL
6019                THEN recon.summary
6020                     || '; hydration burst at ' || (scaling.state->'burst'->>'burst_size')
6021            WHEN recon.summary IS NOT NULL
6022                THEN recon.summary
6023            WHEN scaling.state IS NOT NULL
6024                THEN 'hydration burst at ' || (scaling.state->'burst'->>'burst_size')
6025            ELSE NULL
6026        END AS activity,
6027        COALESCE(comment, '') as comment
6028    FROM clusters
6029    LEFT JOIN comments ON clusters.id = comments.id
6030    LEFT JOIN reconfigurations recon
6031        ON clusters.id = recon.cluster_id
6032    LEFT JOIN mz_internal.mz_cluster_auto_scaling_strategies scaling ON clusters.id = scaling.cluster_id",
6033    access: vec![PUBLIC_SELECT],
6034    ontology: None,
6035}
6036});
6037
6038pub static MZ_SHOW_SECRETS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6039    name: "mz_show_secrets",
6040    schema: MZ_INTERNAL_SCHEMA,
6041    oid: oid::VIEW_MZ_SHOW_SECRETS_OID,
6042    desc: RelationDesc::builder()
6043        .with_column("schema_id", SqlScalarType::String.nullable(false))
6044        .with_column("name", SqlScalarType::String.nullable(false))
6045        .with_column("comment", SqlScalarType::String.nullable(false))
6046        .finish(),
6047    column_comments: BTreeMap::new(),
6048    sql: "WITH comments AS (
6049        SELECT id, comment
6050        FROM mz_internal.mz_comments
6051        WHERE object_type = 'secret' AND object_sub_id IS NULL
6052    )
6053    SELECT schema_id, name, COALESCE(comment, '') as comment
6054    FROM mz_catalog.mz_secrets secrets
6055    LEFT JOIN comments ON secrets.id = comments.id",
6056    access: vec![PUBLIC_SELECT],
6057    ontology: None,
6058});
6059
6060pub static MZ_SHOW_COLUMNS: LazyLock<BuiltinView> = LazyLock::new(|| {
6061    BuiltinView {
6062    name: "mz_show_columns",
6063    schema: MZ_INTERNAL_SCHEMA,
6064    oid: oid::VIEW_MZ_SHOW_COLUMNS_OID,
6065    desc: RelationDesc::builder()
6066        .with_column("id", SqlScalarType::String.nullable(false))
6067        .with_column("name", SqlScalarType::String.nullable(false))
6068        .with_column("nullable", SqlScalarType::Bool.nullable(false))
6069        .with_column("type", SqlScalarType::String.nullable(false))
6070        .with_column("position", SqlScalarType::UInt64.nullable(false))
6071        .with_column("comment", SqlScalarType::String.nullable(false))
6072        .finish(),
6073    column_comments: BTreeMap::new(),
6074    // The `object_type` predicate on the comment join guards against
6075    // stale comment rows that can survive when a builtin's type changes
6076    // but its catalog id is preserved (e.g. a Table → MaterializedView
6077    // schema migration). Without it, a column would match both the old
6078    // and new object_type rows and each row would be emitted twice.
6079    sql: "
6080    SELECT columns.id, columns.name, columns.nullable, columns.type, columns.position, COALESCE(comment, '') as comment
6081    FROM mz_catalog.mz_columns columns
6082    LEFT JOIN mz_catalog.mz_objects obj ON obj.id = columns.id
6083    LEFT JOIN mz_internal.mz_comments comments
6084    ON columns.id = comments.id
6085       AND columns.position = comments.object_sub_id
6086       AND comments.object_type = obj.type",
6087    access: vec![PUBLIC_SELECT],
6088    ontology: None,
6089}
6090});
6091
6092pub static MZ_SHOW_DATABASES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6093    name: "mz_show_databases",
6094    schema: MZ_INTERNAL_SCHEMA,
6095    oid: oid::VIEW_MZ_SHOW_DATABASES_OID,
6096    desc: RelationDesc::builder()
6097        .with_column("name", SqlScalarType::String.nullable(false))
6098        .with_column("comment", SqlScalarType::String.nullable(false))
6099        .finish(),
6100    column_comments: BTreeMap::new(),
6101    sql: "WITH comments AS (
6102        SELECT id, comment
6103        FROM mz_internal.mz_comments
6104        WHERE object_type = 'database' AND object_sub_id IS NULL
6105    )
6106    SELECT name, COALESCE(comment, '') as comment
6107    FROM mz_catalog.mz_databases databases
6108    LEFT JOIN comments ON databases.id = comments.id",
6109    access: vec![PUBLIC_SELECT],
6110    ontology: None,
6111});
6112
6113pub static MZ_SHOW_SCHEMAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6114    name: "mz_show_schemas",
6115    schema: MZ_INTERNAL_SCHEMA,
6116    oid: oid::VIEW_MZ_SHOW_SCHEMAS_OID,
6117    desc: RelationDesc::builder()
6118        .with_column("database_id", SqlScalarType::String.nullable(true))
6119        .with_column("name", SqlScalarType::String.nullable(false))
6120        .with_column("comment", SqlScalarType::String.nullable(false))
6121        .finish(),
6122    column_comments: BTreeMap::new(),
6123    sql: "WITH comments AS (
6124        SELECT id, comment
6125        FROM mz_internal.mz_comments
6126        WHERE object_type = 'schema' AND object_sub_id IS NULL
6127    )
6128    SELECT database_id, name, COALESCE(comment, '') as comment
6129    FROM mz_catalog.mz_schemas schemas
6130    LEFT JOIN comments ON schemas.id = comments.id",
6131    access: vec![PUBLIC_SELECT],
6132    ontology: None,
6133});
6134
6135pub static MZ_SHOW_ROLES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6136    name: "mz_show_roles",
6137    schema: MZ_INTERNAL_SCHEMA,
6138    oid: oid::VIEW_MZ_SHOW_ROLES_OID,
6139    desc: RelationDesc::builder()
6140        .with_column("name", SqlScalarType::String.nullable(false))
6141        .with_column("comment", SqlScalarType::String.nullable(false))
6142        .finish(),
6143    column_comments: BTreeMap::new(),
6144    sql: "WITH comments AS (
6145        SELECT id, comment
6146        FROM mz_internal.mz_comments
6147        WHERE object_type = 'role' AND object_sub_id IS NULL
6148    )
6149    SELECT name, COALESCE(comment, '') as comment
6150    FROM mz_catalog.mz_roles roles
6151    LEFT JOIN comments ON roles.id = comments.id
6152    WHERE roles.id NOT LIKE 's%'
6153      AND roles.id NOT LIKE 'g%'",
6154    access: vec![PUBLIC_SELECT],
6155    ontology: None,
6156});
6157
6158pub static MZ_SHOW_TABLES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6159    name: "mz_show_tables",
6160    schema: MZ_INTERNAL_SCHEMA,
6161    oid: oid::VIEW_MZ_SHOW_TABLES_OID,
6162    desc: RelationDesc::builder()
6163        .with_column("schema_id", SqlScalarType::String.nullable(false))
6164        .with_column("name", SqlScalarType::String.nullable(false))
6165        .with_column("comment", SqlScalarType::String.nullable(false))
6166        .with_column("source_id", SqlScalarType::String.nullable(true))
6167        .finish(),
6168    column_comments: BTreeMap::new(),
6169    sql: "WITH comments AS (
6170        SELECT id, comment
6171        FROM mz_internal.mz_comments
6172        WHERE object_type = 'table' AND object_sub_id IS NULL
6173    )
6174    SELECT schema_id, name, COALESCE(comment, '') as comment, source_id
6175    FROM mz_catalog.mz_tables tables
6176    LEFT JOIN comments ON tables.id = comments.id",
6177    access: vec![PUBLIC_SELECT],
6178    ontology: None,
6179});
6180
6181pub static MZ_SHOW_VIEWS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6182    name: "mz_show_views",
6183    schema: MZ_INTERNAL_SCHEMA,
6184    oid: oid::VIEW_MZ_SHOW_VIEWS_OID,
6185    desc: RelationDesc::builder()
6186        .with_column("schema_id", SqlScalarType::String.nullable(false))
6187        .with_column("name", SqlScalarType::String.nullable(false))
6188        .with_column("comment", SqlScalarType::String.nullable(false))
6189        .finish(),
6190    column_comments: BTreeMap::new(),
6191    sql: "WITH comments AS (
6192        SELECT id, comment
6193        FROM mz_internal.mz_comments
6194        WHERE object_type = 'view' AND object_sub_id IS NULL
6195    )
6196    SELECT schema_id, name, COALESCE(comment, '') as comment
6197    FROM mz_catalog.mz_views views
6198    LEFT JOIN comments ON views.id = comments.id",
6199    access: vec![PUBLIC_SELECT],
6200    ontology: None,
6201});
6202
6203pub static MZ_SHOW_TYPES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6204    name: "mz_show_types",
6205    schema: MZ_INTERNAL_SCHEMA,
6206    oid: oid::VIEW_MZ_SHOW_TYPES_OID,
6207    desc: RelationDesc::builder()
6208        .with_column("schema_id", SqlScalarType::String.nullable(false))
6209        .with_column("name", SqlScalarType::String.nullable(false))
6210        .with_column("comment", SqlScalarType::String.nullable(false))
6211        .finish(),
6212    column_comments: BTreeMap::new(),
6213    sql: "WITH comments AS (
6214        SELECT id, comment
6215        FROM mz_internal.mz_comments
6216        WHERE object_type = 'type' AND object_sub_id IS NULL
6217    )
6218    SELECT schema_id, name, COALESCE(comment, '') as comment
6219    FROM mz_catalog.mz_types types
6220    LEFT JOIN comments ON types.id = comments.id",
6221    access: vec![PUBLIC_SELECT],
6222    ontology: None,
6223});
6224
6225pub static MZ_SHOW_CONNECTIONS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6226    name: "mz_show_connections",
6227    schema: MZ_INTERNAL_SCHEMA,
6228    oid: oid::VIEW_MZ_SHOW_CONNECTIONS_OID,
6229    desc: RelationDesc::builder()
6230        .with_column("schema_id", SqlScalarType::String.nullable(false))
6231        .with_column("name", SqlScalarType::String.nullable(false))
6232        .with_column("type", SqlScalarType::String.nullable(false))
6233        .with_column("comment", SqlScalarType::String.nullable(false))
6234        .finish(),
6235    column_comments: BTreeMap::new(),
6236    sql: "WITH comments AS (
6237        SELECT id, comment
6238        FROM mz_internal.mz_comments
6239        WHERE object_type = 'connection' AND object_sub_id IS NULL
6240    )
6241    SELECT schema_id, name, type, COALESCE(comment, '') as comment
6242    FROM mz_catalog.mz_connections connections
6243    LEFT JOIN comments ON connections.id = comments.id",
6244    access: vec![PUBLIC_SELECT],
6245    ontology: None,
6246});
6247
6248pub static MZ_SHOW_SOURCES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6249    name: "mz_show_sources",
6250    schema: MZ_INTERNAL_SCHEMA,
6251    oid: oid::VIEW_MZ_SHOW_SOURCES_OID,
6252    desc: RelationDesc::builder()
6253        .with_column("id", SqlScalarType::String.nullable(false))
6254        .with_column("name", SqlScalarType::String.nullable(false))
6255        .with_column("type", SqlScalarType::String.nullable(false))
6256        .with_column("cluster", SqlScalarType::String.nullable(true))
6257        .with_column("schema_id", SqlScalarType::String.nullable(false))
6258        .with_column("cluster_id", SqlScalarType::String.nullable(true))
6259        .with_column("comment", SqlScalarType::String.nullable(false))
6260        .finish(),
6261    column_comments: BTreeMap::new(),
6262    sql: "
6263WITH comments AS (
6264    SELECT id, comment
6265    FROM mz_internal.mz_comments
6266    WHERE object_type = 'source' AND object_sub_id IS NULL
6267)
6268SELECT
6269    sources.id,
6270    sources.name,
6271    sources.type,
6272    clusters.name AS cluster,
6273    schema_id,
6274    cluster_id,
6275    COALESCE(comments.comment, '') as comment
6276FROM
6277    mz_catalog.mz_sources AS sources
6278        LEFT JOIN
6279            mz_catalog.mz_clusters AS clusters
6280            ON clusters.id = sources.cluster_id
6281        LEFT JOIN comments ON sources.id = comments.id",
6282    access: vec![PUBLIC_SELECT],
6283    ontology: None,
6284});
6285
6286pub static MZ_SHOW_SINKS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6287    name: "mz_show_sinks",
6288    schema: MZ_INTERNAL_SCHEMA,
6289    oid: oid::VIEW_MZ_SHOW_SINKS_OID,
6290    desc: RelationDesc::builder()
6291        .with_column("id", SqlScalarType::String.nullable(false))
6292        .with_column("name", SqlScalarType::String.nullable(false))
6293        .with_column("type", SqlScalarType::String.nullable(false))
6294        .with_column("cluster", SqlScalarType::String.nullable(false))
6295        .with_column("schema_id", SqlScalarType::String.nullable(false))
6296        .with_column("cluster_id", SqlScalarType::String.nullable(false))
6297        .with_column("comment", SqlScalarType::String.nullable(false))
6298        .finish(),
6299    column_comments: BTreeMap::new(),
6300    sql: "
6301WITH comments AS (
6302    SELECT id, comment
6303    FROM mz_internal.mz_comments
6304    WHERE object_type = 'sink' AND object_sub_id IS NULL
6305)
6306SELECT
6307    sinks.id,
6308    sinks.name,
6309    sinks.type,
6310    clusters.name AS cluster,
6311    schema_id,
6312    cluster_id,
6313    COALESCE(comments.comment, '') as comment
6314FROM
6315    mz_catalog.mz_sinks AS sinks
6316    JOIN
6317        mz_catalog.mz_clusters AS clusters
6318        ON clusters.id = sinks.cluster_id
6319    LEFT JOIN comments ON sinks.id = comments.id",
6320    access: vec![PUBLIC_SELECT],
6321    ontology: None,
6322});
6323
6324pub static MZ_SHOW_MATERIALIZED_VIEWS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6325    name: "mz_show_materialized_views",
6326    schema: MZ_INTERNAL_SCHEMA,
6327    oid: oid::VIEW_MZ_SHOW_MATERIALIZED_VIEWS_OID,
6328    desc: RelationDesc::builder()
6329        .with_column("id", SqlScalarType::String.nullable(false))
6330        .with_column("name", SqlScalarType::String.nullable(false))
6331        .with_column("cluster", SqlScalarType::String.nullable(false))
6332        .with_column("schema_id", SqlScalarType::String.nullable(false))
6333        .with_column("cluster_id", SqlScalarType::String.nullable(false))
6334        .with_column("comment", SqlScalarType::String.nullable(false))
6335        .finish(),
6336    column_comments: BTreeMap::new(),
6337    sql: "
6338WITH
6339    comments AS (
6340        SELECT id, comment
6341        FROM mz_internal.mz_comments
6342        WHERE object_type = 'materialized-view' AND object_sub_id IS NULL
6343    )
6344SELECT
6345    mviews.id as id,
6346    mviews.name,
6347    clusters.name AS cluster,
6348    schema_id,
6349    cluster_id,
6350    COALESCE(comments.comment, '') as comment
6351FROM
6352    mz_catalog.mz_materialized_views AS mviews
6353    JOIN mz_catalog.mz_clusters AS clusters ON clusters.id = mviews.cluster_id
6354    LEFT JOIN comments ON mviews.id = comments.id",
6355    access: vec![PUBLIC_SELECT],
6356    ontology: None,
6357});
6358
6359pub static MZ_SHOW_INDEXES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6360    name: "mz_show_indexes",
6361    schema: MZ_INTERNAL_SCHEMA,
6362    oid: oid::VIEW_MZ_SHOW_INDEXES_OID,
6363    desc: RelationDesc::builder()
6364        .with_column("id", SqlScalarType::String.nullable(false))
6365        .with_column("name", SqlScalarType::String.nullable(false))
6366        .with_column("on", SqlScalarType::String.nullable(false))
6367        .with_column("cluster", SqlScalarType::String.nullable(false))
6368        .with_column(
6369            "key",
6370            SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false),
6371        )
6372        .with_column("on_id", SqlScalarType::String.nullable(false))
6373        .with_column("schema_id", SqlScalarType::String.nullable(false))
6374        .with_column("cluster_id", SqlScalarType::String.nullable(false))
6375        .with_column("comment", SqlScalarType::String.nullable(false))
6376        .finish(),
6377    column_comments: BTreeMap::new(),
6378    sql: "
6379WITH comments AS (
6380    SELECT id, comment
6381    FROM mz_internal.mz_comments
6382    WHERE object_type = 'index' AND object_sub_id IS NULL
6383)
6384SELECT
6385    idxs.id AS id,
6386    idxs.name AS name,
6387    objs.name AS on,
6388    clusters.name AS cluster,
6389    COALESCE(keys.key, '{}'::_text) AS key,
6390    idxs.on_id AS on_id,
6391    objs.schema_id AS schema_id,
6392    clusters.id AS cluster_id,
6393    COALESCE(comments.comment, '') as comment
6394FROM
6395    mz_catalog.mz_indexes AS idxs
6396    JOIN mz_catalog.mz_objects AS objs ON idxs.on_id = objs.id
6397    JOIN mz_catalog.mz_clusters AS clusters ON clusters.id = idxs.cluster_id
6398    LEFT JOIN
6399        (SELECT
6400            idxs.id,
6401            ARRAY_AGG(
6402                CASE
6403                    WHEN idx_cols.on_expression IS NULL THEN obj_cols.name
6404                    ELSE idx_cols.on_expression
6405                END
6406                ORDER BY idx_cols.index_position ASC
6407            ) AS key
6408        FROM
6409            mz_catalog.mz_indexes AS idxs
6410            JOIN mz_catalog.mz_index_columns idx_cols ON idxs.id = idx_cols.index_id
6411            LEFT JOIN mz_catalog.mz_columns obj_cols ON
6412                idxs.on_id = obj_cols.id AND idx_cols.on_position = obj_cols.position
6413        GROUP BY idxs.id) AS keys
6414    ON idxs.id = keys.id
6415    LEFT JOIN comments ON idxs.id = comments.id",
6416    access: vec![PUBLIC_SELECT],
6417    ontology: None,
6418});
6419
6420pub static MZ_SHOW_CLUSTER_REPLICAS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6421    name: "mz_show_cluster_replicas",
6422    schema: MZ_INTERNAL_SCHEMA,
6423    oid: oid::VIEW_MZ_SHOW_CLUSTER_REPLICAS_OID,
6424    desc: RelationDesc::builder()
6425        .with_column("cluster", SqlScalarType::String.nullable(false))
6426        .with_column("replica", SqlScalarType::String.nullable(false))
6427        .with_column("replica_id", SqlScalarType::String.nullable(false))
6428        .with_column("size", SqlScalarType::String.nullable(true))
6429        .with_column("ready", SqlScalarType::Bool.nullable(false))
6430        .with_column("comment", SqlScalarType::String.nullable(false))
6431        .finish(),
6432    column_comments: BTreeMap::new(),
6433    sql: r#"SELECT
6434    mz_catalog.mz_clusters.name AS cluster,
6435    mz_catalog.mz_cluster_replicas.name AS replica,
6436    mz_catalog.mz_cluster_replicas.id as replica_id,
6437    mz_catalog.mz_cluster_replicas.size AS size,
6438    coalesce(statuses.ready, FALSE) AS ready,
6439    coalesce(comments.comment, '') as comment
6440FROM
6441    mz_catalog.mz_cluster_replicas
6442        JOIN mz_catalog.mz_clusters
6443            ON mz_catalog.mz_cluster_replicas.cluster_id = mz_catalog.mz_clusters.id
6444        LEFT JOIN
6445            (
6446                SELECT
6447                    replica_id,
6448                    bool_and(hydrated) AS ready
6449                FROM mz_internal.mz_hydration_statuses
6450                WHERE replica_id is not null
6451                GROUP BY replica_id
6452            ) AS statuses
6453            ON mz_catalog.mz_cluster_replicas.id = statuses.replica_id
6454        LEFT JOIN mz_internal.mz_comments comments
6455            ON mz_catalog.mz_cluster_replicas.id = comments.id
6456            AND comments.object_type = 'cluster-replica'
6457ORDER BY 1, 2"#,
6458    access: vec![PUBLIC_SELECT],
6459    ontology: None,
6460});
6461
6462/// Lightweight data product discovery for MCP (Model Context Protocol).
6463///
6464/// Lists materialized views and indexed views that the current user has
6465/// SELECT privileges on. Non-indexed regular views are excluded because
6466/// querying them would trigger a full recompute. Comments are optional
6467/// enrichment.
6468/// Used by the `get_data_products` and `read_data_product` MCP tools.
6469/// Does not include schema details: use `mz_mcp_data_product_details` for that.
6470pub static MZ_MCP_DATA_PRODUCTS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6471    name: "mz_mcp_data_products",
6472    schema: MZ_INTERNAL_SCHEMA,
6473    oid: oid::VIEW_MZ_MCP_DATA_PRODUCTS_OID,
6474    desc: RelationDesc::builder()
6475        .with_column("object_name", SqlScalarType::String.nullable(false))
6476        .with_column("cluster", SqlScalarType::String.nullable(true))
6477        .with_column("description", SqlScalarType::String.nullable(true))
6478        .with_key(vec![0, 1, 2])
6479        .finish(),
6480    column_comments: BTreeMap::from_iter([
6481        (
6482            "object_name",
6483            "Fully qualified object name (database.schema.name).",
6484        ),
6485        (
6486            "cluster",
6487            "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).",
6488        ),
6489        (
6490            "description",
6491            "Index comment if available, otherwise object comment. Used as data product description.",
6492        ),
6493    ]),
6494    // The `cluster` column is null unless the role has USAGE on the object's
6495    // index/compute cluster, so a data product never advertises a cluster the
6496    // role cannot actually run reads on (DEX-66). Materialized views stay
6497    // listed regardless because they serve from persist, so a read on any
6498    // cluster the role can use is safe. Plain indexed views require at least
6499    // one index cluster the role can use: without one, the default fallback
6500    // to the session cluster would recompute the view, which we deliberately
6501    // avoid (same reason non-indexed views are excluded above).
6502    sql: r#"
6503SELECT DISTINCT
6504    '"' || op.database || '"."' || op.schema || '"."' || op.name || '"' AS object_name,
6505    CASE WHEN cp.name IS NOT NULL THEN COALESCE(c_idx.name, c_obj.name) END AS cluster,
6506    COALESCE(cts_idx.comment, cts_obj.comment) AS description
6507FROM mz_internal.mz_show_my_object_privileges op
6508JOIN mz_objects o ON op.name = o.name AND op.object_type = o.type
6509JOIN mz_schemas s ON s.name = op.schema AND s.id = o.schema_id
6510JOIN mz_databases d ON d.name = op.database AND d.id = s.database_id
6511LEFT JOIN mz_indexes i ON i.on_id = o.id
6512LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6513LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6514LEFT JOIN mz_internal.mz_show_my_cluster_privileges cp
6515    ON cp.name = COALESCE(c_idx.name, c_obj.name) AND cp.privilege_type = 'USAGE'
6516LEFT 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
6517LEFT 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
6518WHERE op.privilege_type = 'SELECT'
6519  AND (o.type = 'materialized-view'
6520       OR (o.type = 'view' AND i.id IS NOT NULL AND cp.name IS NOT NULL))
6521  AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6522"#,
6523    access: vec![PUBLIC_SELECT],
6524    ontology: None,
6525});
6526
6527/// Full data product details with JSON Schema for MCP agents.
6528///
6529/// Extends `mz_mcp_data_products` with column types, index keys (when
6530/// available), and column comments, formatted as a JSON Schema object.
6531/// Used by the `get_data_product_details` MCP tool. Lists materialized
6532/// views and indexed views; non-indexed regular views are excluded to
6533/// avoid triggering full recompute on query. Comments are optional
6534/// enrichment.
6535pub static MZ_MCP_DATA_PRODUCT_DETAILS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6536    name: "mz_mcp_data_product_details",
6537    schema: MZ_INTERNAL_SCHEMA,
6538    oid: oid::VIEW_MZ_MCP_DATA_PRODUCT_DETAILS_OID,
6539    // Note: no `.with_key` here. The view's row identity is semantically
6540    // (object_name, cluster, description) — same as the underlying details
6541    // CTE — but the planner can't prove key propagation through the
6542    // `LEFT JOIN ... ON ... IS NOT DISTINCT FROM` to the hydration CTE,
6543    // so declaring it here would diverge from the inferred RelationDesc
6544    // and fail `verify_builtin_descs`.
6545    desc: RelationDesc::builder()
6546        .with_column("object_name", SqlScalarType::String.nullable(false))
6547        .with_column("cluster", SqlScalarType::String.nullable(true))
6548        .with_column("description", SqlScalarType::String.nullable(true))
6549        .with_column("schema", SqlScalarType::Jsonb.nullable(false))
6550        .with_column("hydration", SqlScalarType::Jsonb.nullable(false))
6551        .finish(),
6552    column_comments: BTreeMap::from_iter([
6553        (
6554            "object_name",
6555            "Fully qualified object name (database.schema.name).",
6556        ),
6557        (
6558            "cluster",
6559            "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).",
6560        ),
6561        (
6562            "description",
6563            "Index comment if available, otherwise object comment. Used as data product description.",
6564        ),
6565        (
6566            "schema",
6567            "JSON Schema describing the object's columns and types.",
6568        ),
6569        (
6570            "hydration",
6571            "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.",
6572        ),
6573    ]),
6574    sql: r#"
6575WITH details_raw AS (
6576    SELECT
6577        '"' || op.database || '"."' || op.schema || '"."' || op.name || '"' AS object_name,
6578        COALESCE(c_idx.name, c_obj.name) AS cluster,
6579        COALESCE(cts_idx.comment, cts_obj.comment) AS description,
6580        COALESCE(jsonb_build_object(
6581        'type', 'object',
6582        'indexedColumns', jsonb_agg(distinct ccol.name) FILTER (WHERE ccol.position = ic.on_position),
6583        'properties', jsonb_strip_nulls(jsonb_object_agg(
6584            ccol.name,
6585            CASE
6586                WHEN ccol.type IN (
6587                    'uint2', 'uint4','uint8', 'int', 'integer', 'smallint',
6588                    'double', 'double precision', 'bigint', 'float',
6589                    'numeric', 'real'
6590                ) THEN jsonb_build_object(
6591                    'type', 'number',
6592                    'description', cts_col.comment
6593                )
6594                WHEN ccol.type = 'boolean' THEN jsonb_build_object(
6595                    'type', 'boolean',
6596                    'description', cts_col.comment
6597                )
6598                WHEN ccol.type = 'bytea' THEN jsonb_build_object(
6599                    'type', 'string',
6600                    'description', cts_col.comment,
6601                    'contentEncoding', 'base64',
6602                    'contentMediaType', 'application/octet-stream'
6603                )
6604                WHEN ccol.type = 'date' THEN jsonb_build_object(
6605                    'type', 'string',
6606                    'format', 'date',
6607                    'description', cts_col.comment
6608                )
6609                WHEN ccol.type = 'time' THEN jsonb_build_object(
6610                    'type', 'string',
6611                    'format', 'time',
6612                    'description', cts_col.comment
6613                )
6614                WHEN ccol.type ilike 'timestamp%%' THEN jsonb_build_object(
6615                    'type', 'string',
6616                    'format', 'date-time',
6617                    'description', cts_col.comment
6618                )
6619                WHEN ccol.type = 'jsonb' THEN jsonb_build_object(
6620                    'type', 'object',
6621                    'description', cts_col.comment
6622                )
6623                WHEN ccol.type = 'uuid' THEN jsonb_build_object(
6624                    'type', 'string',
6625                    'format', 'uuid',
6626                    'description', cts_col.comment
6627                )
6628                ELSE jsonb_build_object(
6629                    'type', 'string',
6630                    'description', cts_col.comment
6631                )
6632            END
6633        ))
6634    ), '{"type": "object", "properties": {}}'::jsonb) AS schema
6635FROM mz_internal.mz_show_my_object_privileges op
6636JOIN mz_objects o ON op.name = o.name AND op.object_type = o.type
6637JOIN mz_schemas s ON s.name = op.schema AND s.id = o.schema_id
6638JOIN mz_databases d ON d.name = op.database AND d.id = s.database_id
6639JOIN mz_columns ccol ON ccol.id = o.id
6640LEFT JOIN mz_indexes i ON i.on_id = o.id
6641LEFT JOIN mz_index_columns ic ON i.id = ic.index_id
6642LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6643LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6644LEFT JOIN mz_internal.mz_show_my_cluster_privileges cp
6645    ON cp.name = COALESCE(c_idx.name, c_obj.name) AND cp.privilege_type = 'USAGE'
6646LEFT 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
6647LEFT 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
6648LEFT 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
6649WHERE op.privilege_type = 'SELECT'
6650  AND (o.type = 'materialized-view'
6651       OR (o.type = 'view' AND i.id IS NOT NULL AND cp.name IS NOT NULL))
6652  AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6653GROUP BY 1, 2, 3
6654),
6655-- Pick the right (object_id, cluster_id) for hydration: the index's id +
6656-- cluster when an index exists (its arrangement is what the data product
6657-- reads from), otherwise the materialized view's own id + cluster.
6658hydration_meta AS (
6659    SELECT DISTINCT
6660        '"' || db.name || '"."' || s.name || '"."' || o.name || '"' AS object_name,
6661        COALESCE(c_idx.name, c_obj.name) AS cluster,
6662        COALESCE(i.id, o.id) AS hydration_object_id,
6663        COALESCE(i.cluster_id, o.cluster_id) AS cluster_id
6664    FROM mz_objects o
6665    JOIN mz_schemas s ON s.id = o.schema_id
6666    JOIN mz_databases db ON db.id = s.database_id
6667    LEFT JOIN mz_indexes i ON i.on_id = o.id
6668    LEFT JOIN mz_clusters c_idx ON c_idx.id = i.cluster_id
6669    LEFT JOIN mz_clusters c_obj ON c_obj.id = o.cluster_id
6670    WHERE (o.type = 'materialized-view' OR (o.type = 'view' AND i.id IS NOT NULL))
6671      AND s.name NOT IN ('mz_catalog', 'mz_internal', 'pg_catalog', 'information_schema', 'mz_introspection')
6672),
6673-- Dedupe by replica before counting: an MV with multiple indexes on the
6674-- same cluster has multiple rows in `hydration_meta`, and joining each
6675-- of them against `mz_cluster_replicas` would otherwise inflate the
6676-- counts by the number of indexes. A replica is "hydrated" only when
6677-- every index dataflow for this data product is hydrated on it.
6678hydration_per_replica AS (
6679    SELECT
6680        m.object_name,
6681        m.cluster,
6682        r.id AS replica_id,
6683        bool_and(COALESCE(h.hydrated, false)) AS replica_hydrated
6684    FROM hydration_meta m
6685    LEFT JOIN mz_catalog.mz_cluster_replicas r ON r.cluster_id = m.cluster_id
6686    LEFT JOIN mz_internal.mz_hydration_statuses h
6687        ON h.replica_id = r.id AND h.object_id = m.hydration_object_id
6688    GROUP BY m.object_name, m.cluster, r.id
6689),
6690hydration AS (
6691    SELECT
6692        object_name,
6693        cluster,
6694        COUNT(replica_id)::int AS replica_count,
6695        COUNT(replica_id) FILTER (WHERE replica_hydrated)::int AS hydrated_replica_count
6696    FROM hydration_per_replica
6697    GROUP BY object_name, cluster
6698)
6699SELECT
6700    d.object_name,
6701    -- Null the advertised cluster unless the role has USAGE on it (DEX-66),
6702    -- matching mz_mcp_data_products. Hydration below still joins on the real
6703    -- d.cluster, so readiness is reported accurately even when the name is
6704    -- hidden.
6705    CASE WHEN EXISTS (
6706        SELECT 1 FROM mz_internal.mz_show_my_cluster_privileges cp
6707        WHERE cp.name = d.cluster AND cp.privilege_type = 'USAGE'
6708    ) THEN d.cluster END AS cluster,
6709    d.description,
6710    d.schema,
6711    jsonb_build_object(
6712        'hydrated',
6713        COALESCE(h.replica_count > 0 AND h.hydrated_replica_count = h.replica_count, false),
6714        'replica_count', COALESCE(h.replica_count, 0),
6715        'hydrated_replica_count', COALESCE(h.hydrated_replica_count, 0)
6716    ) AS hydration
6717FROM details_raw d
6718LEFT JOIN hydration h
6719    ON h.object_name = d.object_name
6720   AND h.cluster IS NOT DISTINCT FROM d.cluster
6721"#,
6722    access: vec![PUBLIC_SELECT],
6723    ontology: None,
6724});
6725
6726pub static MZ_SHOW_ROLE_MEMBERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6727    name: "mz_show_role_members",
6728    schema: MZ_INTERNAL_SCHEMA,
6729    oid: oid::VIEW_MZ_SHOW_ROLE_MEMBERS_OID,
6730    desc: RelationDesc::builder()
6731        .with_column("role", SqlScalarType::String.nullable(false))
6732        .with_column("member", SqlScalarType::String.nullable(false))
6733        .with_column("grantor", SqlScalarType::String.nullable(false))
6734        .finish(),
6735    column_comments: BTreeMap::from_iter([
6736        ("role", "The role that `member` is a member of."),
6737        ("member", "The role that is a member of `role`."),
6738        (
6739            "grantor",
6740            "The role that granted membership of `member` to `role`.",
6741        ),
6742    ]),
6743    sql: r#"SELECT
6744    r1.name AS role,
6745    r2.name AS member,
6746    r3.name AS grantor
6747FROM mz_catalog.mz_role_members rm
6748JOIN mz_catalog.mz_roles r1 ON r1.id = rm.role_id
6749JOIN mz_catalog.mz_roles r2 ON r2.id = rm.member
6750JOIN mz_catalog.mz_roles r3 ON r3.id = rm.grantor
6751ORDER BY role"#,
6752    access: vec![PUBLIC_SELECT],
6753    ontology: None,
6754});
6755
6756pub static MZ_SHOW_MY_ROLE_MEMBERS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6757    name: "mz_show_my_role_members",
6758    schema: MZ_INTERNAL_SCHEMA,
6759    oid: oid::VIEW_MZ_SHOW_MY_ROLE_MEMBERS_OID,
6760    desc: RelationDesc::builder()
6761        .with_column("role", SqlScalarType::String.nullable(false))
6762        .with_column("member", SqlScalarType::String.nullable(false))
6763        .with_column("grantor", SqlScalarType::String.nullable(false))
6764        .finish(),
6765    column_comments: BTreeMap::from_iter([
6766        ("role", "The role that `member` is a member of."),
6767        ("member", "The role that is a member of `role`."),
6768        (
6769            "grantor",
6770            "The role that granted membership of `member` to `role`.",
6771        ),
6772    ]),
6773    sql: r#"SELECT role, member, grantor
6774FROM mz_internal.mz_show_role_members
6775WHERE pg_has_role(member, 'USAGE')"#,
6776    access: vec![PUBLIC_SELECT],
6777    ontology: None,
6778});
6779
6780pub static MZ_SHOW_SYSTEM_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6781    name: "mz_show_system_privileges",
6782    schema: MZ_INTERNAL_SCHEMA,
6783    oid: oid::VIEW_MZ_SHOW_SYSTEM_PRIVILEGES_OID,
6784    desc: RelationDesc::builder()
6785        .with_column("grantor", SqlScalarType::String.nullable(true))
6786        .with_column("grantee", SqlScalarType::String.nullable(true))
6787        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6788        .finish(),
6789    column_comments: BTreeMap::from_iter([
6790        ("grantor", "The role that granted the privilege."),
6791        ("grantee", "The role that the privilege was granted to."),
6792        ("privilege_type", "They type of privilege granted."),
6793    ]),
6794    sql: r#"SELECT
6795    grantor.name AS grantor,
6796    CASE privileges.grantee
6797        WHEN 'p' THEN 'PUBLIC'
6798        ELSE grantee.name
6799    END AS grantee,
6800    privileges.privilege_type AS privilege_type
6801FROM
6802    (SELECT mz_internal.mz_aclexplode(ARRAY[privileges]).*
6803    FROM mz_catalog.mz_system_privileges) AS privileges
6804LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6805LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6806WHERE privileges.grantee NOT LIKE 's%'"#,
6807    access: vec![PUBLIC_SELECT],
6808    ontology: None,
6809});
6810
6811pub static MZ_SHOW_MY_SYSTEM_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6812    name: "mz_show_my_system_privileges",
6813    schema: MZ_INTERNAL_SCHEMA,
6814    oid: oid::VIEW_MZ_SHOW_MY_SYSTEM_PRIVILEGES_OID,
6815    desc: RelationDesc::builder()
6816        .with_column("grantor", SqlScalarType::String.nullable(true))
6817        .with_column("grantee", SqlScalarType::String.nullable(true))
6818        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6819        .finish(),
6820    column_comments: BTreeMap::from_iter([
6821        ("grantor", "The role that granted the privilege."),
6822        ("grantee", "The role that the privilege was granted to."),
6823        ("privilege_type", "They type of privilege granted."),
6824    ]),
6825    sql: r#"SELECT grantor, grantee, privilege_type
6826FROM mz_internal.mz_show_system_privileges
6827WHERE
6828    CASE
6829        WHEN grantee = 'PUBLIC' THEN true
6830        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6831        -- whether the current user holds role `grantee`. For a nonexistent grantee
6832        -- name, both return false. We use mz_session_role_memberships() instead
6833        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6834        -- loads the full system role graph and is blocked in restricted sessions.
6835        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6836    END"#,
6837    access: vec![PUBLIC_SELECT],
6838    ontology: None,
6839});
6840
6841pub static MZ_SHOW_CLUSTER_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6842    name: "mz_show_cluster_privileges",
6843    schema: MZ_INTERNAL_SCHEMA,
6844    oid: oid::VIEW_MZ_SHOW_CLUSTER_PRIVILEGES_OID,
6845    desc: RelationDesc::builder()
6846        .with_column("grantor", SqlScalarType::String.nullable(true))
6847        .with_column("grantee", SqlScalarType::String.nullable(true))
6848        .with_column("name", SqlScalarType::String.nullable(false))
6849        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6850        .finish(),
6851    column_comments: BTreeMap::from_iter([
6852        ("grantor", "The role that granted the privilege."),
6853        ("grantee", "The role that the privilege was granted to."),
6854        ("name", "The name of the cluster."),
6855        ("privilege_type", "They type of privilege granted."),
6856    ]),
6857    sql: r#"SELECT
6858    grantor.name AS grantor,
6859    CASE privileges.grantee
6860        WHEN 'p' THEN 'PUBLIC'
6861        ELSE grantee.name
6862    END AS grantee,
6863    privileges.name AS name,
6864    privileges.privilege_type AS privilege_type
6865FROM
6866    (SELECT mz_internal.mz_aclexplode(privileges).*, name
6867    FROM mz_catalog.mz_clusters
6868    WHERE id NOT LIKE 's%') AS privileges
6869LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6870LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6871WHERE privileges.grantee NOT LIKE 's%'"#,
6872    access: vec![PUBLIC_SELECT],
6873    ontology: None,
6874});
6875
6876pub static MZ_SHOW_MY_CLUSTER_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6877    name: "mz_show_my_cluster_privileges",
6878    schema: MZ_INTERNAL_SCHEMA,
6879    oid: oid::VIEW_MZ_SHOW_MY_CLUSTER_PRIVILEGES_OID,
6880    desc: RelationDesc::builder()
6881        .with_column("grantor", SqlScalarType::String.nullable(true))
6882        .with_column("grantee", SqlScalarType::String.nullable(true))
6883        .with_column("name", SqlScalarType::String.nullable(false))
6884        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6885        .finish(),
6886    column_comments: BTreeMap::from_iter([
6887        ("grantor", "The role that granted the privilege."),
6888        ("grantee", "The role that the privilege was granted to."),
6889        ("name", "The name of the cluster."),
6890        ("privilege_type", "They type of privilege granted."),
6891    ]),
6892    sql: r#"SELECT grantor, grantee, name, privilege_type
6893FROM mz_internal.mz_show_cluster_privileges
6894WHERE
6895    CASE
6896        WHEN grantee = 'PUBLIC' THEN true
6897        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6898        -- whether the current user holds role `grantee`. For a nonexistent grantee
6899        -- name, both return false. We use mz_session_role_memberships() instead
6900        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6901        -- loads the full system role graph and is blocked in restricted sessions.
6902        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6903    END"#,
6904    access: vec![PUBLIC_SELECT],
6905    ontology: None,
6906});
6907
6908pub static MZ_SHOW_DATABASE_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6909    name: "mz_show_database_privileges",
6910    schema: MZ_INTERNAL_SCHEMA,
6911    oid: oid::VIEW_MZ_SHOW_DATABASE_PRIVILEGES_OID,
6912    desc: RelationDesc::builder()
6913        .with_column("grantor", SqlScalarType::String.nullable(true))
6914        .with_column("grantee", SqlScalarType::String.nullable(true))
6915        .with_column("name", SqlScalarType::String.nullable(false))
6916        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6917        .finish(),
6918    column_comments: BTreeMap::from_iter([
6919        ("grantor", "The role that granted the privilege."),
6920        ("grantee", "The role that the privilege was granted to."),
6921        ("name", "The name of the database."),
6922        ("privilege_type", "They type of privilege granted."),
6923    ]),
6924    sql: r#"SELECT
6925    grantor.name AS grantor,
6926    CASE privileges.grantee
6927        WHEN 'p' THEN 'PUBLIC'
6928        ELSE grantee.name
6929    END AS grantee,
6930    privileges.name AS name,
6931    privileges.privilege_type AS privilege_type
6932FROM
6933    (SELECT mz_internal.mz_aclexplode(privileges).*, name
6934    FROM mz_catalog.mz_databases
6935    WHERE id NOT LIKE 's%') AS privileges
6936LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
6937LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
6938WHERE privileges.grantee NOT LIKE 's%'"#,
6939    access: vec![PUBLIC_SELECT],
6940    ontology: None,
6941});
6942
6943pub static MZ_SHOW_MY_DATABASE_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6944    name: "mz_show_my_database_privileges",
6945    schema: MZ_INTERNAL_SCHEMA,
6946    oid: oid::VIEW_MZ_SHOW_MY_DATABASE_PRIVILEGES_OID,
6947    desc: RelationDesc::builder()
6948        .with_column("grantor", SqlScalarType::String.nullable(true))
6949        .with_column("grantee", SqlScalarType::String.nullable(true))
6950        .with_column("name", SqlScalarType::String.nullable(false))
6951        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6952        .finish(),
6953    column_comments: BTreeMap::from_iter([
6954        ("grantor", "The role that granted the privilege."),
6955        ("grantee", "The role that the privilege was granted to."),
6956        ("name", "The name of the cluster."),
6957        ("privilege_type", "They type of privilege granted."),
6958    ]),
6959    sql: r#"SELECT grantor, grantee, name, privilege_type
6960FROM mz_internal.mz_show_database_privileges
6961WHERE
6962    CASE
6963        WHEN grantee = 'PUBLIC' THEN true
6964        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
6965        -- whether the current user holds role `grantee`. For a nonexistent grantee
6966        -- name, both return false. We use mz_session_role_memberships() instead
6967        -- because pg_has_role internally calls mz_role_oid_memberships(), which
6968        -- loads the full system role graph and is blocked in restricted sessions.
6969        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
6970    END"#,
6971    access: vec![PUBLIC_SELECT],
6972    ontology: None,
6973});
6974
6975pub static MZ_SHOW_SCHEMA_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
6976    name: "mz_show_schema_privileges",
6977    schema: MZ_INTERNAL_SCHEMA,
6978    oid: oid::VIEW_MZ_SHOW_SCHEMA_PRIVILEGES_OID,
6979    desc: RelationDesc::builder()
6980        .with_column("grantor", SqlScalarType::String.nullable(true))
6981        .with_column("grantee", SqlScalarType::String.nullable(true))
6982        .with_column("database", SqlScalarType::String.nullable(true))
6983        .with_column("name", SqlScalarType::String.nullable(false))
6984        .with_column("privilege_type", SqlScalarType::String.nullable(false))
6985        .finish(),
6986    column_comments: BTreeMap::from_iter([
6987        ("grantor", "The role that granted the privilege."),
6988        ("grantee", "The role that the privilege was granted to."),
6989        (
6990            "database",
6991            "The name of the database containing the schema.",
6992        ),
6993        ("name", "The name of the schema."),
6994        ("privilege_type", "They type of privilege granted."),
6995    ]),
6996    sql: r#"SELECT
6997    grantor.name AS grantor,
6998    CASE privileges.grantee
6999        WHEN 'p' THEN 'PUBLIC'
7000        ELSE grantee.name
7001    END AS grantee,
7002    databases.name AS database,
7003    privileges.name AS name,
7004    privileges.privilege_type AS privilege_type
7005FROM
7006    (SELECT mz_internal.mz_aclexplode(privileges).*, database_id, name
7007    FROM mz_catalog.mz_schemas
7008    WHERE id NOT LIKE 's%') AS privileges
7009LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
7010LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
7011LEFT JOIN mz_catalog.mz_databases databases ON privileges.database_id = databases.id
7012WHERE privileges.grantee NOT LIKE 's%'"#,
7013    access: vec![PUBLIC_SELECT],
7014    ontology: None,
7015});
7016
7017pub static MZ_SHOW_MY_SCHEMA_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7018    name: "mz_show_my_schema_privileges",
7019    schema: MZ_INTERNAL_SCHEMA,
7020    oid: oid::VIEW_MZ_SHOW_MY_SCHEMA_PRIVILEGES_OID,
7021    desc: RelationDesc::builder()
7022        .with_column("grantor", SqlScalarType::String.nullable(true))
7023        .with_column("grantee", SqlScalarType::String.nullable(true))
7024        .with_column("database", SqlScalarType::String.nullable(true))
7025        .with_column("name", SqlScalarType::String.nullable(false))
7026        .with_column("privilege_type", SqlScalarType::String.nullable(false))
7027        .finish(),
7028    column_comments: BTreeMap::from_iter([
7029        ("grantor", "The role that granted the privilege."),
7030        ("grantee", "The role that the privilege was granted to."),
7031        (
7032            "database",
7033            "The name of the database containing the schema.",
7034        ),
7035        ("name", "The name of the schema."),
7036        ("privilege_type", "They type of privilege granted."),
7037    ]),
7038    sql: r#"SELECT grantor, grantee, database, name, privilege_type
7039FROM mz_internal.mz_show_schema_privileges
7040WHERE
7041    CASE
7042        WHEN grantee = 'PUBLIC' THEN true
7043        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
7044        -- whether the current user holds role `grantee`. For a nonexistent grantee
7045        -- name, both return false. We use mz_session_role_memberships() instead
7046        -- because pg_has_role internally calls mz_role_oid_memberships(), which
7047        -- loads the full system role graph and is blocked in restricted sessions.
7048        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
7049    END"#,
7050    access: vec![PUBLIC_SELECT],
7051    ontology: None,
7052});
7053
7054pub static MZ_SHOW_OBJECT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7055    name: "mz_show_object_privileges",
7056    schema: MZ_INTERNAL_SCHEMA,
7057    oid: oid::VIEW_MZ_SHOW_OBJECT_PRIVILEGES_OID,
7058    desc: RelationDesc::builder()
7059        .with_column("grantor", SqlScalarType::String.nullable(true))
7060        .with_column("grantee", SqlScalarType::String.nullable(true))
7061        .with_column("database", SqlScalarType::String.nullable(true))
7062        .with_column("schema", SqlScalarType::String.nullable(true))
7063        .with_column("name", SqlScalarType::String.nullable(false))
7064        .with_column("object_type", SqlScalarType::String.nullable(false))
7065        .with_column("privilege_type", SqlScalarType::String.nullable(false))
7066        .finish(),
7067    column_comments: BTreeMap::from_iter([
7068        ("grantor", "The role that granted the privilege."),
7069        ("grantee", "The role that the privilege was granted to."),
7070        (
7071            "database",
7072            "The name of the database containing the object.",
7073        ),
7074        ("schema", "The name of the schema containing the object."),
7075        ("name", "The name of the object."),
7076        (
7077            "object_type",
7078            "The type of object the privilege is granted on.",
7079        ),
7080        ("privilege_type", "They type of privilege granted."),
7081    ]),
7082    sql: r#"SELECT
7083    grantor.name AS grantor,
7084    CASE privileges.grantee
7085            WHEN 'p' THEN 'PUBLIC'
7086            ELSE grantee.name
7087        END AS grantee,
7088    databases.name AS database,
7089    schemas.name AS schema,
7090    privileges.name AS name,
7091    privileges.type AS object_type,
7092    privileges.privilege_type AS privilege_type
7093FROM
7094    (SELECT mz_internal.mz_aclexplode(privileges).*, schema_id, name, type
7095    FROM mz_catalog.mz_objects
7096    WHERE id NOT LIKE 's%') AS privileges
7097LEFT JOIN mz_catalog.mz_roles grantor ON privileges.grantor = grantor.id
7098LEFT JOIN mz_catalog.mz_roles grantee ON privileges.grantee = grantee.id
7099LEFT JOIN mz_catalog.mz_schemas schemas ON privileges.schema_id = schemas.id
7100LEFT JOIN mz_catalog.mz_databases databases ON schemas.database_id = databases.id
7101WHERE privileges.grantee NOT LIKE 's%'"#,
7102    access: vec![PUBLIC_SELECT],
7103    ontology: None,
7104});
7105
7106pub static MZ_SHOW_MY_OBJECT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7107    name: "mz_show_my_object_privileges",
7108    schema: MZ_INTERNAL_SCHEMA,
7109    oid: oid::VIEW_MZ_SHOW_MY_OBJECT_PRIVILEGES_OID,
7110    desc: RelationDesc::builder()
7111        .with_column("grantor", SqlScalarType::String.nullable(true))
7112        .with_column("grantee", SqlScalarType::String.nullable(true))
7113        .with_column("database", SqlScalarType::String.nullable(true))
7114        .with_column("schema", SqlScalarType::String.nullable(true))
7115        .with_column("name", SqlScalarType::String.nullable(false))
7116        .with_column("object_type", SqlScalarType::String.nullable(false))
7117        .with_column("privilege_type", SqlScalarType::String.nullable(false))
7118        .finish(),
7119    column_comments: BTreeMap::from_iter([
7120        ("grantor", "The role that granted the privilege."),
7121        ("grantee", "The role that the privilege was granted to."),
7122        (
7123            "database",
7124            "The name of the database containing the object.",
7125        ),
7126        ("schema", "The name of the schema containing the object."),
7127        ("name", "The name of the object."),
7128        (
7129            "object_type",
7130            "The type of object the privilege is granted on.",
7131        ),
7132        ("privilege_type", "They type of privilege granted."),
7133    ]),
7134    sql: r#"SELECT grantor, grantee, database, schema, name, object_type, privilege_type
7135FROM mz_internal.mz_show_object_privileges
7136WHERE
7137    CASE
7138        WHEN grantee = 'PUBLIC' THEN true
7139        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
7140        -- whether the current user holds role `grantee`. For a nonexistent grantee
7141        -- name, both return false. We use mz_session_role_memberships() instead
7142        -- because pg_has_role internally calls mz_role_oid_memberships(), which
7143        -- loads the full system role graph and is blocked in restricted sessions.
7144        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
7145    END"#,
7146    access: vec![PUBLIC_SELECT],
7147    ontology: None,
7148});
7149
7150pub static MZ_SHOW_ALL_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7151    name: "mz_show_all_privileges",
7152    schema: MZ_INTERNAL_SCHEMA,
7153    oid: oid::VIEW_MZ_SHOW_ALL_PRIVILEGES_OID,
7154    desc: RelationDesc::builder()
7155        .with_column("grantor", SqlScalarType::String.nullable(true))
7156        .with_column("grantee", SqlScalarType::String.nullable(true))
7157        .with_column("database", SqlScalarType::String.nullable(true))
7158        .with_column("schema", SqlScalarType::String.nullable(true))
7159        .with_column("name", SqlScalarType::String.nullable(true))
7160        .with_column("object_type", SqlScalarType::String.nullable(false))
7161        .with_column("privilege_type", SqlScalarType::String.nullable(false))
7162        .finish(),
7163    column_comments: BTreeMap::from_iter([
7164        ("grantor", "The role that granted the privilege."),
7165        ("grantee", "The role that the privilege was granted to."),
7166        (
7167            "database",
7168            "The name of the database containing the object.",
7169        ),
7170        ("schema", "The name of the schema containing the object."),
7171        ("name", "The name of the privilege target."),
7172        (
7173            "object_type",
7174            "The type of object the privilege is granted on.",
7175        ),
7176        ("privilege_type", "They type of privilege granted."),
7177    ]),
7178    sql: r#"SELECT grantor, grantee, NULL AS database, NULL AS schema, NULL AS name, 'system' AS object_type, privilege_type
7179FROM mz_internal.mz_show_system_privileges
7180UNION ALL
7181SELECT grantor, grantee, NULL AS database, NULL AS schema, name, 'cluster' AS object_type, privilege_type
7182FROM mz_internal.mz_show_cluster_privileges
7183UNION ALL
7184SELECT grantor, grantee, NULL AS database, NULL AS schema, name, 'database' AS object_type, privilege_type
7185FROM mz_internal.mz_show_database_privileges
7186UNION ALL
7187SELECT grantor, grantee, database, NULL AS schema, name, 'schema' AS object_type, privilege_type
7188FROM mz_internal.mz_show_schema_privileges
7189UNION ALL
7190SELECT grantor, grantee, database, schema, name, object_type, privilege_type
7191FROM mz_internal.mz_show_object_privileges"#,
7192    access: vec![PUBLIC_SELECT],
7193    ontology: None,
7194});
7195
7196pub static MZ_SHOW_ALL_MY_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7197    name: "mz_show_all_my_privileges",
7198    schema: MZ_INTERNAL_SCHEMA,
7199    oid: oid::VIEW_MZ_SHOW_ALL_MY_PRIVILEGES_OID,
7200    desc: RelationDesc::builder()
7201        .with_column("grantor", SqlScalarType::String.nullable(true))
7202        .with_column("grantee", SqlScalarType::String.nullable(true))
7203        .with_column("database", SqlScalarType::String.nullable(true))
7204        .with_column("schema", SqlScalarType::String.nullable(true))
7205        .with_column("name", SqlScalarType::String.nullable(true))
7206        .with_column("object_type", SqlScalarType::String.nullable(false))
7207        .with_column("privilege_type", SqlScalarType::String.nullable(false))
7208        .finish(),
7209    column_comments: BTreeMap::from_iter([
7210        ("grantor", "The role that granted the privilege."),
7211        ("grantee", "The role that the privilege was granted to."),
7212        (
7213            "database",
7214            "The name of the database containing the object.",
7215        ),
7216        ("schema", "The name of the schema containing the object."),
7217        ("name", "The name of the privilege target."),
7218        (
7219            "object_type",
7220            "The type of object the privilege is granted on.",
7221        ),
7222        ("privilege_type", "They type of privilege granted."),
7223    ]),
7224    sql: r#"SELECT grantor, grantee, database, schema, name, object_type, privilege_type
7225FROM mz_internal.mz_show_all_privileges
7226WHERE
7227    CASE
7228        WHEN grantee = 'PUBLIC' THEN true
7229        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
7230        -- whether the current user holds role `grantee`. For a nonexistent grantee
7231        -- name, both return false. We use mz_session_role_memberships() instead
7232        -- because pg_has_role internally calls mz_role_oid_memberships(), which
7233        -- loads the full system role graph and is blocked in restricted sessions.
7234        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
7235    END"#,
7236    access: vec![PUBLIC_SELECT],
7237    ontology: None,
7238});
7239
7240pub static MZ_SHOW_DEFAULT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7241    name: "mz_show_default_privileges",
7242    schema: MZ_INTERNAL_SCHEMA,
7243    oid: oid::VIEW_MZ_SHOW_DEFAULT_PRIVILEGES_OID,
7244    desc: RelationDesc::builder()
7245        .with_column("object_owner", SqlScalarType::String.nullable(true))
7246        .with_column("database", SqlScalarType::String.nullable(true))
7247        .with_column("schema", SqlScalarType::String.nullable(true))
7248        .with_column("object_type", SqlScalarType::String.nullable(false))
7249        .with_column("grantee", SqlScalarType::String.nullable(true))
7250        .with_column("privilege_type", SqlScalarType::String.nullable(true))
7251        .finish(),
7252    column_comments: BTreeMap::from_iter([
7253        (
7254            "object_owner",
7255            "Privileges described in this row will be granted on objects created by `object_owner`.",
7256        ),
7257        (
7258            "database",
7259            "Privileges described in this row will be granted only on objects created in `database` if non-null.",
7260        ),
7261        (
7262            "schema",
7263            "Privileges described in this row will be granted only on objects created in `schema` if non-null.",
7264        ),
7265        (
7266            "object_type",
7267            "Privileges described in this row will be granted only on objects of type `object_type`.",
7268        ),
7269        (
7270            "grantee",
7271            "Privileges described in this row will be granted to `grantee`.",
7272        ),
7273        ("privilege_type", "They type of privilege to be granted."),
7274    ]),
7275    sql: r#"SELECT
7276    CASE defaults.role_id
7277        WHEN 'p' THEN 'PUBLIC'
7278        ELSE object_owner.name
7279    END AS object_owner,
7280    databases.name AS database,
7281    schemas.name AS schema,
7282    object_type,
7283    CASE defaults.grantee
7284        WHEN 'p' THEN 'PUBLIC'
7285        ELSE grantee.name
7286    END AS grantee,
7287    unnest(mz_internal.mz_format_privileges(defaults.privileges)) AS privilege_type
7288FROM mz_catalog.mz_default_privileges defaults
7289LEFT JOIN mz_catalog.mz_roles AS object_owner ON defaults.role_id = object_owner.id
7290LEFT JOIN mz_catalog.mz_roles AS grantee ON defaults.grantee = grantee.id
7291LEFT JOIN mz_catalog.mz_databases AS databases ON defaults.database_id = databases.id
7292LEFT JOIN mz_catalog.mz_schemas AS schemas ON defaults.schema_id = schemas.id
7293WHERE defaults.grantee NOT LIKE 's%'
7294    AND defaults.database_id IS NULL OR defaults.database_id NOT LIKE 's%'
7295    AND defaults.schema_id IS NULL OR defaults.schema_id NOT LIKE 's%'"#,
7296    access: vec![PUBLIC_SELECT],
7297    ontology: None,
7298});
7299
7300pub static MZ_SHOW_MY_DEFAULT_PRIVILEGES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7301    name: "mz_show_my_default_privileges",
7302    schema: MZ_INTERNAL_SCHEMA,
7303    oid: oid::VIEW_MZ_SHOW_MY_DEFAULT_PRIVILEGES_OID,
7304    desc: RelationDesc::builder()
7305        .with_column("object_owner", SqlScalarType::String.nullable(true))
7306        .with_column("database", SqlScalarType::String.nullable(true))
7307        .with_column("schema", SqlScalarType::String.nullable(true))
7308        .with_column("object_type", SqlScalarType::String.nullable(false))
7309        .with_column("grantee", SqlScalarType::String.nullable(true))
7310        .with_column("privilege_type", SqlScalarType::String.nullable(true))
7311        .finish(),
7312    column_comments: BTreeMap::from_iter([
7313        (
7314            "object_owner",
7315            "Privileges described in this row will be granted on objects created by `object_owner`.",
7316        ),
7317        (
7318            "database",
7319            "Privileges described in this row will be granted only on objects created in `database` if non-null.",
7320        ),
7321        (
7322            "schema",
7323            "Privileges described in this row will be granted only on objects created in `schema` if non-null.",
7324        ),
7325        (
7326            "object_type",
7327            "Privileges described in this row will be granted only on objects of type `object_type`.",
7328        ),
7329        (
7330            "grantee",
7331            "Privileges described in this row will be granted to `grantee`.",
7332        ),
7333        ("privilege_type", "They type of privilege to be granted."),
7334    ]),
7335    sql: r#"SELECT object_owner, database, schema, object_type, grantee, privilege_type
7336FROM mz_internal.mz_show_default_privileges
7337WHERE
7338    CASE
7339        WHEN grantee = 'PUBLIC' THEN true
7340        -- Semantically equivalent to pg_has_role(grantee, 'USAGE'), which checks
7341        -- whether the current user holds role `grantee`. For a nonexistent grantee
7342        -- name, both return false. We use mz_session_role_memberships() instead
7343        -- because pg_has_role internally calls mz_role_oid_memberships(), which
7344        -- loads the full system role graph and is blocked in restricted sessions.
7345        ELSE grantee = ANY(mz_internal.mz_session_role_memberships())
7346    END"#,
7347    access: vec![PUBLIC_SELECT],
7348    ontology: None,
7349});
7350
7351pub static MZ_SHOW_NETWORK_POLICIES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7352    name: "mz_show_network_policies",
7353    schema: MZ_INTERNAL_SCHEMA,
7354    oid: oid::VIEW_MZ_SHOW_NETWORK_POLICIES_OID,
7355    desc: RelationDesc::builder()
7356        .with_column("name", SqlScalarType::String.nullable(false))
7357        .with_column("rules", SqlScalarType::String.nullable(true))
7358        .with_column("comment", SqlScalarType::String.nullable(false))
7359        .finish(),
7360    column_comments: BTreeMap::new(),
7361    sql: "
7362WITH comments AS (
7363    SELECT id, comment
7364    FROM mz_internal.mz_comments
7365    WHERE object_type = 'network-policy' AND object_sub_id IS NULL
7366)
7367SELECT
7368    policy.name,
7369    pg_catalog.string_agg(rule.name,',' ORDER BY rule.name) as rules,
7370    COALESCE(comment, '') as comment
7371FROM
7372    mz_internal.mz_network_policies as policy
7373LEFT JOIN
7374    mz_internal.mz_network_policy_rules as rule ON policy.id = rule.policy_id
7375LEFT JOIN
7376    comments ON policy.id = comments.id
7377WHERE
7378    policy.id NOT LIKE 's%'
7379AND
7380    policy.id NOT LIKE 'g%'
7381GROUP BY policy.name, comments.comment;",
7382    access: vec![PUBLIC_SELECT],
7383    ontology: None,
7384});
7385
7386pub static MZ_CLUSTER_REPLICA_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7387    name: "mz_cluster_replica_history",
7388    schema: MZ_INTERNAL_SCHEMA,
7389    oid: oid::VIEW_MZ_CLUSTER_REPLICA_HISTORY_OID,
7390    desc: RelationDesc::builder()
7391        .with_column("replica_id", SqlScalarType::String.nullable(true))
7392        .with_column("size", SqlScalarType::String.nullable(true))
7393        .with_column("cluster_id", SqlScalarType::String.nullable(true))
7394        .with_column("cluster_name", SqlScalarType::String.nullable(true))
7395        .with_column("replica_name", SqlScalarType::String.nullable(true))
7396        .with_column(
7397            "created_at",
7398            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7399        )
7400        .with_column(
7401            "dropped_at",
7402            SqlScalarType::TimestampTz { precision: None }.nullable(true),
7403        )
7404        .with_column(
7405            "credits_per_hour",
7406            SqlScalarType::Numeric { max_scale: None }.nullable(true),
7407        )
7408        .finish(),
7409    column_comments: BTreeMap::from_iter([
7410        ("replica_id", "The ID of a cluster replica."),
7411        (
7412            "size",
7413            "The size of the cluster replica. Corresponds to `mz_cluster_replica_sizes.size`.",
7414        ),
7415        (
7416            "cluster_id",
7417            "The ID of the cluster associated with the replica.",
7418        ),
7419        (
7420            "cluster_name",
7421            "The name of the cluster associated with the replica.",
7422        ),
7423        ("replica_name", "The name of the replica."),
7424        ("created_at", "The time at which the replica was created."),
7425        (
7426            "dropped_at",
7427            "The time at which the replica was dropped, or `NULL` if it still exists.",
7428        ),
7429        (
7430            "credits_per_hour",
7431            "The number of compute credits consumed per hour. Corresponds to `mz_cluster_replica_sizes.credits_per_hour`.",
7432        ),
7433    ]),
7434    sql: r#"
7435        WITH
7436            creates AS
7437            (
7438                SELECT
7439                    details ->> 'logical_size' AS size,
7440                    details ->> 'replica_id' AS replica_id,
7441                    details ->> 'replica_name' AS replica_name,
7442                    details ->> 'cluster_name' AS cluster_name,
7443                    details ->> 'cluster_id' AS cluster_id,
7444                    occurred_at
7445                FROM mz_catalog.mz_audit_events
7446                WHERE
7447                    object_type = 'cluster-replica' AND event_type = 'create'
7448                        AND
7449                    details ->> 'replica_id' IS NOT NULL
7450                        AND
7451                    details ->> 'cluster_id' !~~ 's%'
7452            ),
7453            drops AS
7454            (
7455                SELECT details ->> 'replica_id' AS replica_id, occurred_at
7456                FROM mz_catalog.mz_audit_events
7457                WHERE object_type = 'cluster-replica' AND event_type = 'drop'
7458            )
7459        SELECT
7460            creates.replica_id,
7461            creates.size,
7462            creates.cluster_id,
7463            creates.cluster_name,
7464            creates.replica_name,
7465            creates.occurred_at AS created_at,
7466            drops.occurred_at AS dropped_at,
7467            mz_cluster_replica_sizes.credits_per_hour as credits_per_hour
7468        FROM
7469            creates
7470                LEFT JOIN drops ON creates.replica_id = drops.replica_id
7471                LEFT JOIN
7472                    mz_catalog.mz_cluster_replica_sizes
7473                    ON mz_cluster_replica_sizes.size = creates.size"#,
7474    access: vec![PUBLIC_SELECT],
7475    ontology: Some(Ontology {
7476        entity_name: "replica_history",
7477        description: "Historical record of replica creation/drops",
7478        links: &const { [] },
7479        column_semantic_types: &[],
7480    }),
7481});
7482
7483pub static MZ_CLUSTER_REPLICA_NAME_HISTORY: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7484    name: "mz_cluster_replica_name_history",
7485    schema: MZ_INTERNAL_SCHEMA,
7486    oid: oid::VIEW_MZ_CLUSTER_REPLICA_NAME_HISTORY_OID,
7487    desc: RelationDesc::builder()
7488        .with_column(
7489            "occurred_at",
7490            SqlScalarType::TimestampTz { precision: None }.nullable(true),
7491        )
7492        .with_column("id", SqlScalarType::String.nullable(true))
7493        .with_column("previous_name", SqlScalarType::String.nullable(true))
7494        .with_column("new_name", SqlScalarType::String.nullable(true))
7495        .finish(),
7496    column_comments: BTreeMap::from_iter([
7497        (
7498            "occurred_at",
7499            "The time at which the cluster replica was created or renamed. `NULL` if it's a built in system cluster replica.",
7500        ),
7501        ("id", "The ID of the cluster replica."),
7502        (
7503            "previous_name",
7504            "The previous name of the cluster replica. `NULL` if there was no previous name.",
7505        ),
7506        ("new_name", "The new name of the cluster replica."),
7507    ]),
7508    sql: r#"WITH user_replica_alter_history AS (
7509  SELECT occurred_at,
7510    audit_events.details->>'replica_id' AS id,
7511    audit_events.details->>'old_name' AS previous_name,
7512    audit_events.details->>'new_name' AS new_name
7513  FROM mz_catalog.mz_audit_events AS audit_events
7514  WHERE object_type = 'cluster-replica'
7515    AND audit_events.event_type = 'alter'
7516    AND audit_events.details->>'replica_id' like 'u%'
7517),
7518user_replica_create_history AS (
7519  SELECT occurred_at,
7520    audit_events.details->>'replica_id' AS id,
7521    NULL AS previous_name,
7522    audit_events.details->>'replica_name' AS new_name
7523  FROM mz_catalog.mz_audit_events AS audit_events
7524  WHERE object_type = 'cluster-replica'
7525    AND audit_events.event_type = 'create'
7526    AND audit_events.details->>'replica_id' like 'u%'
7527),
7528-- Because built in system cluster replicas don't have audit events, we need to manually add them
7529system_replicas AS (
7530  -- We assume that the system cluster replicas were created at the beginning of time
7531  SELECT NULL::timestamptz AS occurred_at,
7532    id,
7533    NULL AS previous_name,
7534    name AS new_name
7535  FROM mz_catalog.mz_cluster_replicas
7536  WHERE id LIKE 's%'
7537)
7538SELECT *
7539FROM user_replica_alter_history
7540UNION ALL
7541SELECT *
7542FROM user_replica_create_history
7543UNION ALL
7544SELECT *
7545FROM system_replicas"#,
7546    access: vec![PUBLIC_SELECT],
7547    ontology: Some(Ontology {
7548        entity_name: "replica_name_history",
7549        description: "Historical replica names",
7550        links: &const { [] },
7551        column_semantic_types: &[("id", SemanticType::CatalogItemId)],
7552    }),
7553});
7554
7555pub static MZ_HYDRATION_STATUSES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7556    name: "mz_hydration_statuses",
7557    schema: MZ_INTERNAL_SCHEMA,
7558    oid: oid::VIEW_MZ_HYDRATION_STATUSES_OID,
7559    desc: RelationDesc::builder()
7560        .with_column("object_id", SqlScalarType::String.nullable(false))
7561        .with_column("replica_id", SqlScalarType::String.nullable(true))
7562        .with_column("hydrated", SqlScalarType::Bool.nullable(true))
7563        .finish(),
7564    column_comments: BTreeMap::from_iter([
7565        (
7566            "object_id",
7567            "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`.",
7568        ),
7569        ("replica_id", "The ID of a cluster replica."),
7570        ("hydrated", "Whether the object is hydrated on the replica."),
7571    ]),
7572    sql: r#"WITH
7573-- Joining against the linearizable catalog tables ensures that this view
7574-- always contains the set of installed objects, even when it depends
7575-- on introspection relations that may received delayed updates.
7576--
7577-- Note that this view only includes objects that are maintained by dataflows.
7578-- In particular, some source types (webhook, introspection, ...) are not and
7579-- are therefore omitted.
7580indexes AS (
7581    SELECT
7582        i.id AS object_id,
7583        h.replica_id,
7584        COALESCE(h.hydrated, false) AS hydrated
7585    FROM mz_catalog.mz_indexes i
7586    LEFT JOIN mz_internal.mz_compute_hydration_statuses h
7587        ON (h.object_id = i.id)
7588),
7589materialized_views AS (
7590    SELECT
7591        i.id AS object_id,
7592        h.replica_id,
7593        COALESCE(h.hydrated, false) AS hydrated
7594    FROM mz_catalog.mz_materialized_views i
7595    LEFT JOIN mz_internal.mz_compute_hydration_statuses h
7596        ON (h.object_id = i.id)
7597),
7598-- Hydration is a dataflow concept and not all sources are maintained by
7599-- dataflows, so we need to find the ones that are. Generally, sources that
7600-- have a cluster ID are maintained by a dataflow running on that cluster.
7601-- Webhook sources are an exception to this rule.
7602sources_with_clusters AS (
7603    SELECT id, cluster_id
7604    FROM mz_catalog.mz_sources
7605    WHERE cluster_id IS NOT NULL AND type != 'webhook'
7606),
7607sources AS (
7608    SELECT
7609        s.id AS object_id,
7610        ss.replica_id AS replica_id,
7611        ss.rehydration_latency IS NOT NULL AS hydrated
7612    FROM sources_with_clusters s
7613    LEFT JOIN mz_internal.mz_source_statistics ss USING (id)
7614),
7615-- We don't yet report sink hydration status (database-issues#8331), so we do a best effort attempt here and
7616-- define a sink as hydrated when it's both "running" and has a frontier greater than the minimum.
7617-- There is likely still a possibility of FPs.
7618sinks AS (
7619    SELECT
7620        s.id AS object_id,
7621        r.id AS replica_id,
7622        ss.status = 'running' AND COALESCE(f.write_frontier, 0) > 0 AS hydrated
7623    FROM mz_catalog.mz_sinks s
7624    LEFT JOIN mz_internal.mz_sink_statuses ss USING (id)
7625    JOIN mz_catalog.mz_cluster_replicas r
7626        ON (r.cluster_id = s.cluster_id)
7627    LEFT JOIN mz_catalog.mz_cluster_replica_frontiers f
7628        ON (f.object_id = s.id AND f.replica_id = r.id)
7629)
7630SELECT * FROM indexes
7631UNION ALL
7632SELECT * FROM materialized_views
7633UNION ALL
7634SELECT * FROM sources
7635UNION ALL
7636SELECT * FROM sinks"#,
7637    access: vec![PUBLIC_SELECT],
7638    ontology: Some(Ontology {
7639        entity_name: "hydration_status",
7640        description: "Overall hydration status per object",
7641        links: &const {
7642            [
7643                OntologyLink {
7644                    name: "hydration_of",
7645                    target: "object",
7646                    properties: LinkProperties::fk_typed(
7647                        "object_id",
7648                        "id",
7649                        Cardinality::OneToOne,
7650                        mz_repr::SemanticType::CatalogItemId,
7651                    ),
7652                },
7653                OntologyLink {
7654                    name: "hydration_on_replica",
7655                    target: "replica",
7656                    properties: LinkProperties::fk("replica_id", "id", Cardinality::ManyToOne),
7657                },
7658            ]
7659        },
7660        column_semantic_types: &const {
7661            [
7662                ("object_id", SemanticType::CatalogItemId),
7663                ("replica_id", SemanticType::ReplicaId),
7664            ]
7665        },
7666    }),
7667});
7668
7669pub const MZ_HYDRATION_STATUSES_IND: BuiltinIndex = BuiltinIndex {
7670    name: "mz_hydration_statuses_ind",
7671    schema: MZ_INTERNAL_SCHEMA,
7672    oid: oid::INDEX_MZ_HYDRATION_STATUSES_IND_OID,
7673    sql: "IN CLUSTER mz_catalog_server
7674ON mz_internal.mz_hydration_statuses (object_id, replica_id)",
7675    is_retained_metrics_object: false,
7676};
7677
7678pub static MZ_MATERIALIZATION_DEPENDENCIES: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7679    name: "mz_materialization_dependencies",
7680    schema: MZ_INTERNAL_SCHEMA,
7681    oid: oid::VIEW_MZ_MATERIALIZATION_DEPENDENCIES_OID,
7682    desc: RelationDesc::builder()
7683        .with_column("object_id", SqlScalarType::String.nullable(false))
7684        .with_column("dependency_id", SqlScalarType::String.nullable(false))
7685        .finish(),
7686    column_comments: BTreeMap::from_iter([
7687        (
7688            "object_id",
7689            "The ID of a materialization. Corresponds to `mz_catalog.mz_indexes.id`, `mz_catalog.mz_materialized_views.id`, or `mz_catalog.mz_sinks.id`.",
7690        ),
7691        (
7692            "dependency_id",
7693            "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`.",
7694        ),
7695    ]),
7696    sql: "
7697SELECT object_id, dependency_id
7698FROM mz_internal.mz_compute_dependencies
7699UNION ALL
7700SELECT s.id, d.referenced_object_id AS dependency_id
7701FROM mz_internal.mz_object_dependencies d
7702JOIN mz_catalog.mz_sinks s ON (s.id = d.object_id)
7703JOIN mz_catalog.mz_relations r ON (r.id = d.referenced_object_id)",
7704    access: vec![PUBLIC_SELECT],
7705    ontology: Some(Ontology {
7706        entity_name: "materialization_dep",
7707        description: "Dependencies between materializations",
7708        links: &const {
7709            [
7710                OntologyLink {
7711                    name: "depends_on",
7712                    target: "object",
7713                    properties: LinkProperties::DependsOn {
7714                        source_column: "object_id",
7715                        target_column: "id",
7716                        source_id_type: Some(mz_repr::SemanticType::CatalogItemId),
7717                        requires_mapping: None,
7718                    },
7719                },
7720                OntologyLink {
7721                    name: "dependency_is",
7722                    target: "object",
7723                    properties: LinkProperties::fk("dependency_id", "id", Cardinality::ManyToOne),
7724                },
7725            ]
7726        },
7727        column_semantic_types: &const {
7728            [
7729                ("object_id", SemanticType::CatalogItemId),
7730                ("dependency_id", SemanticType::CatalogItemId),
7731            ]
7732        },
7733    }),
7734});
7735
7736pub static MZ_MATERIALIZATION_LAG: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
7737    name: "mz_materialization_lag",
7738    schema: MZ_INTERNAL_SCHEMA,
7739    oid: oid::VIEW_MZ_MATERIALIZATION_LAG_OID,
7740    desc: RelationDesc::builder()
7741        .with_column("object_id", SqlScalarType::String.nullable(false))
7742        .with_column("local_lag", SqlScalarType::Interval.nullable(true))
7743        .with_column("global_lag", SqlScalarType::Interval.nullable(true))
7744        .with_column(
7745            "slowest_local_input_id",
7746            SqlScalarType::String.nullable(false),
7747        )
7748        .with_column(
7749            "slowest_global_input_id",
7750            SqlScalarType::String.nullable(false),
7751        )
7752        .finish(),
7753    column_comments: BTreeMap::from_iter([
7754        (
7755            "object_id",
7756            "The ID of the materialized view, index, or sink.",
7757        ),
7758        (
7759            "local_lag",
7760            "The amount of time the materialization lags behind its direct inputs.",
7761        ),
7762        (
7763            "global_lag",
7764            "The amount of time the materialization lags behind its root inputs (sources and tables).",
7765        ),
7766        (
7767            "slowest_local_input_id",
7768            "The ID of the slowest direct input.",
7769        ),
7770        (
7771            "slowest_global_input_id",
7772            "The ID of the slowest root input.",
7773        ),
7774    ]),
7775    sql: "
7776WITH MUTUALLY RECURSIVE
7777    -- IDs of objects for which we want to know the lag.
7778    materializations (id text) AS (
7779        SELECT id FROM mz_catalog.mz_indexes
7780        UNION ALL
7781        SELECT id FROM mz_catalog.mz_materialized_views
7782        UNION ALL
7783        SELECT id FROM mz_catalog.mz_sinks
7784    ),
7785    -- Direct dependencies of materializations.
7786    direct_dependencies (id text, dep_id text) AS (
7787        SELECT m.id, d.dependency_id
7788        FROM materializations m
7789        JOIN mz_internal.mz_materialization_dependencies d ON (m.id = d.object_id)
7790    ),
7791    -- All transitive dependencies of materializations.
7792    transitive_dependencies (id text, dep_id text) AS (
7793        SELECT id, dep_id FROM direct_dependencies
7794        UNION
7795        SELECT td.id, dd.dep_id
7796        FROM transitive_dependencies td
7797        JOIN direct_dependencies dd ON (dd.id = td.dep_id)
7798    ),
7799    -- Root dependencies of materializations (sources and tables).
7800    root_dependencies (id text, dep_id text) AS (
7801        SELECT *
7802        FROM transitive_dependencies td
7803        WHERE NOT EXISTS (
7804            SELECT 1
7805            FROM direct_dependencies dd
7806            WHERE dd.id = td.dep_id
7807        )
7808    ),
7809    -- Write progress times of materializations.
7810    materialization_times (id text, time timestamptz) AS (
7811        SELECT m.id, to_timestamp(f.write_frontier::text::double / 1000)
7812        FROM materializations m
7813        JOIN mz_internal.mz_frontiers f ON (m.id = f.object_id)
7814    ),
7815    -- Write progress times of direct dependencies of materializations.
7816    input_times (id text, slowest_dep text, time timestamptz) AS (
7817        SELECT DISTINCT ON (d.id)
7818            d.id,
7819            d.dep_id,
7820            to_timestamp(f.write_frontier::text::double / 1000)
7821        FROM direct_dependencies d
7822        JOIN mz_internal.mz_frontiers f ON (d.dep_id = f.object_id)
7823        ORDER BY d.id, f.write_frontier ASC
7824    ),
7825    -- Write progress times of root dependencies of materializations.
7826    root_times (id text, slowest_dep text, time timestamptz) AS (
7827        SELECT DISTINCT ON (d.id)
7828            d.id,
7829            d.dep_id,
7830            to_timestamp(f.write_frontier::text::double / 1000)
7831        FROM root_dependencies d
7832        JOIN mz_internal.mz_frontiers f ON (d.dep_id = f.object_id)
7833        ORDER BY d.id, f.write_frontier ASC
7834    )
7835SELECT
7836    id AS object_id,
7837    -- Ensure that lag values are always NULL for materializations that have reached the empty
7838    -- frontier, as those have processed all their input data.
7839    -- Also make sure that lag values are never negative, even when input frontiers are before
7840    -- output frontiers (as can happen during hydration).
7841    CASE
7842        WHEN m.time IS NULL THEN INTERVAL '0'
7843        WHEN i.time IS NULL THEN NULL
7844        ELSE greatest(i.time - m.time, INTERVAL '0')
7845    END AS local_lag,
7846    CASE
7847        WHEN m.time IS NULL THEN INTERVAL '0'
7848        WHEN r.time IS NULL THEN NULL
7849        ELSE greatest(r.time - m.time, INTERVAL '0')
7850    END AS global_lag,
7851    i.slowest_dep AS slowest_local_input_id,
7852    r.slowest_dep AS slowest_global_input_id
7853FROM materialization_times m
7854JOIN input_times i USING (id)
7855JOIN root_times r USING (id)",
7856    access: vec![PUBLIC_SELECT],
7857    ontology: Some(Ontology {
7858        entity_name: "materialization_lag",
7859        description: "Lag between a materialization and its inputs",
7860        links: &const {
7861            [
7862                OntologyLink {
7863                    name: "measures_materialization_lag",
7864                    target: "object",
7865                    properties: LinkProperties::measures("object_id", "id", "materialization_lag"),
7866                },
7867                OntologyLink {
7868                    name: "slowest_local_input",
7869                    target: "object",
7870                    properties: LinkProperties::fk(
7871                        "slowest_local_input_id",
7872                        "id",
7873                        Cardinality::ManyToOne,
7874                    ),
7875                },
7876                OntologyLink {
7877                    name: "slowest_global_input",
7878                    target: "object",
7879                    properties: LinkProperties::fk(
7880                        "slowest_global_input_id",
7881                        "id",
7882                        Cardinality::ManyToOne,
7883                    ),
7884                },
7885            ]
7886        },
7887        column_semantic_types: &const {
7888            [
7889                ("object_id", SemanticType::CatalogItemId),
7890                ("slowest_local_input_id", SemanticType::CatalogItemId),
7891                ("slowest_global_input_id", SemanticType::CatalogItemId),
7892            ]
7893        },
7894    }),
7895});
7896/// The output relation shared by all `mz_console_cluster_utilization_overview*`
7897/// views. Every (bucket size, retention) variant produces the same columns so
7898/// the Console can swap between them based on the selected time range.
7899fn console_cluster_utilization_overview_desc() -> RelationDesc {
7900    RelationDesc::builder()
7901        .with_column(
7902            "bucket_start",
7903            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7904        )
7905        .with_column("replica_id", SqlScalarType::String.nullable(false))
7906        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
7907        .with_column(
7908            "max_memory_at",
7909            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7910        )
7911        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
7912        .with_column(
7913            "max_disk_at",
7914            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7915        )
7916        .with_column(
7917            "memory_and_disk_percent",
7918            SqlScalarType::Float64.nullable(true),
7919        )
7920        .with_column(
7921            "max_memory_and_disk_memory_percent",
7922            SqlScalarType::Float64.nullable(true),
7923        )
7924        .with_column(
7925            "max_memory_and_disk_disk_percent",
7926            SqlScalarType::Float64.nullable(true),
7927        )
7928        .with_column(
7929            "max_memory_and_disk_at",
7930            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7931        )
7932        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
7933        .with_column(
7934            "max_heap_at",
7935            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7936        )
7937        .with_column("max_cpu_percent", SqlScalarType::Float64.nullable(true))
7938        .with_column(
7939            "max_cpu_at",
7940            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7941        )
7942        .with_column("offline_events", SqlScalarType::Jsonb.nullable(true))
7943        .with_column(
7944            "bucket_end",
7945            SqlScalarType::TimestampTz { precision: None }.nullable(false),
7946        )
7947        .with_column("name", SqlScalarType::String.nullable(true))
7948        .with_column("cluster_id", SqlScalarType::String.nullable(true))
7949        .with_column("size", SqlScalarType::String.nullable(true))
7950        .finish()
7951}
7952
7953/// Builds the SQL body shared by the `mz_console_cluster_utilization_overview*`
7954/// views, which power the Console's cluster utilization graphs.
7955///
7956/// There is one view per (bucket width, retention window) pair so the Console
7957/// can read a pre-materialized, indexed rollup for each time range it offers
7958/// instead of recomputing this (expensive) query on every page load. The bodies
7959/// must be kept in sync with the equivalent ad-hoc query in the Console
7960/// (`buildReplicaUtilizationHistoryQuery` in
7961/// `console/src/api/materialize/cluster/replicaUtilizationHistory.ts`).
7962///
7963/// * `bin`: the `date_bin` bucket width, e.g. `1 MINUTE`.
7964/// * `retention`: how much history the view retains, e.g. `3 HOURS`, enforced with a temporal
7965///   `mz_now()` filter so the maintained arrangement stays bounded.
7966/// * `group_size`: the expected number of metric samples per (replica, bucket), used for the
7967///   `DISTINCT ON INPUT GROUP SIZE` top-k hint. Replica metrics are scraped roughly once per
7968///   minute, so this is the bucket width in minutes.
7969fn console_cluster_utilization_overview_sql(bin: &str, retention: &str, group_size: u32) -> String {
7970    format!(
7971        r#"WITH replica_history AS (
7972  SELECT replica_id, size, cluster_id
7973  FROM mz_internal.mz_cluster_replica_history
7974  UNION
7975  -- We union the current set of cluster replicas since mz_cluster_replica_history doesn't include system clusters.
7976  SELECT id AS replica_id, size, cluster_id
7977  FROM mz_catalog.mz_cluster_replicas
7978),
7979replica_metrics_history AS (
7980  SELECT
7981    m.occurred_at,
7982    m.replica_id,
7983    r.size,
7984    (SUM(m.cpu_nano_cores::float8) / NULLIF(s.cpu_nano_cores, 0) / NULLIF(s.processes, 0)) AS cpu_percent,
7985    (SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)) AS memory_percent,
7986    (SUM(m.disk_bytes::float8) / NULLIF(s.disk_bytes, 0) / NULLIF(s.processes, 0)) AS disk_percent,
7987    SUM(m.disk_bytes::float8) AS disk_bytes,
7988    SUM(m.memory_bytes::float8) AS memory_bytes,
7989    s.disk_bytes::float8 * s.processes AS total_disk_bytes,
7990    s.memory_bytes::float8 * s.processes AS total_memory_bytes,
7991    MAX(m.heap_bytes::float8) AS heap_bytes,
7992    MAX(m.heap_limit) AS heap_limit,
7993    -- heap_limit is NULL when clusterd isn't launched with --heap-limit (e.g.
7994    -- the emulator's process orchestrator). Fall back to the size-based memory
7995    -- percent so the chart still renders.
7996    COALESCE(
7997      MAX(m.heap_bytes::float8 / NULLIF(m.heap_limit, 0)),
7998      SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)
7999    ) AS heap_percent
8000  FROM
8001    replica_history AS r
8002    INNER JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
8003    INNER JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.replica_id
8004  GROUP BY
8005    m.occurred_at,
8006    m.replica_id,
8007    r.size,
8008    s.cpu_nano_cores,
8009    s.memory_bytes,
8010    s.disk_bytes,
8011    s.processes
8012),
8013replica_utilization_history_binned AS (
8014  -- NOTE: we read directly from replica_metrics_history rather than re-joining
8015  -- replica_history; every replica_id here already came from replica_history,
8016  -- so the join was redundant (and could fan out a replica that changed size).
8017  SELECT
8018    m.occurred_at,
8019    m.replica_id,
8020    m.cpu_percent,
8021    m.memory_percent,
8022    m.memory_bytes,
8023    m.disk_percent,
8024    m.disk_bytes,
8025    m.total_disk_bytes,
8026    m.total_memory_bytes,
8027    m.heap_bytes,
8028    m.heap_percent,
8029    m.size,
8030    date_bin('{bin}', m.occurred_at, '1970-01-01'::timestamp) AS bucket_start
8031  FROM replica_metrics_history AS m
8032  WHERE mz_now() <= date_bin('{bin}', m.occurred_at, '1970-01-01'::timestamp) + INTERVAL '{retention}'
8033),
8034-- For each (replica, bucket), take the sample with the highest memory.
8035max_memory AS (
8036  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, memory_percent, occurred_at
8037  FROM replica_utilization_history_binned
8038  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
8039  ORDER BY bucket_start, replica_id, COALESCE(memory_bytes, 0) DESC
8040),
8041-- For each (replica, bucket), take the sample with the highest disk.
8042max_disk AS (
8043  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, disk_percent, occurred_at
8044  FROM replica_utilization_history_binned
8045  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
8046  ORDER BY bucket_start, replica_id, COALESCE(disk_bytes, 0) DESC
8047),
8048-- For each (replica, bucket), take the sample with the highest cpu.
8049max_cpu AS (
8050  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, cpu_percent, occurred_at
8051  FROM replica_utilization_history_binned
8052  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
8053  ORDER BY bucket_start, replica_id, COALESCE(cpu_percent, 0) DESC
8054),
8055/*
8056  For each (replica, bucket), take the sample with the highest combined memory
8057  and disk. This is different from adding max_memory and max_disk per bucket
8058  because both values may not occur at the same time if the bucket interval is
8059  large.
8060*/
8061max_memory_and_disk AS (
8062  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, memory_percent, disk_percent, memory_and_disk_percent, occurred_at
8063  FROM (
8064    SELECT *,
8065      CASE
8066        WHEN disk_bytes IS NULL AND memory_bytes IS NULL THEN NULL
8067        ELSE (COALESCE(memory_bytes, 0) + COALESCE(disk_bytes, 0)) / NULLIF((total_memory_bytes + total_disk_bytes), 0)
8068      END AS memory_and_disk_percent
8069    FROM replica_utilization_history_binned
8070  ) AS max_memory_and_disk_inner
8071  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
8072  ORDER BY bucket_start, replica_id, COALESCE(memory_and_disk_percent, 0) DESC
8073),
8074-- For each (replica, bucket), take the sample with the highest heap.
8075max_heap AS (
8076  SELECT DISTINCT ON (bucket_start, replica_id) bucket_start, replica_id, heap_percent, occurred_at
8077  FROM replica_utilization_history_binned
8078  OPTIONS (DISTINCT ON INPUT GROUP SIZE = {group_size})
8079  ORDER BY bucket_start, replica_id, COALESCE(heap_bytes, 0) DESC
8080),
8081-- For each (replica, bucket), collect its offline events at that time.
8082replica_offline_event_history AS (
8083  SELECT
8084    date_bin('{bin}', occurred_at, '1970-01-01'::timestamp) AS bucket_start,
8085    replica_id,
8086    jsonb_agg(
8087      jsonb_build_object(
8088        'replicaId', rsh.replica_id,
8089        'occurredAt', rsh.occurred_at,
8090        'status', rsh.status,
8091        'reason', rsh.reason
8092      )
8093    ) AS offline_events
8094  FROM mz_internal.mz_cluster_replica_status_history AS rsh
8095  -- We assume the statuses for process 0 are the same as all processes.
8096  WHERE process_id = '0'
8097    AND status = 'offline'
8098    AND mz_now() <= date_bin('{bin}', occurred_at, '1970-01-01'::timestamp) + INTERVAL '{retention}'
8099  GROUP BY bucket_start, replica_id
8100)
8101SELECT
8102  bucket_start,
8103  replica_id,
8104  max_memory.memory_percent,
8105  max_memory.occurred_at AS max_memory_at,
8106  max_disk.disk_percent,
8107  max_disk.occurred_at AS max_disk_at,
8108  max_memory_and_disk.memory_and_disk_percent AS memory_and_disk_percent,
8109  max_memory_and_disk.memory_percent AS max_memory_and_disk_memory_percent,
8110  max_memory_and_disk.disk_percent AS max_memory_and_disk_disk_percent,
8111  max_memory_and_disk.occurred_at AS max_memory_and_disk_at,
8112  max_heap.heap_percent,
8113  max_heap.occurred_at AS max_heap_at,
8114  max_cpu.cpu_percent AS max_cpu_percent,
8115  max_cpu.occurred_at AS max_cpu_at,
8116  replica_offline_event_history.offline_events,
8117  bucket_start + INTERVAL '{bin}' AS bucket_end,
8118  replica_name_history.new_name AS name,
8119  replica_history.cluster_id,
8120  replica_history.size
8121FROM max_memory
8122JOIN max_disk USING (bucket_start, replica_id)
8123JOIN max_cpu USING (bucket_start, replica_id)
8124JOIN max_memory_and_disk USING (bucket_start, replica_id)
8125JOIN max_heap USING (bucket_start, replica_id)
8126JOIN replica_history USING (replica_id)
8127/*
8128  TOP k=1 over the name history via a LATERAL subquery + LIMIT: for each bucket,
8129  get the most recent replica name as of the end of the bucket.
8130*/
8131CROSS JOIN LATERAL (
8132  SELECT new_name
8133  FROM mz_internal.mz_cluster_replica_name_history AS replica_name_history
8134  WHERE replica_id = replica_name_history.id
8135    -- We treat NULLs as the beginning of time.
8136    AND bucket_start + INTERVAL '{bin}' >= COALESCE(replica_name_history.occurred_at, '1970-01-01'::timestamp)
8137  ORDER BY replica_name_history.occurred_at DESC
8138  LIMIT 1
8139) AS replica_name_history
8140LEFT JOIN replica_offline_event_history USING (bucket_start, replica_id)"#,
8141        bin = bin,
8142        retention = retention,
8143        group_size = group_size,
8144    )
8145}
8146
8147/// Schema for the un-binned 3-hour console cluster utilization base. Unlike the
8148/// binned `_overview*` views, this exposes raw per-(replica, sample) metrics so
8149/// the Console can bin client-side.
8150fn console_cluster_utilization_unbinned_3h_desc() -> RelationDesc {
8151    RelationDesc::builder()
8152        .with_column("replica_id", SqlScalarType::String.nullable(false))
8153        .with_column("cluster_id", SqlScalarType::String.nullable(true))
8154        .with_column("size", SqlScalarType::String.nullable(false))
8155        .with_column("name", SqlScalarType::String.nullable(true))
8156        .with_column(
8157            "occurred_at",
8158            SqlScalarType::TimestampTz { precision: None }.nullable(false),
8159        )
8160        .with_column("cpu_percent", SqlScalarType::Float64.nullable(true))
8161        .with_column("memory_percent", SqlScalarType::Float64.nullable(true))
8162        .with_column("disk_percent", SqlScalarType::Float64.nullable(true))
8163        .with_column("heap_percent", SqlScalarType::Float64.nullable(true))
8164        .with_column(
8165            "memory_and_disk_percent",
8166            SqlScalarType::Float64.nullable(true),
8167        )
8168        .finish()
8169}
8170
8171/// Builds the SQL for the un-binned 3-hour console cluster utilization base: one
8172/// row per (replica, metric sample) over `retention`, with no `date_bin`/top-k,
8173/// so the Console bins it client-side. The binned `_overview*` views handle the
8174/// longer windows. A temporal `mz_now()` filter bounds the maintained
8175/// arrangement. Kept in sync with the Console
8176/// (`buildConsoleClusterUtilizationUnbinned3hQuery` in
8177/// `replicaUtilizationHistory.ts`).
8178fn console_cluster_utilization_unbinned_3h_sql(retention: &str) -> String {
8179    format!(
8180        r#"WITH replica_history AS (
8181  -- Dedup to one row per replica (prefer the current size). Size is fixed per
8182  -- replica so this is normally a no-op, but a stray duplicate size in history
8183  -- would fan out the metrics join; with no Top-1 dedup here that would emit two
8184  -- rows per (replica_id, occurred_at) and break the Console SUBSCRIBE upsert key.
8185  SELECT DISTINCT ON (replica_id) replica_id, size, cluster_id
8186  FROM (
8187    -- We union the current set of cluster replicas since mz_cluster_replica_history doesn't include system clusters.
8188    SELECT id AS replica_id, size, cluster_id, 0 AS source_rank
8189    FROM mz_catalog.mz_cluster_replicas
8190    UNION ALL
8191    SELECT replica_id, size, cluster_id, 1 AS source_rank
8192    FROM mz_internal.mz_cluster_replica_history
8193  ) all_replicas
8194  ORDER BY replica_id, source_rank
8195),
8196replica_metrics AS (
8197  SELECT
8198    m.occurred_at,
8199    m.replica_id,
8200    r.cluster_id,
8201    r.size,
8202    (SUM(m.cpu_nano_cores::float8) / NULLIF(s.cpu_nano_cores, 0) / NULLIF(s.processes, 0)) AS cpu_percent,
8203    (SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)) AS memory_percent,
8204    (SUM(m.disk_bytes::float8) / NULLIF(s.disk_bytes, 0) / NULLIF(s.processes, 0)) AS disk_percent,
8205    COALESCE(
8206      MAX(m.heap_bytes::float8 / NULLIF(m.heap_limit, 0)),
8207      SUM(m.memory_bytes::float8) / NULLIF(s.memory_bytes, 0) / NULLIF(s.processes, 0)
8208    ) AS heap_percent,
8209    CASE
8210      WHEN SUM(m.disk_bytes::float8) IS NULL AND SUM(m.memory_bytes::float8) IS NULL THEN NULL
8211      ELSE (COALESCE(SUM(m.memory_bytes::float8), 0) + COALESCE(SUM(m.disk_bytes::float8), 0))
8212           / NULLIF((s.memory_bytes::float8 + s.disk_bytes::float8) * s.processes, 0)
8213    END AS memory_and_disk_percent
8214  FROM replica_history AS r
8215    INNER JOIN mz_catalog.mz_cluster_replica_sizes AS s ON r.size = s.size
8216    INNER JOIN mz_internal.mz_cluster_replica_metrics_history AS m ON m.replica_id = r.replica_id
8217  -- No aggregation over time: one row per (replica, sample) so the Console bins
8218  -- client-side. The temporal mz_now() filter keeps the maintained arrangement
8219  -- bounded to the retention window.
8220  WHERE mz_now() <= m.occurred_at + INTERVAL '{retention}'
8221  GROUP BY
8222    m.occurred_at,
8223    m.replica_id,
8224    r.cluster_id,
8225    r.size,
8226    s.cpu_nano_cores,
8227    s.memory_bytes,
8228    s.disk_bytes,
8229    s.processes
8230)
8231SELECT
8232  m.replica_id,
8233  m.cluster_id,
8234  m.size,
8235  replica_name_history.new_name AS name,
8236  m.occurred_at,
8237  m.cpu_percent,
8238  m.memory_percent,
8239  m.disk_percent,
8240  m.heap_percent,
8241  m.memory_and_disk_percent
8242FROM replica_metrics AS m
8243/* Most recent replica name as of the sample time. */
8244CROSS JOIN LATERAL (
8245  SELECT new_name
8246  FROM mz_internal.mz_cluster_replica_name_history AS replica_name_history
8247  WHERE m.replica_id = replica_name_history.id
8248    -- We treat NULLs as the beginning of time.
8249    AND m.occurred_at >= COALESCE(replica_name_history.occurred_at, '1970-01-01'::timestamp)
8250  ORDER BY replica_name_history.occurred_at DESC
8251  LIMIT 1
8252) AS replica_name_history"#,
8253        retention = retention,
8254    )
8255}
8256
8257/**
8258 * Displays cluster utilization over 14 days bucketed by 1 hour, for the
8259 * Console's environment overview and cluster pages, to speed up load times.
8260 * This view (and its `_3h`/`_24h` siblings) is kept in sync with
8261 * MaterializeInc/console/src/api/materialize/cluster/replicaUtilizationHistory.ts
8262 */
8263pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW: LazyLock<BuiltinView> =
8264    LazyLock::new(|| BuiltinView {
8265        name: "mz_console_cluster_utilization_overview",
8266        schema: MZ_INTERNAL_SCHEMA,
8267        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_OID,
8268        desc: console_cluster_utilization_overview_desc(),
8269        column_comments: BTreeMap::new(),
8270        sql: Box::leak(
8271            console_cluster_utilization_overview_sql("1 HOUR", "14 DAYS", 60).into_boxed_str(),
8272        ),
8273        access: vec![PUBLIC_SELECT],
8274        ontology: None,
8275    });
8276
8277/**
8278 * Un-binned cluster utilization over the last 3 hours, for the Console's "Last
8279 * hour" / "Last 3 hours" graphs. Unlike the binned `_overview*` views, this
8280 * exposes raw per-(replica, sample) metrics and the Console bins client-side.
8281 * See `console_cluster_utilization_unbinned_3h_sql` for details.
8282 */
8283pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H: LazyLock<BuiltinView> =
8284    LazyLock::new(|| BuiltinView {
8285        name: "mz_console_cluster_utilization_overview_3h",
8286        schema: MZ_INTERNAL_SCHEMA,
8287        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_OID,
8288        desc: console_cluster_utilization_unbinned_3h_desc(),
8289        column_comments: BTreeMap::new(),
8290        sql: Box::leak(console_cluster_utilization_unbinned_3h_sql("3 HOURS").into_boxed_str()),
8291        access: vec![PUBLIC_SELECT],
8292        ontology: None,
8293    });
8294
8295/**
8296 * Cluster utilization over the last 24 hours bucketed by 5 minutes, for the
8297 * Console's "Last 6 hours" / "Last 24 hours" cluster utilization graphs. See
8298 * `console_cluster_utilization_overview_sql` for details.
8299 */
8300pub static MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H: LazyLock<BuiltinView> =
8301    LazyLock::new(|| BuiltinView {
8302        name: "mz_console_cluster_utilization_overview_24h",
8303        schema: MZ_INTERNAL_SCHEMA,
8304        oid: oid::VIEW_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_OID,
8305        desc: console_cluster_utilization_overview_desc(),
8306        column_comments: BTreeMap::new(),
8307        sql: Box::leak(
8308            console_cluster_utilization_overview_sql("5 MINUTES", "24 HOURS", 5).into_boxed_str(),
8309        ),
8310        access: vec![PUBLIC_SELECT],
8311        ontology: None,
8312    });
8313/**
8314 * Traces the blue/green deployment lineage in the audit log to determine all cluster
8315 * IDs that are logically the same cluster.
8316 * cluster_id: The ID of a cluster.
8317 * current_deployment_cluster_id: The cluster ID of the last cluster in
8318 *   cluster_id's blue/green lineage.
8319 * cluster_name: The name of the cluster.
8320 * The approach taken is as follows. First, find all extant clusters and add them
8321 * to the result set. Per cluster, we do the following:
8322 * 1. Find the most recent create or rename event. This moment represents when the cluster took
8323 *    on its final logical identity.
8324 * 2. Look for a cluster that had the same name (or the same name with `_dbt_deploy` appended)
8325 *    that was dropped within one minute of that moment. That cluster is almost certainly the
8326 *    logical predecessor of the current cluster. Add the cluster to the result set.
8327 * 3. Repeat the procedure until a cluster with no logical predecessor is discovered.
8328 * Limiting the search for a dropped cluster to a window of one minute is a heuristic,
8329 * but one that's likely to be pretty good one. If a name is reused after more
8330 * than one minute, that's a good sign that it wasn't an automatic blue/green
8331 * process, but someone turning on a new use case that happens to have the same
8332 * name as a previous but logically distinct use case.
8333 */
8334pub static MZ_CLUSTER_DEPLOYMENT_LINEAGE: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
8335    name: "mz_cluster_deployment_lineage",
8336    schema: MZ_INTERNAL_SCHEMA,
8337    oid: oid::VIEW_MZ_CLUSTER_DEPLOYMENT_LINEAGE_OID,
8338    desc: RelationDesc::builder()
8339        .with_column("cluster_id", SqlScalarType::String.nullable(true))
8340        .with_column(
8341            "current_deployment_cluster_id",
8342            SqlScalarType::String.nullable(false),
8343        )
8344        .with_column("cluster_name", SqlScalarType::String.nullable(false))
8345        .with_key(vec![0, 1, 2])
8346        .finish(),
8347    column_comments: BTreeMap::from_iter([
8348        (
8349            "cluster_id",
8350            "The ID of the cluster. Corresponds to `mz_clusters.id` (though the cluster may no longer exist).",
8351        ),
8352        (
8353            "current_deployment_cluster_id",
8354            "The cluster ID of the last cluster in `cluster_id`'s blue/green lineage (the cluster is guaranteed to exist).",
8355        ),
8356        ("cluster_name", "The name of the cluster"),
8357    ]),
8358    sql: r#"WITH MUTUALLY RECURSIVE cluster_events (
8359  cluster_id text,
8360  cluster_name text,
8361  event_type text,
8362  occurred_at timestamptz
8363) AS (
8364  SELECT coalesce(details->>'id', details->>'cluster_id') AS cluster_id,
8365    coalesce(details->>'name', details->>'new_name') AS cluster_name,
8366    event_type,
8367    occurred_at
8368  FROM mz_audit_events
8369  WHERE (
8370      event_type IN ('create', 'drop')
8371      OR (
8372        event_type = 'alter'
8373        AND details ? 'new_name'
8374      )
8375    )
8376    AND object_type = 'cluster'
8377    AND mz_now() < occurred_at + INTERVAL '30 days'
8378),
8379mz_cluster_deployment_lineage (
8380  cluster_id text,
8381  current_deployment_cluster_id text,
8382  cluster_name text
8383) AS (
8384  SELECT c.id,
8385    c.id,
8386    c.name
8387  FROM mz_clusters c
8388  WHERE c.id LIKE 'u%'
8389  UNION
8390  SELECT *
8391  FROM dropped_clusters
8392),
8393-- Closest create or rename event based on the current clusters in the result set
8394most_recent_create_or_rename (
8395  cluster_id text,
8396  current_deployment_cluster_id text,
8397  cluster_name text,
8398  occurred_at timestamptz
8399) AS (
8400  SELECT DISTINCT ON (e.cluster_id) e.cluster_id,
8401    c.current_deployment_cluster_id,
8402    e.cluster_name,
8403    e.occurred_at
8404  FROM mz_cluster_deployment_lineage c
8405    JOIN cluster_events e ON c.cluster_id = e.cluster_id
8406    AND c.cluster_name = e.cluster_name
8407  WHERE e.event_type <> 'drop'
8408  ORDER BY e.cluster_id,
8409    e.occurred_at DESC
8410),
8411-- Clusters that were dropped most recently within 1 minute of most_recent_create_or_rename
8412dropped_clusters (
8413  cluster_id text,
8414  current_deployment_cluster_id text,
8415  cluster_name text
8416) AS (
8417  SELECT DISTINCT ON (cr.cluster_id) e.cluster_id,
8418    cr.current_deployment_cluster_id,
8419    cr.cluster_name
8420  FROM most_recent_create_or_rename cr
8421    JOIN cluster_events e ON e.occurred_at BETWEEN cr.occurred_at - interval '1 minute'
8422    AND cr.occurred_at + interval '1 minute'
8423    AND (
8424      e.cluster_name = cr.cluster_name
8425      OR e.cluster_name = cr.cluster_name || '_dbt_deploy'
8426    )
8427  WHERE e.event_type = 'drop'
8428  ORDER BY cr.cluster_id,
8429    abs(
8430      extract(
8431        epoch
8432        FROM cr.occurred_at - e.occurred_at
8433      )
8434    )
8435)
8436SELECT *
8437FROM mz_cluster_deployment_lineage"#,
8438    access: vec![PUBLIC_SELECT],
8439    ontology: Some(Ontology {
8440        entity_name: "cluster_deployment",
8441        description: "Cluster deployment lineage information",
8442        links: &const {
8443            [
8444                OntologyLink {
8445                    name: "deployment_of",
8446                    target: "cluster",
8447                    properties: LinkProperties::fk("cluster_id", "id", Cardinality::ManyToOne),
8448                },
8449                OntologyLink {
8450                    name: "current_deployment",
8451                    target: "cluster",
8452                    properties: LinkProperties::fk(
8453                        "current_deployment_cluster_id",
8454                        "id",
8455                        Cardinality::ManyToOne,
8456                    ),
8457                },
8458            ]
8459        },
8460        column_semantic_types: &[],
8461    }),
8462});
8463
8464pub const MZ_SHOW_DATABASES_IND: BuiltinIndex = BuiltinIndex {
8465    name: "mz_show_databases_ind",
8466    schema: MZ_INTERNAL_SCHEMA,
8467    oid: oid::INDEX_MZ_SHOW_DATABASES_IND_OID,
8468    sql: "IN CLUSTER mz_catalog_server
8469ON mz_internal.mz_show_databases (name)",
8470    is_retained_metrics_object: false,
8471};
8472
8473pub const MZ_SHOW_SCHEMAS_IND: BuiltinIndex = BuiltinIndex {
8474    name: "mz_show_schemas_ind",
8475    schema: MZ_INTERNAL_SCHEMA,
8476    oid: oid::INDEX_MZ_SHOW_SCHEMAS_IND_OID,
8477    sql: "IN CLUSTER mz_catalog_server
8478ON mz_internal.mz_show_schemas (database_id)",
8479    is_retained_metrics_object: false,
8480};
8481
8482pub const MZ_SHOW_CONNECTIONS_IND: BuiltinIndex = BuiltinIndex {
8483    name: "mz_show_connections_ind",
8484    schema: MZ_INTERNAL_SCHEMA,
8485    oid: oid::INDEX_MZ_SHOW_CONNECTIONS_IND_OID,
8486    sql: "IN CLUSTER mz_catalog_server
8487ON mz_internal.mz_show_connections (schema_id)",
8488    is_retained_metrics_object: false,
8489};
8490
8491pub const MZ_SHOW_TABLES_IND: BuiltinIndex = BuiltinIndex {
8492    name: "mz_show_tables_ind",
8493    schema: MZ_INTERNAL_SCHEMA,
8494    oid: oid::INDEX_MZ_SHOW_TABLES_IND_OID,
8495    sql: "IN CLUSTER mz_catalog_server
8496ON mz_internal.mz_show_tables (schema_id)",
8497    is_retained_metrics_object: false,
8498};
8499
8500pub const MZ_SHOW_SOURCES_IND: BuiltinIndex = BuiltinIndex {
8501    name: "mz_show_sources_ind",
8502    schema: MZ_INTERNAL_SCHEMA,
8503    oid: oid::INDEX_MZ_SHOW_SOURCES_IND_OID,
8504    sql: "IN CLUSTER mz_catalog_server
8505ON mz_internal.mz_show_sources (schema_id)",
8506    is_retained_metrics_object: false,
8507};
8508
8509pub const MZ_SHOW_VIEWS_IND: BuiltinIndex = BuiltinIndex {
8510    name: "mz_show_views_ind",
8511    schema: MZ_INTERNAL_SCHEMA,
8512    oid: oid::INDEX_MZ_SHOW_VIEWS_IND_OID,
8513    sql: "IN CLUSTER mz_catalog_server
8514ON mz_internal.mz_show_views (schema_id)",
8515    is_retained_metrics_object: false,
8516};
8517
8518pub const MZ_SHOW_MATERIALIZED_VIEWS_IND: BuiltinIndex = BuiltinIndex {
8519    name: "mz_show_materialized_views_ind",
8520    schema: MZ_INTERNAL_SCHEMA,
8521    oid: oid::INDEX_MZ_SHOW_MATERIALIZED_VIEWS_IND_OID,
8522    sql: "IN CLUSTER mz_catalog_server
8523ON mz_internal.mz_show_materialized_views (schema_id)",
8524    is_retained_metrics_object: false,
8525};
8526
8527pub const MZ_SHOW_SINKS_IND: BuiltinIndex = BuiltinIndex {
8528    name: "mz_show_sinks_ind",
8529    schema: MZ_INTERNAL_SCHEMA,
8530    oid: oid::INDEX_MZ_SHOW_SINKS_IND_OID,
8531    sql: "IN CLUSTER mz_catalog_server
8532ON mz_internal.mz_show_sinks (schema_id)",
8533    is_retained_metrics_object: false,
8534};
8535
8536pub const MZ_SHOW_TYPES_IND: BuiltinIndex = BuiltinIndex {
8537    name: "mz_show_types_ind",
8538    schema: MZ_INTERNAL_SCHEMA,
8539    oid: oid::INDEX_MZ_SHOW_TYPES_IND_OID,
8540    sql: "IN CLUSTER mz_catalog_server
8541ON mz_internal.mz_show_types (schema_id)",
8542    is_retained_metrics_object: false,
8543};
8544
8545pub const MZ_SHOW_ROLES_IND: BuiltinIndex = BuiltinIndex {
8546    name: "mz_show_roles_ind",
8547    schema: MZ_INTERNAL_SCHEMA,
8548    oid: oid::INDEX_MZ_SHOW_ROLES_IND_OID,
8549    sql: "IN CLUSTER mz_catalog_server
8550ON mz_internal.mz_show_roles (name)",
8551    is_retained_metrics_object: false,
8552};
8553
8554pub const MZ_SHOW_ALL_OBJECTS_IND: BuiltinIndex = BuiltinIndex {
8555    name: "mz_show_all_objects_ind",
8556    schema: MZ_INTERNAL_SCHEMA,
8557    oid: oid::INDEX_MZ_SHOW_ALL_OBJECTS_IND_OID,
8558    sql: "IN CLUSTER mz_catalog_server
8559ON mz_internal.mz_show_all_objects (schema_id)",
8560    is_retained_metrics_object: false,
8561};
8562
8563pub const MZ_SHOW_INDEXES_IND: BuiltinIndex = BuiltinIndex {
8564    name: "mz_show_indexes_ind",
8565    schema: MZ_INTERNAL_SCHEMA,
8566    oid: oid::INDEX_MZ_SHOW_INDEXES_IND_OID,
8567    sql: "IN CLUSTER mz_catalog_server
8568ON mz_internal.mz_show_indexes (schema_id)",
8569    is_retained_metrics_object: false,
8570};
8571
8572pub const MZ_SHOW_COLUMNS_IND: BuiltinIndex = BuiltinIndex {
8573    name: "mz_show_columns_ind",
8574    schema: MZ_INTERNAL_SCHEMA,
8575    oid: oid::INDEX_MZ_SHOW_COLUMNS_IND_OID,
8576    sql: "IN CLUSTER mz_catalog_server
8577ON mz_internal.mz_show_columns (id)",
8578    is_retained_metrics_object: false,
8579};
8580
8581pub const MZ_SHOW_CLUSTERS_IND: BuiltinIndex = BuiltinIndex {
8582    name: "mz_show_clusters_ind",
8583    schema: MZ_INTERNAL_SCHEMA,
8584    oid: oid::INDEX_MZ_SHOW_CLUSTERS_IND_OID,
8585    sql: "IN CLUSTER mz_catalog_server
8586ON mz_internal.mz_show_clusters (name)",
8587    is_retained_metrics_object: false,
8588};
8589
8590pub const MZ_SHOW_CLUSTER_REPLICAS_IND: BuiltinIndex = BuiltinIndex {
8591    name: "mz_show_cluster_replicas_ind",
8592    schema: MZ_INTERNAL_SCHEMA,
8593    oid: oid::INDEX_MZ_SHOW_CLUSTER_REPLICAS_IND_OID,
8594    sql: "IN CLUSTER mz_catalog_server
8595ON mz_internal.mz_show_cluster_replicas (cluster)",
8596    is_retained_metrics_object: false,
8597};
8598
8599pub const MZ_SHOW_SECRETS_IND: BuiltinIndex = BuiltinIndex {
8600    name: "mz_show_secrets_ind",
8601    schema: MZ_INTERNAL_SCHEMA,
8602    oid: oid::INDEX_MZ_SHOW_SECRETS_IND_OID,
8603    sql: "IN CLUSTER mz_catalog_server
8604ON mz_internal.mz_show_secrets (schema_id)",
8605    is_retained_metrics_object: false,
8606};
8607
8608pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_IND: BuiltinIndex = BuiltinIndex {
8609    name: "mz_console_cluster_utilization_overview_ind",
8610    schema: MZ_INTERNAL_SCHEMA,
8611    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_IND_OID,
8612    sql: "IN CLUSTER mz_catalog_server
8613ON mz_internal.mz_console_cluster_utilization_overview (cluster_id)",
8614    is_retained_metrics_object: false,
8615};
8616
8617pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_IND: BuiltinIndex = BuiltinIndex {
8618    name: "mz_console_cluster_utilization_overview_3h_ind",
8619    schema: MZ_INTERNAL_SCHEMA,
8620    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_3H_IND_OID,
8621    sql: "IN CLUSTER mz_catalog_server
8622ON mz_internal.mz_console_cluster_utilization_overview_3h (cluster_id)",
8623    is_retained_metrics_object: false,
8624};
8625
8626pub const MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_IND: BuiltinIndex = BuiltinIndex {
8627    name: "mz_console_cluster_utilization_overview_24h_ind",
8628    schema: MZ_INTERNAL_SCHEMA,
8629    oid: oid::INDEX_MZ_CONSOLE_CLUSTER_UTILIZATION_OVERVIEW_24H_IND_OID,
8630    sql: "IN CLUSTER mz_catalog_server
8631ON mz_internal.mz_console_cluster_utilization_overview_24h (cluster_id)",
8632    is_retained_metrics_object: false,
8633};
8634
8635pub const MZ_CLUSTER_DEPLOYMENT_LINEAGE_IND: BuiltinIndex = BuiltinIndex {
8636    name: "mz_cluster_deployment_lineage_ind",
8637    schema: MZ_INTERNAL_SCHEMA,
8638    oid: oid::INDEX_MZ_CLUSTER_DEPLOYMENT_LINEAGE_IND_OID,
8639    sql: "IN CLUSTER mz_catalog_server
8640ON mz_internal.mz_cluster_deployment_lineage (cluster_id)",
8641    is_retained_metrics_object: false,
8642};
8643
8644pub const MZ_SOURCE_STATUSES_IND: BuiltinIndex = BuiltinIndex {
8645    name: "mz_source_statuses_ind",
8646    schema: MZ_INTERNAL_SCHEMA,
8647    oid: oid::INDEX_MZ_SOURCE_STATUSES_IND_OID,
8648    sql: "IN CLUSTER mz_catalog_server
8649ON mz_internal.mz_source_statuses (id)",
8650    is_retained_metrics_object: false,
8651};
8652
8653pub const MZ_SINK_STATUSES_IND: BuiltinIndex = BuiltinIndex {
8654    name: "mz_sink_statuses_ind",
8655    schema: MZ_INTERNAL_SCHEMA,
8656    oid: oid::INDEX_MZ_SINK_STATUSES_IND_OID,
8657    sql: "IN CLUSTER mz_catalog_server
8658ON mz_internal.mz_sink_statuses (id)",
8659    is_retained_metrics_object: false,
8660};
8661
8662pub const MZ_SOURCE_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8663    name: "mz_source_status_history_ind",
8664    schema: MZ_INTERNAL_SCHEMA,
8665    oid: oid::INDEX_MZ_SOURCE_STATUS_HISTORY_IND_OID,
8666    sql: "IN CLUSTER mz_catalog_server
8667ON mz_internal.mz_source_status_history (source_id)",
8668    is_retained_metrics_object: false,
8669};
8670
8671pub const MZ_SINK_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8672    name: "mz_sink_status_history_ind",
8673    schema: MZ_INTERNAL_SCHEMA,
8674    oid: oid::INDEX_MZ_SINK_STATUS_HISTORY_IND_OID,
8675    sql: "IN CLUSTER mz_catalog_server
8676ON mz_internal.mz_sink_status_history (sink_id)",
8677    is_retained_metrics_object: false,
8678};
8679
8680// In both `mz_source_statistics` and `mz_sink_statistics` we cast the `SUM` of
8681// uint8's to `uint8` instead of leaving them as `numeric`. This is because we want to
8682// save index space, and we don't expect the sum to be > 2^63
8683// (even if a source with 2000 workers, that each produce 400 terabytes in a month ~ 2^61).
8684//
8685//
8686// These aggregations are just to make `GROUP BY` happy. Each id has a single row in the
8687// underlying relation.
8688//
8689// We append WITH_HISTORY because we want to build a separate view + index that doesn't
8690// retain history. This is because retaining its history causes
8691// MZ_SOURCE_STATISTICS_WITH_HISTORY_IND to hold all records/updates, which causes CPU and latency
8692// of querying it to spike.
8693pub static MZ_SOURCE_STATISTICS_WITH_HISTORY: LazyLock<BuiltinView> =
8694    LazyLock::new(|| BuiltinView {
8695        name: "mz_source_statistics_with_history",
8696        schema: MZ_INTERNAL_SCHEMA,
8697        oid: oid::VIEW_MZ_SOURCE_STATISTICS_WITH_HISTORY_OID,
8698        desc: RelationDesc::builder()
8699            .with_column("id", SqlScalarType::String.nullable(false))
8700            .with_column("replica_id", SqlScalarType::String.nullable(true))
8701            .with_column("messages_received", SqlScalarType::UInt64.nullable(false))
8702            .with_column("bytes_received", SqlScalarType::UInt64.nullable(false))
8703            .with_column("updates_staged", SqlScalarType::UInt64.nullable(false))
8704            .with_column("updates_committed", SqlScalarType::UInt64.nullable(false))
8705            .with_column("records_indexed", SqlScalarType::UInt64.nullable(false))
8706            .with_column("bytes_indexed", SqlScalarType::UInt64.nullable(false))
8707            .with_column(
8708                "rehydration_latency",
8709                SqlScalarType::Interval.nullable(true),
8710            )
8711            .with_column(
8712                "snapshot_records_known",
8713                SqlScalarType::UInt64.nullable(true),
8714            )
8715            .with_column(
8716                "snapshot_records_staged",
8717                SqlScalarType::UInt64.nullable(true),
8718            )
8719            .with_column("snapshot_committed", SqlScalarType::Bool.nullable(false))
8720            .with_column("offset_known", SqlScalarType::UInt64.nullable(true))
8721            .with_column("offset_committed", SqlScalarType::UInt64.nullable(true))
8722            .with_key(vec![0, 1])
8723            .finish(),
8724        column_comments: BTreeMap::new(),
8725        sql: "
8726WITH
8727    -- For each subsource, statistics are reported as its parent source
8728    subsource_to_parent AS
8729    (
8730        SELECT subsource.id AS id, parent.id AS report_id
8731        FROM mz_catalog.mz_sources AS subsource
8732            JOIN mz_internal.mz_object_dependencies AS dep ON subsource.id = dep.object_id
8733            JOIN mz_catalog.mz_sources AS parent ON parent.id = dep.referenced_object_id
8734        WHERE subsource.type = 'subsource'
8735    ),
8736    -- For each table from source, statistics are reported as its parent source
8737    table_to_parent AS
8738    (
8739        SELECT id, source_id AS report_id
8740        FROM mz_catalog.mz_tables
8741        WHERE source_id IS NOT NULL
8742    ),
8743    -- For each source and subsource, statistics are reported as itself
8744    source_refl AS
8745    (
8746        SELECT id, id AS report_id
8747        FROM mz_catalog.mz_sources
8748        WHERE type NOT IN ('progress', 'log')
8749    ),
8750    -- For each table from source, statistics are reported as itself
8751    table_refl AS
8752    (
8753        SELECT id, id AS report_id
8754        FROM mz_catalog.mz_tables
8755        WHERE source_id IS NOT NULL
8756    ),
8757    report_paths AS
8758    (
8759        SELECT id, report_id FROM subsource_to_parent
8760        UNION ALL SELECT id, report_id FROM table_to_parent
8761        UNION ALL SELECT id, report_id FROM source_refl
8762        UNION ALL SELECT id, report_id FROM table_refl
8763    )
8764SELECT
8765    report_paths.report_id AS id,
8766    replica_id,
8767    -- Counters
8768    SUM(messages_received)::uint8 AS messages_received,
8769    SUM(bytes_received)::uint8 AS bytes_received,
8770    SUM(updates_staged)::uint8 AS updates_staged,
8771    SUM(updates_committed)::uint8 AS updates_committed,
8772    -- Resetting Gauges
8773    SUM(records_indexed)::uint8 AS records_indexed,
8774    SUM(bytes_indexed)::uint8 AS bytes_indexed,
8775    -- Ensure we aggregate to NULL when not all workers are done rehydrating.
8776    CASE
8777        WHEN bool_or(rehydration_latency IS NULL) THEN NULL
8778        ELSE MAX(rehydration_latency)::interval
8779    END AS rehydration_latency,
8780    SUM(snapshot_records_known)::uint8 AS snapshot_records_known,
8781    SUM(snapshot_records_staged)::uint8 AS snapshot_records_staged,
8782    bool_and(snapshot_committed) as snapshot_committed,
8783    -- Gauges
8784    MAX(offset_known)::uint8 AS offset_known,
8785    MIN(offset_committed)::uint8 AS offset_committed
8786FROM mz_internal.mz_source_statistics_raw
8787    JOIN report_paths USING (id)
8788GROUP BY report_paths.report_id, replica_id",
8789        access: vec![PUBLIC_SELECT],
8790        ontology: None,
8791    });
8792
8793pub const MZ_SOURCE_STATISTICS_WITH_HISTORY_IND: BuiltinIndex = BuiltinIndex {
8794    name: "mz_source_statistics_with_history_ind",
8795    schema: MZ_INTERNAL_SCHEMA,
8796    oid: oid::INDEX_MZ_SOURCE_STATISTICS_WITH_HISTORY_IND_OID,
8797    sql: "IN CLUSTER mz_catalog_server
8798ON mz_internal.mz_source_statistics_with_history (id, replica_id)",
8799    is_retained_metrics_object: true,
8800};
8801
8802// The non historical version of MZ_SOURCE_STATISTICS_WITH_HISTORY.
8803// Used to query MZ_SOURCE_STATISTICS at the current time.
8804pub static MZ_SOURCE_STATISTICS: LazyLock<BuiltinView> = LazyLock::new(|| {
8805    BuiltinView {
8806        name: "mz_source_statistics",
8807        schema: MZ_INTERNAL_SCHEMA,
8808        oid: oid::VIEW_MZ_SOURCE_STATISTICS_OID,
8809        // We need to add a redundant where clause for a new dataflow to be created.
8810        desc: RelationDesc::builder()
8811            .with_column("id", SqlScalarType::String.nullable(false))
8812            .with_column("replica_id", SqlScalarType::String.nullable(true))
8813            .with_column("messages_received", SqlScalarType::UInt64.nullable(false))
8814            .with_column("bytes_received", SqlScalarType::UInt64.nullable(false))
8815            .with_column("updates_staged", SqlScalarType::UInt64.nullable(false))
8816            .with_column("updates_committed", SqlScalarType::UInt64.nullable(false))
8817            .with_column("records_indexed", SqlScalarType::UInt64.nullable(false))
8818            .with_column("bytes_indexed", SqlScalarType::UInt64.nullable(false))
8819            .with_column(
8820                "rehydration_latency",
8821                SqlScalarType::Interval.nullable(true),
8822            )
8823            .with_column(
8824                "snapshot_records_known",
8825                SqlScalarType::UInt64.nullable(true),
8826            )
8827            .with_column(
8828                "snapshot_records_staged",
8829                SqlScalarType::UInt64.nullable(true),
8830            )
8831            .with_column("snapshot_committed", SqlScalarType::Bool.nullable(false))
8832            .with_column("offset_known", SqlScalarType::UInt64.nullable(true))
8833            .with_column("offset_committed", SqlScalarType::UInt64.nullable(true))
8834            .with_key(vec![0, 1])
8835            .finish(),
8836        column_comments: BTreeMap::from_iter([
8837            (
8838                "id",
8839                "The ID of the source. Corresponds to `mz_catalog.mz_sources.id`.",
8840            ),
8841            (
8842                "replica_id",
8843                "The ID of a replica running the source. Corresponds to `mz_catalog.mz_cluster_replicas.id`.",
8844            ),
8845            (
8846                "messages_received",
8847                "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.",
8848            ),
8849            (
8850                "bytes_received",
8851                "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.",
8852            ),
8853            (
8854                "updates_staged",
8855                "The number of updates (insertions plus deletions) the source has written but not yet committed to the storage layer.",
8856            ),
8857            (
8858                "updates_committed",
8859                "The number of updates (insertions plus deletions) the source has committed to the storage layer.",
8860            ),
8861            (
8862                "records_indexed",
8863                "The number of individual records indexed in the source envelope state.",
8864            ),
8865            (
8866                "bytes_indexed",
8867                "The number of bytes stored in the source's internal index, if any.",
8868            ),
8869            (
8870                "rehydration_latency",
8871                "The amount of time it took for the source to rehydrate its internal index, if any, after the source last restarted.",
8872            ),
8873            (
8874                "snapshot_records_known",
8875                "The size of the source's snapshot, measured in number of records. See below to learn what constitutes a record.",
8876            ),
8877            (
8878                "snapshot_records_staged",
8879                "The number of records in the source's snapshot that Materialize has read. See below to learn what constitutes a record.",
8880            ),
8881            (
8882                "snapshot_committed",
8883                "Whether the source has committed the initial snapshot for a source.",
8884            ),
8885            (
8886                "offset_known",
8887                "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.",
8888            ),
8889            (
8890                "offset_committed",
8891                "The offset of the the data that Materialize has durably ingested. See below to learn what constitutes an offset.",
8892            ),
8893        ]),
8894        sql: "SELECT * FROM mz_internal.mz_source_statistics_with_history WHERE length(id) > 0",
8895        access: vec![PUBLIC_SELECT],
8896        ontology: Some(Ontology {
8897            entity_name: "source_statistics",
8898            description: "Aggregated source ingestion statistics",
8899            links: &const {
8900                [OntologyLink {
8901                    name: "statistics_of_source",
8902                    target: "source",
8903                    properties: LinkProperties::measures("id", "id", "ingestion_statistics"),
8904                }]
8905            },
8906            column_semantic_types: &const {
8907                [
8908                    ("id", SemanticType::CatalogItemId),
8909                    ("replica_id", SemanticType::ReplicaId),
8910                    ("messages_received", SemanticType::RecordCount),
8911                    ("bytes_received", SemanticType::ByteCount),
8912                    ("updates_staged", SemanticType::RecordCount),
8913                    ("updates_committed", SemanticType::RecordCount),
8914                    ("records_indexed", SemanticType::RecordCount),
8915                    ("bytes_indexed", SemanticType::ByteCount),
8916                    ("snapshot_records_known", SemanticType::RecordCount),
8917                    ("snapshot_records_staged", SemanticType::RecordCount),
8918                ]
8919            },
8920        }),
8921    }
8922});
8923
8924pub const MZ_SOURCE_STATISTICS_IND: BuiltinIndex = BuiltinIndex {
8925    name: "mz_source_statistics_ind",
8926    schema: MZ_INTERNAL_SCHEMA,
8927    oid: oid::INDEX_MZ_SOURCE_STATISTICS_IND_OID,
8928    sql: "IN CLUSTER mz_catalog_server
8929ON mz_internal.mz_source_statistics (id, replica_id)",
8930    is_retained_metrics_object: false,
8931};
8932
8933pub static MZ_SINK_STATISTICS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
8934    name: "mz_sink_statistics",
8935    schema: MZ_INTERNAL_SCHEMA,
8936    oid: oid::VIEW_MZ_SINK_STATISTICS_OID,
8937    desc: RelationDesc::builder()
8938        .with_column("id", SqlScalarType::String.nullable(false))
8939        .with_column("replica_id", SqlScalarType::String.nullable(true))
8940        .with_column("messages_staged", SqlScalarType::UInt64.nullable(false))
8941        .with_column("messages_committed", SqlScalarType::UInt64.nullable(false))
8942        .with_column("bytes_staged", SqlScalarType::UInt64.nullable(false))
8943        .with_column("bytes_committed", SqlScalarType::UInt64.nullable(false))
8944        .with_key(vec![0, 1])
8945        .finish(),
8946    column_comments: BTreeMap::from_iter([
8947        (
8948            "id",
8949            "The ID of the sink. Corresponds to `mz_catalog.mz_sinks.id`.",
8950        ),
8951        (
8952            "replica_id",
8953            "The ID of a replica running the sink. Corresponds to `mz_catalog.mz_cluster_replicas.id`.",
8954        ),
8955        (
8956            "messages_staged",
8957            "The number of messages staged but possibly not committed to the sink.",
8958        ),
8959        (
8960            "messages_committed",
8961            "The number of messages committed to the sink.",
8962        ),
8963        (
8964            "bytes_staged",
8965            "The number of bytes staged but possibly not committed to the sink. This counts both keys and values, if applicable.",
8966        ),
8967        (
8968            "bytes_committed",
8969            "The number of bytes committed to the sink. This counts both keys and values, if applicable.",
8970        ),
8971    ]),
8972    sql: "
8973SELECT
8974    id,
8975    replica_id,
8976    SUM(messages_staged)::uint8 AS messages_staged,
8977    SUM(messages_committed)::uint8 AS messages_committed,
8978    SUM(bytes_staged)::uint8 AS bytes_staged,
8979    SUM(bytes_committed)::uint8 AS bytes_committed
8980FROM mz_internal.mz_sink_statistics_raw
8981GROUP BY id, replica_id",
8982    access: vec![PUBLIC_SELECT],
8983    ontology: Some(Ontology {
8984        entity_name: "sink_statistics",
8985        description: "Aggregated sink export statistics",
8986        links: &const {
8987            [OntologyLink {
8988                name: "statistics_of_sink",
8989                target: "sink",
8990                properties: LinkProperties::measures("id", "id", "export_statistics"),
8991            }]
8992        },
8993        column_semantic_types: &const {
8994            [
8995                ("id", SemanticType::CatalogItemId),
8996                ("replica_id", SemanticType::ReplicaId),
8997                ("messages_staged", SemanticType::RecordCount),
8998                ("messages_committed", SemanticType::RecordCount),
8999                ("bytes_staged", SemanticType::ByteCount),
9000                ("bytes_committed", SemanticType::ByteCount),
9001            ]
9002        },
9003    }),
9004});
9005
9006pub const MZ_SINK_STATISTICS_IND: BuiltinIndex = BuiltinIndex {
9007    name: "mz_sink_statistics_ind",
9008    schema: MZ_INTERNAL_SCHEMA,
9009    oid: oid::INDEX_MZ_SINK_STATISTICS_IND_OID,
9010    sql: "IN CLUSTER mz_catalog_server
9011ON mz_internal.mz_sink_statistics (id, replica_id)",
9012    is_retained_metrics_object: true,
9013};
9014
9015pub const MZ_CLUSTER_REPLICA_STATUSES_IND: BuiltinIndex = BuiltinIndex {
9016    name: "mz_cluster_replica_statuses_ind",
9017    schema: MZ_INTERNAL_SCHEMA,
9018    oid: oid::INDEX_MZ_CLUSTER_REPLICA_STATUSES_IND_OID,
9019    sql: "IN CLUSTER mz_catalog_server
9020ON mz_internal.mz_cluster_replica_statuses (replica_id)",
9021    is_retained_metrics_object: false,
9022};
9023
9024pub const MZ_CLUSTER_REPLICA_STATUS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9025    name: "mz_cluster_replica_status_history_ind",
9026    schema: MZ_INTERNAL_SCHEMA,
9027    oid: oid::INDEX_MZ_CLUSTER_REPLICA_STATUS_HISTORY_IND_OID,
9028    sql: "IN CLUSTER mz_catalog_server
9029ON mz_internal.mz_cluster_replica_status_history (replica_id)",
9030    is_retained_metrics_object: false,
9031};
9032
9033pub const MZ_CLUSTER_REPLICA_METRICS_IND: BuiltinIndex = BuiltinIndex {
9034    name: "mz_cluster_replica_metrics_ind",
9035    schema: MZ_INTERNAL_SCHEMA,
9036    oid: oid::INDEX_MZ_CLUSTER_REPLICA_METRICS_IND_OID,
9037    sql: "IN CLUSTER mz_catalog_server
9038ON mz_internal.mz_cluster_replica_metrics (replica_id)",
9039    is_retained_metrics_object: false,
9040};
9041
9042pub const MZ_CLUSTER_REPLICA_METRICS_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9043    name: "mz_cluster_replica_metrics_history_ind",
9044    schema: MZ_INTERNAL_SCHEMA,
9045    oid: oid::INDEX_MZ_CLUSTER_REPLICA_METRICS_HISTORY_IND_OID,
9046    sql: "IN CLUSTER mz_catalog_server
9047ON mz_internal.mz_cluster_replica_metrics_history (replica_id)",
9048    is_retained_metrics_object: false,
9049};
9050
9051pub const MZ_CLUSTER_REPLICA_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9052    name: "mz_cluster_replica_history_ind",
9053    schema: MZ_INTERNAL_SCHEMA,
9054    oid: oid::INDEX_MZ_CLUSTER_REPLICA_HISTORY_IND_OID,
9055    sql: "IN CLUSTER mz_catalog_server
9056ON mz_internal.mz_cluster_replica_history (dropped_at)",
9057    is_retained_metrics_object: true,
9058};
9059
9060pub const MZ_CLUSTER_REPLICA_NAME_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9061    name: "mz_cluster_replica_name_history_ind",
9062    schema: MZ_INTERNAL_SCHEMA,
9063    oid: oid::INDEX_MZ_CLUSTER_REPLICA_NAME_HISTORY_IND_OID,
9064    sql: "IN CLUSTER mz_catalog_server
9065ON mz_internal.mz_cluster_replica_name_history (id)",
9066    is_retained_metrics_object: false,
9067};
9068
9069pub const MZ_OBJECT_LIFETIMES_IND: BuiltinIndex = BuiltinIndex {
9070    name: "mz_object_lifetimes_ind",
9071    schema: MZ_INTERNAL_SCHEMA,
9072    oid: oid::INDEX_MZ_OBJECT_LIFETIMES_IND_OID,
9073    sql: "IN CLUSTER mz_catalog_server
9074ON mz_internal.mz_object_lifetimes (id)",
9075    is_retained_metrics_object: false,
9076};
9077
9078pub const MZ_OBJECT_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9079    name: "mz_object_history_ind",
9080    schema: MZ_INTERNAL_SCHEMA,
9081    oid: oid::INDEX_MZ_OBJECT_HISTORY_IND_OID,
9082    sql: "IN CLUSTER mz_catalog_server
9083ON mz_internal.mz_object_history (id)",
9084    is_retained_metrics_object: false,
9085};
9086
9087pub const MZ_OBJECT_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
9088    name: "mz_object_dependencies_ind",
9089    schema: MZ_INTERNAL_SCHEMA,
9090    oid: oid::INDEX_MZ_OBJECT_DEPENDENCIES_IND_OID,
9091    sql: "IN CLUSTER mz_catalog_server
9092ON mz_internal.mz_object_dependencies (object_id)",
9093    is_retained_metrics_object: true,
9094};
9095
9096pub const MZ_COMPUTE_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
9097    name: "mz_compute_dependencies_ind",
9098    schema: MZ_INTERNAL_SCHEMA,
9099    oid: oid::INDEX_MZ_COMPUTE_DEPENDENCIES_IND_OID,
9100    sql: "IN CLUSTER mz_catalog_server
9101ON mz_internal.mz_compute_dependencies (dependency_id)",
9102    is_retained_metrics_object: false,
9103};
9104
9105pub const MZ_OBJECT_TRANSITIVE_DEPENDENCIES_IND: BuiltinIndex = BuiltinIndex {
9106    name: "mz_object_transitive_dependencies_ind",
9107    schema: MZ_INTERNAL_SCHEMA,
9108    oid: oid::INDEX_MZ_OBJECT_TRANSITIVE_DEPENDENCIES_IND_OID,
9109    sql: "IN CLUSTER mz_catalog_server
9110ON mz_internal.mz_object_transitive_dependencies (object_id)",
9111    is_retained_metrics_object: false,
9112};
9113
9114pub const MZ_OBJECT_GRAPH_EDGES_IND: BuiltinIndex = BuiltinIndex {
9115    name: "mz_object_graph_edges_ind",
9116    schema: MZ_INTERNAL_SCHEMA,
9117    oid: oid::INDEX_MZ_OBJECT_GRAPH_EDGES_IND_OID,
9118    sql: "IN CLUSTER mz_catalog_server
9119ON mz_internal.mz_object_graph_edges (object_id)",
9120    is_retained_metrics_object: false,
9121};
9122
9123pub const MZ_FRONTIERS_IND: BuiltinIndex = BuiltinIndex {
9124    name: "mz_frontiers_ind",
9125    schema: MZ_INTERNAL_SCHEMA,
9126    oid: oid::INDEX_MZ_FRONTIERS_IND_OID,
9127    sql: "IN CLUSTER mz_catalog_server
9128ON mz_internal.mz_frontiers (object_id)",
9129    is_retained_metrics_object: false,
9130};
9131
9132pub const MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_IND: BuiltinIndex = BuiltinIndex {
9133    name: "mz_wallclock_global_lag_recent_history_ind",
9134    schema: MZ_INTERNAL_SCHEMA,
9135    oid: oid::INDEX_MZ_WALLCLOCK_GLOBAL_LAG_RECENT_HISTORY_IND_OID,
9136    sql: "IN CLUSTER mz_catalog_server
9137ON mz_internal.mz_wallclock_global_lag_recent_history (object_id)",
9138    is_retained_metrics_object: false,
9139};
9140
9141pub const MZ_RECENT_ACTIVITY_LOG_THINNED_IND: BuiltinIndex = BuiltinIndex {
9142    name: "mz_recent_activity_log_thinned_ind",
9143    schema: MZ_INTERNAL_SCHEMA,
9144    oid: oid::INDEX_MZ_RECENT_ACTIVITY_LOG_THINNED_IND_OID,
9145    sql: "IN CLUSTER mz_catalog_server
9146-- sql_hash because we plan to join
9147-- this against mz_internal.mz_sql_text
9148ON mz_internal.mz_recent_activity_log_thinned (sql_hash)",
9149    is_retained_metrics_object: false,
9150};
9151
9152pub const MZ_WEBHOOK_SOURCES_IND: BuiltinIndex = BuiltinIndex {
9153    name: "mz_webhook_sources_ind",
9154    schema: MZ_INTERNAL_SCHEMA,
9155    oid: oid::INDEX_MZ_WEBHOOK_SOURCES_IND_OID,
9156    sql: "IN CLUSTER mz_catalog_server
9157ON mz_internal.mz_webhook_sources (id)",
9158    is_retained_metrics_object: true,
9159};
9160
9161pub const MZ_COMMENTS_IND: BuiltinIndex = BuiltinIndex {
9162    name: "mz_comments_ind",
9163    schema: MZ_INTERNAL_SCHEMA,
9164    oid: oid::INDEX_MZ_COMMENTS_IND_OID,
9165    sql: "IN CLUSTER mz_catalog_server
9166ON mz_internal.mz_comments (id)",
9167    is_retained_metrics_object: true,
9168};
9169
9170pub static MZ_ANALYTICS: BuiltinConnection = BuiltinConnection {
9171    name: "mz_analytics",
9172    schema: MZ_INTERNAL_SCHEMA,
9173    oid: oid::CONNECTION_MZ_ANALYTICS_OID,
9174    sql: "CREATE CONNECTION mz_internal.mz_analytics TO AWS (ASSUME ROLE ARN = '')",
9175    access: &[MzAclItem {
9176        grantee: MZ_SYSTEM_ROLE_ID,
9177        grantor: MZ_ANALYTICS_ROLE_ID,
9178        acl_mode: rbac::all_object_privileges(SystemObjectType::Object(ObjectType::Connection)),
9179    }],
9180    owner_id: &MZ_ANALYTICS_ROLE_ID,
9181    runtime_alterable: true,
9182};