From c2e9873daa4ebcb7dde9f410c1d1cadc51c8aadf Mon Sep 17 00:00:00 2001 From: Sang Jun Bak Date: Mon, 19 Oct 2026 12:55:19 -0400 Subject: [PATCH] draft: Implement type builtin relations --- .../content/sql/system-catalog/mz_internal.md | 1 + .../checks/all_checks/builtin_version_pin.py | 12 +- .../src/catalog/builtin_table_updates.rs | 170 +------- .../catalog/open/builtin_schema_migration.rs | 65 ++- src/catalog-decode/src/create_sql.rs | 157 ++++++- src/catalog/src/builtin.rs | 19 +- src/catalog/src/builtin/builtin.rs | 249 ++++++++++- src/catalog/src/builtin/mz_catalog.rs | 286 ++++++++++--- src/catalog/src/builtin/mz_internal.rs | 65 ++- src/pgrepr-consts/src/oid.rs | 15 +- .../autogenerated/mz_internal.slt | 1 + test/sqllogictest/catalog_server_explain.slt | 18 +- .../information_schema_tables.slt | 18 +- .../mz_catalog_server_index_accounting.slt | 176 ++++---- test/sqllogictest/mz_types.slt | 403 ++++++++++++++++++ test/sqllogictest/mz_views.slt | 1 + test/sqllogictest/oid.slt | 1 + test/testdrive/catalog.td | 17 +- 18 files changed, 1270 insertions(+), 404 deletions(-) create mode 100644 test/sqllogictest/mz_types.slt diff --git a/doc/user/content/sql/system-catalog/mz_internal.md b/doc/user/content/sql/system-catalog/mz_internal.md index 6c26b0d118a46..e91556ffa38c7 100644 --- a/doc/user/content/sql/system-catalog/mz_internal.md +++ b/doc/user/content/sql/system-catalog/mz_internal.md @@ -1637,6 +1637,7 @@ The `mz_webhook_sources` table contains a row for each webhook source in the sys + diff --git a/misc/python/materialize/checks/all_checks/builtin_version_pin.py b/misc/python/materialize/checks/all_checks/builtin_version_pin.py index 407a4cd46deee..16a9273cf6fda 100644 --- a/misc/python/materialize/checks/all_checks/builtin_version_pin.py +++ b/misc/python/materialize/checks/all_checks/builtin_version_pin.py @@ -34,10 +34,10 @@ # dependencies, so conversions there cannot poison customer catalogs. # mz_role_auth is excluded because only mz_system can read it. _STABLE_BUILTINS: dict[str, MzVersion | None] = { - "mz_array_types": None, + "mz_array_types": MzVersion.parse_mz("v26.48.0"), "mz_audit_events": MzVersion.parse_mz("v26.33.0"), "mz_aws_privatelink_connections": MzVersion.parse_mz("v26.37.0"), - "mz_base_types": None, + "mz_base_types": MzVersion.parse_mz("v26.48.0"), "mz_cluster_replica_sizes": None, "mz_cluster_replicas": MzVersion.parse_mz("v26.30.0"), "mz_clusters": MzVersion.parse_mz("v26.30.0"), @@ -50,15 +50,15 @@ "mz_kafka_connections": MzVersion.parse_mz("v26.37.0"), "mz_kafka_sinks": MzVersion.parse_mz("v26.38.0"), "mz_kafka_sources": MzVersion.parse_mz("v26.34.0"), - "mz_list_types": None, - "mz_map_types": None, + "mz_list_types": MzVersion.parse_mz("v26.48.0"), + "mz_map_types": MzVersion.parse_mz("v26.48.0"), "mz_operators": None, - "mz_pseudo_types": None, + "mz_pseudo_types": MzVersion.parse_mz("v26.48.0"), "mz_sinks": MzVersion.parse_mz("v26.38.0"), "mz_ssh_tunnel_connections": MzVersion.parse_mz("v26.37.0"), "mz_system_privileges": MzVersion.parse_mz("v26.30.0"), "mz_tables": MzVersion.parse_mz("v26.39.0"), - "mz_types": None, + "mz_types": MzVersion.parse_mz("v26.48.0"), "mz_views": MzVersion.parse_mz("v26.39.0"), } diff --git a/src/adapter/src/catalog/builtin_table_updates.rs b/src/adapter/src/catalog/builtin_table_updates.rs index 6cdb40587ad33..df3e62a2f0d3e 100644 --- a/src/adapter/src/catalog/builtin_table_updates.rs +++ b/src/adapter/src/catalog/builtin_table_updates.rs @@ -14,15 +14,13 @@ use ipnet::IpNet; use mz_audit_log::VersionedStorageUsage; use mz_catalog::SYSTEM_CONN_ID; use mz_catalog::builtin::{ - BuiltinTable, MZ_AGGREGATES, MZ_ARRAY_TYPES, MZ_BASE_TYPES, MZ_CLUSTER_REPLICA_SIZE_INTERNAL, - MZ_CLUSTER_REPLICA_SIZES, MZ_COLUMNS, MZ_EGRESS_IPS, MZ_FUNCTIONS, MZ_INDEX_COLUMNS, - MZ_LICENSE_KEYS, MZ_LIST_TYPES, MZ_MAP_TYPES, MZ_OPERATORS, MZ_PSEUDO_TYPES, MZ_ROLE_AUTH, - MZ_SESSIONS, MZ_STORAGE_USAGE_BY_SHARD, MZ_SUBSCRIPTIONS, MZ_TYPE_PG_METADATA, MZ_TYPES, - MZ_WEBHOOKS_SOURCES, + BuiltinTable, MZ_AGGREGATES, MZ_CLUSTER_REPLICA_SIZE_INTERNAL, MZ_CLUSTER_REPLICA_SIZES, + MZ_COLUMNS, MZ_EGRESS_IPS, MZ_FUNCTIONS, MZ_INDEX_COLUMNS, MZ_LICENSE_KEYS, MZ_OPERATORS, + MZ_ROLE_AUTH, MZ_SESSIONS, MZ_STORAGE_USAGE_BY_SHARD, MZ_SUBSCRIPTIONS, MZ_WEBHOOKS_SOURCES, }; use mz_catalog::memory::error::Error; use mz_catalog::memory::objects::{ - CatalogItem, DataSourceDesc, Func, Index, Table, TableDataSource, Type, + CatalogItem, DataSourceDesc, Func, Index, Table, TableDataSource, }; use mz_expr::MirScalarExpr; use mz_license_keys::ValidatedLicenseKey; @@ -31,13 +29,9 @@ use mz_ore::cast::CastFrom; use mz_ore::collections::CollectionExt; use mz_persist_client::batch::ProtoBatch; use mz_repr::adt::array::ArrayDimension; -use mz_repr::adt::mz_acl_item::PrivilegeMap; use mz_repr::role_id::RoleId; -use mz_repr::{ - CatalogItemId, Datum, Diff, GlobalId, ReprColumnType, Row, RowPacker, SqlScalarType, -}; +use mz_repr::{CatalogItemId, Datum, Diff, GlobalId, ReprColumnType, Row, SqlScalarType}; use mz_sql::ast::{CreateIndexStatement, Statement}; -use mz_sql::catalog::{CatalogType, TypeCategory}; use mz_sql::func::FuncImplCatalogDetails; use mz_sql::names::SchemaSpecifier; use mz_sql_parser::ast::display::AstDisplay; @@ -127,7 +121,6 @@ impl CatalogState { diff: Diff, ) -> Vec> { let entry = self.get_entry(&id); - let oid = entry.oid(); let conn_id = entry.item().conn_id().unwrap_or(&SYSTEM_CONN_ID); let schema_id = &self .get_schema( @@ -138,8 +131,6 @@ impl CatalogState { .id; let name = &entry.name().item; let owner_id = entry.owner_id(); - let privileges_row = self.pack_privilege_array_row(entry.privileges()); - let privileges = privileges_row.unpack_first(); let mut updates = match entry.item() { CatalogItem::Index(index) => self.pack_index_update(id, index, diff), CatalogItem::Source(source) => { @@ -161,19 +152,17 @@ impl CatalogState { // mz_sinks, mz_kafka_sinks and mz_iceberg_sinks read create_sql // out of mz_catalog_raw, so there is nothing to pack here. CatalogItem::Sink(_) => vec![], - CatalogItem::Type(ty) => { - self.pack_type_update(id, oid, schema_id, name, owner_id, privileges, ty, diff) - } CatalogItem::Func(func) => { self.pack_func_update(id, schema_id, name, owner_id, func, diff) } - // Tables, views, materialized views, and metric sinks are exposed - // through materialized views derived from `mz_catalog_raw`, and - // logs and secrets never had builtin-table rows, so none pack a row - // here. + // Tables, views, materialized views, types, and metric sinks are + // exposed through materialized views derived from `mz_catalog_raw`, + // and logs and secrets never had builtin-table rows, so none pack a + // row here. CatalogItem::Table(_) | CatalogItem::View(_) | CatalogItem::MaterializedView(_) + | CatalogItem::Type(_) | CatalogItem::Log(_) | CatalogItem::Secret(_) | CatalogItem::MetricSink(_) => vec![], @@ -318,128 +307,6 @@ impl CatalogState { updates } - fn pack_type_update( - &self, - id: CatalogItemId, - oid: u32, - schema_id: &SchemaSpecifier, - name: &str, - owner_id: &RoleId, - privileges: Datum, - typ: &Type, - diff: Diff, - ) -> Vec> { - let mut out = vec![]; - - let redacted = typ.create_sql.as_ref().map(|create_sql| { - mz_sql::parse::parse(create_sql) - .unwrap_or_else(|_| panic!("create_sql cannot be invalid: {}", create_sql)) - .into_element() - .ast - .to_ast_string_redacted() - }); - - out.push(BuiltinTableUpdate::row( - &*MZ_TYPES, - Row::pack_slice(&[ - Datum::String(&id.to_string()), - Datum::UInt32(oid), - Datum::String(&schema_id.to_string()), - Datum::String(name), - Datum::String(&TypeCategory::from_catalog_type(&typ.details.typ).to_string()), - Datum::String(&owner_id.to_string()), - privileges, - if let Some(create_sql) = &typ.create_sql { - Datum::String(create_sql) - } else { - Datum::Null - }, - if let Some(redacted) = &redacted { - Datum::String(redacted) - } else { - Datum::Null - }, - ]), - diff, - )); - - let mut row = Row::default(); - let mut packer = row.packer(); - - fn append_modifier(packer: &mut RowPacker<'_>, mods: &[i64]) { - if mods.is_empty() { - packer.push(Datum::Null); - } else { - packer.push_list(mods.iter().map(|m| Datum::Int64(*m))); - } - } - - let index_id = match &typ.details.typ { - CatalogType::Array { - element_reference: element_id, - } => { - packer.push(Datum::String(&id.to_string())); - packer.push(Datum::String(&element_id.to_string())); - &MZ_ARRAY_TYPES - } - CatalogType::List { - element_reference: element_id, - element_modifiers, - } => { - packer.push(Datum::String(&id.to_string())); - packer.push(Datum::String(&element_id.to_string())); - append_modifier(&mut packer, element_modifiers); - &MZ_LIST_TYPES - } - CatalogType::Map { - key_reference: key_id, - value_reference: value_id, - key_modifiers, - value_modifiers, - } => { - packer.push(Datum::String(&id.to_string())); - packer.push(Datum::String(&key_id.to_string())); - packer.push(Datum::String(&value_id.to_string())); - append_modifier(&mut packer, key_modifiers); - append_modifier(&mut packer, value_modifiers); - &MZ_MAP_TYPES - } - CatalogType::Pseudo => { - packer.push(Datum::String(&id.to_string())); - &MZ_PSEUDO_TYPES - } - _ => { - packer.push(Datum::String(&id.to_string())); - &MZ_BASE_TYPES - } - }; - out.push(BuiltinTableUpdate::row(index_id, row, diff)); - - if let Some(pg_metadata) = &typ.details.pg_metadata { - // Resolved the same way as a column's wire type, so this matches the - // `type_len` pgwire sends in `RowDescription`. Pseudo-types and arrays - // of them (`_record`) have no scalar type, so their length is unknown. - let typlen = - match mz_sql::plan::scalar_type_from_catalog(&self.for_system_session(), id, &[]) { - Ok(scalar_type) => Datum::Int16(mz_pgrepr::Type::from(&scalar_type).typlen()), - Err(_) => Datum::Null, - }; - out.push(BuiltinTableUpdate::row( - &*MZ_TYPE_PG_METADATA, - Row::pack_slice(&[ - Datum::String(&id.to_string()), - Datum::UInt32(pg_metadata.typinput_oid), - Datum::UInt32(pg_metadata.typreceive_oid), - Datum::UInt32(pg_metadata.typsend_oid), - typlen, - ]), - diff, - )); - } - - out - } - fn pack_func_update( &self, id: CatalogItemId, @@ -710,23 +577,6 @@ impl CatalogState { ) } - fn pack_privilege_array_row(&self, privileges: &PrivilegeMap) -> Row { - let mut row = Row::default(); - let flat_privileges: Vec<_> = privileges.all_values_owned().collect(); - row.packer() - .try_push_array( - &[ArrayDimension { - lower_bound: 1, - length: flat_privileges.len(), - }], - flat_privileges - .into_iter() - .map(|mz_acl_item| Datum::MzAclItem(mz_acl_item.clone())), - ) - .expect("privileges is 1 dimensional, and its length is used for the array length"); - row - } - pub fn pack_webhook_source_update( &self, item_id: CatalogItemId, diff --git a/src/adapter/src/catalog/open/builtin_schema_migration.rs b/src/adapter/src/catalog/open/builtin_schema_migration.rs index 757bb0345ba0d..15549b6424b84 100644 --- a/src/adapter/src/catalog/open/builtin_schema_migration.rs +++ b/src/adapter/src/catalog/open/builtin_schema_migration.rs @@ -325,24 +325,6 @@ static MIGRATIONS: LazyLock> = LazyLock::new(|| { MZ_INTERNAL_SCHEMA, "mz_kafka_source_tables", ), - // `mz_type_pg_metadata` gained a trailing `typsend` column. See the NOTE - // above: this version must stay at the workspace's current dev version - // until the change ships. - MigrationStep::replacement( - "26.40.0-dev.0", - CatalogItemType::Table, - MZ_INTERNAL_SCHEMA, - "mz_type_pg_metadata", - ), - // `mz_type_pg_metadata` gained a trailing `typlen` column. See the NOTE - // above: this version must stay at the workspace's current dev version - // until the change ships. - MigrationStep::replacement( - "26.47.0-dev.0", - CatalogItemType::Table, - MZ_INTERNAL_SCHEMA, - "mz_type_pg_metadata", - ), // Converting the connection-detail builtin tables to materialized views // changes their catalog fingerprint, so each needs an explicit // replacement step. @@ -552,6 +534,53 @@ static MIGRATIONS: LazyLock> = LazyLock::new(|| { MZ_INTERNAL_SCHEMA, "mz_materialized_view_refresh_strategies", ), + // Converting the type relations from builtin tables to materialized + // views over mz_catalog_raw changes their catalog fingerprint, so each + // needs an explicit replacement step. See the NOTE above: this version + // must stay at the workspace's current dev version until the change + // ships. + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_array_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_base_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_list_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_map_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_pseudo_types", + ), + MigrationStep::replacement( + "26.48.0-dev.0", + CatalogItemType::MaterializedView, + MZ_INTERNAL_SCHEMA, + "mz_type_pg_metadata", + ), ] }); diff --git a/src/catalog-decode/src/create_sql.rs b/src/catalog-decode/src/create_sql.rs index 083486f32d71f..07646494bb8a7 100644 --- a/src/catalog-decode/src/create_sql.rs +++ b/src/catalog-decode/src/create_sql.rs @@ -21,13 +21,13 @@ use mz_sql_parser::ast::display::AstDisplay; use mz_sql_parser::ast::item_refs::collect_item_references; use mz_sql_parser::ast::{ AstInfo, AvroSchema, ConnectionOption, ConnectionOptionName, CreateConnectionType, - CreateSinkConnection, CreateSourceOptionName, CreateSubsourceOptionName, Format, - FormatSpecifier, IcebergSinkConfigOptionName, IcebergSinkMode, IndexOptionName, - KafkaSinkConfigOptionName, KafkaSourceConfigOptionName, MaterializedViewOptionName, - PgConfigOptionName, ProtobufSchema, Raw, RawClusterName, RawItemName, RefreshAtOptionValue, - RefreshEveryOptionValue, RefreshOptionValue, SinkEnvelope, SourceEnvelope, SourceErrorPolicy, - Statement, TableFromSourceOptionName, TableOptionName, UnresolvedItemName, Value, - WithOptionValue, + CreateSinkConnection, CreateSourceOptionName, CreateSubsourceOptionName, CreateTypeAs, + CreateTypeListOptionName, CreateTypeMapOptionName, Format, FormatSpecifier, + IcebergSinkConfigOptionName, IcebergSinkMode, IndexOptionName, KafkaSinkConfigOptionName, + KafkaSourceConfigOptionName, MaterializedViewOptionName, PgConfigOptionName, ProtobufSchema, + Raw, RawClusterName, RawDataType, RawItemName, RefreshAtOptionValue, RefreshEveryOptionValue, + RefreshOptionValue, SinkEnvelope, SourceEnvelope, SourceErrorPolicy, Statement, + TableFromSourceOptionName, TableOptionName, UnresolvedItemName, Value, WithOptionValue, }; use prost::Message as _; use serde_json::json; @@ -143,6 +143,44 @@ where Ok(()) } +/// The item id and type modifiers of the data type a `CREATE TYPE` option names. +/// +/// `plan_create_type` only accepts named types for list elements, map keys and +/// values, and record fields, so a persisted statement always carries a +/// `RawDataType::Other` whose name is an item id. +fn type_option( + value: Option>, + option: &str, +) -> Result<(String, Vec), String> { + match value { + Some(WithOptionValue::DataType(RawDataType::Other { name, typ_mod })) => { + Ok((item_id(name)?, typ_mod)) + } + _ => Err(format!("{option} is not a named data type")), + } +} + +/// Records a type reference as `id_key`, plus `modifiers_key` when the reference +/// carries type modifiers. Modifiers are rendered as a `bigint list` literal +/// (`{38,2}`) for the views to cast, and an absent key is how `mz_list_types` +/// and `mz_map_types` report a reference without modifiers as NULL. +fn insert_type_reference( + info: &mut BTreeMap<&str, serde_json::Value>, + id_key: &'static str, + modifiers_key: &'static str, + (id, modifiers): (String, Vec), +) { + info.insert(id_key, json!(id)); + if !modifiers.is_empty() { + let modifiers = modifiers + .iter() + .map(|m| m.to_string()) + .collect::>() + .join(","); + info.insert(modifiers_key, json!(format!("{{{modifiers}}}"))); + } +} + /// Records the `REFRESH` options among `options` as `refresh` in `info`, one entry per option in /// statement order. fn insert_refresh_strategies<'a, T: AstInfo + 'a>( @@ -526,7 +564,41 @@ pub fn item_details(a: &str) -> Result { )?; "index" } - CreateType(_) => "type", + // Everything mz_types, mz_base_types, mz_list_types and mz_map_types read. + CreateType(stmt) => { + let (type_kind, category) = match stmt.as_type { + CreateTypeAs::List { options } => { + let element = options + .into_iter() + .find(|o| o.name == CreateTypeListOptionName::ElementType) + .and_then(|o| o.value); + let element = type_option(element, "ELEMENT TYPE")?; + insert_type_reference(&mut info, "element_id", "element_modifiers", element); + ("list", "list") + } + CreateTypeAs::Map { options } => { + let mut key = None; + let mut value = None; + for option in options { + match option.name { + CreateTypeMapOptionName::KeyType => key = option.value, + CreateTypeMapOptionName::ValueType => value = option.value, + } + } + let key = type_option(key, "KEY TYPE")?; + let value = type_option(value, "VALUE TYPE")?; + insert_type_reference(&mut info, "key_id", "key_modifiers", key); + insert_type_reference(&mut info, "value_id", "value_modifiers", value); + ("map", "pseudo") + } + CreateTypeAs::Record { .. } => ("record", "composite"), + }; + info.insert("type_kind", json!(type_kind)); + // Mirrors `TypeCategory::from_catalog_type` in `mz_sql` for the three + // kinds `CREATE TYPE` can produce. + info.insert("category", json!(category)); + "type" + } // NOTE: every statement that creates a catalog item needs an arm above. These // catalog views run this over every item row before their type filter drops the // unwanted rows, so one unclassified `create_sql` takes out `mz_objects`, @@ -2265,4 +2337,73 @@ mod tests { let out = super::item_details(mv_sql_with_replacement).expect("ok"); assert_eq!(out["replacement_target"], json!("u7")); } + + // --- parse_catalog_create_sql, CreateType arm ---------------------------- + + #[mz_ore::test] + fn catalog_type_list() { + let sql = "CREATE TYPE \"materialize\".\"public\".\"int4_list\" \ + AS LIST (ELEMENT TYPE = [s20 AS \"pg_catalog\".\"int4\"])"; + let out = super::item_details(sql).expect("ok"); + assert_eq!( + out, + json!({ + "type": "type", + "type_kind": "list", + "category": "list", + "element_id": "s20", + }) + ); + } + + #[mz_ore::test] + fn catalog_type_list_with_modifiers() { + let sql = "CREATE TYPE \"materialize\".\"public\".\"nums\" \ + AS LIST (ELEMENT TYPE = [s27 AS \"pg_catalog\".\"numeric\"](38, 2))"; + let out = super::item_details(sql).expect("ok"); + assert_eq!(out["element_id"], json!("s27")); + assert_eq!(out["element_modifiers"], json!("{38,2}")); + } + + #[mz_ore::test] + fn catalog_type_map() { + let sql = "CREATE TYPE \"materialize\".\"public\".\"m\" \ + AS MAP (KEY TYPE = [s46 AS \"pg_catalog\".\"text\"], \ + VALUE TYPE = [s27 AS \"pg_catalog\".\"numeric\"](5, 2))"; + let out = super::item_details(sql).expect("ok"); + assert_eq!( + out, + json!({ + "type": "type", + "type_kind": "map", + "category": "pseudo", + "key_id": "s46", + "value_id": "s27", + "value_modifiers": "{5,2}", + }) + ); + } + + #[mz_ore::test] + fn catalog_type_record() { + let sql = "CREATE TYPE \"materialize\".\"public\".\"y1\" \ + AS (\"id\" [s20 AS \"pg_catalog\".\"int4\"], \ + \"name\" [s46 AS \"pg_catalog\".\"text\"])"; + let out = super::item_details(sql).expect("ok"); + assert_eq!( + out, + json!({ + "type": "type", + "type_kind": "record", + "category": "composite", + }) + ); + } + + #[mz_ore::test] + fn catalog_type_unresolved_element_is_an_error() { + let sql = "CREATE TYPE \"materialize\".\"public\".\"l\" AS LIST (ELEMENT TYPE = int4)"; + let err = super::item_details(sql).unwrap_err(); + assert!(err.contains("unresolved item name"), "{err}"); + } } diff --git a/src/catalog/src/builtin.rs b/src/catalog/src/builtin.rs index 437ae11ec27d4..3b6f5b4c570af 100644 --- a/src/catalog/src/builtin.rs +++ b/src/catalog/src/builtin.rs @@ -1205,17 +1205,17 @@ pub static BUILTINS_STATIC: LazyLock>> = LazyLock::ne Builtin::MaterializedView(&MZ_KAFKA_SOURCE_TABLES), Builtin::MaterializedView(&MZ_SINKS), Builtin::MaterializedView(&MZ_VIEWS), - Builtin::Table(&MZ_TYPES), - Builtin::Table(&MZ_TYPE_PG_METADATA), - Builtin::Table(&MZ_ARRAY_TYPES), - Builtin::Table(&MZ_BASE_TYPES), - Builtin::Table(&MZ_LIST_TYPES), - Builtin::Table(&MZ_MAP_TYPES), + Builtin::MaterializedView(&MZ_TYPES), + Builtin::MaterializedView(&MZ_TYPE_PG_METADATA), + Builtin::MaterializedView(&MZ_ARRAY_TYPES), + Builtin::MaterializedView(&MZ_BASE_TYPES), + Builtin::MaterializedView(&MZ_LIST_TYPES), + Builtin::MaterializedView(&MZ_MAP_TYPES), Builtin::MaterializedView(&MZ_ROLES), Builtin::Table(&MZ_ROLE_AUTH), Builtin::MaterializedView(&MZ_ROLE_MEMBERS), Builtin::MaterializedView(&MZ_ROLE_PARAMETERS), - Builtin::Table(&MZ_PSEUDO_TYPES), + Builtin::MaterializedView(&MZ_PSEUDO_TYPES), Builtin::Table(&MZ_FUNCTIONS), Builtin::Table(&MZ_OPERATORS), Builtin::Table(&MZ_AGGREGATES), @@ -1561,7 +1561,8 @@ pub static BUILTINS_STATIC: LazyLock>> = LazyLock::ne // themselves. The real ones are built below. { let ontology_preview = ontology::generate_views(&builtin_items); - let builtin_reporter_preview: Vec<_> = builtin::builtins(&builtin_items).collect(); + let builtin_reporter_preview: Vec<_> = + builtin::builtins(&builtin_types, &builtin_items).collect(); let generator_input: Vec<_> = builtin_types .iter() .chain(builtin_funcs.iter()) @@ -1588,7 +1589,7 @@ pub static BUILTINS_STATIC: LazyLock>> = LazyLock::ne // Generate builtin relations reporting builtin objects last, since they need a complete view // of all other builtins. - let mut builtin_builtins = builtin::builtins(&builtin_items).collect(); + let mut builtin_builtins = builtin::builtins(&builtin_types, &builtin_items).collect(); // Construct the full list of builtins, retaining dependency order. let mut builtins = Vec::new(); diff --git a/src/catalog/src/builtin/builtin.rs b/src/catalog/src/builtin/builtin.rs index b2662c03e4a33..62a3f66f0e25e 100644 --- a/src/catalog/src/builtin/builtin.rs +++ b/src/catalog/src/builtin/builtin.rs @@ -9,26 +9,31 @@ //! Constant builtin views exposing information about builtin objects. +use std::collections::BTreeMap; + use itertools::Itertools; use mz_catalog_protos::objects::CatalogItemType; use mz_ore::collections::CollectionExt; use mz_ore::iter::IteratorExt; use mz_pgrepr::oid; +use mz_repr::adt::char::CharLength; use mz_repr::adt::mz_acl_item::MzAclItem; use mz_repr::namespaces::MZ_INTERNAL_SCHEMA; use mz_repr::{RelationDesc, SqlScalarType}; use mz_sql::ast::Statement; use mz_sql::ast::display::{AstDisplay, escaped_string_literal}; -use mz_sql::catalog::{NameReference, ObjectType}; +use mz_sql::catalog::{CatalogType, NameReference, ObjectType, TypeCategory}; use mz_sql::rbac; use mz_sql::session::user::MZ_SYSTEM_ROLE_ID; -use crate::builtin::{Builtin, BuiltinView, PUBLIC_SELECT, assert_safe_builtin_name}; +use crate::builtin::{Builtin, BuiltinType, BuiltinView, PUBLIC_SELECT, assert_safe_builtin_name}; /// Generate builtin views reporting the given builtins. /// -/// Used in the [`super::BUILTINS_STATIC`] initializer. +/// Used in the [`super::BUILTINS_STATIC`] initializer, which keeps the +/// `Builtin::Type`s in `builtin_types` apart from the other `builtin_items`. pub(super) fn builtins( + builtin_types: &[Builtin], builtin_items: &[Builtin], ) -> impl Iterator> { let sources: &'static BuiltinView = Box::leak(Box::new(make_builtin_sources(builtin_items))); @@ -38,13 +43,21 @@ pub(super) fn builtins( let indexes: &'static BuiltinView = Box::leak(Box::new(make_builtin_indexes(builtin_items))); let log_indexes: &'static BuiltinView = Box::leak(Box::new(make_builtin_log_indexes(builtin_items))); + let types: &'static BuiltinView = Box::leak(Box::new(make_builtin_types(builtin_types))); // The generated views above, and `mz_builtin_views` itself, are listed in // `mz_builtin_views` with placeholder SQL rather than their real // definitions. See `make_builtin_views`. let views: &'static BuiltinView = Box::leak(Box::new(make_builtin_views( builtin_items, - [log_indexes, sources, materialized_views, tables, indexes], + [ + log_indexes, + sources, + materialized_views, + tables, + indexes, + types, + ], ))); // Creation order: `mz_builtin_sources` reads `mz_builtin_log_indexes`, so @@ -55,6 +68,7 @@ pub(super) fn builtins( materialized_views, tables, indexes, + types, views, ] .into_iter() @@ -405,6 +419,231 @@ FROM (VALUES {values}) AS v(oid, schema_name, name, col_list, privileges)" } } +/// Generates `mz_internal.mz_builtin_types`, which `mz_catalog.mz_types`, the +/// per-kind `mz_catalog.mz_*_types` views and `mz_internal.mz_type_pg_metadata` +/// read to report builtin types. [`builtin_types_ctes`] is how they join it to +/// catalog ids. +/// +/// `type_kind` names the per-kind view a type belongs to: `array`, `pseudo`, +/// or `base` for everything else, range types included. `element_schema_name` +/// and `element_name` name an array type's element type. `typlen` is NULL for +/// a type with no scalar type, see [`builtin_scalar_type`]. +fn make_builtin_types(builtin_types: &[Builtin]) -> BuiltinView { + let types: Vec<&BuiltinType> = builtin_types + .iter() + .filter_map(|b| match b { + Builtin::Type(t) => Some(*t), + _ => None, + }) + .collect(); + let by_name: BTreeMap<&str, &BuiltinType> = + types.iter().map(|t| (t.name, *t)).collect(); + // The privileges `apply_system_object_mapping_update` grants every builtin + // type. + let privileges = make_privileges_sql( + &[rbac::default_builtin_object_privilege(ObjectType::Type)], + &rbac::owner_privilege(ObjectType::Type, MZ_SYSTEM_ROLE_ID), + ); + let values = types + .iter() + .map(|t| { + assert_safe_builtin_name(t.name, "type"); + let (type_kind, element) = match &t.details.typ { + CatalogType::Array { element_reference } => ("array", Some(*element_reference)), + CatalogType::Pseudo => ("pseudo", None), + // `mz_list_types` and `mz_map_types` read only user-defined + // types, so a builtin list or map would go unreported. + CatalogType::List { .. } | CatalogType::Map { .. } => panic!( + "builtin type {} is a list or map, which mz_builtin_types cannot report", + t.name + ), + _ => ("base", None), + }; + let (element_schema_name, element_name) = match element { + Some(name) => { + let element = by_name.get(name).unwrap_or_else(|| { + panic!( + "builtin type {} references unknown builtin type {name}", + t.name + ) + }); + ( + escaped_string_literal(element.schema).to_string(), + escaped_string_literal(name).to_string(), + ) + } + None => ("NULL::text".to_string(), "NULL::text".to_string()), + }; + let category = TypeCategory::from_catalog_type(&t.details.typ).to_string(); + let (typinput, typreceive, typsend) = match &t.details.pg_metadata { + Some(pg) => ( + format!("{}::oid", pg.typinput_oid), + format!("{}::oid", pg.typreceive_oid), + format!("{}::oid", pg.typsend_oid), + ), + None => ( + "NULL::oid".to_string(), + "NULL::oid".to_string(), + "NULL::oid".to_string(), + ), + }; + let typlen = match builtin_scalar_type(&t.details.typ, &by_name) { + Some(scalar_type) => { + format!("{}::int2", mz_pgrepr::Type::from(&scalar_type).typlen()) + } + None => "NULL::int2".to_string(), + }; + format!( + "({}::oid, {}, {}, {}, '{type_kind}', {privileges}, {element_schema_name}, {element_name}, {typinput}, {typreceive}, {typsend}, {typlen})", + t.oid, + escaped_string_literal(t.schema), + escaped_string_literal(t.name), + escaped_string_literal(&category), + ) + }) + .join(","); + let sql = format!( + " +SELECT oid, schema_name, name, category, type_kind, privileges, element_schema_name, element_name, typinput, typreceive, typsend, typlen +FROM (VALUES {values}) AS v(oid, schema_name, name, category, type_kind, privileges, element_schema_name, element_name, typinput, typreceive, typsend, typlen)" + ); + + BuiltinView { + name: "mz_builtin_types", + schema: MZ_INTERNAL_SCHEMA, + oid: oid::VIEW_MZ_BUILTIN_TYPES_OID, + desc: RelationDesc::builder() + .with_column("oid", SqlScalarType::Oid.nullable(false)) + .with_column("schema_name", SqlScalarType::String.nullable(false)) + .with_column("name", SqlScalarType::String.nullable(false)) + .with_column("category", SqlScalarType::String.nullable(false)) + .with_column("type_kind", SqlScalarType::String.nullable(false)) + .with_column( + "privileges", + SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false), + ) + .with_column("element_schema_name", SqlScalarType::String.nullable(true)) + .with_column("element_name", SqlScalarType::String.nullable(true)) + .with_column("typinput", SqlScalarType::Oid.nullable(true)) + .with_column("typreceive", SqlScalarType::Oid.nullable(true)) + .with_column("typsend", SqlScalarType::Oid.nullable(true)) + .with_column("typlen", SqlScalarType::Int16.nullable(true)) + // NOTE: The declared keys must exactly match the keys the + // optimizer derives from the generated VALUES list + // (`verify_builtin_descs` enforces this). Builtin type names are + // unique across schemas, which `test_builtins_static_dependency_order` + // asserts, so `name` is a key. + .with_key(vec![0]) + .with_key(vec![2]) + .finish(), + column_comments: Default::default(), + sql: Box::leak(sql.into_boxed_str()), + access: vec![PUBLIC_SELECT], + ontology: None, + } +} + +/// The CTEs through which the type views reach builtin types: `builtin_mappings` +/// maps a builtin type's schema and name to its catalog id via its `GidMapping` +/// record, and `builtin_types` is `mz_builtin_types` extended with that id. +pub(super) fn builtin_types_ctes() -> String { + let object_type = gid_mapping_object_type(CatalogItemType::Type); + format!( + "builtin_mappings AS ( + SELECT + data->'key'->>'schema_name' AS schema_name, + data->'key'->>'object_name' AS name, + 's' || (data->'value'->>'catalog_id') AS id + FROM mz_internal.mz_catalog_raw + WHERE + data->>'kind' = 'GidMapping' AND + data->'key'->>'object_type' = {object_type} + ), + builtin_types AS ( + SELECT + m.id, + t.oid, + t.schema_name, + t.name, + t.category, + t.type_kind, + t.privileges, + t.element_schema_name, + t.element_name, + t.typinput, + t.typreceive, + t.typsend, + t.typlen + FROM mz_internal.mz_builtin_types t + JOIN builtin_mappings m USING (schema_name, name) + )" + ) +} + +/// The `SqlScalarType` of a column of the builtin type `typ`, with element +/// references resolved through `by_name`. Mirrors `scalar_type_from_catalog` +/// for the modifier-free builtin types, so that `mz_pgrepr::Type::from` on the +/// result yields the `typlen` pgwire sends for the type. `None` for +/// pseudo-types and containers of them, which have no scalar type. +fn builtin_scalar_type( + typ: &CatalogType, + by_name: &BTreeMap<&str, &BuiltinType>, +) -> Option { + let resolve = |name: &str| { + let element = by_name + .get(name) + .unwrap_or_else(|| panic!("builtin type references unknown builtin type {name}")); + builtin_scalar_type(&element.details.typ, by_name) + }; + Some(match typ { + CatalogType::Pseudo => return None, + CatalogType::Array { element_reference } => { + SqlScalarType::Array(Box::new(resolve(element_reference)?)) + } + CatalogType::Range { element_reference } => SqlScalarType::Range { + element_type: Box::new(resolve(element_reference)?), + }, + // These scalar types carry the catalog id of their defining type, which + // a builtin type does not have statically. + CatalogType::List { .. } | CatalogType::Map { .. } | CatalogType::Record { .. } => { + panic!("builtin types cannot be lists, maps or records") + } + CatalogType::AclItem => SqlScalarType::AclItem, + CatalogType::Bool => SqlScalarType::Bool, + CatalogType::Bytes => SqlScalarType::Bytes, + CatalogType::Char => SqlScalarType::Char { + length: Some(CharLength::ONE), + }, + CatalogType::Date => SqlScalarType::Date, + CatalogType::Float32 => SqlScalarType::Float32, + CatalogType::Float64 => SqlScalarType::Float64, + CatalogType::Int16 => SqlScalarType::Int16, + CatalogType::Int32 => SqlScalarType::Int32, + CatalogType::Int64 => SqlScalarType::Int64, + CatalogType::UInt16 => SqlScalarType::UInt16, + CatalogType::UInt32 => SqlScalarType::UInt32, + CatalogType::UInt64 => SqlScalarType::UInt64, + CatalogType::MzTimestamp => SqlScalarType::MzTimestamp, + CatalogType::Interval => SqlScalarType::Interval, + CatalogType::Jsonb => SqlScalarType::Jsonb, + CatalogType::Numeric => SqlScalarType::Numeric { max_scale: None }, + CatalogType::Oid => SqlScalarType::Oid, + CatalogType::PgLegacyChar => SqlScalarType::PgLegacyChar, + CatalogType::PgLegacyName => SqlScalarType::PgLegacyName, + CatalogType::RegClass => SqlScalarType::RegClass, + CatalogType::RegProc => SqlScalarType::RegProc, + CatalogType::RegType => SqlScalarType::RegType, + CatalogType::String => SqlScalarType::String, + CatalogType::Time => SqlScalarType::Time, + CatalogType::Timestamp => SqlScalarType::Timestamp { precision: None }, + CatalogType::TimestampTz => SqlScalarType::TimestampTz { precision: None }, + CatalogType::Uuid => SqlScalarType::Uuid, + CatalogType::VarChar => SqlScalarType::VarChar { max_length: None }, + CatalogType::Int2Vector => SqlScalarType::Int2Vector, + CatalogType::MzAclItem => SqlScalarType::MzAclItem, + }) +} + /// Generates `mz_internal.mz_builtin_views`, listing every builtin view, /// including itself and the `generated` views. /// @@ -425,7 +664,7 @@ FROM (VALUES {values}) AS v(oid, schema_name, name, col_list, privileges)" /// declared keys rely on. fn make_builtin_views( builtin_items: &[Builtin], - generated: [&BuiltinView; 5], + generated: [&BuiltinView; 6], ) -> BuiltinView { let iter = builtin_items.iter().filter_map(|b| match b { Builtin::View(x) => Some(*x), diff --git a/src/catalog/src/builtin/mz_catalog.rs b/src/catalog/src/builtin/mz_catalog.rs index 675132b968591..00eee4fe9097e 100644 --- a/src/catalog/src/builtin/mz_catalog.rs +++ b/src/catalog/src/builtin/mz_catalog.rs @@ -1880,10 +1880,11 @@ SELECT * FROM builtin_mvs").into_boxed_str()), }), } }); -pub static MZ_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_TYPES: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_types", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_TYPES_OID, + oid: oid::MV_MZ_TYPES_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .with_column("oid", SqlScalarType::Oid.nullable(false)) @@ -1920,6 +1921,56 @@ pub static MZ_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { "The redacted `CREATE` SQL statement for the type.", ), ]), + // Builtin types are reported through `mz_internal.mz_builtin_types`, a + // generated constant view, and take their ids from their `GidMapping` + // records. See `make_builtin_types`. + sql: Box::leak(format!(" +IN CLUSTER mz_catalog_server +WITH ( + ASSERT NOT NULL id, + ASSERT NOT NULL oid, + ASSERT NOT NULL schema_id, + ASSERT NOT NULL name, + ASSERT NOT NULL category, + ASSERT NOT NULL owner_id, + ASSERT NOT NULL privileges +) AS +WITH + user_types AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + (data->'value'->>'oid')::oid AS oid, + mz_internal.parse_catalog_id(data->'value'->'schema_id') AS schema_id, + data->'value'->>'name' AS name, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'category' AS category, + mz_internal.parse_catalog_id(data->'value'->'owner_id') AS owner_id, + mz_internal.parse_catalog_privileges(data->'value'->'privileges') AS privileges, + data->'value'->'definition'->'V1'->>'create_sql' AS create_sql, + mz_internal.redact_sql(data->'value'->'definition'->'V1'->>'create_sql') AS redacted_create_sql + FROM mz_internal.mz_catalog_raw + WHERE + data->>'kind' = 'Item' AND + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'type' = 'type' + ), + {builtin_ctes}, + builtin_rows AS ( + SELECT + t.id, + t.oid, + s.id AS schema_id, + t.name, + t.category, + '{MZ_SYSTEM_ROLE_ID}' AS owner_id, + t.privileges, + NULL::text AS create_sql, + NULL::text AS redacted_create_sql + FROM builtin_types t + JOIN mz_schemas s ON s.name = t.schema_name + WHERE s.database_id IS NULL + ) +SELECT * FROM user_types +UNION ALL +SELECT * FROM builtin_rows", builtin_ctes = super::builtin::builtin_types_ctes()).into_boxed_str()), is_retained_metrics_object: false, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -1950,54 +2001,94 @@ pub static MZ_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { ] }, }), +} }); -pub static MZ_ARRAY_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { - name: "mz_array_types", - schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_ARRAY_TYPES_OID, - desc: RelationDesc::builder() - .with_column("id", SqlScalarType::String.nullable(false)) - .with_column("element_id", SqlScalarType::String.nullable(false)) - .finish(), - column_comments: BTreeMap::from_iter([ - ("id", "The ID of the array type."), - ("element_id", "The ID of the array's element type."), - ]), - is_retained_metrics_object: false, - access: vec![PUBLIC_SELECT], - ontology: Some(Ontology { - entity_name: "array_type", - description: "An array type with its element type", - links: &const { - [ - OntologyLink { - name: "detail_of", - target: "type", - properties: LinkProperties::fk("id", "id", Cardinality::OneToOne), - }, - OntologyLink { - name: "has_element_type", - target: "type", - properties: LinkProperties::fk("element_id", "id", Cardinality::ManyToOne), - }, - ] - }, - column_semantic_types: &const { - [ - ("id", SemanticType::CatalogItemId), - ("element_id", SemanticType::CatalogItemId), - ] - }, - }), -}); -pub static MZ_BASE_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_ARRAY_TYPES: LazyLock = + LazyLock::new(|| BuiltinMaterializedView { + name: "mz_array_types", + schema: MZ_CATALOG_SCHEMA, + oid: oid::MV_MZ_ARRAY_TYPES_OID, + desc: RelationDesc::builder() + .with_column("id", SqlScalarType::String.nullable(false)) + .with_column("element_id", SqlScalarType::String.nullable(false)) + .finish(), + column_comments: BTreeMap::from_iter([ + ("id", "The ID of the array type."), + ("element_id", "The ID of the array's element type."), + ]), + // `CREATE TYPE` creates list, map and record types (`plan_create_type`), + // so every array type is builtin. + sql: Box::leak( + format!( + " +IN CLUSTER mz_catalog_server +WITH ( + ASSERT NOT NULL id, + ASSERT NOT NULL element_id +) AS +WITH + {builtin_ctes} +SELECT t.id, e.id AS element_id +FROM builtin_types t +JOIN builtin_mappings e ON e.schema_name = t.element_schema_name AND e.name = t.element_name +WHERE t.type_kind = 'array'", + builtin_ctes = super::builtin::builtin_types_ctes() + ) + .into_boxed_str(), + ), + is_retained_metrics_object: false, + access: vec![PUBLIC_SELECT], + ontology: Some(Ontology { + entity_name: "array_type", + description: "An array type with its element type", + links: &const { + [ + OntologyLink { + name: "detail_of", + target: "type", + properties: LinkProperties::fk("id", "id", Cardinality::OneToOne), + }, + OntologyLink { + name: "has_element_type", + target: "type", + properties: LinkProperties::fk("element_id", "id", Cardinality::ManyToOne), + }, + ] + }, + column_semantic_types: &const { + [ + ("id", SemanticType::CatalogItemId), + ("element_id", SemanticType::CatalogItemId), + ] + }, + }), + }); +pub static MZ_BASE_TYPES: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_base_types", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_BASE_TYPES_OID, + oid: oid::MV_MZ_BASE_TYPES_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .finish(), column_comments: BTreeMap::from_iter([("id", "The ID of the type.")]), + // A user-defined record type has no per-kind relation of its own and is + // reported as a base type. + sql: Box::leak(format!(" +IN CLUSTER mz_catalog_server +WITH (ASSERT NOT NULL id) AS +WITH + {builtin_ctes}, + user_items AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql') AS parsed + FROM mz_internal.mz_catalog_raw + WHERE data->>'kind' = 'Item' + ) +SELECT id FROM builtin_types WHERE type_kind = 'base' +UNION ALL +SELECT id FROM user_items WHERE parsed->>'type' = 'type' AND parsed->>'type_kind' = 'record'", builtin_ctes = super::builtin::builtin_types_ctes()).into_boxed_str()), is_retained_metrics_object: false, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -2006,11 +2097,13 @@ pub static MZ_BASE_TYPES: LazyLock = LazyLock::new(|| BuiltinTable links: &const { [] }, column_semantic_types: &[("id", SemanticType::CatalogItemId)], }), +} }); -pub static MZ_LIST_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_LIST_TYPES: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_list_types", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_LIST_TYPES_OID, + oid: oid::MV_MZ_LIST_TYPES_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .with_column("element_id", SqlScalarType::String.nullable(false)) @@ -2031,6 +2124,28 @@ pub static MZ_LIST_TYPES: LazyLock = LazyLock::new(|| BuiltinTable "The element type modifiers, or `NULL` if none.", ), ]), + // Every list type is user-defined: `make_builtin_types` asserts that no + // builtin type is a list or a map. + sql: " +IN CLUSTER mz_catalog_server +WITH ( + ASSERT NOT NULL id, + ASSERT NOT NULL element_id +) AS +WITH + user_items AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql') AS parsed + FROM mz_internal.mz_catalog_raw + WHERE data->>'kind' = 'Item' + ) +SELECT + id, + parsed->>'element_id' AS element_id, + (parsed->>'element_modifiers')::bigint list AS element_modifiers +FROM user_items +WHERE parsed->>'type' = 'type' AND parsed->>'type_kind' = 'list'", is_retained_metrics_object: false, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -2057,11 +2172,13 @@ pub static MZ_LIST_TYPES: LazyLock = LazyLock::new(|| BuiltinTable ] }, }), +} }); -pub static MZ_MAP_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_MAP_TYPES: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_map_types", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_MAP_TYPES_OID, + oid: oid::MV_MZ_MAP_TYPES_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .with_column("key_id", SqlScalarType::String.nullable(false)) @@ -2096,6 +2213,31 @@ pub static MZ_MAP_TYPES: LazyLock = LazyLock::new(|| BuiltinTable "The value type modifiers, or `NULL` if none.", ), ]), + // Every map type is user-defined: `make_builtin_types` asserts that no + // builtin type is a list or a map. + sql: " +IN CLUSTER mz_catalog_server +WITH ( + ASSERT NOT NULL id, + ASSERT NOT NULL key_id, + ASSERT NOT NULL value_id +) AS +WITH + user_items AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql') AS parsed + FROM mz_internal.mz_catalog_raw + WHERE data->>'kind' = 'Item' + ) +SELECT + id, + parsed->>'key_id' AS key_id, + parsed->>'value_id' AS value_id, + (parsed->>'key_modifiers')::bigint list AS key_modifiers, + (parsed->>'value_modifiers')::bigint list AS value_modifiers +FROM user_items +WHERE parsed->>'type' = 'type' AND parsed->>'type_kind' = 'map'", is_retained_metrics_object: false, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -2128,6 +2270,7 @@ pub static MZ_MAP_TYPES: LazyLock = LazyLock::new(|| BuiltinTable ] }, }), +} }); pub static MZ_ROLES: LazyLock = LazyLock::new(|| { // The two built-in super-roles are identified by name; their names live as @@ -2366,23 +2509,38 @@ pub static MZ_ROLE_AUTH: LazyLock = LazyLock::new(|| BuiltinTable access: vec![rbac::owner_privilege(ObjectType::Table, MZ_SYSTEM_ROLE_ID)], ontology: None, }); -pub static MZ_PSEUDO_TYPES: LazyLock = LazyLock::new(|| BuiltinTable { - name: "mz_pseudo_types", - schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_PSEUDO_TYPES_OID, - desc: RelationDesc::builder() - .with_column("id", SqlScalarType::String.nullable(false)) - .finish(), - column_comments: BTreeMap::from_iter([("id", "The ID of the type.")]), - is_retained_metrics_object: false, - access: vec![PUBLIC_SELECT], - ontology: Some(Ontology { - entity_name: "pseudo_type", - description: "A pseudo-type used in function signatures", - links: &const { [] }, - column_semantic_types: &[("id", SemanticType::CatalogItemId)], - }), -}); +pub static MZ_PSEUDO_TYPES: LazyLock = + LazyLock::new(|| BuiltinMaterializedView { + name: "mz_pseudo_types", + schema: MZ_CATALOG_SCHEMA, + oid: oid::MV_MZ_PSEUDO_TYPES_OID, + desc: RelationDesc::builder() + .with_column("id", SqlScalarType::String.nullable(false)) + .finish(), + column_comments: BTreeMap::from_iter([("id", "The ID of the type.")]), + // `CREATE TYPE` cannot create a pseudo-type, so every pseudo-type is + // builtin. + sql: Box::leak( + format!( + " +IN CLUSTER mz_catalog_server +WITH (ASSERT NOT NULL id) AS +WITH + {builtin_ctes} +SELECT id FROM builtin_types WHERE type_kind = 'pseudo'", + builtin_ctes = super::builtin::builtin_types_ctes() + ) + .into_boxed_str(), + ), + is_retained_metrics_object: false, + access: vec![PUBLIC_SELECT], + ontology: Some(Ontology { + entity_name: "pseudo_type", + description: "A pseudo-type used in function signatures", + links: &const { [] }, + column_semantic_types: &[("id", SemanticType::CatalogItemId)], + }), + }); pub static MZ_FUNCTIONS: LazyLock = LazyLock::new(|| { BuiltinTable { name: "mz_functions", diff --git a/src/catalog/src/builtin/mz_internal.rs b/src/catalog/src/builtin/mz_internal.rs index e4280212c68f5..7d7fc2dac3a1d 100644 --- a/src/catalog/src/builtin/mz_internal.rs +++ b/src/catalog/src/builtin/mz_internal.rs @@ -682,27 +682,50 @@ WHERE data->>'kind' = 'NetworkPolicy'", }); /// PostgreSQL-specific metadata about types that doesn't make sense to expose -/// in the `mz_types` table as part of our public, stable API. -pub static MZ_TYPE_PG_METADATA: LazyLock = LazyLock::new(|| BuiltinTable { - name: "mz_type_pg_metadata", - schema: MZ_INTERNAL_SCHEMA, - oid: oid::TABLE_MZ_TYPE_PG_METADATA_OID, - desc: RelationDesc::builder() - .with_column("id", SqlScalarType::String.nullable(false)) - .with_column("typinput", SqlScalarType::Oid.nullable(false)) - .with_column("typreceive", SqlScalarType::Oid.nullable(false)) - // NOTE: `pg_type_all_databases` still needs `COALESCE` on this column, - // because its `LEFT JOIN` against this table yields NULLs for types with - // no PostgreSQL metadata. - .with_column("typsend", SqlScalarType::Oid.nullable(false)) - // NULL for pseudo-types and arrays of them. - .with_column("typlen", SqlScalarType::Int16.nullable(true)) - .finish(), - column_comments: BTreeMap::new(), - is_retained_metrics_object: false, - access: vec![PUBLIC_SELECT], - ontology: None, -}); +/// in `mz_types` as part of our public, stable API. +pub static MZ_TYPE_PG_METADATA: LazyLock = + LazyLock::new(|| BuiltinMaterializedView { + name: "mz_type_pg_metadata", + schema: MZ_INTERNAL_SCHEMA, + oid: oid::MV_MZ_TYPE_PG_METADATA_OID, + desc: RelationDesc::builder() + .with_column("id", SqlScalarType::String.nullable(false)) + .with_column("typinput", SqlScalarType::Oid.nullable(false)) + .with_column("typreceive", SqlScalarType::Oid.nullable(false)) + // NOTE: `pg_type_all_databases` still needs `COALESCE` on this column, + // because its `LEFT JOIN` against this table yields NULLs for types with + // no PostgreSQL metadata. + .with_column("typsend", SqlScalarType::Oid.nullable(false)) + // NULL for pseudo-types and arrays of them. + .with_column("typlen", SqlScalarType::Int16.nullable(true)) + .finish(), + column_comments: BTreeMap::new(), + // User-defined types carry no PostgreSQL metadata. A builtin type has + // either all three function oids or none (`CatalogTypePgMetadata`), so + // `typinput` stands in for the whole record. + sql: Box::leak( + format!( + " +IN CLUSTER mz_catalog_server +WITH ( + ASSERT NOT NULL id, + ASSERT NOT NULL typinput, + ASSERT NOT NULL typreceive, + ASSERT NOT NULL typsend +) AS +WITH + {builtin_ctes} +SELECT id, typinput, typreceive, typsend, typlen +FROM builtin_types +WHERE typinput IS NOT NULL", + builtin_ctes = super::builtin::builtin_types_ctes() + ) + .into_boxed_str(), + ), + is_retained_metrics_object: false, + access: vec![PUBLIC_SELECT], + ontology: None, + }); pub static MZ_AGGREGATES: LazyLock = LazyLock::new(|| BuiltinTable { name: "mz_aggregates", schema: MZ_INTERNAL_SCHEMA, diff --git a/src/pgrepr-consts/src/oid.rs b/src/pgrepr-consts/src/oid.rs index a483b13efad34..9593d23bf8a98 100644 --- a/src/pgrepr-consts/src/oid.rs +++ b/src/pgrepr-consts/src/oid.rs @@ -436,15 +436,15 @@ pub const MV_MZ_SOURCES_OID: u32 = 16710; pub const MV_MZ_SINKS_OID: u32 = 16711; pub const MV_MZ_VIEWS_OID: u32 = 16712; pub const MV_MZ_MATERIALIZED_VIEWS_OID: u32 = 16713; -pub const TABLE_MZ_TYPES_OID: u32 = 16714; -pub const TABLE_MZ_TYPE_PG_METADATA_OID: u32 = 16715; -pub const TABLE_MZ_ARRAY_TYPES_OID: u32 = 16716; -pub const TABLE_MZ_BASE_TYPES_OID: u32 = 16717; -pub const TABLE_MZ_LIST_TYPES_OID: u32 = 16718; -pub const TABLE_MZ_MAP_TYPES_OID: u32 = 16719; +pub const MV_MZ_TYPES_OID: u32 = 16714; +pub const MV_MZ_TYPE_PG_METADATA_OID: u32 = 16715; +pub const MV_MZ_ARRAY_TYPES_OID: u32 = 16716; +pub const MV_MZ_BASE_TYPES_OID: u32 = 16717; +pub const MV_MZ_LIST_TYPES_OID: u32 = 16718; +pub const MV_MZ_MAP_TYPES_OID: u32 = 16719; pub const MV_MZ_ROLES_OID: u32 = 16720; pub const MV_MZ_ROLE_MEMBERS_OID: u32 = 16721; -pub const TABLE_MZ_PSEUDO_TYPES_OID: u32 = 16722; +pub const MV_MZ_PSEUDO_TYPES_OID: u32 = 16722; pub const TABLE_MZ_FUNCTIONS_OID: u32 = 16723; pub const TABLE_MZ_OPERATORS_OID: u32 = 16724; pub const TABLE_MZ_AGGREGATES_OID: u32 = 16725; @@ -845,3 +845,4 @@ pub const FUNC_LIST_LTE_OID: u32 = 17135; pub const FUNC_LIST_GT_OID: u32 = 17136; pub const FUNC_LIST_GTE_OID: u32 = 17137; pub const FUNC_JQ_OID: u32 = 17138; +pub const VIEW_MZ_BUILTIN_TYPES_OID: u32 = 17139; diff --git a/test/sqllogictest/autogenerated/mz_internal.slt b/test/sqllogictest/autogenerated/mz_internal.slt index c0b108101adf1..78b55492221a9 100644 --- a/test/sqllogictest/autogenerated/mz_internal.slt +++ b/test/sqllogictest/autogenerated/mz_internal.slt @@ -817,6 +817,7 @@ mz_builtin_log_indexes mz_builtin_materialized_views mz_builtin_sources mz_builtin_tables +mz_builtin_types mz_builtin_views mz_catalog_raw mz_cluster_auto_scaling_strategies diff --git a/test/sqllogictest/catalog_server_explain.slt b/test/sqllogictest/catalog_server_explain.slt index 89d6a413e069d..151ed2668e31c 100644 --- a/test/sqllogictest/catalog_server_explain.slt +++ b/test/sqllogictest/catalog_server_explain.slt @@ -5127,7 +5127,7 @@ mz_catalog.mz_materialized_views: Project: #4, #0, #5, #1, #6, #2, #9, #8, #3, #7 Map: {=r/s1, s1=r/s1}, "s1" →Arrange (#1{schema_name}, #2{name}) - →Constant (49 rows) + →Constant (56 rows) →Arrange (empty key) (#0{schema_name}) (#0{schema_name}, #1{name}) →Fused with Child Map/Filter/Project Project: #4, #3, #5 @@ -5472,7 +5472,7 @@ mz_catalog.mz_tables: Project: #3, #0, #4, #1, #5, #2, #6, #6, #6 Map: "s1", null →Arrange (#1{schema_name}, #2{name}) - →Constant (25 rows) + →Constant (18 rows) →Arrange (#0{schema_name}) (#0{schema_name}, #1{name}) →Fused with Child Map/Filter/Project Project: #5, #4, #6 @@ -5527,7 +5527,7 @@ mz_catalog.mz_views: Project: #5, #0, #6, #1, #2, #8, #3, #4, #7 Map: "s1" →Arrange (#1{schema_name}, #2{name}) - →Constant (196 rows) + →Constant (197 rows) →Arrange (#0{schema_name}) (#0{schema_name}, #1{name}) →Fused with Child Map/Filter/Project Project: #5, #4, #6 @@ -5830,7 +5830,7 @@ mz_internal.mz_materialized_view_refresh_strategies: Project: #0..=#3, #3 Map: "on-commit", null, null →Arrange (#1{name}, #0{schema_name}) - →Constant (49 rows) + →Constant (56 rows) →Arrange (((#0{data} -> "key") ->> "object_name"), ((#0{data} -> "key") ->> "schema_name")) →Fused with Child Map/Filter/Project Project: #0 @@ -6014,7 +6014,7 @@ mz_internal.mz_object_dependencies: Project: #4..=#6, #2 Filter: case when (#7{ref_kind} = "func") then (#3{object_type} = "8") else case when (#7{ref_kind} = "type") then (#3{object_type} = "7") else ((#3{object_type} != "7") AND (#3{object_type} != "8")) end end →Arrange (#0{object_schema}..=#2{object_type}) (#3{ref_schema}, #4{ref_name}) - →Constant (1064 rows) + →Constant (1095 rows) →Arrange (#1{schema_name}..=#3{object_type}) →Fused with Child Map/Filter/Project Project: #5, #4, #2, #3 @@ -8610,7 +8610,7 @@ query T multiline EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_materialized_views"; ---- Explained Query (fast path): - →Constant (49 rows) + →Constant (56 rows) Target cluster: mz_catalog_server @@ -8630,7 +8630,7 @@ query T multiline EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_tables"; ---- Explained Query (fast path): - →Constant (25 rows) + →Constant (18 rows) Target cluster: mz_catalog_server @@ -8640,7 +8640,7 @@ query T multiline EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_views"; ---- Explained Query (fast path): - →Constant (196 rows) + →Constant (197 rows) Target cluster: mz_catalog_server @@ -10558,7 +10558,7 @@ Explained Query: Project: #3, #4 Filter: case when (#2{ref_kind} = "func") then (#5{object_type} = "8") else case when (#2{ref_kind} = "type") then (#5{object_type} = "7") else ((#5{object_type} != "7") AND (#5{object_type} != "8")) end end →Unarranged Raw Stream - →Constant (1064 rows) + →Constant (1095 rows) →Arrange (#1{schema_name}..=#3{object_type}) →Fused with Child Map/Filter/Project Project: #5, #4, #2, #3 diff --git a/test/sqllogictest/information_schema_tables.slt b/test/sqllogictest/information_schema_tables.slt index d46846611327a..f86f5c96144e9 100644 --- a/test/sqllogictest/information_schema_tables.slt +++ b/test/sqllogictest/information_schema_tables.slt @@ -94,7 +94,7 @@ VIEW materialize mz_catalog mz_array_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_audit_events @@ -106,7 +106,7 @@ MATERIALIZED VIEW materialize mz_catalog mz_base_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_cluster_replica_frontiers @@ -174,11 +174,11 @@ MATERIALIZED VIEW materialize mz_catalog mz_list_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_map_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_materialized_views @@ -194,7 +194,7 @@ BASE TABLE materialize mz_catalog mz_pseudo_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_recent_storage_usage @@ -262,7 +262,7 @@ VIEW materialize mz_catalog mz_types -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_views @@ -309,6 +309,10 @@ mz_builtin_tables VIEW materialize mz_internal +mz_builtin_types +VIEW +materialize +mz_internal mz_builtin_views VIEW materialize @@ -834,7 +838,7 @@ BASE TABLE materialize mz_internal mz_type_pg_metadata -BASE TABLE +MATERIALIZED VIEW materialize mz_internal mz_wallclock_global_lag diff --git a/test/sqllogictest/mz_catalog_server_index_accounting.slt b/test/sqllogictest/mz_catalog_server_index_accounting.slt index de8511cb13bec..8c5ce989030b9 100644 --- a/test/sqllogictest/mz_catalog_server_index_accounting.slt +++ b/test/sqllogictest/mz_catalog_server_index_accounting.slt @@ -37,110 +37,110 @@ mz_arrangement_heap_capacity_raw_s2_primary_idx CREATE␠INDEX␠"mz_arrangemen mz_arrangement_heap_size_raw_s2_primary_idx CREATE␠INDEX␠"mz_arrangement_heap_size_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_arrangement_heap_size_raw"␠("operator_id",␠"worker_id") mz_arrangement_records_raw_s2_primary_idx CREATE␠INDEX␠"mz_arrangement_records_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_arrangement_records_raw"␠("operator_id",␠"worker_id") mz_arrangement_sharing_raw_s2_primary_idx CREATE␠INDEX␠"mz_arrangement_sharing_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_arrangement_sharing_raw"␠("operator_id",␠"worker_id") -mz_cluster_auto_scaling_strategies_ind CREATE␠INDEX␠"mz_cluster_auto_scaling_strategies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s526␠AS␠"mz_internal"."mz_cluster_auto_scaling_strategies"]␠("cluster_id") -mz_cluster_deployment_lineage_ind CREATE␠INDEX␠"mz_cluster_deployment_lineage_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s775␠AS␠"mz_internal"."mz_cluster_deployment_lineage"]␠("cluster_id") +mz_cluster_auto_scaling_strategies_ind CREATE␠INDEX␠"mz_cluster_auto_scaling_strategies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s527␠AS␠"mz_internal"."mz_cluster_auto_scaling_strategies"]␠("cluster_id") +mz_cluster_deployment_lineage_ind CREATE␠INDEX␠"mz_cluster_deployment_lineage_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s776␠AS␠"mz_internal"."mz_cluster_deployment_lineage"]␠("cluster_id") mz_cluster_prometheus_metrics_s2_primary_idx CREATE␠INDEX␠"mz_cluster_prometheus_metrics_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_cluster_prometheus_metrics"␠("process_id",␠"metric_name",␠"labels") -mz_cluster_reconfigurations_ind CREATE␠INDEX␠"mz_cluster_reconfigurations_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s525␠AS␠"mz_internal"."mz_cluster_reconfigurations"]␠("cluster_id") -mz_cluster_replica_frontiers_ind CREATE␠INDEX␠"mz_cluster_replica_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s769␠AS␠"mz_catalog"."mz_cluster_replica_frontiers"]␠("object_id") -mz_cluster_replica_history_ind CREATE␠INDEX␠"mz_cluster_replica_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s624␠AS␠"mz_internal"."mz_cluster_replica_history"]␠("dropped_at") -mz_cluster_replica_metrics_history_ind CREATE␠INDEX␠"mz_cluster_replica_metrics_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s531␠AS␠"mz_internal"."mz_cluster_replica_metrics_history"]␠("replica_id") -mz_cluster_replica_metrics_ind CREATE␠INDEX␠"mz_cluster_replica_metrics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s532␠AS␠"mz_internal"."mz_cluster_replica_metrics"]␠("replica_id") -mz_cluster_replica_name_history_ind CREATE␠INDEX␠"mz_cluster_replica_name_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s625␠AS␠"mz_internal"."mz_cluster_replica_name_history"]␠("id") +mz_cluster_reconfigurations_ind CREATE␠INDEX␠"mz_cluster_reconfigurations_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s526␠AS␠"mz_internal"."mz_cluster_reconfigurations"]␠("cluster_id") +mz_cluster_replica_frontiers_ind CREATE␠INDEX␠"mz_cluster_replica_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s770␠AS␠"mz_catalog"."mz_cluster_replica_frontiers"]␠("object_id") +mz_cluster_replica_history_ind CREATE␠INDEX␠"mz_cluster_replica_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s625␠AS␠"mz_internal"."mz_cluster_replica_history"]␠("dropped_at") +mz_cluster_replica_metrics_history_ind CREATE␠INDEX␠"mz_cluster_replica_metrics_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s532␠AS␠"mz_internal"."mz_cluster_replica_metrics_history"]␠("replica_id") +mz_cluster_replica_metrics_ind CREATE␠INDEX␠"mz_cluster_replica_metrics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s533␠AS␠"mz_internal"."mz_cluster_replica_metrics"]␠("replica_id") +mz_cluster_replica_name_history_ind CREATE␠INDEX␠"mz_cluster_replica_name_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s626␠AS␠"mz_internal"."mz_cluster_replica_name_history"]␠("id") mz_cluster_replica_resource_usage_s2_primary_idx CREATE␠INDEX␠"mz_cluster_replica_resource_usage_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_cluster_replica_resource_usage"␠("process_id",␠"source",␠"metric") -mz_cluster_replica_size_internal_ind CREATE␠INDEX␠"mz_cluster_replica_size_internal_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s521␠AS␠"mz_internal"."mz_cluster_replica_size_internal"]␠("size") -mz_cluster_replica_sizes_ind CREATE␠INDEX␠"mz_cluster_replica_sizes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s520␠AS␠"mz_catalog"."mz_cluster_replica_sizes"]␠("size") -mz_cluster_replica_status_history_ind CREATE␠INDEX␠"mz_cluster_replica_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s533␠AS␠"mz_internal"."mz_cluster_replica_status_history"]␠("replica_id") -mz_cluster_replica_statuses_ind CREATE␠INDEX␠"mz_cluster_replica_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s534␠AS␠"mz_internal"."mz_cluster_replica_statuses"]␠("replica_id") -mz_cluster_replicas_ind CREATE␠INDEX␠"mz_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s530␠AS␠"mz_catalog"."mz_cluster_replicas"]␠("id") -mz_clusters_ind CREATE␠INDEX␠"mz_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s522␠AS␠"mz_catalog"."mz_clusters"]␠("id") -mz_columns_ind CREATE␠INDEX␠"mz_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s493␠AS␠"mz_catalog"."mz_columns"]␠("name") -mz_comments_ind CREATE␠INDEX␠"mz_comments_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s549␠AS␠"mz_internal"."mz_comments"]␠("id") +mz_cluster_replica_size_internal_ind CREATE␠INDEX␠"mz_cluster_replica_size_internal_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s522␠AS␠"mz_internal"."mz_cluster_replica_size_internal"]␠("size") +mz_cluster_replica_sizes_ind CREATE␠INDEX␠"mz_cluster_replica_sizes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s521␠AS␠"mz_catalog"."mz_cluster_replica_sizes"]␠("size") +mz_cluster_replica_status_history_ind CREATE␠INDEX␠"mz_cluster_replica_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s534␠AS␠"mz_internal"."mz_cluster_replica_status_history"]␠("replica_id") +mz_cluster_replica_statuses_ind CREATE␠INDEX␠"mz_cluster_replica_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s535␠AS␠"mz_internal"."mz_cluster_replica_statuses"]␠("replica_id") +mz_cluster_replicas_ind CREATE␠INDEX␠"mz_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s531␠AS␠"mz_catalog"."mz_cluster_replicas"]␠("id") +mz_clusters_ind CREATE␠INDEX␠"mz_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s523␠AS␠"mz_catalog"."mz_clusters"]␠("id") +mz_columns_ind CREATE␠INDEX␠"mz_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s494␠AS␠"mz_catalog"."mz_columns"]␠("name") +mz_comments_ind CREATE␠INDEX␠"mz_comments_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s550␠AS␠"mz_internal"."mz_comments"]␠("id") mz_compute_dataflow_global_ids_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_dataflow_global_ids_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_dataflow_global_ids_per_worker"␠("id",␠"worker_id",␠"global_id") -mz_compute_dependencies_ind CREATE␠INDEX␠"mz_compute_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s750␠AS␠"mz_internal"."mz_compute_dependencies"]␠("dependency_id") +mz_compute_dependencies_ind CREATE␠INDEX␠"mz_compute_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s751␠AS␠"mz_internal"."mz_compute_dependencies"]␠("dependency_id") mz_compute_error_counts_raw_s2_primary_idx CREATE␠INDEX␠"mz_compute_error_counts_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_error_counts_raw"␠("export_id",␠"worker_id") mz_compute_exports_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_exports_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_exports_per_worker"␠("export_id",␠"worker_id") mz_compute_frontiers_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_frontiers_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_frontiers_per_worker"␠("export_id",␠"worker_id") -mz_compute_hydration_times_ind CREATE␠INDEX␠"mz_compute_hydration_times_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s760␠AS␠"mz_internal"."mz_compute_hydration_times"]␠("replica_id") +mz_compute_hydration_times_ind CREATE␠INDEX␠"mz_compute_hydration_times_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s761␠AS␠"mz_internal"."mz_compute_hydration_times"]␠("replica_id") mz_compute_hydration_times_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_hydration_times_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_hydration_times_per_worker"␠("export_id",␠"worker_id") mz_compute_import_frontiers_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_import_frontiers_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_import_frontiers_per_worker"␠("export_id",␠"import_id",␠"worker_id") mz_compute_lir_mapping_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_lir_mapping_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_lir_mapping_per_worker"␠("global_id",␠"lir_id",␠"worker_id") mz_compute_operator_durations_histogram_raw_s2_primary_idx CREATE␠INDEX␠"mz_compute_operator_durations_histogram_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_operator_durations_histogram_raw"␠("id",␠"worker_id",␠"duration_ns") mz_compute_operator_hydration_statuses_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_compute_operator_hydration_statuses_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_compute_operator_hydration_statuses_per_worker"␠("export_id",␠"lir_id",␠"worker_id") -mz_connections_ind CREATE␠INDEX␠"mz_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s528␠AS␠"mz_catalog"."mz_connections"]␠("schema_id") -mz_console_cluster_utilization_overview_24h_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_24h_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s756␠AS␠"mz_internal"."mz_console_cluster_utilization_overview_24h"]␠("cluster_id") -mz_console_cluster_utilization_overview_3h_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_3h_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s755␠AS␠"mz_internal"."mz_console_cluster_utilization_overview_3h"]␠("cluster_id") -mz_console_cluster_utilization_overview_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s754␠AS␠"mz_internal"."mz_console_cluster_utilization_overview"]␠("cluster_id") -mz_databases_ind CREATE␠INDEX␠"mz_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s491␠AS␠"mz_catalog"."mz_databases"]␠("name") +mz_connections_ind CREATE␠INDEX␠"mz_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s529␠AS␠"mz_catalog"."mz_connections"]␠("schema_id") +mz_console_cluster_utilization_overview_24h_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_24h_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s757␠AS␠"mz_internal"."mz_console_cluster_utilization_overview_24h"]␠("cluster_id") +mz_console_cluster_utilization_overview_3h_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_3h_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s756␠AS␠"mz_internal"."mz_console_cluster_utilization_overview_3h"]␠("cluster_id") +mz_console_cluster_utilization_overview_ind CREATE␠INDEX␠"mz_console_cluster_utilization_overview_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s755␠AS␠"mz_internal"."mz_console_cluster_utilization_overview"]␠("cluster_id") +mz_databases_ind CREATE␠INDEX␠"mz_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s492␠AS␠"mz_catalog"."mz_databases"]␠("name") mz_dataflow_addresses_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_dataflow_addresses_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_dataflow_addresses_per_worker"␠("id",␠"worker_id") mz_dataflow_channels_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_dataflow_channels_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_dataflow_channels_per_worker"␠("id",␠"worker_id") mz_dataflow_operator_reachability_raw_s2_primary_idx CREATE␠INDEX␠"mz_dataflow_operator_reachability_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_dataflow_operator_reachability_raw"␠("id",␠"worker_id",␠"source",␠"port",␠"update_type",␠"time") mz_dataflow_operators_per_worker_s2_primary_idx CREATE␠INDEX␠"mz_dataflow_operators_per_worker_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_dataflow_operators_per_worker"␠("id",␠"worker_id") -mz_frontiers_ind CREATE␠INDEX␠"mz_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s741␠AS␠"mz_internal"."mz_frontiers"]␠("object_id") -mz_hydration_statuses_ind CREATE␠INDEX␠"mz_hydration_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s771␠AS␠"mz_internal"."mz_hydration_statuses"]␠("object_id",␠"replica_id") -mz_indexes_ind CREATE␠INDEX␠"mz_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s494␠AS␠"mz_catalog"."mz_indexes"]␠("id") -mz_kafka_sources_ind CREATE␠INDEX␠"mz_kafka_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s487␠AS␠"mz_catalog"."mz_kafka_sources"]␠("id") -mz_materialized_views_ind CREATE␠INDEX␠"mz_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s553␠AS␠"mz_catalog"."mz_materialized_views"]␠("id") +mz_frontiers_ind CREATE␠INDEX␠"mz_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s742␠AS␠"mz_internal"."mz_frontiers"]␠("object_id") +mz_hydration_statuses_ind CREATE␠INDEX␠"mz_hydration_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s772␠AS␠"mz_internal"."mz_hydration_statuses"]␠("object_id",␠"replica_id") +mz_indexes_ind CREATE␠INDEX␠"mz_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s495␠AS␠"mz_catalog"."mz_indexes"]␠("id") +mz_kafka_sources_ind CREATE␠INDEX␠"mz_kafka_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s488␠AS␠"mz_catalog"."mz_kafka_sources"]␠("id") +mz_materialized_views_ind CREATE␠INDEX␠"mz_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s554␠AS␠"mz_catalog"."mz_materialized_views"]␠("id") mz_message_batch_counts_received_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_batch_counts_received_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_batch_counts_received_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id") mz_message_batch_counts_sent_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_batch_counts_sent_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_batch_counts_sent_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id") mz_message_counts_received_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_counts_received_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_counts_received_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id") mz_message_counts_sent_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_counts_sent_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_counts_sent_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id") -mz_metric_sinks_ind CREATE␠INDEX␠"mz_metric_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s551␠AS␠"mz_internal"."mz_metric_sinks"]␠("id") -mz_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s849␠AS␠"mz_internal"."mz_notices"]␠("id") -mz_object_arrangement_size_history_object_ind CREATE␠INDEX␠"mz_object_arrangement_size_history_object_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s763␠AS␠"mz_internal"."mz_object_arrangement_size_history"]␠("object_id") -mz_object_arrangement_size_history_ts_ind CREATE␠INDEX␠"mz_object_arrangement_size_history_ts_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s763␠AS␠"mz_internal"."mz_object_arrangement_size_history"]␠("collection_timestamp") -mz_object_arrangement_sizes_ind CREATE␠INDEX␠"mz_object_arrangement_sizes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s761␠AS␠"mz_internal"."mz_object_arrangement_sizes"]␠("replica_id") -mz_object_dependencies_ind CREATE␠INDEX␠"mz_object_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s489␠AS␠"mz_internal"."mz_object_dependencies"]␠("object_id") -mz_object_graph_edges_ind CREATE␠INDEX␠"mz_object_graph_edges_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s752␠AS␠"mz_internal"."mz_object_graph_edges"]␠("object_id") -mz_object_history_ind CREATE␠INDEX␠"mz_object_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s564␠AS␠"mz_internal"."mz_object_history"]␠("id") -mz_object_lifetimes_ind CREATE␠INDEX␠"mz_object_lifetimes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s565␠AS␠"mz_internal"."mz_object_lifetimes"]␠("id") -mz_object_transitive_dependencies_ind CREATE␠INDEX␠"mz_object_transitive_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s581␠AS␠"mz_internal"."mz_object_transitive_dependencies"]␠("object_id") -mz_objects_ind CREATE␠INDEX␠"mz_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s561␠AS␠"mz_catalog"."mz_objects"]␠("schema_id") +mz_metric_sinks_ind CREATE␠INDEX␠"mz_metric_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s552␠AS␠"mz_internal"."mz_metric_sinks"]␠("id") +mz_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s850␠AS␠"mz_internal"."mz_notices"]␠("id") +mz_object_arrangement_size_history_object_ind CREATE␠INDEX␠"mz_object_arrangement_size_history_object_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s764␠AS␠"mz_internal"."mz_object_arrangement_size_history"]␠("object_id") +mz_object_arrangement_size_history_ts_ind CREATE␠INDEX␠"mz_object_arrangement_size_history_ts_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s764␠AS␠"mz_internal"."mz_object_arrangement_size_history"]␠("collection_timestamp") +mz_object_arrangement_sizes_ind CREATE␠INDEX␠"mz_object_arrangement_sizes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s762␠AS␠"mz_internal"."mz_object_arrangement_sizes"]␠("replica_id") +mz_object_dependencies_ind CREATE␠INDEX␠"mz_object_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s490␠AS␠"mz_internal"."mz_object_dependencies"]␠("object_id") +mz_object_graph_edges_ind CREATE␠INDEX␠"mz_object_graph_edges_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s753␠AS␠"mz_internal"."mz_object_graph_edges"]␠("object_id") +mz_object_history_ind CREATE␠INDEX␠"mz_object_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s565␠AS␠"mz_internal"."mz_object_history"]␠("id") +mz_object_lifetimes_ind CREATE␠INDEX␠"mz_object_lifetimes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s566␠AS␠"mz_internal"."mz_object_lifetimes"]␠("id") +mz_object_transitive_dependencies_ind CREATE␠INDEX␠"mz_object_transitive_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s582␠AS␠"mz_internal"."mz_object_transitive_dependencies"]␠("object_id") +mz_objects_ind CREATE␠INDEX␠"mz_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s562␠AS␠"mz_catalog"."mz_objects"]␠("schema_id") mz_peek_durations_histogram_raw_s2_primary_idx CREATE␠INDEX␠"mz_peek_durations_histogram_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_peek_durations_histogram_raw"␠("worker_id",␠"type",␠"duration_ns") -mz_recent_activity_log_thinned_ind CREATE␠INDEX␠"mz_recent_activity_log_thinned_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s725␠AS␠"mz_internal"."mz_recent_activity_log_thinned"]␠("sql_hash") -mz_recent_sql_text_ind CREATE␠INDEX␠"mz_recent_sql_text_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s721␠AS␠"mz_internal"."mz_recent_sql_text"]␠("sql_hash") -mz_recent_storage_usage_ind CREATE␠INDEX␠"mz_recent_storage_usage_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s840␠AS␠"mz_catalog"."mz_recent_storage_usage"]␠("object_id") -mz_roles_ind CREATE␠INDEX␠"mz_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s512␠AS␠"mz_catalog"."mz_roles"]␠("id") +mz_recent_activity_log_thinned_ind CREATE␠INDEX␠"mz_recent_activity_log_thinned_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s726␠AS␠"mz_internal"."mz_recent_activity_log_thinned"]␠("sql_hash") +mz_recent_sql_text_ind CREATE␠INDEX␠"mz_recent_sql_text_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s722␠AS␠"mz_internal"."mz_recent_sql_text"]␠("sql_hash") +mz_recent_storage_usage_ind CREATE␠INDEX␠"mz_recent_storage_usage_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s841␠AS␠"mz_catalog"."mz_recent_storage_usage"]␠("object_id") +mz_roles_ind CREATE␠INDEX␠"mz_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s513␠AS␠"mz_catalog"."mz_roles"]␠("id") mz_scheduling_elapsed_raw_s2_primary_idx CREATE␠INDEX␠"mz_scheduling_elapsed_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_scheduling_elapsed_raw"␠("id",␠"worker_id") mz_scheduling_parks_histogram_raw_s2_primary_idx CREATE␠INDEX␠"mz_scheduling_parks_histogram_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_scheduling_parks_histogram_raw"␠("worker_id",␠"slept_for_ns",␠"requested_ns") -mz_schemas_ind CREATE␠INDEX␠"mz_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s492␠AS␠"mz_catalog"."mz_schemas"]␠("database_id") -mz_secrets_ind CREATE␠INDEX␠"mz_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s527␠AS␠"mz_catalog"."mz_secrets"]␠("name") -mz_show_all_objects_ind CREATE␠INDEX␠"mz_show_all_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s609␠AS␠"mz_internal"."mz_show_all_objects"]␠("schema_id") -mz_show_cluster_replicas_ind CREATE␠INDEX␠"mz_show_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s773␠AS␠"mz_internal"."mz_show_cluster_replicas"]␠("cluster") -mz_show_clusters_ind CREATE␠INDEX␠"mz_show_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s611␠AS␠"mz_internal"."mz_show_clusters"]␠("name") -mz_show_columns_ind CREATE␠INDEX␠"mz_show_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s610␠AS␠"mz_internal"."mz_show_columns"]␠("id") -mz_show_connections_ind CREATE␠INDEX␠"mz_show_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s619␠AS␠"mz_internal"."mz_show_connections"]␠("schema_id") -mz_show_databases_ind CREATE␠INDEX␠"mz_show_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s613␠AS␠"mz_internal"."mz_show_databases"]␠("name") -mz_show_indexes_ind CREATE␠INDEX␠"mz_show_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s623␠AS␠"mz_internal"."mz_show_indexes"]␠("schema_id") -mz_show_materialized_views_ind CREATE␠INDEX␠"mz_show_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s622␠AS␠"mz_internal"."mz_show_materialized_views"]␠("schema_id") -mz_show_roles_ind CREATE␠INDEX␠"mz_show_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s618␠AS␠"mz_internal"."mz_show_roles"]␠("name") -mz_show_schemas_ind CREATE␠INDEX␠"mz_show_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s614␠AS␠"mz_internal"."mz_show_schemas"]␠("database_id") -mz_show_secrets_ind CREATE␠INDEX␠"mz_show_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s612␠AS␠"mz_internal"."mz_show_secrets"]␠("schema_id") -mz_show_sinks_ind CREATE␠INDEX␠"mz_show_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s621␠AS␠"mz_internal"."mz_show_sinks"]␠("schema_id") -mz_show_sources_ind CREATE␠INDEX␠"mz_show_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s620␠AS␠"mz_internal"."mz_show_sources"]␠("schema_id") -mz_show_tables_ind CREATE␠INDEX␠"mz_show_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s615␠AS␠"mz_internal"."mz_show_tables"]␠("schema_id") -mz_show_types_ind CREATE␠INDEX␠"mz_show_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s617␠AS␠"mz_internal"."mz_show_types"]␠("schema_id") -mz_show_views_ind CREATE␠INDEX␠"mz_show_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s616␠AS␠"mz_internal"."mz_show_views"]␠("schema_id") -mz_sink_statistics_ind CREATE␠INDEX␠"mz_sink_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s738␠AS␠"mz_internal"."mz_sink_statistics"]␠("id",␠"replica_id") -mz_sink_status_history_ind CREATE␠INDEX␠"mz_sink_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s710␠AS␠"mz_internal"."mz_sink_status_history"]␠("sink_id") -mz_sink_statuses_ind CREATE␠INDEX␠"mz_sink_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s711␠AS␠"mz_internal"."mz_sink_statuses"]␠("id") -mz_sinks_ind CREATE␠INDEX␠"mz_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s504␠AS␠"mz_catalog"."mz_sinks"]␠("id") -mz_source_statistics_ind CREATE␠INDEX␠"mz_source_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s736␠AS␠"mz_internal"."mz_source_statistics"]␠("id",␠"replica_id") -mz_source_statistics_with_history_ind CREATE␠INDEX␠"mz_source_statistics_with_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s734␠AS␠"mz_internal"."mz_source_statistics_with_history"]␠("id",␠"replica_id") -mz_source_status_history_ind CREATE␠INDEX␠"mz_source_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s712␠AS␠"mz_internal"."mz_source_status_history"]␠("source_id") -mz_source_statuses_ind CREATE␠INDEX␠"mz_source_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s729␠AS␠"mz_internal"."mz_source_statuses"]␠("id") -mz_sources_ind CREATE␠INDEX␠"mz_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s497␠AS␠"mz_catalog"."mz_sources"]␠("id") -mz_tables_ind CREATE␠INDEX␠"mz_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s496␠AS␠"mz_catalog"."mz_tables"]␠("schema_id") -mz_types_ind CREATE␠INDEX␠"mz_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s506␠AS␠"mz_catalog"."mz_types"]␠("schema_id") -mz_views_ind CREATE␠INDEX␠"mz_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s505␠AS␠"mz_catalog"."mz_views"]␠("schema_id") -mz_wallclock_global_lag_recent_history_ind CREATE␠INDEX␠"mz_wallclock_global_lag_recent_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s745␠AS␠"mz_internal"."mz_wallclock_global_lag_recent_history"]␠("object_id") -mz_webhook_sources_ind CREATE␠INDEX␠"mz_webhook_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s550␠AS␠"mz_internal"."mz_webhook_sources"]␠("id") -pg_attrdef_all_databases_ind CREATE␠INDEX␠"pg_attrdef_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s650␠AS␠"mz_internal"."pg_attrdef_all_databases"]␠("oid",␠"adrelid",␠"adnum",␠"adbin",␠"adsrc") -pg_attribute_all_databases_ind CREATE␠INDEX␠"pg_attribute_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s643␠AS␠"mz_internal"."pg_attribute_all_databases"]␠("attrelid",␠"attname",␠"atttypid",␠"attlen",␠"attnum",␠"atttypmod",␠"attnotnull",␠"atthasdef",␠"attidentity",␠"attgenerated",␠"attisdropped",␠"attcollation",␠"database_name",␠"pg_type_database_name") -pg_authid_core_ind CREATE␠INDEX␠"pg_authid_core_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s660␠AS␠"mz_internal"."pg_authid_core"]␠("rolname") -pg_class_all_databases_ind CREATE␠INDEX␠"pg_class_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s631␠AS␠"mz_internal"."pg_class_all_databases"]␠("relname") -pg_description_all_databases_ind CREATE␠INDEX␠"pg_description_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s640␠AS␠"mz_internal"."pg_description_all_databases"]␠("objoid",␠"classoid",␠"objsubid",␠"description",␠"oid_database_name",␠"class_database_name") -pg_namespace_all_databases_ind CREATE␠INDEX␠"pg_namespace_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s628␠AS␠"mz_internal"."pg_namespace_all_databases"]␠("nspname") -pg_type_all_databases_ind CREATE␠INDEX␠"pg_type_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s637␠AS␠"mz_internal"."pg_type_all_databases"]␠("oid") +mz_schemas_ind CREATE␠INDEX␠"mz_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s493␠AS␠"mz_catalog"."mz_schemas"]␠("database_id") +mz_secrets_ind CREATE␠INDEX␠"mz_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s528␠AS␠"mz_catalog"."mz_secrets"]␠("name") +mz_show_all_objects_ind CREATE␠INDEX␠"mz_show_all_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s610␠AS␠"mz_internal"."mz_show_all_objects"]␠("schema_id") +mz_show_cluster_replicas_ind CREATE␠INDEX␠"mz_show_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s774␠AS␠"mz_internal"."mz_show_cluster_replicas"]␠("cluster") +mz_show_clusters_ind CREATE␠INDEX␠"mz_show_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s612␠AS␠"mz_internal"."mz_show_clusters"]␠("name") +mz_show_columns_ind CREATE␠INDEX␠"mz_show_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s611␠AS␠"mz_internal"."mz_show_columns"]␠("id") +mz_show_connections_ind CREATE␠INDEX␠"mz_show_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s620␠AS␠"mz_internal"."mz_show_connections"]␠("schema_id") +mz_show_databases_ind CREATE␠INDEX␠"mz_show_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s614␠AS␠"mz_internal"."mz_show_databases"]␠("name") +mz_show_indexes_ind CREATE␠INDEX␠"mz_show_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s624␠AS␠"mz_internal"."mz_show_indexes"]␠("schema_id") +mz_show_materialized_views_ind CREATE␠INDEX␠"mz_show_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s623␠AS␠"mz_internal"."mz_show_materialized_views"]␠("schema_id") +mz_show_roles_ind CREATE␠INDEX␠"mz_show_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s619␠AS␠"mz_internal"."mz_show_roles"]␠("name") +mz_show_schemas_ind CREATE␠INDEX␠"mz_show_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s615␠AS␠"mz_internal"."mz_show_schemas"]␠("database_id") +mz_show_secrets_ind CREATE␠INDEX␠"mz_show_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s613␠AS␠"mz_internal"."mz_show_secrets"]␠("schema_id") +mz_show_sinks_ind CREATE␠INDEX␠"mz_show_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s622␠AS␠"mz_internal"."mz_show_sinks"]␠("schema_id") +mz_show_sources_ind CREATE␠INDEX␠"mz_show_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s621␠AS␠"mz_internal"."mz_show_sources"]␠("schema_id") +mz_show_tables_ind CREATE␠INDEX␠"mz_show_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s616␠AS␠"mz_internal"."mz_show_tables"]␠("schema_id") +mz_show_types_ind CREATE␠INDEX␠"mz_show_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s618␠AS␠"mz_internal"."mz_show_types"]␠("schema_id") +mz_show_views_ind CREATE␠INDEX␠"mz_show_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s617␠AS␠"mz_internal"."mz_show_views"]␠("schema_id") +mz_sink_statistics_ind CREATE␠INDEX␠"mz_sink_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s739␠AS␠"mz_internal"."mz_sink_statistics"]␠("id",␠"replica_id") +mz_sink_status_history_ind CREATE␠INDEX␠"mz_sink_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s711␠AS␠"mz_internal"."mz_sink_status_history"]␠("sink_id") +mz_sink_statuses_ind CREATE␠INDEX␠"mz_sink_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s712␠AS␠"mz_internal"."mz_sink_statuses"]␠("id") +mz_sinks_ind CREATE␠INDEX␠"mz_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s505␠AS␠"mz_catalog"."mz_sinks"]␠("id") +mz_source_statistics_ind CREATE␠INDEX␠"mz_source_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s737␠AS␠"mz_internal"."mz_source_statistics"]␠("id",␠"replica_id") +mz_source_statistics_with_history_ind CREATE␠INDEX␠"mz_source_statistics_with_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s735␠AS␠"mz_internal"."mz_source_statistics_with_history"]␠("id",␠"replica_id") +mz_source_status_history_ind CREATE␠INDEX␠"mz_source_status_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s713␠AS␠"mz_internal"."mz_source_status_history"]␠("source_id") +mz_source_statuses_ind CREATE␠INDEX␠"mz_source_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s730␠AS␠"mz_internal"."mz_source_statuses"]␠("id") +mz_sources_ind CREATE␠INDEX␠"mz_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s498␠AS␠"mz_catalog"."mz_sources"]␠("id") +mz_tables_ind CREATE␠INDEX␠"mz_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s497␠AS␠"mz_catalog"."mz_tables"]␠("schema_id") +mz_types_ind CREATE␠INDEX␠"mz_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s507␠AS␠"mz_catalog"."mz_types"]␠("schema_id") +mz_views_ind CREATE␠INDEX␠"mz_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s506␠AS␠"mz_catalog"."mz_views"]␠("schema_id") +mz_wallclock_global_lag_recent_history_ind CREATE␠INDEX␠"mz_wallclock_global_lag_recent_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s746␠AS␠"mz_internal"."mz_wallclock_global_lag_recent_history"]␠("object_id") +mz_webhook_sources_ind CREATE␠INDEX␠"mz_webhook_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s551␠AS␠"mz_internal"."mz_webhook_sources"]␠("id") +pg_attrdef_all_databases_ind CREATE␠INDEX␠"pg_attrdef_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s651␠AS␠"mz_internal"."pg_attrdef_all_databases"]␠("oid",␠"adrelid",␠"adnum",␠"adbin",␠"adsrc") +pg_attribute_all_databases_ind CREATE␠INDEX␠"pg_attribute_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s644␠AS␠"mz_internal"."pg_attribute_all_databases"]␠("attrelid",␠"attname",␠"atttypid",␠"attlen",␠"attnum",␠"atttypmod",␠"attnotnull",␠"atthasdef",␠"attidentity",␠"attgenerated",␠"attisdropped",␠"attcollation",␠"database_name",␠"pg_type_database_name") +pg_authid_core_ind CREATE␠INDEX␠"pg_authid_core_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s661␠AS␠"mz_internal"."pg_authid_core"]␠("rolname") +pg_class_all_databases_ind CREATE␠INDEX␠"pg_class_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s632␠AS␠"mz_internal"."pg_class_all_databases"]␠("relname") +pg_description_all_databases_ind CREATE␠INDEX␠"pg_description_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s641␠AS␠"mz_internal"."pg_description_all_databases"]␠("objoid",␠"classoid",␠"objsubid",␠"description",␠"oid_database_name",␠"class_database_name") +pg_namespace_all_databases_ind CREATE␠INDEX␠"pg_namespace_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s629␠AS␠"mz_internal"."pg_namespace_all_databases"]␠("nspname") +pg_type_all_databases_ind CREATE␠INDEX␠"pg_type_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s638␠AS␠"mz_internal"."pg_type_all_databases"]␠("oid") # Record all transitive dependencies (tables, sources, views, mvs) of indexes on # the mz_catalog_server cluster. @@ -255,6 +255,18 @@ mz_builtin_tables object_type mz_builtin_tables oid mz_builtin_tables privileges mz_builtin_tables schema_name +mz_builtin_types category +mz_builtin_types element_name +mz_builtin_types element_schema_name +mz_builtin_types name +mz_builtin_types oid +mz_builtin_types privileges +mz_builtin_types schema_name +mz_builtin_types type_kind +mz_builtin_types typinput +mz_builtin_types typlen +mz_builtin_types typreceive +mz_builtin_types typsend mz_builtin_views create_sql mz_builtin_views definition mz_builtin_views name diff --git a/test/sqllogictest/mz_types.slt b/test/sqllogictest/mz_types.slt new file mode 100644 index 0000000000000..441d47dd67888 --- /dev/null +++ b/test/sqllogictest/mz_types.slt @@ -0,0 +1,403 @@ +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. + +# Behavioural tests for the type relations: mz_catalog.mz_types, the per-kind +# mz_catalog.mz_array_types, mz_base_types, mz_list_types, mz_map_types and +# mz_pseudo_types, and mz_internal.mz_type_pg_metadata. Each is a +# BuiltinMaterializedView over mz_internal.mz_catalog_raw (see MZ_TYPES in +# src/catalog/src/builtin/mz_catalog.rs). User-defined types come from each +# item's persisted create_sql through parse_catalog_create_sql +# (src/catalog-decode/src/create_sql.rs). Builtin types come from +# mz_internal.mz_builtin_types, a generated constant view +# (make_builtin_types in src/catalog/src/builtin/builtin.rs), joined to their +# GidMapping records for ids. +# +# pg_type reads through all seven relations, so the values pinned here are a +# compatibility surface. + +mode cockroach + +# Stable object ids, so create_sql can name dependencies by id. +reset-server + +# --- user types --------------------------------------------------------------- + +# `int4_list` is the first object created after the reset, so it is u1. +statement ok +CREATE TYPE int4_list AS LIST (ELEMENT TYPE = int4) + +query TT +SELECT id, category FROM mz_types WHERE name = 'int4_list' +---- +u1 list + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'public' AND database_id IS NOT NULL) +FROM mz_types WHERE name = 'int4_list' +---- +true + +query T +SELECT owner_id = (SELECT id FROM mz_roles WHERE name = 'materialize') +FROM mz_types WHERE name = 'int4_list' +---- +true + +query T rowsort +SELECT unnest(privileges)::text FROM mz_types WHERE name = 'int4_list' +---- +=U/materialize +materialize=U/materialize + +query T multiline +SELECT create_sql FROM mz_types WHERE name = 'int4_list' +---- +CREATE TYPE "materialize"."public"."int4_list" AS LIST (ELEMENT TYPE = [s20 AS "pg_catalog"."int4"]) +EOF + +query T multiline +SELECT redacted_create_sql FROM mz_types WHERE name = 'int4_list' +---- +CREATE TYPE materialize.public.int4_list AS LIST (ELEMENT TYPE = [s20 AS pg_catalog.int4]) +EOF + +# The oid column is read out of the durable JSON, while a regtype cast resolves +# the name through the in-memory catalog. Two independent paths that must agree. +query T +SELECT oid = 'int4_list'::regtype::oid FROM mz_types WHERE name = 'int4_list' +---- +true + +# --- mz_list_types ------------------------------------------------------------ + +query TT +SELECT element_id = (SELECT id FROM mz_types WHERE name = 'int4'), element_modifiers IS NULL +FROM mz_list_types WHERE id = 'u1' +---- +true true + +# Element type modifiers come back as a bigint list. +statement ok +CREATE TYPE numeric_list AS LIST (ELEMENT TYPE = numeric(38, 2)) + +query TT +SELECT element_modifiers::text, pg_typeof(element_modifiers)::text = 'bigint list' +FROM mz_list_types l JOIN mz_types t USING (id) WHERE t.name = 'numeric_list' +---- +{38,2} true + +# --- mz_map_types ------------------------------------------------------------- + +statement ok +CREATE TYPE text_numeric_map AS MAP (KEY TYPE = text, VALUE TYPE = numeric(5, 2)) + +# A map's category is pseudo, as TypeCategory::from_catalog_type reports it. +query T +SELECT category FROM mz_types WHERE name = 'text_numeric_map' +---- +pseudo + +query TTTT +SELECT + key_id = (SELECT id FROM mz_types WHERE name = 'text'), + value_id = (SELECT id FROM mz_types WHERE name = 'numeric'), + key_modifiers IS NULL, + value_modifiers::text +FROM mz_map_types m JOIN mz_types t USING (id) WHERE t.name = 'text_numeric_map' +---- +true true true {5,2} + +# --- record types ------------------------------------------------------------- + +# A record type has no per-kind relation of its own and is reported as a base +# type. +statement ok +CREATE TYPE rec AS (a int4, b text) + +query T +SELECT category FROM mz_types WHERE name = 'rec' +---- +composite + +query I +SELECT count(*) FROM mz_base_types b JOIN mz_types t USING (id) WHERE t.name = 'rec' +---- +1 + +# User-defined types never carry PostgreSQL metadata. +query I +SELECT count(*) +FROM mz_internal.mz_type_pg_metadata m JOIN mz_types t USING (id) +WHERE t.id LIKE 'u%' +---- +0 + +# --- builtin types ------------------------------------------------------------ + +query TTTTT +SELECT id LIKE 's%', owner_id, create_sql IS NULL, redacted_create_sql IS NULL, category +FROM mz_types WHERE name = 'int4' +---- +true s1 true true numeric + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'pg_catalog' AND database_id IS NULL) +FROM mz_types WHERE name = 'int4' +---- +true + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'mz_catalog' AND database_id IS NULL) +FROM mz_types WHERE name = 'uint2' +---- +true + +# The oid is the compiled-in PostgreSQL oid, and the regtype cast agrees. +query IT +SELECT oid, oid = 'int4'::regtype::oid FROM mz_types WHERE name = 'int4' +---- +23 true + +query T rowsort +SELECT unnest(privileges)::text FROM mz_types WHERE name = 'int4' +---- +=U/mz_system +mz_system=U/mz_system + +# Exactly once: the user and builtin branches must not both claim a row. +query I +SELECT count(*) FROM mz_types WHERE name = 'int4' +---- +1 + +# --- mz_array_types ----------------------------------------------------------- + +# Array types are all builtin; element references resolve to the element's id. +query T +SELECT t.name +FROM mz_array_types a +JOIN mz_types arr ON a.id = arr.id +JOIN mz_types t ON a.element_id = t.id +WHERE arr.name = '_int4' +---- +int4 + +query I +SELECT count(*) FROM mz_array_types WHERE id LIKE 'u%' +---- +0 + +# --- mz_pseudo_types and mz_base_types ---------------------------------------- + +query T rowsort +SELECT t.name +FROM mz_pseudo_types p JOIN mz_types t USING (id) +WHERE t.name IN ('anyelement', 'record', 'list', 'map', 'int4') +---- +anyelement +list +map +record + +# Range types have no per-kind relation of their own and are base types. +query T rowsort +SELECT t.name +FROM mz_base_types b JOIN mz_types t USING (id) +WHERE t.name IN ('int4', 'int4range', '_int4', 'anyelement') +---- +int4 +int4range + +# --- mz_type_pg_metadata ------------------------------------------------------ + +query IIII +SELECT typinput, typreceive, typsend, typlen +FROM mz_internal.mz_type_pg_metadata m JOIN mz_types t USING (id) +WHERE t.name = 'int4' +---- +42 2406 2407 4 + +# typlen is the length pgwire reports for a column of the type: -1 for +# variable-length types, NULL for pseudo-types and arrays of them, which have +# no scalar type. +query TT rowsort +SELECT t.name, m.typlen::text +FROM mz_internal.mz_type_pg_metadata m JOIN mz_types t USING (id) +WHERE t.name IN ('_int4', 'text', 'record', '_record', 'anyelement', 'int4range') +---- +_int4 -1 +_record NULL +anyelement NULL +int4range -1 +record NULL +text -1 + +# A type without PostgreSQL metadata has no row. +query I +SELECT count(*) +FROM mz_internal.mz_type_pg_metadata m JOIN mz_types t USING (id) +WHERE t.name = 'uint2' +---- +0 + +# --- Cross-relation invariants ------------------------------------------------ + +# Every type is in exactly one per-kind relation, and every per-kind row is a +# type. +query I +SELECT count(*) FROM ( + SELECT id FROM mz_types + EXCEPT ALL + SELECT id FROM ( + SELECT id FROM mz_array_types + UNION ALL SELECT id FROM mz_base_types + UNION ALL SELECT id FROM mz_list_types + UNION ALL SELECT id FROM mz_map_types + UNION ALL SELECT id FROM mz_pseudo_types + ) +) +---- +0 + +query I +SELECT count(*) FROM ( + SELECT id FROM ( + SELECT id FROM mz_array_types + UNION ALL SELECT id FROM mz_base_types + UNION ALL SELECT id FROM mz_list_types + UNION ALL SELECT id FROM mz_map_types + UNION ALL SELECT id FROM mz_pseudo_types + ) + EXCEPT ALL + SELECT id FROM mz_types +) +---- +0 + +query I +SELECT count(*) FROM ( + SELECT id FROM mz_internal.mz_type_pg_metadata + EXCEPT ALL + SELECT id FROM mz_types +) +---- +0 + +# Every builtin type the catalog compiles in must be reported... +query T +SELECT array_agg(name ORDER BY name) FROM ( + SELECT name FROM mz_internal.mz_builtin_types + EXCEPT + SELECT name FROM mz_types WHERE id LIKE 's%' +) +---- +NULL + +# ...and nothing else: every builtin row traces back to the reporter. +query T +SELECT array_agg(name ORDER BY name) FROM ( + SELECT name FROM mz_types WHERE id LIKE 's%' + EXCEPT + SELECT name FROM mz_internal.mz_builtin_types +) +---- +NULL + +# The reporter view backing these MVs is itself a builtin view, reported +# through mz_views with placeholder SQL. See mz_views.slt for the full contract. +query I +SELECT count(*) FROM mz_views WHERE name = 'mz_builtin_types' +---- +1 + +# --- pg_type reads through all seven relations -------------------------------- + +query TTTII rowsort +SELECT typname, typtype::text, typcategory::text, typlen, typelem +FROM pg_type +WHERE typname IN ('int4', '_int4', 'anyelement', 'int4_list', 'text_numeric_map', 'rec') +---- +_int4 b A -1 23 +anyelement p P NULL 0 +int4 b N 4 0 +int4_list l U NULL 23 +rec b C NULL 0 +text_numeric_map m P NULL 0 + +# --- Lifecycle ---------------------------------------------------------------- + +statement ok +DROP TYPE rec + +query I +SELECT count(*) FROM mz_types WHERE name = 'rec' +---- +0 + +query I +SELECT count(*) FROM mz_base_types WHERE id LIKE 'u%' +---- +0 + +# --- Robustness over the whole catalog ---------------------------------------- + +# The MVs run parse_catalog_create_sql over every Item row in the catalog, so a +# single item whose create_sql the parser rejects makes every type relation +# unreadable rather than dropping one row. Create one item of every kind +# reachable here and confirm the relations still resolve. + +statement ok +CREATE TABLE t (a int4_list) + +statement ok +CREATE VIEW v AS SELECT a FROM t + +statement ok +CREATE MATERIALIZED VIEW mv AS SELECT count(*) FROM t + +statement ok +CREATE INDEX t_idx ON t (a) + +statement ok +CREATE SECRET sec AS 'hunter2' + +query T +SELECT count(*) > 0 FROM mz_types +---- +true + +query I +SELECT count(*) FROM mz_list_types WHERE id LIKE 'u%' +---- +2 + +# --- NOT NULL invariants ------------------------------------------------------ + +# The MVs declare ASSERT NOT NULL for these columns. +query I +SELECT count(*) +FROM mz_types +WHERE id IS NULL + OR oid IS NULL + OR schema_id IS NULL + OR name IS NULL + OR category IS NULL + OR owner_id IS NULL + OR privileges IS NULL +---- +0 + +query I +SELECT count(*) FROM mz_internal.mz_type_pg_metadata +WHERE id IS NULL OR typinput IS NULL OR typreceive IS NULL OR typsend IS NULL +---- +0 + +statement ok +DROP TABLE t CASCADE diff --git a/test/sqllogictest/mz_views.slt b/test/sqllogictest/mz_views.slt index d64ff930d12a2..a88b6723835d5 100644 --- a/test/sqllogictest/mz_views.slt +++ b/test/sqllogictest/mz_views.slt @@ -194,6 +194,7 @@ mz_builtin_log_indexes mz_builtin_materialized_views mz_builtin_sources mz_builtin_tables +mz_builtin_types mz_builtin_views query T multiline diff --git a/test/sqllogictest/oid.slt b/test/sqllogictest/oid.slt index e02e9d8412121..0d2dea7ed33d3 100644 --- a/test/sqllogictest/oid.slt +++ b/test/sqllogictest/oid.slt @@ -1265,3 +1265,4 @@ SELECT oid, name FROM mz_objects WHERE id LIKE 's%' AND oid < 20000 ORDER BY oid 17131 max 17132 min 17138 jq +17139 mz_builtin_types diff --git a/test/testdrive/catalog.td b/test/testdrive/catalog.td index 42af1c2aa7dc1..55ff5f7a5d3a2 100644 --- a/test/testdrive/catalog.td +++ b/test/testdrive/catalog.td @@ -510,19 +510,13 @@ mz_cluster_replica_frontiers source "" > SHOW TABLES FROM mz_catalog name comment -------------------------------------- -mz_array_types "" -mz_base_types "" mz_cluster_replica_sizes "" mz_columns "" mz_egress_ips "" mz_functions "" mz_index_columns "" -mz_list_types "" -mz_map_types "" mz_operators "" -mz_pseudo_types "" mz_role_auth "" -mz_types "" > SHOW VIEWS FROM mz_catalog name comment @@ -561,6 +555,12 @@ mz_system_privileges mz_catalog_server "" mz_materialized_views mz_catalog_server "" mz_tables mz_catalog_server "" mz_views mz_catalog_server "" +mz_types mz_catalog_server "" +mz_array_types mz_catalog_server "" +mz_base_types mz_catalog_server "" +mz_list_types mz_catalog_server "" +mz_map_types mz_catalog_server "" +mz_pseudo_types mz_catalog_server "" # Check default sources, tables, and views in mz_catalog_unstable. @@ -612,7 +612,6 @@ mz_replica_hydration_history "" mz_sessions "" mz_storage_usage_by_shard "" mz_subscriptions "" -mz_type_pg_metadata "" mz_webhook_sources "" > SHOW VIEWS FROM mz_internal @@ -624,6 +623,7 @@ mz_builtin_log_indexes "" mz_builtin_materialized_views "" mz_builtin_sources "" mz_builtin_tables "" +mz_builtin_types "" mz_builtin_views "" mz_cluster_deployment_lineage "" mz_cluster_replica_history "" @@ -743,6 +743,7 @@ mz_replacements mz_catalog_server "" mz_replica_system_parameters mz_catalog_server "" mz_source_references mz_catalog_server "" mz_sql_server_source_tables mz_catalog_server "" +mz_type_pg_metadata mz_catalog_server "" # Check default sources, tables, and views in mz_introspection. @@ -840,7 +841,7 @@ test_table "" # `SHOW TABLES` and `mz_tables` should agree. > SELECT COUNT(*) FROM mz_tables WHERE id LIKE 's%' -25 +18 # There is one entry in mz_indexes for each field_number/expression of the index. > SELECT COUNT(id) FROM mz_indexes WHERE id LIKE 's%'