diff --git a/datafusion/core/src/catalog/information_schema.rs b/datafusion/core/src/catalog/information_schema.rs index fd8e250feab3..0467dffa7025 100644 --- a/datafusion/core/src/catalog/information_schema.rs +++ b/datafusion/core/src/catalog/information_schema.rs @@ -41,6 +41,7 @@ use super::{ const INFORMATION_SCHEMA: &str = "information_schema"; const TABLES: &str = "tables"; +const VIEWS: &str = "views"; const COLUMNS: &str = "columns"; /// Wraps another [`CatalogProvider`] and adds a "information_schema" @@ -124,28 +125,21 @@ impl InformationSchemaProvider { builder.add_table( &catalog_name, &schema_name, - table_name, + &table_name, table.table_type(), - table.get_table_definition(), ); } } } // Add a final list for the information schema tables themselves - builder.add_table( - &catalog_name, - INFORMATION_SCHEMA, - TABLES, - TableType::View, - None::<&str>, - ); + builder.add_table(&catalog_name, INFORMATION_SCHEMA, TABLES, TableType::View); + builder.add_table(&catalog_name, INFORMATION_SCHEMA, VIEWS, TableType::View); builder.add_table( &catalog_name, INFORMATION_SCHEMA, COLUMNS, TableType::View, - None::<&str>, ); } @@ -154,6 +148,32 @@ impl InformationSchemaProvider { Arc::new(mem_table) } + fn make_views(&self) -> Arc { + let mut builder = InformationSchemaViewBuilder::new(); + + for catalog_name in self.catalog_list.catalog_names() { + let catalog = self.catalog_list.catalog(&catalog_name).unwrap(); + + for schema_name in catalog.schema_names() { + if schema_name != INFORMATION_SCHEMA { + let schema = catalog.schema(&schema_name).unwrap(); + for table_name in schema.table_names() { + let table = schema.table(&table_name).unwrap(); + builder.add_view( + &catalog_name, + &schema_name, + &table_name, + table.get_table_definition(), + ) + } + } + } + } + + let mem_table: MemTable = builder.into(); + Arc::new(mem_table) + } + /// Construct the `information_schema.columns` virtual table fn make_columns(&self) -> Arc { let mut builder = InformationSchemaColumnsBuilder::new(); @@ -194,7 +214,7 @@ impl SchemaProvider for InformationSchemaProvider { } fn table_names(&self) -> Vec { - vec![TABLES.to_string(), COLUMNS.to_string()] + vec![TABLES.to_string(), VIEWS.to_string(), COLUMNS.to_string()] } fn table(&self, name: &str) -> Option> { @@ -202,13 +222,15 @@ impl SchemaProvider for InformationSchemaProvider { Some(self.make_tables()) } else if name.eq_ignore_ascii_case("columns") { Some(self.make_columns()) + } else if name.eq_ignore_ascii_case("views") { + Some(self.make_views()) } else { None } } fn table_exist(&self, name: &str) -> bool { - return matches!(name.to_ascii_lowercase().as_str(), TABLES | COLUMNS); + return matches!(name.to_ascii_lowercase().as_str(), TABLES | VIEWS | COLUMNS); } } @@ -220,7 +242,6 @@ struct InformationSchemaTablesBuilder { schema_names: StringBuilder, table_names: StringBuilder, table_types: StringBuilder, - definitions: StringBuilder, } impl InformationSchemaTablesBuilder { @@ -234,7 +255,6 @@ impl InformationSchemaTablesBuilder { schema_names: StringBuilder::new(default_capacity), table_names: StringBuilder::new(default_capacity), table_types: StringBuilder::new(default_capacity), - definitions: StringBuilder::new(default_capacity), } } @@ -244,7 +264,6 @@ impl InformationSchemaTablesBuilder { schema_name: impl AsRef, table_name: impl AsRef, table_type: TableType, - definition: Option>, ) { // Note: append_value is actually infallable. self.catalog_names @@ -261,7 +280,6 @@ impl InformationSchemaTablesBuilder { TableType::Temporary => "LOCAL TEMPORARY", }) .unwrap(); - self.definitions.append_option(definition.as_ref()).unwrap(); } } @@ -272,7 +290,6 @@ impl From for MemTable { Field::new("table_schema", DataType::Utf8, false), Field::new("table_name", DataType::Utf8, false), Field::new("table_type", DataType::Utf8, false), - Field::new("definition", DataType::Utf8, true), ]); let InformationSchemaTablesBuilder { @@ -280,7 +297,6 @@ impl From for MemTable { mut schema_names, mut table_names, mut table_types, - mut definitions, } = value; let schema = Arc::new(schema); @@ -291,6 +307,80 @@ impl From for MemTable { Arc::new(schema_names.finish()), Arc::new(table_names.finish()), Arc::new(table_types.finish()), + ], + ) + .unwrap(); + + MemTable::try_new(schema, vec![vec![batch]]).unwrap() + } +} + +/// Builds the `information_schema.VIEWS` table row by row +/// +/// Columns are based on +struct InformationSchemaViewBuilder { + catalog_names: StringBuilder, + schema_names: StringBuilder, + table_names: StringBuilder, + definitions: StringBuilder, +} + +impl InformationSchemaViewBuilder { + fn new() -> Self { + // StringBuilder requires providing an initial capacity, so + // pick 10 here arbitrarily as this is not performance + // critical code and the number of tables is unavailable here. + let default_capacity = 10; + Self { + catalog_names: StringBuilder::new(default_capacity), + schema_names: StringBuilder::new(default_capacity), + table_names: StringBuilder::new(default_capacity), + definitions: StringBuilder::new(default_capacity), + } + } + + fn add_view( + &mut self, + catalog_name: impl AsRef, + schema_name: impl AsRef, + table_name: impl AsRef, + definition: Option>, + ) { + // Note: append_value is actually infallable. + self.catalog_names + .append_value(catalog_name.as_ref()) + .unwrap(); + self.schema_names + .append_value(schema_name.as_ref()) + .unwrap(); + self.table_names.append_value(table_name.as_ref()).unwrap(); + self.definitions.append_option(definition.as_ref()).unwrap(); + } +} + +impl From for MemTable { + fn from(value: InformationSchemaViewBuilder) -> Self { + let schema = Schema::new(vec![ + Field::new("table_catalog", DataType::Utf8, false), + Field::new("table_schema", DataType::Utf8, false), + Field::new("table_name", DataType::Utf8, false), + Field::new("definition", DataType::Utf8, true), + ]); + + let InformationSchemaViewBuilder { + mut catalog_names, + mut schema_names, + mut table_names, + mut definitions, + } = value; + + let schema = Arc::new(schema); + let batch = RecordBatch::try_new( + schema.clone(), + vec![ + Arc::new(catalog_names.finish()), + Arc::new(schema_names.finish()), + Arc::new(table_names.finish()), Arc::new(definitions.finish()), ], ) diff --git a/datafusion/core/tests/sql/information_schema.rs b/datafusion/core/tests/sql/information_schema.rs index f528c3c3c56d..5a4aa297f808 100644 --- a/datafusion/core/tests/sql/information_schema.rs +++ b/datafusion/core/tests/sql/information_schema.rs @@ -52,12 +52,13 @@ async fn information_schema_tables_no_tables() { .unwrap(); let expected = vec![ - "+---------------+--------------------+------------+------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+---------------+--------------------+------------+------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "+---------------+--------------------+------------+------------+------------+", + "+---------------+--------------------+------------+------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+---------------+--------------------+------------+------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "+---------------+--------------------+------------+------------+", ]; assert_batches_sorted_eq!(expected, &result); } @@ -76,13 +77,14 @@ async fn information_schema_tables_tables_default_catalog() { .unwrap(); let expected = vec![ - "+---------------+--------------------+------------+------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+---------------+--------------------+------------+------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "| datafusion | public | t | BASE TABLE | |", - "+---------------+--------------------+------------+------------+------------+", + "+---------------+--------------------+------------+------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+---------------+--------------------+------------+------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "| datafusion | public | t | BASE TABLE |", + "+---------------+--------------------+------------+------------+", ]; assert_batches_sorted_eq!(expected, &result); @@ -95,14 +97,15 @@ async fn information_schema_tables_tables_default_catalog() { .unwrap(); let expected = vec![ - "+---------------+--------------------+------------+------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+---------------+--------------------+------------+------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "| datafusion | public | t | BASE TABLE | |", - "| datafusion | public | t2 | BASE TABLE | |", - "+---------------+--------------------+------------+------------+------------+", + "+---------------+--------------------+------------+------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+---------------+--------------------+------------+------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "| datafusion | public | t | BASE TABLE |", + "| datafusion | public | t2 | BASE TABLE |", + "+---------------+--------------------+------------+------------+", ]; assert_batches_sorted_eq!(expected, &result); } @@ -139,19 +142,22 @@ async fn information_schema_tables_tables_with_multiple_catalogs() { .unwrap(); let expected = vec![ - "+------------------+--------------------+------------+------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+------------------+--------------------+------------+------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "| my_catalog | information_schema | columns | VIEW | |", - "| my_catalog | information_schema | tables | VIEW | |", - "| my_catalog | my_schema | t1 | BASE TABLE | |", - "| my_catalog | my_schema | t2 | BASE TABLE | |", - "| my_other_catalog | information_schema | columns | VIEW | |", - "| my_other_catalog | information_schema | tables | VIEW | |", - "| my_other_catalog | my_other_schema | t3 | BASE TABLE | |", - "+------------------+--------------------+------------+------------+------------+", + "+------------------+--------------------+------------+------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+------------------+--------------------+------------+------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "| my_catalog | information_schema | columns | VIEW |", + "| my_catalog | information_schema | tables | VIEW |", + "| my_catalog | information_schema | views | VIEW |", + "| my_catalog | my_schema | t1 | BASE TABLE |", + "| my_catalog | my_schema | t2 | BASE TABLE |", + "| my_other_catalog | information_schema | columns | VIEW |", + "| my_other_catalog | information_schema | tables | VIEW |", + "| my_other_catalog | information_schema | views | VIEW |", + "| my_other_catalog | my_other_schema | t3 | BASE TABLE |", + "+------------------+--------------------+------------+------------+", ]; assert_batches_sorted_eq!(expected, &result); } @@ -200,15 +206,16 @@ async fn information_schema_tables_table_types() { .unwrap(); let expected = vec![ - "+---------------+--------------------+------------+-----------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+---------------+--------------------+------------+-----------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "| datafusion | public | physical | BASE TABLE | |", - "| datafusion | public | query | VIEW | |", - "| datafusion | public | temp | LOCAL TEMPORARY | |", - "+---------------+--------------------+------------+-----------------+------------+", + "+---------------+--------------------+------------+-----------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+---------------+--------------------+------------+-----------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "| datafusion | public | physical | BASE TABLE |", + "| datafusion | public | query | VIEW |", + "| datafusion | public | temp | LOCAL TEMPORARY |", + "+---------------+--------------------+------------+-----------------+", ]; assert_batches_sorted_eq!(expected, &result); } @@ -273,13 +280,14 @@ async fn information_schema_show_tables() { let result = plan_and_collect(&ctx, "SHOW TABLES").await.unwrap(); let expected = vec![ - "+---------------+--------------------+------------+------------+------------+", - "| table_catalog | table_schema | table_name | table_type | definition |", - "+---------------+--------------------+------------+------------+------------+", - "| datafusion | information_schema | columns | VIEW | |", - "| datafusion | information_schema | tables | VIEW | |", - "| datafusion | public | t | BASE TABLE | |", - "+---------------+--------------------+------------+------------+------------+", + "+---------------+--------------------+------------+------------+", + "| table_catalog | table_schema | table_name | table_type |", + "+---------------+--------------------+------------+------------+", + "| datafusion | information_schema | columns | VIEW |", + "| datafusion | information_schema | tables | VIEW |", + "| datafusion | information_schema | views | VIEW |", + "| datafusion | public | t | BASE TABLE |", + "+---------------+--------------------+------------+------------+", ]; assert_batches_sorted_eq!(expected, &result); @@ -542,11 +550,11 @@ async fn show_create_view() { assert_eq!(results[0].num_rows(), 1); let expected = vec![ - "+------+--------------------------------------+", - "| name | definition |", - "+------+--------------------------------------+", - "| xyz | CREATE VIEW xyz AS SELECT * FROM abc |", - "+------+--------------------------------------+", + "+---------------+--------------+------------+--------------------------------------+", + "| table_catalog | table_schema | table_name | definition |", + "+---------------+--------------+------------+--------------------------------------+", + "| datafusion | public | xyz | CREATE VIEW xyz AS SELECT * FROM abc |", + "+---------------+--------------+------------+--------------------------------------+", ]; assert_batches_eq!(expected, &results); } @@ -567,11 +575,11 @@ async fn show_create_view_in_catalog() { assert_eq!(results[0].num_rows(), 1); let expected = vec![ - "+----------+-------------------------------------------+", - "| name | definition |", - "+----------+-------------------------------------------+", - "| test.xyz | CREATE VIEW test.xyz AS SELECT * FROM abc |", - "+----------+-------------------------------------------+", + "+---------------+--------------+------------+-------------------------------------------+", + "| table_catalog | table_schema | table_name | definition |", + "+---------------+--------------+------------+-------------------------------------------+", + "| datafusion | test | xyz | CREATE VIEW test.xyz AS SELECT * FROM abc |", + "+---------------+--------------+------------+-------------------------------------------+", ]; assert_batches_eq!(expected, &results); } @@ -587,11 +595,11 @@ async fn show_create_table() { let results = plan_and_collect(&ctx, result_sql).await.unwrap(); let expected = vec![ - "+------+------------+", - "| name | definition |", - "+------+------------+", - "| abc | |", - "+------+------------+", + "+---------------+--------------+------------+------------+", + "| table_catalog | table_schema | table_name | definition |", + "+---------------+--------------+------------+------------+", + "| datafusion | public | abc | |", + "+---------------+--------------+------------+------------+", ]; assert_batches_eq!(expected, &results); diff --git a/datafusion/sql/src/planner.rs b/datafusion/sql/src/planner.rs index edabbf8f2b11..6a5f7135f0cb 100644 --- a/datafusion/sql/src/planner.rs +++ b/datafusion/sql/src/planner.rs @@ -2455,8 +2455,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { .join(" AND "); let query = format!( - "SELECT '{}' as name, definition FROM information_schema.tables WHERE {}", - table_name, where_clause + "SELECT table_catalog, table_schema, table_name, definition FROM information_schema.views WHERE {}", + where_clause ); let mut rewrite = DFParser::parse_sql(&query)?;