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