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