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