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