diff --git a/doc/user/content/reference/system-catalog/mz_internal.md b/doc/user/content/reference/system-catalog/mz_internal.md index bc2694d5c903f..ad6f3608075af 100644 --- a/doc/user/content/reference/system-catalog/mz_internal.md +++ b/doc/user/content/reference/system-catalog/mz_internal.md @@ -1500,6 +1500,8 @@ The `mz_webhook_sources` table contains a row for each webhook source in the sys + + diff --git a/src/adapter/src/catalog/builtin_table_updates.rs b/src/adapter/src/catalog/builtin_table_updates.rs index 24ea3082ccf43..0cc43bb95a40b 100644 --- a/src/adapter/src/catalog/builtin_table_updates.rs +++ b/src/adapter/src/catalog/builtin_table_updates.rs @@ -21,13 +21,13 @@ use mz_catalog::builtin::{ MZ_LICENSE_KEYS, MZ_LIST_TYPES, MZ_MAP_TYPES, MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES, MZ_OBJECT_DEPENDENCIES, MZ_OBJECT_GLOBAL_IDS, MZ_OPERATORS, MZ_PSEUDO_TYPES, MZ_REPLACEMENTS, MZ_ROLE_AUTH, MZ_SESSIONS, MZ_SINKS, MZ_SOURCE_REFERENCES, MZ_STORAGE_USAGE_BY_SHARD, - MZ_SUBSCRIPTIONS, MZ_TABLES, MZ_TYPE_PG_METADATA, MZ_TYPES, MZ_VIEWS, MZ_WEBHOOKS_SOURCES, + MZ_SUBSCRIPTIONS, MZ_TYPE_PG_METADATA, MZ_TYPES, MZ_WEBHOOKS_SOURCES, }; use mz_catalog::durable::SourceReferences; use mz_catalog::memory::error::Error; use mz_catalog::memory::objects::{ CatalogEntry, CatalogItem, DataSourceDesc, Func, Index, MaterializedView, Sink, Table, - TableDataSource, Type, View, + TableDataSource, Type, }; use mz_expr::MirScalarExpr; use mz_license_keys::ValidatedLicenseKey; @@ -163,13 +163,6 @@ impl CatalogState { let privileges = privileges_row.unpack_first(); let mut updates = match entry.item() { CatalogItem::Index(index) => self.pack_index_update(id, index, diff), - CatalogItem::Table(table) => { - // Source-table metadata (mz_postgres/mysql/sql_server/kafka_source_tables) - // is now derived from the persisted create_sql by materialized - // views over mz_catalog_raw, so ingestion-export tables need no - // special packing here. - self.pack_table_update(id, oid, schema_id, name, owner_id, privileges, diff, table) - } CatalogItem::Source(source) => { match &source.data_source { DataSourceDesc::Webhook { .. } => { @@ -186,9 +179,6 @@ impl CatalogState { | DataSourceDesc::Catalog => vec![], } } - CatalogItem::View(view) => { - self.pack_view_update(id, oid, schema_id, name, owner_id, privileges, view, diff) - } CatalogItem::MaterializedView(mview) => { self.pack_materialized_view_update(id, mview, diff) } @@ -201,7 +191,10 @@ impl CatalogState { CatalogItem::Func(func) => { self.pack_func_update(id, schema_id, name, owner_id, func, diff) } - CatalogItem::Log(_) | CatalogItem::Secret(_) => vec![], + CatalogItem::Table(_) + | CatalogItem::View(_) + | CatalogItem::Log(_) + | CatalogItem::Secret(_) => vec![], // Connection details (mz_kafka_connections, mz_ssh_tunnel_connections, // mz_aws_connections, mz_aws_privatelink_connections) are now derived // from the persisted create_sql by materialized views over @@ -330,110 +323,6 @@ impl CatalogState { ) } - fn pack_table_update( - &self, - id: CatalogItemId, - oid: u32, - schema_id: &SchemaSpecifier, - name: &str, - owner_id: &RoleId, - privileges: Datum, - diff: Diff, - table: &Table, - ) -> Vec> { - let redacted = table.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() - }); - let source_id = if let TableDataSource::DataSource { - desc: DataSourceDesc::IngestionExport { ingestion_id, .. }, - .. - } = &table.data_source - { - Some(ingestion_id.to_string()) - } else { - None - }; - - vec![BuiltinTableUpdate::row( - &*MZ_TABLES, - Row::pack_slice(&[ - Datum::String(&id.to_string()), - Datum::UInt32(oid), - Datum::String(&schema_id.to_string()), - Datum::String(name), - Datum::String(&owner_id.to_string()), - privileges, - if let Some(create_sql) = &table.create_sql { - Datum::String(create_sql) - } else { - Datum::Null - }, - if let Some(redacted) = &redacted { - Datum::String(redacted) - } else { - Datum::Null - }, - if let Some(source_id) = source_id.as_ref() { - Datum::String(source_id) - } else { - Datum::Null - }, - ]), - diff, - )] - } - - fn pack_view_update( - &self, - id: CatalogItemId, - oid: u32, - schema_id: &SchemaSpecifier, - name: &str, - owner_id: &RoleId, - privileges: Datum, - view: &View, - diff: Diff, - ) -> Vec> { - let create_stmt = mz_sql::parse::parse(&view.create_sql) - .unwrap_or_else(|e| { - panic!( - "create_sql cannot be invalid: `{}` --- error: `{}`", - view.create_sql, e - ) - }) - .into_element() - .ast; - let query = match &create_stmt { - Statement::CreateView(stmt) => &stmt.definition.query, - _ => unreachable!(), - }; - - let mut query_string = query.to_ast_string_stable(); - // PostgreSQL appends a semicolon in `pg_views.definition`, we - // do the same for compatibility's sake. - query_string.push(';'); - - vec![BuiltinTableUpdate::row( - &*MZ_VIEWS, - Row::pack_slice(&[ - Datum::String(&id.to_string()), - Datum::UInt32(oid), - Datum::String(&schema_id.to_string()), - Datum::String(name), - Datum::String(&query_string), - Datum::String(&owner_id.to_string()), - privileges, - Datum::String(&view.create_sql), - Datum::String(&create_stmt.to_ast_string_redacted()), - ]), - diff, - )] - } - fn pack_materialized_view_update( &self, 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 58b4ec995a88a..ecc20168c56e9 100644 --- a/src/adapter/src/catalog/open/builtin_schema_migration.rs +++ b/src/adapter/src/catalog/open/builtin_schema_migration.rs @@ -355,6 +355,23 @@ static MIGRATIONS: LazyLock> = LazyLock::new(|| { MZ_CATALOG_SCHEMA, "mz_aws_privatelink_connections", ), + // Converting mz_tables and mz_views from builtin tables to + // materialized views over mz_catalog_raw changes their catalog + // fingerprints, 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.38.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_tables", + ), + MigrationStep::replacement( + "26.38.0-dev.0", + CatalogItemType::MaterializedView, + MZ_CATALOG_SCHEMA, + "mz_views", + ), ] }); diff --git a/src/catalog/src/builtin.rs b/src/catalog/src/builtin.rs index 3dfb9e2ae8774..e56025c441d3d 100644 --- a/src/catalog/src/builtin.rs +++ b/src/catalog/src/builtin.rs @@ -1117,7 +1117,7 @@ pub static BUILTINS_STATIC: LazyLock>> = LazyLock::ne Builtin::Table(&MZ_COLUMNS), // mz_indexes is generated dynamically below with inlined builtin VALUES. Builtin::Table(&MZ_INDEX_COLUMNS), - Builtin::Table(&MZ_TABLES), + Builtin::MaterializedView(&MZ_TABLES), // mz_sources is generated dynamically below with inlined builtin VALUES. Builtin::Table(&MZ_SOURCE_REFERENCES), Builtin::MaterializedView(&MZ_POSTGRES_SOURCES), @@ -1126,7 +1126,7 @@ pub static BUILTINS_STATIC: LazyLock>> = LazyLock::ne Builtin::MaterializedView(&MZ_SQL_SERVER_SOURCE_TABLES), Builtin::MaterializedView(&MZ_KAFKA_SOURCE_TABLES), Builtin::Table(&MZ_SINKS), - Builtin::Table(&MZ_VIEWS), + Builtin::MaterializedView(&MZ_VIEWS), Builtin::Table(&MZ_TYPES), Builtin::Table(&MZ_TYPE_PG_METADATA), Builtin::Table(&MZ_ARRAY_TYPES), diff --git a/src/catalog/src/builtin/builtin.rs b/src/catalog/src/builtin/builtin.rs index e62910c4ab1fe..da7b1cee76400 100644 --- a/src/catalog/src/builtin/builtin.rs +++ b/src/catalog/src/builtin/builtin.rs @@ -25,8 +25,8 @@ use mz_sql::rbac; use mz_sql::session::user::MZ_SYSTEM_ROLE_ID; use crate::builtin::{ - Builtin, BuiltinLog, BuiltinMaterializedView, BuiltinSource, BuiltinView, Cardinality, - LinkProperties, Ontology, OntologyLink, PUBLIC_SELECT, + Builtin, BuiltinLog, BuiltinMaterializedView, BuiltinSource, BuiltinTable, BuiltinView, + Cardinality, LinkProperties, Ontology, OntologyLink, PUBLIC_SELECT, }; /// Generate builtin views reporting the given builtins. @@ -47,14 +47,32 @@ pub(super) fn builtins( Builtin::MaterializedView(x) => Some(*x), _ => None, }); + let table_iter = builtin_items.iter().filter_map(|b| match b { + Builtin::Table(x) => Some(*x), + _ => None, + }); + + let sources: &'static BuiltinView = + Box::leak(Box::new(make_builtin_sources(source_iter, log_iter))); + let materialized_views: &'static BuiltinView = + Box::leak(Box::new(make_builtin_materialized_views(mv_iter))); + let tables: &'static BuiltinView = Box::leak(Box::new(make_builtin_tables(table_iter))); - let sources = make_builtin_sources(source_iter, log_iter); - let materialized_views = make_builtin_materialized_views(mv_iter); + // 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 view_iter = builtin_items.iter().filter_map(|b| match b { + Builtin::View(x) => Some(*x), + _ => None, + }); + let views: &'static BuiltinView = Box::leak(Box::new(make_builtin_views( + view_iter, + [sources, materialized_views, tables], + ))); - [sources, materialized_views].into_iter().map(|v| { - let static_ref = Box::leak(Box::new(v)); - Builtin::View(static_ref) - }) + [sources, materialized_views, tables, views] + .into_iter() + .map(Builtin::View) } fn make_builtin_sources( @@ -170,6 +188,145 @@ FROM (VALUES {values}) AS v(oid, schema_name, name, cluster_name, definition, pr } } +fn make_builtin_tables(iter: impl Iterator) -> BuiltinView { + let owner_priv = rbac::owner_privilege(ObjectType::Table, MZ_SYSTEM_ROLE_ID); + let values = iter + .map(|table| { + let schema = escaped_string_literal(table.schema); + let name = escaped_string_literal(table.name); + let privileges = make_privileges_sql(&table.access, &owner_priv); + format!("({}::oid, {}, {}, {})", table.oid, schema, name, privileges) + }) + .join(","); + let sql = format!( + " +SELECT oid, schema_name, name, privileges +FROM (VALUES {values}) AS v(oid, schema_name, name, privileges)" + ); + + BuiltinView { + name: "mz_builtin_tables", + schema: MZ_INTERNAL_SCHEMA, + oid: oid::VIEW_MZ_BUILTIN_TABLES_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( + "privileges", + SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false), + ) + // NOTE: The declared keys must exactly match the keys the + // optimizer derives from the generated VALUES list + // (`verify_builtin_descs` enforces this). Table names happen to + // be unique across builtin schemas today, so `name` is a key. If + // a table is ever added whose bare name collides with another + // schema's, drop the `name` key here. + .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, + } +} + +/// Generates `mz_internal.mz_builtin_views`, listing every builtin view, +/// including itself and the `generated` views. +/// +/// Views from `iter` are listed with their real definition and create SQL. +/// The generated views are instead listed with a short placeholder query. +/// Real SQL is impossible for `mz_builtin_views` itself, its definition would +/// have to contain its own text. It is impractical for the other generated +/// views, whose SQL embeds metadata about every builtin object. +/// `mz_builtin_materialized_views` for example carries the SQL of every +/// builtin materialized view, so re-embedding its definition here would +/// produce enormous rows that make `SELECT * FROM mz_views` unusable. +/// +/// The placeholder is a valid SQL statement, because `mz_views` applies +/// `mz_internal.redact_sql` to the `create_sql` column and that function +/// errors on unparseable input, which would poison the whole materialized +/// view. The placeholder also embeds the view's qualified name so that the +/// `definition` and `create_sql` columns stay unique across rows, which the +/// declared keys rely on. +fn make_builtin_views<'a>( + iter: impl Iterator, + generated: [&BuiltinView; 3], +) -> BuiltinView { + let owner_priv = rbac::owner_privilege(ObjectType::View, MZ_SYSTEM_ROLE_ID); + + let make_row = |oid: u32, schema: &str, name: &str, access: &[MzAclItem], create_sql: &str| { + let stmt = mz_sql::parse::parse(create_sql) + .expect("valid sql") + .into_element() + .ast; + let Statement::CreateView(stmt) = stmt else { + panic!("invalid builtin view SQL"); + }; + + let definition = format!("{};", stmt.definition.query.to_ast_string_stable()); + let definition = escaped_string_literal(&definition); + let create_sql = stmt.to_ast_string_stable(); + let create_sql = escaped_string_literal(&create_sql); + + let schema = escaped_string_literal(schema); + let name = escaped_string_literal(name); + let privileges = make_privileges_sql(access, &owner_priv); + + format!( + "({}::oid, {}, {}, {}, {}, {})", + oid, schema, name, definition, privileges, create_sql + ) + }; + + let mut view = BuiltinView { + name: "mz_builtin_views", + schema: MZ_INTERNAL_SCHEMA, + oid: oid::VIEW_MZ_BUILTIN_VIEWS_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("definition", SqlScalarType::String.nullable(false)) + .with_column( + "privileges", + SqlScalarType::Array(Box::new(SqlScalarType::MzAclItem)).nullable(false), + ) + .with_column("create_sql", SqlScalarType::String.nullable(false)) + // NOTE: The declared keys must exactly match the keys the + // optimizer derives from the generated VALUES list + // (`verify_builtin_descs` enforces this). + .with_key(vec![0]) + .with_key(vec![2]) + .with_key(vec![3]) + .with_key(vec![5]) + .finish(), + column_comments: Default::default(), + sql: "", + access: vec![PUBLIC_SELECT], + ontology: None, + }; + + let full_values = iter.map(|v| make_row(v.oid, v.schema, v.name, &v.access, &v.create_sql())); + let placeholder_values = generated.iter().copied().chain([&view]).map(|v| { + let create_sql = format!( + "CREATE VIEW {}.{} AS SELECT ''", + v.schema, v.name, v.schema, v.name + ); + make_row(v.oid, v.schema, v.name, &v.access, &create_sql) + }); + let values = full_values.chain(placeholder_values).join(","); + let sql = format!( + " +SELECT oid, schema_name, name, definition, privileges, create_sql +FROM (VALUES {values}) AS v(oid, schema_name, name, definition, privileges, create_sql)" + ); + + view.sql = Box::leak(sql.into_boxed_str()); + view +} + /// Convert the given list of [`MzAclItem`] to the equivalent SQL syntax. fn make_privileges_sql(privs: &[MzAclItem], owner_priv: &MzAclItem) -> String { let privs = privs.iter().chain_one(owner_priv); diff --git a/src/catalog/src/builtin/mz_catalog.rs b/src/catalog/src/builtin/mz_catalog.rs index c2fb6d2a75be2..1e9dc3a48c382 100644 --- a/src/catalog/src/builtin/mz_catalog.rs +++ b/src/catalog/src/builtin/mz_catalog.rs @@ -976,10 +976,11 @@ pub static MZ_INDEX_COLUMNS: LazyLock = LazyLock::new(|| BuiltinTa column_semantic_types: &[("index_id", SemanticType::CatalogItemId)], }), }); -pub static MZ_TABLES: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_TABLES: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_tables", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_TABLES_OID, + oid: oid::MV_MZ_TABLES_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .with_column("oid", SqlScalarType::Oid.nullable(false)) @@ -1019,6 +1020,68 @@ pub static MZ_TABLES: LazyLock = LazyLock::new(|| BuiltinTable { "The ID of the source associated with the table, if any. Corresponds to `mz_sources.id`.", ), ]), + // Temporary tables live in a per-session temporary schema that has no + // durable schema record. Their rows keep the temporary schema sentinel + // "0" that the previous builtin-table implementation exposed. + 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 owner_id, + ASSERT NOT NULL privileges +) AS +WITH + user_tables AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + (data->'value'->>'oid')::oid AS oid, + CASE WHEN data->'value'->>'ephemeral_owner_session' IS NULL + THEN mz_internal.parse_catalog_id(data->'value'->'schema_id') + ELSE '0' + END AS schema_id, + data->'value'->>'name' AS name, + 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, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'source_id' AS source_id + FROM mz_internal.mz_catalog_raw + WHERE + data->>'kind' = 'Item' AND + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'type' = 'table' + ), + 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' = '1' + ), + builtin_tables AS ( + SELECT + m.id, + t.oid, + s.id AS schema_id, + t.name, + '{MZ_SYSTEM_ROLE_ID}' AS owner_id, + t.privileges, + NULL::text AS create_sql, + NULL::text AS redacted_create_sql, + NULL::text AS source_id + FROM mz_internal.mz_builtin_tables t + JOIN builtin_mappings m USING (schema_name, name) + JOIN mz_schemas s ON s.name = t.schema_name + WHERE s.database_id IS NULL + ) +SELECT * FROM user_tables +UNION ALL +SELECT * FROM builtin_tables").into_boxed_str()), is_retained_metrics_object: true, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -1059,6 +1122,7 @@ pub static MZ_TABLES: LazyLock = LazyLock::new(|| BuiltinTable { ] }, }), +} }); pub static MZ_CONNECTIONS: LazyLock = LazyLock::new(|| { @@ -1342,10 +1406,11 @@ pub static MZ_SINKS: LazyLock = LazyLock::new(|| { }), } }); -pub static MZ_VIEWS: LazyLock = LazyLock::new(|| BuiltinTable { +pub static MZ_VIEWS: LazyLock = LazyLock::new(|| { + BuiltinMaterializedView { name: "mz_views", schema: MZ_CATALOG_SCHEMA, - oid: oid::TABLE_MZ_VIEWS_OID, + oid: oid::MV_MZ_VIEWS_OID, desc: RelationDesc::builder() .with_column("id", SqlScalarType::String.nullable(false)) .with_column("oid", SqlScalarType::Oid.nullable(false)) @@ -1382,6 +1447,74 @@ pub static MZ_VIEWS: LazyLock = LazyLock::new(|| BuiltinTable { "The redacted `CREATE` SQL statement for the view.", ), ]), + // Temporary views live in a per-session temporary schema that has no + // durable schema record. Their rows keep the temporary schema sentinel + // "0" that the previous builtin-table implementation exposed. + // + // The generated `mz_builtin_*` views appear here with placeholder + // definition and create SQL. See `make_builtin_views`. + 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 definition, + ASSERT NOT NULL owner_id, + ASSERT NOT NULL privileges, + ASSERT NOT NULL create_sql, + ASSERT NOT NULL redacted_create_sql +) AS +WITH + user_views AS ( + SELECT + mz_internal.parse_catalog_id(data->'key'->'gid') AS id, + (data->'value'->>'oid')::oid AS oid, + CASE WHEN data->'value'->>'ephemeral_owner_session' IS NULL + THEN mz_internal.parse_catalog_id(data->'value'->'schema_id') + ELSE '0' + END AS schema_id, + data->'value'->>'name' AS name, + mz_internal.parse_catalog_create_sql(data->'value'->'definition'->'V1'->>'create_sql')->>'definition' AS definition, + 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' = 'view' + ), + 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' = '4' + ), + builtin_views AS ( + SELECT + m.id, + v.oid, + s.id AS schema_id, + v.name, + v.definition, + '{MZ_SYSTEM_ROLE_ID}' AS owner_id, + v.privileges, + v.create_sql, + mz_internal.redact_sql(v.create_sql) AS redacted_create_sql + FROM mz_internal.mz_builtin_views v + JOIN builtin_mappings m USING (schema_name, name) + JOIN mz_schemas s ON s.name = v.schema_name + WHERE s.database_id IS NULL + ) +SELECT * FROM user_views +UNION ALL +SELECT * FROM builtin_views").into_boxed_str()), is_retained_metrics_object: false, access: vec![PUBLIC_SELECT], ontology: Some(Ontology { @@ -1413,6 +1546,7 @@ pub static MZ_VIEWS: LazyLock = LazyLock::new(|| BuiltinTable { ] }, }), +} }); pub static MZ_MATERIALIZED_VIEWS: LazyLock = LazyLock::new(|| { diff --git a/src/expr/src/scalar/func/impls/jsonb.rs b/src/expr/src/scalar/func/impls/jsonb.rs index 6929ea134192f..da17f3963e874 100644 --- a/src/expr/src/scalar/func/impls/jsonb.rs +++ b/src/expr/src/scalar/func/impls/jsonb.rs @@ -456,7 +456,15 @@ fn parse_catalog_create_sql<'a>(a: &'a str) -> Result { "connection" } - CreateView(_) => "view", + CreateView(stmt) => { + let mut definition = stmt.definition.query.to_ast_string_stable(); + // PostgreSQL appends a semicolon in `pg_views.definition`, we + // do the same for compatibility's sake. + definition.push(';'); + info.insert("definition", json!(definition)); + + "view" + } CreateMaterializedView(stmt) => { let Some(in_cluster) = stmt.in_cluster else { return Err("missing IN CLUSTER".into()); @@ -473,7 +481,13 @@ fn parse_catalog_create_sql<'a>(a: &'a str) -> Result { "materialized-view" } - CreateTable(_) | CreateTableFromSource(_) => "table", + CreateTable(_) => "table", + CreateTableFromSource(stmt) => { + let source_id = get_item_id(stmt.source)?; + info.insert("source_id", json!(source_id)); + + "table" + } CreateSource(stmt) => { let Some(in_cluster) = stmt.in_cluster else { return Err("missing IN CLUSTER".into()); @@ -1697,4 +1711,241 @@ mod tests { let out = super::parse_catalog_create_sql(sql).expect("ok"); assert_eq!(as_serde(out).get("envelope_type"), None); } + + // --- parse_catalog_create_sql -------------------------------------------- + + /// `type` for a `create_sql`, or the error message if parsing failed. + fn item_type(sql: &str) -> Result { + match super::parse_catalog_create_sql(sql) { + Ok(out) => match as_serde(out) { + serde_json::Value::Object(mut m) => match m.remove("type") { + Some(serde_json::Value::String(s)) => Ok(s), + other => panic!("no string `type` key: {other:?}"), + }, + other => panic!("not a JSON object: {other:?}"), + }, + Err(EvalError::InvalidCatalogJson(msg)) => Err(msg.to_string()), + Err(e) => panic!("unexpected error variant: {e:?}"), + } + } + + fn view_sql(query: &str) -> String { + format!("CREATE VIEW \"materialize\".\"public\".\"v\" AS {query}") + } + + /// `definition` for a `CREATE VIEW` whose query is `query`. + fn view_definition(query: &str) -> String { + match as_serde(super::parse_catalog_create_sql(&view_sql(query)).expect("ok")) { + serde_json::Value::Object(mut m) => match m.remove("definition") { + Some(serde_json::Value::String(s)) => s, + other => panic!("no string `definition` key: {other:?}"), + }, + other => panic!("not a JSON object: {other:?}"), + } + } + + /// `mz_tables` and `mz_views` select rows by + /// `parse_catalog_create_sql(...)->>'type'`, and the function runs over + /// every `Item` row in the catalog, so a statement kind that changes its + /// reported type silently gains or loses rows in those relations. Pin the + /// type of every kind the catalog can hold. + #[mz_ore::test] + #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux` + fn catalog_item_type_per_statement_kind() { + let cases = [ + ( + "CREATE TABLE \"materialize\".\"public\".\"t\" (a int4)", + "table", + ), + // A table created from a source, and a webhook table, are both + // `table`, so both land in mz_tables. + ( + "CREATE TABLE \"materialize\".\"public\".\"tbl\" \ + FROM SOURCE [u1 AS \"materialize\".\"public\".\"src\"] \ + (REFERENCE = \"topic\") FORMAT TEXT", + "table", + ), + ( + "CREATE TABLE \"materialize\".\"public\".\"wht\" FROM WEBHOOK BODY FORMAT JSON", + "table", + ), + ( + "CREATE VIEW \"materialize\".\"public\".\"v\" AS SELECT 1", + "view", + ), + ( + "CREATE MATERIALIZED VIEW \"materialize\".\"public\".\"mv\" \ + IN CLUSTER [u1] AS SELECT 1", + "materialized-view", + ), + ( + "CREATE SOURCE \"materialize\".\"public\".\"lg\" \ + IN CLUSTER [u1] FROM LOAD GENERATOR COUNTER", + "source", + ), + ( + "CREATE SOURCE \"materialize\".\"public\".\"wh\" \ + IN CLUSTER [u1] FROM WEBHOOK BODY FORMAT JSON", + "source", + ), + ( + "CREATE SUBSOURCE \"materialize\".\"public\".\"sub\" (id int4) \ + OF SOURCE [u1 AS \"materialize\".\"public\".\"src\"]", + "subsource", + ), + ( + "CREATE SUBSOURCE \"materialize\".\"public\".\"progress\" (id int4) \ + WITH (PROGRESS)", + "subsource", + ), + ( + "CREATE SINK \"materialize\".\"public\".\"snk\" IN CLUSTER [u1] \ + FROM [u1 AS \"materialize\".\"public\".\"t\"] \ + INTO KAFKA CONNECTION [u2 AS \"materialize\".\"public\".\"c\"] \ + (TOPIC 'tp') FORMAT JSON ENVELOPE DEBEZIUM", + "sink", + ), + ( + "CREATE INDEX \"i\" IN CLUSTER [u1] \ + ON [u1 AS \"materialize\".\"public\".\"t\"] (\"a\")", + "index", + ), + ( + "CREATE TYPE \"materialize\".\"public\".\"ty\" AS LIST (ELEMENT TYPE = int4)", + "type", + ), + ( + "CREATE SECRET \"materialize\".\"public\".\"s\" AS 'x'", + "secret", + ), + ( + "CREATE CONNECTION \"materialize\".\"public\".\"c\" \ + TO KAFKA (BROKER 'b', SECURITY PROTOCOL PLAINTEXT)", + "connection", + ), + ]; + for (sql, expected) in cases { + assert_eq!(item_type(sql).as_deref(), Ok(expected), "for {sql}"); + } + } + + /// `mz_views.definition` is produced here. It used to be produced by + /// `pack_view_update` in the adapter, so the exact rendering is a + /// compatibility surface: `pg_views.definition` reads it. + #[mz_ore::test] + #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux` + fn catalog_view_definition() { + // Identifiers and function names come back fully quoted, literals + // untouched, and PostgreSQL's trailing semicolon is appended. + assert_eq!(view_definition("SELECT 1"), "SELECT 1;"); + assert_eq!( + view_definition("WITH c AS (SELECT 1 AS a) SELECT a FROM c"), + "WITH \"c\" AS (SELECT 1 AS \"a\") SELECT \"a\" FROM \"c\";" + ); + assert_eq!( + view_definition("SELECT 1 UNION ALL SELECT 2"), + "SELECT 1 UNION ALL SELECT 2;" + ); + assert_eq!( + view_definition("SELECT (SELECT max(a) FROM [u1 AS \"materialize\".\"public\".\"t\"])"), + "SELECT (SELECT \"max\"(\"a\") FROM [u1 AS \"materialize\".\"public\".\"t\"]);" + ); + // Identifiers needing quotes, an embedded double quote, non-ASCII, an + // embedded single quote in a literal, and ORDER BY all survive. + assert_eq!( + view_definition( + "SELECT \"a b\", \"héllo\", \"q\"\"x\" \ + FROM [u1 AS \"materialize\".\"public\".\"t\"] \ + WHERE s = 'lit''eral' AND n = 42 ORDER BY 1" + ), + "SELECT \"a b\", \"héllo\", \"q\"\"x\" \ + FROM [u1 AS \"materialize\".\"public\".\"t\"] \ + WHERE \"s\" = 'lit''eral' AND \"n\" = 42 ORDER BY 1;" + ); + } + + /// The rendering must be a fixed point: `pg_views` consumers re-issue + /// `definition` as the body of a new view, so a second pass through the + /// parser has to produce the identical string. The trailing `;` is part of + /// what gets re-parsed. + #[mz_ore::test] + #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux` + fn catalog_view_definition_is_idempotent() { + for query in [ + "SELECT 1", + "WITH c AS (SELECT 1 AS a) SELECT a FROM c", + "SELECT 1 UNION ALL SELECT 2", + "SELECT \"a b\", \"q\"\"x\" FROM [u1 AS \"materialize\".\"public\".\"t\"] \ + WHERE s = 'lit''eral' ORDER BY 1", + ] { + let once = view_definition(query); + assert_eq!( + view_definition(&once), + once, + "not a fixed point for {query}" + ); + } + } + + /// `mz_tables.source_id` comes from this key. A table with no source must + /// omit it entirely, so the MV's `->>'source_id'` yields SQL NULL. + #[mz_ore::test] + #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux` + fn catalog_table_source_id() { + let from_source = as_serde( + super::parse_catalog_create_sql( + "CREATE TABLE \"materialize\".\"public\".\"tbl\" \ + FROM SOURCE [u1 AS \"materialize\".\"public\".\"src\"] \ + (REFERENCE = \"topic\") FORMAT TEXT", + ) + .expect("ok"), + ); + assert_eq!(from_source, json!({ "type": "table", "source_id": "u1" })); + + for sql in [ + "CREATE TABLE \"materialize\".\"public\".\"t\" (a int4)", + "CREATE TABLE \"materialize\".\"public\".\"wht\" FROM WEBHOOK BODY FORMAT JSON", + ] { + assert_eq!( + as_serde(super::parse_catalog_create_sql(sql).expect("ok")), + json!({ "type": "table" }), + "for {sql}" + ); + } + } + + /// Every error here is fatal to the whole of `mz_tables`/`mz_views`, not to + /// one row: the MVs call this function inside their `WHERE` clause, so an + /// item the parser rejects makes the relation unreadable for everyone. + #[mz_ore::test] + #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux` + fn catalog_create_sql_errors() { + assert_eq!( + item_type("this is not sql"), + Err( + "failed to parse create_sql: Expected a keyword at the beginning of a statement, \ + found identifier \"this\"" + .to_string() + ) + ); + assert_eq!( + item_type("CREATE TABLE t (a int4); CREATE TABLE u (b int4)"), + Err("expected a single statement, found 2".to_string()) + ); + // A statement that is not a CREATE of a catalog item, e.g. if a future + // change persists something else in an Item record. + assert_eq!( + item_type("SELECT 1"), + Err("not a CREATE item statement".to_string()) + ); + // Catalog `create_sql` always names items by id. An unresolved name + // means the record was written wrong. + assert_eq!( + item_type( + "CREATE TABLE \"materialize\".\"public\".\"tbl\" \ + FROM SOURCE src (REFERENCE = \"topic\")" + ), + Err("unresolved item name".to_string()) + ); + } } diff --git a/src/pgrepr-consts/src/oid.rs b/src/pgrepr-consts/src/oid.rs index 9d1c0672e5086..cf91d3ad020b2 100644 --- a/src/pgrepr-consts/src/oid.rs +++ b/src/pgrepr-consts/src/oid.rs @@ -429,12 +429,10 @@ pub const MV_MZ_SCHEMAS_OID: u32 = 16703; pub const TABLE_MZ_COLUMNS_OID: u32 = 16704; pub const MV_MZ_INDEXES_OID: u32 = 16705; pub const TABLE_MZ_INDEX_COLUMNS_OID: u32 = 16706; -pub const TABLE_MZ_TABLES_OID: u32 = 16707; pub const MV_MZ_CONNECTIONS_OID: u32 = 16708; pub const MV_MZ_SSH_TUNNEL_CONNECTIONS_OID: u32 = 16709; pub const MV_MZ_SOURCES_OID: u32 = 16710; pub const TABLE_MZ_SINKS_OID: u32 = 16711; -pub const TABLE_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; @@ -822,3 +820,7 @@ pub const FUNC_PARSE_CONNECTION_DETAILS_OID: u32 = 17112; pub const FUNC_MZ_AWS_ACCOUNT_ID_OID: u32 = 17113; pub const FUNC_MZ_AWS_EXTERNAL_ID_PREFIX_OID: u32 = 17114; pub const FUNC_MZ_AWS_CONNECTION_ROLE_ARN_OID: u32 = 17115; +pub const VIEW_MZ_BUILTIN_TABLES_OID: u32 = 17116; +pub const VIEW_MZ_BUILTIN_VIEWS_OID: u32 = 17117; +pub const MV_MZ_TABLES_OID: u32 = 17118; +pub const MV_MZ_VIEWS_OID: u32 = 17119; diff --git a/test/0dt/mzcompose.py b/test/0dt/mzcompose.py index c15458d8213aa..31776e4e57b52 100644 --- a/test/0dt/mzcompose.py +++ b/test/0dt/mzcompose.py @@ -1780,7 +1780,7 @@ def get_persist_shard_id(item_id: str, service: str) -> str: ) mz_tables_gid = c.sql_query( - "SELECT id FROM mz_tables WHERE name = 'mz_tables'", + "SELECT id FROM mz_materialized_views WHERE name = 'mz_tables'", service="mz_old", )[0][0] mv_gid = c.sql_query( @@ -1810,7 +1810,7 @@ def get_persist_shard_id(item_id: str, service: str) -> str: c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, "mz_new") new_mz_tables_gid = c.sql_query( - "SELECT id FROM mz_tables WHERE name = 'mz_tables'", + "SELECT id FROM mz_materialized_views WHERE name = 'mz_tables'", service="mz_new", reuse_connection=False, )[0][0] @@ -1863,7 +1863,7 @@ def get_persist_shard_id(item_id: str, service: str) -> str: ) mz_tables_gid = c.sql_query( - "SELECT id FROM mz_tables WHERE name = 'mz_tables'", + "SELECT id FROM mz_materialized_views WHERE name = 'mz_tables'", service="mz_old", )[0][0] mv_gid = c.sql_query( @@ -1894,7 +1894,7 @@ def get_persist_shard_id(item_id: str, service: str) -> str: c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, "mz_new") new_mz_tables_gid = c.sql_query( - "SELECT id FROM mz_tables WHERE name = 'mz_tables'", + "SELECT id FROM mz_materialized_views WHERE name = 'mz_tables'", service="mz_new", reuse_connection=False, )[0][0] diff --git a/test/cluster/mzcompose.py b/test/cluster/mzcompose.py index ab42df766d60f..08b18f9d97e5d 100644 --- a/test/cluster/mzcompose.py +++ b/test/cluster/mzcompose.py @@ -4260,7 +4260,7 @@ def workflow_test_subscribe_hydration_status( FROM mz_internal.mz_subscriptions s, unnest(s.referenced_object_ids) as sroi(id) JOIN mz_introspection.mz_compute_hydration_times_per_worker h ON h.export_id = s.id - JOIN mz_tables t ON (t.id = sroi.id) + JOIN mz_materialized_views t ON (t.id = sroi.id) WHERE t.name = 'mz_tables' true """)) @@ -4275,7 +4275,7 @@ def workflow_test_subscribe_hydration_status( FROM mz_internal.mz_subscriptions s, unnest(s.referenced_object_ids) as sroi(id) JOIN mz_introspection.mz_compute_hydration_times_per_worker h ON h.export_id = s.id - JOIN mz_tables t ON (t.id = sroi.id) + JOIN mz_materialized_views t ON (t.id = sroi.id) WHERE t.name = 'mz_tables' """)) @@ -5649,9 +5649,9 @@ def workflow_test_adhoc_system_indexes( WHERE i.name = 'mz_test_idx1' """) assert output[0] == ("u1", "mz_tables", "mz_catalog_server"), output - output = c.sql_query("EXPLAIN SELECT * FROM mz_tables WHERE char_length(name) = 9") + output = c.sql_query("EXPLAIN SELECT * FROM mz_tables WHERE char_length(name) = 8") assert "mz_test_idx1" in output[0][0], output - output = c.sql_query("SELECT * FROM mz_tables WHERE char_length(name) = 9") + output = c.sql_query("SELECT * FROM mz_tables WHERE char_length(name) = 8") assert len(output) > 0 # The system user should be able to create a new index on an unstable diff --git a/test/sqllogictest/alter.slt b/test/sqllogictest/alter.slt index 44f3aa6575187..1935ee5ff34e5 100644 --- a/test/sqllogictest/alter.slt +++ b/test/sqllogictest/alter.slt @@ -24,7 +24,7 @@ ALTER SYSTEM SET enable_rbac_checks TO false; ---- COMPLETE 0 -query error must be owner of TABLE mz_catalog.mz_tables +query error mz_tables is a materialized view not a table ALTER TABLE mz_tables RENAME TO foo; query error must be owner of SOURCE mz_internal.mz_storage_shards @@ -33,7 +33,7 @@ ALTER SOURCE mz_internal.mz_storage_shards RENAME TO foo; simple conn=mz_system,user=mz_system ALTER TABLE mz_tables RENAME TO foo; ---- -db error: ERROR: system item 'mz_catalog.mz_tables' cannot be modified +db error: ERROR: mz_tables is a materialized view not a table simple conn=mz_system,user=mz_system ALTER SOURCE mz_internal.mz_storage_shards RENAME TO foo; diff --git a/test/sqllogictest/autogenerated/mz_internal.slt b/test/sqllogictest/autogenerated/mz_internal.slt index d4fc2da6c6547..68cd3a070168d 100644 --- a/test/sqllogictest/autogenerated/mz_internal.slt +++ b/test/sqllogictest/autogenerated/mz_internal.slt @@ -780,6 +780,8 @@ mz_aws_privatelink_connection_status_history mz_aws_privatelink_connection_statuses mz_builtin_materialized_views mz_builtin_sources +mz_builtin_tables +mz_builtin_views mz_catalog_raw mz_cluster_auto_scaling_strategies mz_cluster_deployment_lineage diff --git a/test/sqllogictest/catalog_server_explain.slt b/test/sqllogictest/catalog_server_explain.slt index 300281a804373..c01af7956ed6a 100644 --- a/test/sqllogictest/catalog_server_explain.slt +++ b/test/sqllogictest/catalog_server_explain.slt @@ -4981,7 +4981,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 (37 rows) + →Constant (39 rows) →Arrange (empty key) (#0{schema_name}) (#0{schema_name}, #1{name}) →Fused with Child Map/Filter/Project Project: #4, #3, #5 @@ -5287,6 +5287,110 @@ Target cluster: mz_catalog_server EOF +query T multiline +EXPLAIN MATERIALIZED VIEW "mz_catalog"."mz_tables"; +---- +mz_catalog.mz_tables: + →Union + →Fused with Child Map/Filter/Project + Project: #6..=#11, #4, #12, #13 + Filter: ("Item" = #3) AND ("table" = (#5 ->> "type")) + Map: (((#2 -> "definition") -> "V1") ->> "create_sql"), parse_catalog_create_sql(#4), parse_catalog_id((#1 -> "gid")), text_to_oid((#2 ->> "oid")), case when ((#2 ->> "ephemeral_owner_session")) IS NULL then parse_catalog_id(((#0{data} -> "value") -> "schema_id")) else "0" end, (#2 ->> "name"), parse_catalog_id((#2 -> "owner_id")), parse_catalog_privileges((#2 -> "privileges")), redact_sql(#4), (#5 ->> "source_id") + →Read mz_internal.mz_catalog_raw + →Delta Join [%0[#1{schema_name}, #2{name}] » %1:mz_catalog_raw[#0{schema_name}, #1{name}] » %2:mz_schemas[#1{name}]] [%1:mz_catalog_raw[#0{schema_name}] » %0[#1{schema_name}, #2{name}] » %2:mz_schemas[#1{name}]] [%2:mz_schemas[#1{name}] » %1:mz_catalog_raw[#0{schema_name}] » %0[#1{schema_name}, #2{name}]] + path %0: + Final closure: + Project: #3, #0, #4, #1, #5, #2, #6, #6, #6 + Map: "s1", null + path %1: + Final closure: + Project: #3, #0, #4, #1, #5, #2, #6, #6, #6 + Map: "s1", null + path %2: + Final closure: + Project: #3, #0, #4, #1, #5, #2, #6, #6, #6 + Map: "s1", null + →Arrange (#1{schema_name}, #2{name}) + →Constant (32 rows) + →Arrange (#0{schema_name}) (#0{schema_name}, #1{name}) + →Fused with Child Map/Filter/Project + Project: #5, #4, #6 + Filter: ("1" = (#1 ->> "object_type")) AND ("GidMapping" = #3) AND (#4) IS NOT NULL AND (#5) IS NOT NULL + Map: (#1 ->> "object_name"), (#1 ->> "schema_name"), ("s" || (#2 ->> "catalog_id")) + →Read mz_internal.mz_catalog_raw + →Arrange (#1{name}) + →Fused with Child Map/Filter/Project + Project: #1, #3 + Filter: (#0{database_id}) IS NULL + →Arranged mz_catalog.mz_schemas + Key: (#2{database_id}) + +Source mz_internal.mz_catalog_raw + project=(#0..=#3) + map=((#0{data} -> "key"), (#0{data} -> "value"), (#0{data} ->> "kind")) + +Used Indexes: + - mz_catalog.mz_schemas_ind (*** full scan ***) + +Target cluster: mz_catalog_server + +EOF + +query T multiline +EXPLAIN MATERIALIZED VIEW "mz_catalog"."mz_views"; +---- +mz_catalog.mz_views: + →Union + →Fused with Child Map/Filter/Project + Project: #6..=#12, #4, #13 + Filter: ("Item" = #3) AND ("view" = (#5 ->> "type")) + Map: (((#2 -> "definition") -> "V1") ->> "create_sql"), parse_catalog_create_sql(#4), parse_catalog_id((#1 -> "gid")), text_to_oid((#2 ->> "oid")), case when ((#2 ->> "ephemeral_owner_session")) IS NULL then parse_catalog_id(((#0{data} -> "value") -> "schema_id")) else "0" end, (#2 ->> "name"), (#5 ->> "definition"), parse_catalog_id((#2 -> "owner_id")), parse_catalog_privileges((#2 -> "privileges")), redact_sql(#4) + →Read mz_internal.mz_catalog_raw + →Delta Join [%0[#1{schema_name}, #2{name}] » %1:mz_catalog_raw[#0{schema_name}, #1{name}] » %2:mz_schemas[#1{name}]] [%1:mz_catalog_raw[#0{schema_name}] » %0[#1{schema_name}, #2{name}] » %2:mz_schemas[#1{name}]] [%2:mz_schemas[#1{name}] » %1:mz_catalog_raw[#0{schema_name}] » %0[#1{schema_name}, #2{name}]] + path %0: + Final closure: + Project: #5, #0, #6, #1, #2, #8, #3, #4, #7 + Map: "s1" + path %1: + after %0: + Project: #3, #1, #4..=#6, #0, #2, #7 + Map: redact_sql(#6{create_sql}) + Final closure: + Project: #5, #0, #6, #1, #2, #8, #3, #4, #7 + Map: "s1" + path %2: + after %0: + Project: #4, #1, #5..=#7, #2, #3, #8 + Map: redact_sql(#7{create_sql}) + Final closure: + Project: #5, #0, #6, #1, #2, #8, #3, #4, #7 + Map: "s1" + →Arrange (#1{schema_name}, #2{name}) + →Constant (192 rows) + →Arrange (#0{schema_name}) (#0{schema_name}, #1{name}) + →Fused with Child Map/Filter/Project + Project: #5, #4, #6 + Filter: ("4" = (#1 ->> "object_type")) AND ("GidMapping" = #3) AND (#4) IS NOT NULL AND (#5) IS NOT NULL + Map: (#1 ->> "object_name"), (#1 ->> "schema_name"), ("s" || (#2 ->> "catalog_id")) + →Read mz_internal.mz_catalog_raw + →Arrange (#1{name}) + →Fused with Child Map/Filter/Project + Project: #1, #3 + Filter: (#0{database_id}) IS NULL + →Arranged mz_catalog.mz_schemas + Key: (#2{database_id}) + +Source mz_internal.mz_catalog_raw + project=(#0..=#3) + map=((#0{data} -> "key"), (#0{data} -> "value"), (#0{data} ->> "kind")) + +Used Indexes: + - mz_catalog.mz_schemas_ind (*** full scan ***) + +Target cluster: mz_catalog_server + +EOF + query T multiline EXPLAIN MATERIALIZED VIEW "mz_internal"."mz_aws_connections"; ---- @@ -7946,7 +8050,7 @@ query T multiline EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_materialized_views"; ---- Explained Query (fast path): - →Constant (37 rows) + →Constant (39 rows) Target cluster: mz_catalog_server @@ -7962,6 +8066,26 @@ Target cluster: mz_catalog_server EOF +query T multiline +EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_tables"; +---- +Explained Query (fast path): + →Constant (32 rows) + +Target cluster: mz_catalog_server + +EOF + +query T multiline +EXPLAIN SELECT * FROM "mz_internal"."mz_builtin_views"; +---- +Explained Query (fast path): + →Constant (192 rows) + +Target cluster: mz_catalog_server + +EOF + query T multiline EXPLAIN SELECT * FROM "mz_internal"."mz_cluster_deployment_lineage"; ---- diff --git a/test/sqllogictest/information_schema_tables.slt b/test/sqllogictest/information_schema_tables.slt index 8ea3e4fdb71a7..4933d92477960 100644 --- a/test/sqllogictest/information_schema_tables.slt +++ b/test/sqllogictest/information_schema_tables.slt @@ -250,7 +250,7 @@ MATERIALIZED VIEW materialize mz_catalog mz_tables -BASE TABLE +MATERIALIZED VIEW materialize mz_catalog mz_timezone_abbreviations @@ -266,7 +266,7 @@ BASE TABLE materialize mz_catalog mz_views -BASE TABLE +MATERIALIZED VIEW materialize mz_internal mz_activity_log_thinned @@ -297,6 +297,14 @@ mz_builtin_sources VIEW materialize mz_internal +mz_builtin_tables +VIEW +materialize +mz_internal +mz_builtin_views +VIEW +materialize +mz_internal mz_catalog_raw SOURCE materialize diff --git a/test/sqllogictest/mz_catalog_server_index_accounting.slt b/test/sqllogictest/mz_catalog_server_index_accounting.slt index 3c108d65f8048..41b8983b93f9f 100644 --- a/test/sqllogictest/mz_catalog_server_index_accounting.slt +++ b/test/sqllogictest/mz_catalog_server_index_accounting.slt @@ -37,107 +37,107 @@ 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␠[s518␠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␠[s765␠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␠[s520␠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␠[s767␠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␠[s517␠AS␠"mz_internal"."mz_cluster_reconfigurations"]␠("cluster_id") -mz_cluster_replica_frontiers_ind CREATE␠INDEX␠"mz_cluster_replica_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s759␠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␠[s615␠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␠[s523␠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␠[s524␠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␠[s616␠AS␠"mz_internal"."mz_cluster_replica_name_history"]␠("id") -mz_cluster_replica_size_internal_ind CREATE␠INDEX␠"mz_cluster_replica_size_internal_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s513␠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␠[s512␠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␠[s525␠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␠[s526␠AS␠"mz_internal"."mz_cluster_replica_statuses"]␠("replica_id") -mz_cluster_replicas_ind CREATE␠INDEX␠"mz_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s522␠AS␠"mz_catalog"."mz_cluster_replicas"]␠("id") -mz_clusters_ind CREATE␠INDEX␠"mz_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s514␠AS␠"mz_catalog"."mz_clusters"]␠("id") -mz_columns_ind CREATE␠INDEX␠"mz_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s485␠AS␠"mz_catalog"."mz_columns"]␠("name") -mz_comments_ind CREATE␠INDEX␠"mz_comments_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s541␠AS␠"mz_internal"."mz_comments"]␠("id") +mz_cluster_reconfigurations_ind CREATE␠INDEX␠"mz_cluster_reconfigurations_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s519␠AS␠"mz_internal"."mz_cluster_reconfigurations"]␠("cluster_id") +mz_cluster_replica_frontiers_ind CREATE␠INDEX␠"mz_cluster_replica_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s761␠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␠[s617␠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␠[s525␠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␠[s526␠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␠[s618␠AS␠"mz_internal"."mz_cluster_replica_name_history"]␠("id") +mz_cluster_replica_size_internal_ind CREATE␠INDEX␠"mz_cluster_replica_size_internal_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s515␠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␠[s514␠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␠[s527␠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␠[s528␠AS␠"mz_internal"."mz_cluster_replica_statuses"]␠("replica_id") +mz_cluster_replicas_ind CREATE␠INDEX␠"mz_cluster_replicas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s524␠AS␠"mz_catalog"."mz_cluster_replicas"]␠("id") +mz_clusters_ind CREATE␠INDEX␠"mz_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s516␠AS␠"mz_catalog"."mz_clusters"]␠("id") +mz_columns_ind CREATE␠INDEX␠"mz_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s487␠AS␠"mz_catalog"."mz_columns"]␠("name") +mz_comments_ind CREATE␠INDEX␠"mz_comments_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s543␠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␠[s741␠AS␠"mz_internal"."mz_compute_dependencies"]␠("dependency_id") +mz_compute_dependencies_ind CREATE␠INDEX␠"mz_compute_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s743␠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␠[s750␠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␠[s752␠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␠[s520␠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␠[s746␠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␠[s745␠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␠[s744␠AS␠"mz_internal"."mz_console_cluster_utilization_overview"]␠("cluster_id") -mz_databases_ind CREATE␠INDEX␠"mz_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s483␠AS␠"mz_catalog"."mz_databases"]␠("name") +mz_connections_ind CREATE␠INDEX␠"mz_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s522␠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␠[s748␠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␠[s747␠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␠[s746␠AS␠"mz_internal"."mz_console_cluster_utilization_overview"]␠("cluster_id") +mz_databases_ind CREATE␠INDEX␠"mz_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s485␠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␠[s732␠AS␠"mz_internal"."mz_frontiers"]␠("object_id") -mz_hydration_statuses_ind CREATE␠INDEX␠"mz_hydration_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s761␠AS␠"mz_internal"."mz_hydration_statuses"]␠("object_id",␠"replica_id") -mz_indexes_ind CREATE␠INDEX␠"mz_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s486␠AS␠"mz_catalog"."mz_indexes"]␠("id") -mz_kafka_sources_ind CREATE␠INDEX␠"mz_kafka_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s480␠AS␠"mz_catalog"."mz_kafka_sources"]␠("id") -mz_materialized_views_ind CREATE␠INDEX␠"mz_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s544␠AS␠"mz_catalog"."mz_materialized_views"]␠("id") +mz_frontiers_ind CREATE␠INDEX␠"mz_frontiers_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s734␠AS␠"mz_internal"."mz_frontiers"]␠("object_id") +mz_hydration_statuses_ind CREATE␠INDEX␠"mz_hydration_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s763␠AS␠"mz_internal"."mz_hydration_statuses"]␠("object_id",␠"replica_id") +mz_indexes_ind CREATE␠INDEX␠"mz_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s488␠AS␠"mz_catalog"."mz_indexes"]␠("id") +mz_kafka_sources_ind CREATE␠INDEX␠"mz_kafka_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s482␠AS␠"mz_catalog"."mz_kafka_sources"]␠("id") +mz_materialized_views_ind CREATE␠INDEX␠"mz_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s546␠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_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s835␠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␠[s753␠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␠[s753␠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␠[s751␠AS␠"mz_internal"."mz_object_arrangement_sizes"]␠("replica_id") -mz_object_dependencies_ind CREATE␠INDEX␠"mz_object_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s481␠AS␠"mz_internal"."mz_object_dependencies"]␠("object_id") -mz_object_history_ind CREATE␠INDEX␠"mz_object_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s555␠AS␠"mz_internal"."mz_object_history"]␠("id") -mz_object_lifetimes_ind CREATE␠INDEX␠"mz_object_lifetimes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s556␠AS␠"mz_internal"."mz_object_lifetimes"]␠("id") -mz_object_transitive_dependencies_ind CREATE␠INDEX␠"mz_object_transitive_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s572␠AS␠"mz_internal"."mz_object_transitive_dependencies"]␠("object_id") -mz_objects_ind CREATE␠INDEX␠"mz_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s552␠AS␠"mz_catalog"."mz_objects"]␠("schema_id") +mz_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s837␠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␠[s755␠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␠[s755␠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␠[s753␠AS␠"mz_internal"."mz_object_arrangement_sizes"]␠("replica_id") +mz_object_dependencies_ind CREATE␠INDEX␠"mz_object_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s483␠AS␠"mz_internal"."mz_object_dependencies"]␠("object_id") +mz_object_history_ind CREATE␠INDEX␠"mz_object_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s557␠AS␠"mz_internal"."mz_object_history"]␠("id") +mz_object_lifetimes_ind CREATE␠INDEX␠"mz_object_lifetimes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s558␠AS␠"mz_internal"."mz_object_lifetimes"]␠("id") +mz_object_transitive_dependencies_ind CREATE␠INDEX␠"mz_object_transitive_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s574␠AS␠"mz_internal"."mz_object_transitive_dependencies"]␠("object_id") +mz_objects_ind CREATE␠INDEX␠"mz_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s554␠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␠[s716␠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␠[s712␠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␠[s828␠AS␠"mz_catalog"."mz_recent_storage_usage"]␠("object_id") -mz_roles_ind CREATE␠INDEX␠"mz_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s504␠AS␠"mz_catalog"."mz_roles"]␠("id") +mz_recent_activity_log_thinned_ind CREATE␠INDEX␠"mz_recent_activity_log_thinned_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s718␠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␠[s714␠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␠[s830␠AS␠"mz_catalog"."mz_recent_storage_usage"]␠("object_id") +mz_roles_ind CREATE␠INDEX␠"mz_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s506␠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␠[s484␠AS␠"mz_catalog"."mz_schemas"]␠("database_id") -mz_secrets_ind CREATE␠INDEX␠"mz_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s519␠AS␠"mz_catalog"."mz_secrets"]␠("name") -mz_show_all_objects_ind CREATE␠INDEX␠"mz_show_all_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s600␠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␠[s763␠AS␠"mz_internal"."mz_show_cluster_replicas"]␠("cluster") -mz_show_clusters_ind CREATE␠INDEX␠"mz_show_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s602␠AS␠"mz_internal"."mz_show_clusters"]␠("name") -mz_show_columns_ind CREATE␠INDEX␠"mz_show_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s601␠AS␠"mz_internal"."mz_show_columns"]␠("id") -mz_show_connections_ind CREATE␠INDEX␠"mz_show_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s610␠AS␠"mz_internal"."mz_show_connections"]␠("schema_id") -mz_show_databases_ind CREATE␠INDEX␠"mz_show_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s604␠AS␠"mz_internal"."mz_show_databases"]␠("name") -mz_show_indexes_ind CREATE␠INDEX␠"mz_show_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s614␠AS␠"mz_internal"."mz_show_indexes"]␠("schema_id") -mz_show_materialized_views_ind CREATE␠INDEX␠"mz_show_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s613␠AS␠"mz_internal"."mz_show_materialized_views"]␠("schema_id") -mz_show_roles_ind CREATE␠INDEX␠"mz_show_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s609␠AS␠"mz_internal"."mz_show_roles"]␠("name") -mz_show_schemas_ind CREATE␠INDEX␠"mz_show_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s605␠AS␠"mz_internal"."mz_show_schemas"]␠("database_id") -mz_show_secrets_ind CREATE␠INDEX␠"mz_show_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s603␠AS␠"mz_internal"."mz_show_secrets"]␠("schema_id") -mz_show_sinks_ind CREATE␠INDEX␠"mz_show_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s612␠AS␠"mz_internal"."mz_show_sinks"]␠("schema_id") -mz_show_sources_ind CREATE␠INDEX␠"mz_show_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s611␠AS␠"mz_internal"."mz_show_sources"]␠("schema_id") -mz_show_tables_ind CREATE␠INDEX␠"mz_show_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s606␠AS␠"mz_internal"."mz_show_tables"]␠("schema_id") -mz_show_types_ind CREATE␠INDEX␠"mz_show_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s608␠AS␠"mz_internal"."mz_show_types"]␠("schema_id") -mz_show_views_ind CREATE␠INDEX␠"mz_show_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s607␠AS␠"mz_internal"."mz_show_views"]␠("schema_id") -mz_sink_statistics_ind CREATE␠INDEX␠"mz_sink_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s729␠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␠[s701␠AS␠"mz_internal"."mz_sink_status_history"]␠("sink_id") -mz_sink_statuses_ind CREATE␠INDEX␠"mz_sink_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s702␠AS␠"mz_internal"."mz_sink_statuses"]␠("id") -mz_sinks_ind CREATE␠INDEX␠"mz_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s496␠AS␠"mz_catalog"."mz_sinks"]␠("id") -mz_source_statistics_ind CREATE␠INDEX␠"mz_source_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s727␠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␠[s725␠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␠[s703␠AS␠"mz_internal"."mz_source_status_history"]␠("source_id") -mz_source_statuses_ind CREATE␠INDEX␠"mz_source_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s720␠AS␠"mz_internal"."mz_source_statuses"]␠("id") -mz_sources_ind CREATE␠INDEX␠"mz_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s489␠AS␠"mz_catalog"."mz_sources"]␠("id") -mz_tables_ind CREATE␠INDEX␠"mz_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s488␠AS␠"mz_catalog"."mz_tables"]␠("schema_id") -mz_types_ind CREATE␠INDEX␠"mz_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s498␠AS␠"mz_catalog"."mz_types"]␠("schema_id") -mz_views_ind CREATE␠INDEX␠"mz_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s497␠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␠[s736␠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␠[s542␠AS␠"mz_internal"."mz_webhook_sources"]␠("id") -pg_attrdef_all_databases_ind CREATE␠INDEX␠"pg_attrdef_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s641␠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␠[s634␠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␠[s651␠AS␠"mz_internal"."pg_authid_core"]␠("rolname") -pg_class_all_databases_ind CREATE␠INDEX␠"pg_class_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s622␠AS␠"mz_internal"."pg_class_all_databases"]␠("relname") -pg_description_all_databases_ind CREATE␠INDEX␠"pg_description_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s631␠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␠[s619␠AS␠"mz_internal"."pg_namespace_all_databases"]␠("nspname") -pg_type_all_databases_ind CREATE␠INDEX␠"pg_type_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s628␠AS␠"mz_internal"."pg_type_all_databases"]␠("oid") +mz_schemas_ind CREATE␠INDEX␠"mz_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s486␠AS␠"mz_catalog"."mz_schemas"]␠("database_id") +mz_secrets_ind CREATE␠INDEX␠"mz_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s521␠AS␠"mz_catalog"."mz_secrets"]␠("name") +mz_show_all_objects_ind CREATE␠INDEX␠"mz_show_all_objects_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s602␠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␠[s765␠AS␠"mz_internal"."mz_show_cluster_replicas"]␠("cluster") +mz_show_clusters_ind CREATE␠INDEX␠"mz_show_clusters_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s604␠AS␠"mz_internal"."mz_show_clusters"]␠("name") +mz_show_columns_ind CREATE␠INDEX␠"mz_show_columns_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s603␠AS␠"mz_internal"."mz_show_columns"]␠("id") +mz_show_connections_ind CREATE␠INDEX␠"mz_show_connections_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s612␠AS␠"mz_internal"."mz_show_connections"]␠("schema_id") +mz_show_databases_ind CREATE␠INDEX␠"mz_show_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s606␠AS␠"mz_internal"."mz_show_databases"]␠("name") +mz_show_indexes_ind CREATE␠INDEX␠"mz_show_indexes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s616␠AS␠"mz_internal"."mz_show_indexes"]␠("schema_id") +mz_show_materialized_views_ind CREATE␠INDEX␠"mz_show_materialized_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s615␠AS␠"mz_internal"."mz_show_materialized_views"]␠("schema_id") +mz_show_roles_ind CREATE␠INDEX␠"mz_show_roles_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s611␠AS␠"mz_internal"."mz_show_roles"]␠("name") +mz_show_schemas_ind CREATE␠INDEX␠"mz_show_schemas_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s607␠AS␠"mz_internal"."mz_show_schemas"]␠("database_id") +mz_show_secrets_ind CREATE␠INDEX␠"mz_show_secrets_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s605␠AS␠"mz_internal"."mz_show_secrets"]␠("schema_id") +mz_show_sinks_ind CREATE␠INDEX␠"mz_show_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s614␠AS␠"mz_internal"."mz_show_sinks"]␠("schema_id") +mz_show_sources_ind CREATE␠INDEX␠"mz_show_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s613␠AS␠"mz_internal"."mz_show_sources"]␠("schema_id") +mz_show_tables_ind CREATE␠INDEX␠"mz_show_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s608␠AS␠"mz_internal"."mz_show_tables"]␠("schema_id") +mz_show_types_ind CREATE␠INDEX␠"mz_show_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s610␠AS␠"mz_internal"."mz_show_types"]␠("schema_id") +mz_show_views_ind CREATE␠INDEX␠"mz_show_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s609␠AS␠"mz_internal"."mz_show_views"]␠("schema_id") +mz_sink_statistics_ind CREATE␠INDEX␠"mz_sink_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s731␠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␠[s703␠AS␠"mz_internal"."mz_sink_status_history"]␠("sink_id") +mz_sink_statuses_ind CREATE␠INDEX␠"mz_sink_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s704␠AS␠"mz_internal"."mz_sink_statuses"]␠("id") +mz_sinks_ind CREATE␠INDEX␠"mz_sinks_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s498␠AS␠"mz_catalog"."mz_sinks"]␠("id") +mz_source_statistics_ind CREATE␠INDEX␠"mz_source_statistics_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s729␠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␠[s727␠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␠[s705␠AS␠"mz_internal"."mz_source_status_history"]␠("source_id") +mz_source_statuses_ind CREATE␠INDEX␠"mz_source_statuses_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s722␠AS␠"mz_internal"."mz_source_statuses"]␠("id") +mz_sources_ind CREATE␠INDEX␠"mz_sources_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s491␠AS␠"mz_catalog"."mz_sources"]␠("id") +mz_tables_ind CREATE␠INDEX␠"mz_tables_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s490␠AS␠"mz_catalog"."mz_tables"]␠("schema_id") +mz_types_ind CREATE␠INDEX␠"mz_types_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s500␠AS␠"mz_catalog"."mz_types"]␠("schema_id") +mz_views_ind CREATE␠INDEX␠"mz_views_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s499␠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␠[s738␠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␠[s544␠AS␠"mz_internal"."mz_webhook_sources"]␠("id") +pg_attrdef_all_databases_ind CREATE␠INDEX␠"pg_attrdef_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s643␠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␠[s636␠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␠[s653␠AS␠"mz_internal"."pg_authid_core"]␠("rolname") +pg_class_all_databases_ind CREATE␠INDEX␠"pg_class_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s624␠AS␠"mz_internal"."pg_class_all_databases"]␠("relname") +pg_description_all_databases_ind CREATE␠INDEX␠"pg_description_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s633␠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␠[s621␠AS␠"mz_internal"."pg_namespace_all_databases"]␠("nspname") +pg_type_all_databases_ind CREATE␠INDEX␠"pg_type_all_databases_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s630␠AS␠"mz_internal"."pg_type_all_databases"]␠("oid") # Record all transitive dependencies (tables, sources, views, mvs) of indexes on # the mz_catalog_server cluster. @@ -224,6 +224,16 @@ mz_builtin_materialized_views name mz_builtin_materialized_views oid mz_builtin_materialized_views privileges mz_builtin_materialized_views schema_name +mz_builtin_tables name +mz_builtin_tables oid +mz_builtin_tables privileges +mz_builtin_tables schema_name +mz_builtin_views create_sql +mz_builtin_views definition +mz_builtin_views name +mz_builtin_views oid +mz_builtin_views privileges +mz_builtin_views schema_name mz_catalog_raw data mz_cluster_auto_scaling_strategies cluster_id mz_cluster_auto_scaling_strategies state diff --git a/test/sqllogictest/mz_tables.slt b/test/sqllogictest/mz_tables.slt new file mode 100644 index 0000000000000..b4660bdcf3741 --- /dev/null +++ b/test/sqllogictest/mz_tables.slt @@ -0,0 +1,223 @@ +# 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 mz_catalog.mz_tables. It is a BuiltinMaterializedView +# over mz_internal.mz_catalog_raw (see MZ_TABLES in +# src/catalog/src/builtin/mz_catalog.rs) with two union branches, user tables +# and builtin tables, and it derives every column from the durable catalog JSON +# through parse_catalog_id, parse_catalog_privileges, redact_sql and +# parse_catalog_create_sql (src/expr/src/scalar/func/impls/jsonb.rs). +# +# Before that it was a BuiltinTable populated by pack_table_update in the +# adapter, so the values pinned here are a compatibility surface: +# information_schema.tables, pg_tables and pg_class all read through it. +# +# Assertions filter by object name rather than counting rows, so the file also +# holds under --auto-index-selects, which creates extra views. + +mode cockroach + +# Stable object ids, so create_sql can name dependencies by id. +reset-server + +# --- user_tables branch ------------------------------------------------------- + +# `t` is the first object created after the reset, so it is u1 and create_sql +# below can name it. +statement ok +CREATE TABLE t (a int, b text NOT NULL) + +query T +SELECT id FROM mz_tables WHERE name = 't' +---- +u1 + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'public' AND database_id IS NOT NULL) +FROM mz_tables WHERE name = 't' +---- +true + +query T +SELECT owner_id = (SELECT id FROM mz_roles WHERE name = 'materialize') +FROM mz_tables WHERE name = 't' +---- +true + +query T multiline +SELECT create_sql FROM mz_tables WHERE name = 't' +---- +CREATE TABLE "materialize"."public"."t" ("a" [s20 AS "pg_catalog"."int4"], "b" [s46 AS "pg_catalog"."text"] NOT NULL) +EOF + +query T multiline +SELECT redacted_create_sql FROM mz_tables WHERE name = 't' +---- +CREATE TABLE materialize.public.t (a [s20 AS pg_catalog.int4], b [s46 AS pg_catalog.text] NOT NULL) +EOF + +# A table with no source omits `source_id` from the parsed create_sql, so the +# column must be NULL rather than absent or empty. +query T +SELECT source_id IS NULL FROM mz_tables WHERE name = 't' +---- +true + +# --- Cross-check against the in-memory catalog -------------------------------- + +# The oid column is read out of the durable JSON, while a regclass cast resolves +# the name through the in-memory catalog. Two independent paths that must agree. +# +# NB create_sql cannot be compared against SHOW CREATE TABLE the same way: SHOW +# humanizes item ids back to names and pretty-prints, so it is deliberately not +# the stored string. +query T +SELECT oid = 't'::regclass::oid FROM mz_tables WHERE name = 't' +---- +true + +# --- source_id, for a table created from a source ----------------------------- + +statement ok +CREATE SOURCE lg FROM LOAD GENERATOR COUNTER + +statement ok +CREATE TABLE lg_tbl FROM SOURCE lg (REFERENCE counter) + +query T +SELECT source_id = (SELECT id FROM mz_sources WHERE name = 'lg') +FROM mz_tables WHERE name = 'lg_tbl' +---- +true + +# --- Temporary tables --------------------------------------------------------- + +# Temporary items are durable catalog items tagged with their owning session, +# parented to a sentinel schema id shared by every session. The MV maps that +# sentinel to schema_id '0', which is what the old SchemaSpecifier::Temporary +# populator printed (src/sql/src/names.rs). + +statement ok +CREATE TEMP TABLE tt (x int) + +query TTT +SELECT id LIKE 'u%', schema_id, create_sql IS NOT NULL FROM mz_tables WHERE name = 'tt' +---- +true 0 true + +# Two sessions may each hold a temporary table of the same name: name +# uniqueness is scoped by the owning session. Both rows show up here, since +# mz_tables reports every item and per-session visibility lives in name +# resolution, not in this MV. +simple conn=other +CREATE TEMP TABLE tt (y text); +---- +COMPLETE 0 + +query I +SELECT count(*) FROM mz_tables WHERE name = 'tt' +---- +2 + +query T +SELECT array_agg(DISTINCT schema_id) FROM mz_tables WHERE name = 'tt' +---- +{0} + +# --- builtin_tables branch ---------------------------------------------------- + +query TTTT +SELECT id LIKE 's%', owner_id, create_sql IS NULL, source_id IS NULL +FROM mz_tables WHERE name = 'mz_kafka_sinks' +---- +true s1 true true + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'mz_catalog' AND database_id IS NULL) +FROM mz_tables WHERE name = 'mz_kafka_sinks' +---- +true + +# Exactly once: the user and builtin branches must not both claim a row. +query I +SELECT count(*) FROM mz_tables WHERE name = 'mz_kafka_sinks' +---- +1 + +# Every builtin table the catalog generates must be reported. +query T +SELECT array_agg(name ORDER BY name) FROM ( + SELECT name FROM mz_internal.mz_builtin_tables + EXCEPT + SELECT name FROM mz_tables 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_tables WHERE id LIKE 's%' + EXCEPT + SELECT name FROM mz_internal.mz_builtin_tables +) +---- +NULL + +# The reporter view backing this MV 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_tables' +---- +1 + +# --- Robustness over the whole catalog ---------------------------------------- + +# The MV runs parse_catalog_create_sql over every Item row in the catalog, in +# its WHERE clause, so a single item whose create_sql the parser rejects makes +# the entire relation unreadable rather than dropping one row. Create one item +# of every kind reachable here and confirm the relation still resolves. + +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 TYPE ty AS LIST (ELEMENT TYPE = int4) + +statement ok +CREATE SECRET sec AS 'hunter2' + +query T +SELECT count(*) > 0 FROM mz_tables +---- +true + +# --- NOT NULL invariants ------------------------------------------------------ + +# The MV declares ASSERT NOT NULL for these columns. +query I +SELECT count(*) +FROM mz_tables +WHERE id IS NULL + OR oid IS NULL + OR schema_id IS NULL + OR name IS NULL + OR owner_id IS NULL + OR privileges IS NULL +---- +0 + +statement ok +DROP TABLE t CASCADE diff --git a/test/sqllogictest/mz_views.slt b/test/sqllogictest/mz_views.slt new file mode 100644 index 0000000000000..5fd82a0c8a6b5 --- /dev/null +++ b/test/sqllogictest/mz_views.slt @@ -0,0 +1,266 @@ +# 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 mz_catalog.mz_views. It is a BuiltinMaterializedView +# over mz_internal.mz_catalog_raw (see MZ_VIEWS in +# src/catalog/src/builtin/mz_catalog.rs) with two union branches, user views and +# builtin views, and it derives every column from the durable catalog JSON +# through parse_catalog_id, parse_catalog_privileges, redact_sql and +# parse_catalog_create_sql (src/expr/src/scalar/func/impls/jsonb.rs). +# +# Before that it was a BuiltinTable populated by pack_view_update in the +# adapter, so the values pinned here are a compatibility surface. The +# `definition` column in particular is what pg_views.definition exposes. +# +# Assertions filter by object name rather than counting rows, so the file also +# holds under --auto-index-selects, which creates extra views. + +mode cockroach + +# Stable object ids, so create_sql can name dependencies by id. +reset-server + +# --- user_views branch -------------------------------------------------------- + +# `t` is the first object created after the reset, so it is u1 and the +# definitions below can name it. +statement ok +CREATE TABLE t (a int, b text NOT NULL) + +statement ok +CREATE VIEW v AS SELECT a, b FROM t WHERE a > 42 + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'public' AND database_id IS NOT NULL) +FROM mz_views WHERE name = 'v' +---- +true + +query T +SELECT owner_id = (SELECT id FROM mz_roles WHERE name = 'materialize') +FROM mz_views WHERE name = 'v' +---- +true + +# definition is the view's query rendered stably, with the trailing semicolon +# PostgreSQL puts in pg_views.definition. +query T multiline +SELECT definition FROM mz_views WHERE name = 'v' +---- +SELECT "a", "b" FROM [u1 AS "materialize"."public"."t"] WHERE "a" > 42; +EOF + +query T multiline +SELECT create_sql FROM mz_views WHERE name = 'v' +---- +CREATE VIEW "materialize"."public"."v" AS SELECT "a", "b" FROM [u1 AS "materialize"."public"."t"] WHERE "a" > 42 +EOF + +# Redaction replaces literals but keeps structure and resolves quoting. +query T multiline +SELECT redacted_create_sql FROM mz_views WHERE name = 'v' +---- +CREATE VIEW materialize.public.v AS SELECT a, b FROM [u1 AS materialize.public.t] WHERE a > '' +EOF + +# --- Cross-check against the in-memory catalog -------------------------------- + +# The oid column is read out of the durable JSON, while a regclass cast resolves +# the name through the in-memory catalog. Two independent paths that must agree. +# +# NB create_sql cannot be compared against SHOW CREATE VIEW the same way: SHOW +# humanizes item ids back to names and pretty-prints, so it is deliberately not +# the stored string. +query T +SELECT oid = 'v'::regclass::oid FROM mz_views WHERE name = 'v' +---- +true + +# definition must be a fixed point: consumers re-issue it as the body of a new +# view, so planning it again and rendering it again has to reproduce it exactly. +# This goes out through the planner and back through the durable catalog, so it +# is an end-to-end check on the rendering rather than a self-comparison. +statement ok +CREATE VIEW v_roundtrip AS SELECT "a", "b" FROM [u1 AS "materialize"."public"."t"] WHERE "a" > 42; + +query T +SELECT (SELECT definition FROM mz_views WHERE name = 'v') + = (SELECT definition FROM mz_views WHERE name = 'v_roundtrip') +---- +true + +# --- Temporary views ---------------------------------------------------------- + +# Temporary items are durable catalog items tagged with their owning session, +# parented to a sentinel schema id shared by every session. The MV maps that +# sentinel to schema_id '0', which is what the old SchemaSpecifier::Temporary +# populator printed (src/sql/src/names.rs). + +statement ok +CREATE TEMP VIEW tv AS SELECT a FROM t + +query TTT +SELECT id LIKE 'u%', schema_id, create_sql IS NOT NULL FROM mz_views WHERE name = 'tv' +---- +true 0 true + +query T multiline +SELECT definition FROM mz_views WHERE name = 'tv' +---- +SELECT "a" FROM [u1 AS "materialize"."public"."t"]; +EOF + +# Two sessions may each hold a temporary view of the same name: name uniqueness +# is scoped by the owning session. Both rows show up here, since mz_views +# reports every item and per-session visibility lives in name resolution, not in +# this MV. +simple conn=other +CREATE TEMP VIEW tv AS SELECT 1 AS other; +---- +COMPLETE 0 + +query I +SELECT count(*) FROM mz_views WHERE name = 'tv' +---- +2 + +query T +SELECT array_agg(DISTINCT schema_id) FROM mz_views WHERE name = 'tv' +---- +{0} + +# --- builtin_views branch ----------------------------------------------------- + +query TTT +SELECT id LIKE 's%', owner_id, create_sql IS NOT NULL +FROM mz_views WHERE name = 'mz_objects' +---- +true s1 true + +query T +SELECT schema_id = (SELECT id FROM mz_schemas WHERE name = 'mz_catalog' AND database_id IS NULL) +FROM mz_views WHERE name = 'mz_objects' +---- +true + +# Exactly once: the user and builtin branches must not both claim a row. +query I +SELECT count(*) FROM mz_views WHERE name = 'mz_objects' +---- +1 + +# Every builtin view the catalog generates must be reported. +query T +SELECT array_agg(name ORDER BY name) FROM ( + SELECT name FROM mz_internal.mz_builtin_views + EXCEPT + SELECT name FROM mz_views 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_views WHERE id LIKE 's%' + EXCEPT + SELECT name FROM mz_internal.mz_builtin_views +) +---- +NULL + +# --- Generated reporter views --------------------------------------------------- + +# The builtin branch reads mz_internal.mz_builtin_views, a generated constant +# view that lists every builtin view, including itself and the other generated +# mz_builtin_* views. The generated views are listed with a short placeholder +# instead of their real SQL: mz_builtin_views cannot textually contain itself, +# and the other reporters embed metadata about every builtin object, so +# re-embedding their SQL would produce enormous rows. + +# The placeholder rows are exactly the generated views, and nothing else is +# elided. +query T +SELECT name FROM mz_views WHERE definition LIKE '%definition elided%' AND id LIKE 's%' ORDER BY name +---- +mz_builtin_materialized_views +mz_builtin_sources +mz_builtin_tables +mz_builtin_views + +query T multiline +SELECT definition FROM mz_views WHERE name = 'mz_builtin_views' +---- +SELECT ''; +EOF + +query T multiline +SELECT create_sql FROM mz_views WHERE name = 'mz_builtin_views' +---- +CREATE VIEW "mz_internal"."mz_builtin_views" AS SELECT '' +EOF + +# The placeholder must stay parseable: redact_sql errors on unparseable input, +# and an error would poison the whole materialized view, not just this row. +query T multiline +SELECT redacted_create_sql FROM mz_views WHERE name = 'mz_builtin_views' +---- +CREATE VIEW mz_internal.mz_builtin_views AS SELECT '' +EOF + +# --- Robustness over the whole catalog ---------------------------------------- + +# The MV runs parse_catalog_create_sql over every Item row in the catalog, in +# its WHERE clause, so a single item whose create_sql the parser rejects makes +# the entire relation unreadable rather than dropping one row. Create one item +# of every kind reachable here and confirm the relation still resolves. + +statement ok +CREATE MATERIALIZED VIEW mv AS SELECT count(*) FROM t + +statement ok +CREATE INDEX t_idx ON t (a) + +statement ok +CREATE TYPE ty AS LIST (ELEMENT TYPE = int4) + +statement ok +CREATE SECRET sec AS 'hunter2' + +statement ok +CREATE SOURCE lg FROM LOAD GENERATOR COUNTER + +statement ok +CREATE TABLE lg_tbl FROM SOURCE lg (REFERENCE counter) + +query T +SELECT count(*) > 0 FROM mz_views +---- +true + +# --- NOT NULL invariants ------------------------------------------------------ + +# The MV declares ASSERT NOT NULL for every column. +query I +SELECT count(*) +FROM mz_views +WHERE id IS NULL + OR oid IS NULL + OR schema_id IS NULL + OR name IS NULL + OR definition IS NULL + OR owner_id IS NULL + OR privileges IS NULL + OR create_sql IS NULL + OR redacted_create_sql IS NULL +---- +0 + +statement ok +DROP TABLE t CASCADE diff --git a/test/sqllogictest/object_ownership.slt b/test/sqllogictest/object_ownership.slt index fb332383cf59d..531367a9b063d 100644 --- a/test/sqllogictest/object_ownership.slt +++ b/test/sqllogictest/object_ownership.slt @@ -2159,7 +2159,7 @@ db error: ERROR: cannot alter item mz_introspection.mz_dataflow_operators becaus simple conn=mz_system,user=mz_system ALTER TABLE mz_views OWNER TO mz_system ---- -db error: ERROR: cannot alter item mz_catalog.mz_views because it is required by the database system +db error: ERROR: mz_views is a materialized view not a table simple conn=mz_system,user=mz_system ALTER VIEW mz_relations OWNER TO mz_system diff --git a/test/sqllogictest/oid.slt b/test/sqllogictest/oid.slt index a563054773926..3da845e259129 100644 --- a/test/sqllogictest/oid.slt +++ b/test/sqllogictest/oid.slt @@ -861,12 +861,10 @@ SELECT oid, name FROM mz_objects WHERE id LIKE 's%' AND oid < 20000 ORDER BY oid 16704 mz_columns 16705 mz_indexes 16706 mz_index_columns -16707 mz_tables 16708 mz_connections 16709 mz_ssh_tunnel_connections 16710 mz_sources 16711 mz_sinks -16712 mz_views 16713 mz_materialized_views 16714 mz_types 16715 mz_type_pg_metadata @@ -1245,3 +1243,7 @@ SELECT oid, name FROM mz_objects WHERE id LIKE 's%' AND oid < 20000 ORDER BY oid 17113 mz_aws_account_id 17114 mz_aws_external_id_prefix 17115 mz_aws_connection_role_arn +17116 mz_builtin_tables +17117 mz_builtin_views +17118 mz_tables +17119 mz_views diff --git a/test/sqllogictest/show_create_system_objects.slt b/test/sqllogictest/show_create_system_objects.slt index c63218213bd9e..d5d5d466ad6c8 100644 --- a/test/sqllogictest/show_create_system_objects.slt +++ b/test/sqllogictest/show_create_system_objects.slt @@ -12,5 +12,5 @@ mode cockroach query error cannot show create for system object mz_internal\.mz_source_statistics SHOW CREATE SOURCE mz_internal.mz_source_statistics -query error cannot show create for system object mz_catalog\.mz_tables -SHOW CREATE TABLE mz_tables +query error cannot show create for system object mz_catalog\.mz_columns +SHOW CREATE TABLE mz_columns diff --git a/test/testdrive-old-kafka-src-syntax/update.td b/test/testdrive-old-kafka-src-syntax/update.td index 7038ec7920bca..33339603145e0 100644 --- a/test/testdrive-old-kafka-src-syntax/update.td +++ b/test/testdrive-old-kafka-src-syntax/update.td @@ -76,7 +76,7 @@ contains:invalid input syntax for type double precision contains:cannot mutate materialized view ! UPDATE mz_tables SET a = 1 -contains:cannot mutate system table +contains:cannot mutate materialized view 'mz_catalog.mz_tables' ! UPDATE t SET a = 1 contains:unknown column a diff --git a/test/testdrive/catalog.td b/test/testdrive/catalog.td index eef5713d4fc51..fd8b64a6b150e 100644 --- a/test/testdrive/catalog.td +++ b/test/testdrive/catalog.td @@ -525,9 +525,7 @@ mz_operators "" mz_pseudo_types "" mz_role_auth "" mz_sinks "" -mz_tables "" mz_types "" -mz_views "" > SHOW VIEWS FROM mz_catalog name comment @@ -561,6 +559,8 @@ mz_sources mz_catalog_server "" mz_ssh_tunnel_connections mz_catalog_server "" mz_system_privileges mz_catalog_server "" mz_materialized_views mz_catalog_server "" +mz_tables mz_catalog_server "" +mz_views mz_catalog_server "" # Check default sources, tables, and views in mz_catalog_unstable. @@ -625,6 +625,8 @@ name comment mz_activity_log_thinned "" mz_builtin_materialized_views "" mz_builtin_sources "" +mz_builtin_tables "" +mz_builtin_views "" mz_cluster_deployment_lineage "" mz_cluster_replica_history "" mz_cluster_replica_metrics "" @@ -830,7 +832,7 @@ test_table "" # `SHOW TABLES` and `mz_tables` should agree. > SELECT COUNT(*) FROM mz_tables WHERE id LIKE 's%' -34 +32 # 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%' diff --git a/test/testdrive/temporary.td b/test/testdrive/temporary.td index 4a4e7e2e615e5..70e23e45a895d 100644 --- a/test/testdrive/temporary.td +++ b/test/testdrive/temporary.td @@ -241,13 +241,13 @@ contains:unknown catalog item 'temp_v' contains:cannot create temporary item in non-temporary schema # Regression test: a persistent (non-temporary) item must not be allowed to -# depend on a TEMPORARY object. Temporary objects live only in the in-memory, -# session-scoped catalog and are never persisted, so a persisted item that -# references one is a dangling catalog entry. The coordinator applies durable -# items before temporary ones (it assumes the invariant holds, see -# apply.rs), so on the next catalog rebuild deserializing the persisted item -# fails to resolve the temporary id and panics with -# PlanError(InvalidId(...)): invalid persisted SQL. +# depend on a TEMPORARY object. A temporary object is durable, but it is owned +# by the session that created it and is reclaimed when that session ends, or at +# the next writable catalog open if the process died first. A persisted item +# referencing one therefore becomes a dangling catalog entry as soon as the +# owning session goes away, and on the next catalog rebuild deserializing it +# fails to resolve the id and panics with PlanError(InvalidId(...)): invalid +# persisted SQL. # # CREATE enforces this via ErrorKind::InvalidTemporaryDependency. ALTER SINK # ... SET FROM went through a separate catalog path that skipped the check, diff --git a/test/testdrive/update.td b/test/testdrive/update.td index 1bcb28cbcf16d..8ff37126e8a33 100644 --- a/test/testdrive/update.td +++ b/test/testdrive/update.td @@ -76,7 +76,7 @@ contains:invalid input syntax for type double precision contains:cannot mutate materialized view ! UPDATE mz_tables SET a = 1 -contains:cannot mutate system table +contains:cannot mutate materialized view 'mz_catalog.mz_tables' ! UPDATE t SET a = 1 contains:unknown column a