Skip to main content

mz_catalog/builtin/
mz_internal.rs

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