diff --git a/datafusion-cli/src/main.rs b/datafusion-cli/src/main.rs index 2f84a9aaf44e6..c46228e7680bf 100644 --- a/datafusion-cli/src/main.rs +++ b/datafusion-cli/src/main.rs @@ -370,8 +370,9 @@ fn get_session_config(args: &Args) -> Result { config_options.sql_parser.dialect = Dialect::Spark; } - let mut session_config = - SessionConfig::from(config_options).with_information_schema(true); + let mut session_config = SessionConfig::from(config_options) + .with_system_catalog(Some(String::from("system"))) + .with_information_schema(true); if args.reads_sql_from_stdin() { // When stdin carries the session's SQL — the REPL (including any rc diff --git a/datafusion/catalog/src/information_schema.rs b/datafusion/catalog/src/information_schema.rs index d9ad7791af67c..30ed1a3753a98 100644 --- a/datafusion/catalog/src/information_schema.rs +++ b/datafusion/catalog/src/information_schema.rs @@ -83,6 +83,7 @@ impl InformationSchemaProvider { pub fn new(catalog_list: Arc) -> Self { Self { config: InformationSchemaConfig { + system_catalog_name: None, catalog_list, table_functions: HashMap::new(), }, @@ -98,10 +99,16 @@ impl InformationSchemaProvider { self.config.table_functions = table_functions; self } + + pub fn with_system_catalog(mut self, catalog_name: String) -> Self { + self.config.system_catalog_name = Some(catalog_name); + self + } } #[derive(Clone, Debug)] struct InformationSchemaConfig { + system_catalog_name: Option, catalog_list: Arc, table_functions: HashMap>, } @@ -148,6 +155,17 @@ impl InformationSchemaConfig { } } + if let Some(system_catalog_name) = &self.system_catalog_name { + for table_name in INFORMATION_SCHEMA_TABLES { + builder.add_table( + system_catalog_name, + INFORMATION_SCHEMA, + table_name, + TableType::View, + ); + } + } + Ok(()) } @@ -177,21 +195,46 @@ impl InformationSchemaConfig { if schema_name != INFORMATION_SCHEMA { // schema name may not exist in the catalog, so we need to check if let Some(schema) = catalog.schema(&schema_name) { - for table_name in schema.table_names() { - if let Some(table) = schema.table(&table_name).await? { - builder.add_view( - &catalog_name, - &schema_name, - &table_name, - table.get_table_definition(), - ) - } - } + Self::add_views( + builder, + &catalog_name, + &schema_name, + schema.as_ref(), + ) + .await?; } } + + // Add the information schema views themselves + Self::add_views(builder, &catalog_name, INFORMATION_SCHEMA, self).await?; } } + // Add the system information schema + if let Some(system_catalog_name) = &self.system_catalog_name { + Self::add_views(builder, system_catalog_name, INFORMATION_SCHEMA, self) + .await?; + } + + Ok(()) + } + + async fn add_views( + builder: &mut InformationSchemaViewBuilder, + catalog_name: &str, + schema_name: &str, + schema: &dyn SchemaProvider, + ) -> Result<(), DataFusionError> { + for table_name in schema.table_names() { + if let Some(table) = schema.table(&table_name).await? { + builder.add_view( + catalog_name, + schema_name, + &table_name, + table.get_table_definition(), + ) + } + } Ok(()) } @@ -207,21 +250,13 @@ impl InformationSchemaConfig { if schema_name != INFORMATION_SCHEMA { // schema name may not exist in the catalog, so we need to check if let Some(schema) = catalog.schema(&schema_name) { - for table_name in schema.table_names() { - if let Some(table) = schema.table(&table_name).await? { - for (field_position, field) in - table.schema().fields().iter().enumerate() - { - builder.add_column( - &catalog_name, - &schema_name, - &table_name, - field_position, - field, - ) - } - } - } + Self::add_columns( + builder, + &catalog_name, + &schema_name, + schema.as_ref(), + ) + .await?; } } } @@ -230,6 +265,29 @@ impl InformationSchemaConfig { Ok(()) } + async fn add_columns( + builder: &mut InformationSchemaColumnsBuilder, + catalog_name: &str, + schema_name: &str, + schema: &dyn SchemaProvider, + ) -> Result<(), DataFusionError> { + for table_name in schema.table_names() { + if let Some(table) = schema.table(&table_name).await? { + for (field_position, field) in table.schema().fields().iter().enumerate() + { + builder.add_column( + catalog_name, + schema_name, + &table_name, + field_position, + field, + ) + } + } + } + Ok(()) + } + /// Construct the `information_schema.df_settings` virtual table fn make_df_settings( &self, @@ -575,6 +633,24 @@ fn remove_native_type_prefix(native_type: &NativeType) -> String { #[async_trait] impl SchemaProvider for InformationSchemaProvider { + fn table_names(&self) -> Vec { + self.config.table_names() + } + + async fn table( + &self, + name: &str, + ) -> Result>, DataFusionError> { + self.config.get_table(name) + } + + fn table_exist(&self, name: &str) -> bool { + self.config.table_exist(name) + } +} + +#[async_trait] +impl SchemaProvider for InformationSchemaConfig { fn table_names(&self) -> Vec { INFORMATION_SCHEMA_TABLES .iter() @@ -586,7 +662,20 @@ impl SchemaProvider for InformationSchemaProvider { &self, name: &str, ) -> Result>, DataFusionError> { - let config = self.config.clone(); + self.get_table(name) + } + + fn table_exist(&self, name: &str) -> bool { + INFORMATION_SCHEMA_TABLES.contains(&name.to_ascii_lowercase().as_str()) + } +} + +impl InformationSchemaConfig { + fn get_table( + &self, + name: &str, + ) -> Result>, DataFusionError> { + let config = self.clone(); let table: Arc = match name.to_ascii_lowercase().as_str() { TABLES => Arc::new(InformationSchemaTables::new(config)), COLUMNS => Arc::new(InformationSchemaColumns::new(config)), @@ -602,10 +691,6 @@ impl SchemaProvider for InformationSchemaProvider { StreamingTable::try_new(Arc::clone(table.schema()), vec![table]).unwrap(), ))) } - - fn table_exist(&self, name: &str) -> bool { - INFORMATION_SCHEMA_TABLES.contains(&name.to_ascii_lowercase().as_str()) - } } #[derive(Debug)] @@ -1564,6 +1649,7 @@ mod tests { #[tokio::test] async fn make_tables_uses_table_type() { let config = InformationSchemaConfig { + system_catalog_name: None, catalog_list: Arc::new(Fixture), table_functions: HashMap::new(), }; diff --git a/datafusion/common/src/config.rs b/datafusion/common/src/config.rs index 4246dcc451d45..2b22de90470fa 100644 --- a/datafusion/common/src/config.rs +++ b/datafusion/common/src/config.rs @@ -237,6 +237,9 @@ config_namespace! { /// The default schema name - this impacts what SQL queries use if not specified pub default_schema: String, default = "public".to_string() + /// The system schema name - when set a global system and `information_schema` catalog will be available + pub system_catalog: Option, default = None + /// Should DataFusion provide access to `information_schema` /// virtual tables for displaying schema information pub information_schema: bool, default = false diff --git a/datafusion/core/src/execution/context/mod.rs b/datafusion/core/src/execution/context/mod.rs index 1b1174e535d18..edb6361340144 100644 --- a/datafusion/core/src/execution/context/mod.rs +++ b/datafusion/core/src/execution/context/mod.rs @@ -2846,7 +2846,9 @@ mod tests { async fn sql_create_catalog() -> Result<()> { // the information schema used to introduce cyclic Arcs let ctx = SessionContext::new_with_config( - SessionConfig::new().with_information_schema(true), + SessionConfig::new() + .with_information_schema(true) + .with_system_catalog(Some(String::from("system"))), ); // Create catalog @@ -2862,7 +2864,7 @@ mod tests { .await?; // Check table exists in schema - let results = ctx.sql("SELECT * FROM information_schema.tables WHERE table_catalog='test' AND table_schema='abc' AND table_name = 'y'").await.unwrap().collect().await.unwrap(); + let results = ctx.sql("SELECT * FROM system.information_schema.tables WHERE table_catalog='test' AND table_schema='abc' AND table_name = 'y'").await.unwrap().collect().await.unwrap(); assert_eq!(results[0].num_rows(), 1); Ok(()) diff --git a/datafusion/core/src/execution/session_state.rs b/datafusion/core/src/execution/session_state.rs index 040a90a2446af..31ce00f16b81d 100644 --- a/datafusion/core/src/execution/session_state.rs +++ b/datafusion/core/src/execution/session_state.rs @@ -37,7 +37,7 @@ use arrow_schema::DataType; use arrow_schema::FieldRef; use datafusion_catalog::MemoryCatalogProviderList; use datafusion_catalog::information_schema::{ - INFORMATION_SCHEMA, InformationSchemaProvider, + INFORMATION_SCHEMA, INFORMATION_SCHEMA_TABLES, InformationSchemaProvider, }; use datafusion_catalog::{TableFunction, TableFunctionImpl}; use datafusion_common::alias::AliasGenerator; @@ -48,7 +48,7 @@ use datafusion_common::display::{PlanType, StringifiedPlan, ToStringifiedPlan}; use datafusion_common::tree_node::TreeNode; use datafusion_common::{ DFSchema, DataFusionError, ResolvedTableReference, TableReference, config_err, - exec_err, plan_datafusion_err, + exec_err, plan_datafusion_err, plan_err, }; use datafusion_execution::TaskContext; use datafusion_execution::config::SessionConfig; @@ -78,7 +78,9 @@ use datafusion_physical_expr_common::physical_expr::PhysicalExpr; use datafusion_physical_optimizer::optimizer::PhysicalOptimizer; use datafusion_physical_plan::ExecutionPlan; use datafusion_physical_plan::operator_statistics::StatisticsRegistry; -use datafusion_session::{PhysicalOptimizerContext, PhysicalOptimizerRule, Session}; +use datafusion_session::{ + CatalogProvider, PhysicalOptimizerContext, PhysicalOptimizerRule, Session, +}; #[cfg(feature = "sql")] use datafusion_sql::{ parser::{DFParserBuilder, Statement}, @@ -93,7 +95,9 @@ use log::{debug, info}; use object_store::ObjectStore; #[cfg(feature = "sql")] use sqlparser::{ - ast::{Expr as SQLExpr, ExprWithAlias as SQLExprWithAlias}, + ast::{ + Expr as SQLExpr, ExprWithAlias as SQLExprWithAlias, Statement as SQLStatement, + }, dialect::dialect_from_str, }; use url::Url; @@ -417,28 +421,75 @@ impl SessionState { table_ref: impl Into, ) -> datafusion_common::Result> { let resolved_ref = self.resolve_table_ref(table_ref); - if self.config().information_schema() - && *resolved_ref.schema == *INFORMATION_SCHEMA + let catalog_name = resolved_ref.catalog.as_ref(); + + if let Some(system_catalog) = self.inner.config.system_catalog() + && system_catalog == catalog_name { - return Ok(Arc::new( - InformationSchemaProvider::new(Arc::clone(&self.inner.catalog_list)) - .with_table_functions(self.inner.table_functions.clone()), - )); + if *resolved_ref.schema == *INFORMATION_SCHEMA { + return Ok(Arc::new( + InformationSchemaProvider::new(Arc::clone(&self.inner.catalog_list)) + .with_table_functions(self.inner.table_functions.clone()) + .with_system_catalog(String::from(system_catalog)), + )); + } + } else { + let catalog = + self.inner + .catalog_list + .catalog(catalog_name) + .ok_or_else(|| { + plan_datafusion_err!("failed to resolve catalog: {catalog_name}") + })?; + + if self.inner.config.information_schema() + && *resolved_ref.schema == *INFORMATION_SCHEMA + { + #[derive(Debug)] + struct SingleCatalogList { + catalog_name: String, + catalog: Arc, + } + + impl CatalogProviderList for SingleCatalogList { + fn register_catalog( + &self, + _: String, + _: Arc, + ) -> Option> { + None + } + + fn catalog_names(&self) -> Vec { + vec![self.catalog_name.clone()] + } + + fn catalog(&self, name: &str) -> Option> { + if name == self.catalog_name { + Some(Arc::clone(&self.catalog)) + } else { + None + } + } + } + + let catalog_list = Arc::new(SingleCatalogList { + catalog_name: catalog_name.to_string(), + catalog, + }); + + return Ok(Arc::new( + InformationSchemaProvider::new(catalog_list) + .with_table_functions(self.inner.table_functions.clone()), + )); + } + + if let Some(schema) = catalog.schema(&resolved_ref.schema) { + return Ok(schema); + } } - self.inner - .catalog_list - .catalog(&resolved_ref.catalog) - .ok_or_else(|| { - plan_datafusion_err!( - "failed to resolve catalog: {}", - resolved_ref.catalog - ) - })? - .schema(&resolved_ref.schema) - .ok_or_else(|| { - plan_datafusion_err!("failed to resolve schema: {}", resolved_ref.schema) - }) + plan_err!("failed to resolve schema: {}", resolved_ref.schema) } /// Add `analyzer_rule` to the end of the list of @@ -622,7 +673,34 @@ impl SessionState { &self, statement: Statement, ) -> datafusion_common::Result { - let references = self.resolve_table_references(&statement)?; + let mut references = self.resolve_table_references(&statement)?; + let require_info_schema = match &statement { + Statement::Statement(s) => matches!( + s.as_ref(), + SQLStatement::ShowCreate { .. } + | SQLStatement::ShowTables { .. } + | SQLStatement::ShowColumns { .. } + | SQLStatement::ShowVariable { .. } + | SQLStatement::ShowFunctions { .. } + ), + _ => false, + }; + + if require_info_schema { + let info_tables = INFORMATION_SCHEMA_TABLES.iter(); + let info_table_refs: Vec<_> = if let Some(system_catalog) = + self.config().system_catalog() + { + info_tables + .map(|n| TableReference::full(system_catalog, INFORMATION_SCHEMA, *n)) + .collect() + } else { + info_tables + .map(|n| TableReference::partial(INFORMATION_SCHEMA, *n)) + .collect() + }; + references.extend(info_table_refs); + } let mut provider = SessionContextProvider { state: self, diff --git a/datafusion/execution/src/config.rs b/datafusion/execution/src/config.rs index 80658477c9c07..a0cec95c50bf6 100644 --- a/datafusion/execution/src/config.rs +++ b/datafusion/execution/src/config.rs @@ -227,6 +227,13 @@ impl SessionConfig { self.options.execution.target_partitions } + /// Get [`system_catalog`] + /// + /// [`system_catalog`]: datafusion_common::config::CatalogOptions::system_catalog + pub fn system_catalog(&self) -> Option<&str> { + self.options.catalog.system_catalog.as_deref() + } + /// Get [`information_schema`] /// /// [`information_schema`]: datafusion_common::config::CatalogOptions::information_schema @@ -312,6 +319,14 @@ impl SessionConfig { self } + /// Set [`system_catalog`] + /// + /// [`system_catalog`]: datafusion_common::config::CatalogOptions::system_catalog + pub fn with_system_catalog(mut self, catalog_name: Option) -> Self { + self.options_mut().catalog.system_catalog = catalog_name; + self + } + /// Set [`information_schema`] /// /// [`information_schema`]: datafusion_common::config::CatalogOptions::information_schema diff --git a/datafusion/sql/src/resolve.rs b/datafusion/sql/src/resolve.rs index b6b1a74849be0..9abc687a2c39b 100644 --- a/datafusion/sql/src/resolve.rs +++ b/datafusion/sql/src/resolve.rs @@ -25,28 +25,6 @@ use crate::planner::object_name_to_table_reference; use datafusion_common::TableReference; use sqlparser::ast::*; -// following constants are used in `resolve_table_references` -// and should be same as `datafusion/catalog/src/information_schema.rs` -const INFORMATION_SCHEMA: &str = "information_schema"; -const TABLES: &str = "tables"; -const VIEWS: &str = "views"; -const COLUMNS: &str = "columns"; -const DF_SETTINGS: &str = "df_settings"; -const SCHEMATA: &str = "schemata"; -const ROUTINES: &str = "routines"; -const PARAMETERS: &str = "parameters"; - -/// All information schema tables -const INFORMATION_SCHEMA_TABLES: &[&str] = &[ - TABLES, - VIEWS, - COLUMNS, - DF_SETTINGS, - SCHEMATA, - ROUTINES, - PARAMETERS, -]; - // Collect table/CTE references as `TableReference`s and normalize them during traversal. // This avoids a second normalization/conversion pass after visiting the AST. struct RelationVisitor { @@ -127,34 +105,6 @@ impl Visitor for RelationVisitor { self.insert_relation(obj_name)?; } - // SHOW statements will later be rewritten into a SELECT from the information_schema - let requires_information_schema = matches!( - statement, - Statement::ShowFunctions { .. } - | Statement::ShowVariable { .. } - | Statement::ShowStatus { .. } - | Statement::ShowVariables { .. } - | Statement::ShowCreate { .. } - | Statement::ShowColumns { .. } - | Statement::ShowTables { .. } - | Statement::ShowCollation { .. } - ); - if requires_information_schema { - for s in INFORMATION_SCHEMA_TABLES { - // Information schema references are synthesized here, so convert directly. - let obj = ObjectName::from(vec![ - Ident::new(INFORMATION_SCHEMA), - Ident::new(*s), - ]); - match object_name_to_table_reference(obj, self.enable_ident_normalization) - { - Ok(tbl_ref) => { - self.relations.insert(tbl_ref); - } - Err(e) => return ControlFlow::Break(e), - } - } - } ControlFlow::Continue(()) } } diff --git a/datafusion/sql/src/statement.rs b/datafusion/sql/src/statement.rs index fe0b359839171..75a7e6170fe6f 100644 --- a/datafusion/sql/src/statement.rs +++ b/datafusion/sql/src/statement.rs @@ -16,6 +16,7 @@ // under the License. use std::collections::{BTreeMap, HashMap, HashSet}; +use std::ops::Deref; use std::path::Path; use std::str::FromStr; use std::sync::Arc; @@ -1675,14 +1676,16 @@ impl SqlToRel<'_, S> { /// Generate a logical plan from a "SHOW TABLES" query fn show_tables_to_plan(&self) -> Result { - if self.has_table("information_schema", "tables") { - let query = "SELECT * FROM information_schema.tables;"; - let mut rewrite = DFParser::parse_sql(query)?; - assert_eq!(rewrite.len(), 1); - self.statement_to_plan(rewrite.pop_front().unwrap()) // length of rewrite is 1 - } else { - plan_err!("SHOW TABLES is not supported unless information_schema is enabled") - } + let Some(tables_table_ref) = self.resolve_info_table("tables") else { + return plan_err!( + "SHOW TABLES is not supported unless information_schema is enabled" + ); + }; + + let query = format!("SELECT * FROM {tables_table_ref};"); + let mut rewrite = DFParser::parse_sql(&query)?; + assert_eq!(rewrite.len(), 1); + self.statement_to_plan(rewrite.pop_front().unwrap()) // length of rewrite is 1 } fn describe_table_to_plan(&self, table_name: ObjectName) -> Result { @@ -2208,11 +2211,11 @@ impl SqlToRel<'_, S> { } fn show_variable_to_plan(&self, variable: &[Ident]) -> Result { - if !self.has_table("information_schema", "df_settings") { + let Some(df_settings_table_ref) = self.resolve_info_table("df_settings") else { return plan_err!( "SHOW [VARIABLE] is not supported unless information_schema is enabled" ); - } + }; let verbose = variable .last() @@ -2227,7 +2230,7 @@ impl SqlToRel<'_, S> { } let variable = object_name_to_string(&ObjectName::from(variable_vec)); - let base_query = format!("SELECT {columns} FROM information_schema.df_settings"); + let base_query = format!("SELECT {columns} FROM {df_settings_table_ref}"); let query = if variable == "all" { // Add an ORDER BY so the output comes out in a consistent order format!("{base_query} ORDER BY name") @@ -2996,11 +2999,11 @@ impl SqlToRel<'_, S> { self.options.enable_ident_normalization, )?; - if !self.has_table("information_schema", "columns") { + let Some(columns_table_ref) = self.resolve_info_table("columns") else { return plan_err!( "SHOW COLUMNS is not supported unless information_schema is enabled" ); - } + }; // Do a table lookup to verify the table exists let table_ref = self.object_name_to_table_reference(sql_table_name)?; @@ -3013,9 +3016,8 @@ impl SqlToRel<'_, S> { "table_catalog, table_schema, table_name, column_name, data_type, is_nullable" }; - let query = format!( - "SELECT {select_list} FROM information_schema.columns WHERE {where_clause}" - ); + let query = + format!("SELECT {select_list} FROM {columns_table_ref} WHERE {where_clause}"); let mut rewrite = DFParser::parse_sql(&query)?; assert_eq!(rewrite.len(), 1); @@ -3144,11 +3146,12 @@ FROM ( &self, sql_table_name: ObjectName, ) -> Result { - if !self.has_table("information_schema", "tables") { + let Some(tables_table_ref) = self.resolve_info_table("views") else { return plan_err!( "SHOW CREATE TABLE is not supported unless information_schema is enabled" ); - } + }; + // Figure out the where clause let where_clause = object_name_to_qualifier( &sql_table_name, @@ -3160,7 +3163,7 @@ FROM ( let _ = self.context_provider.get_table_source(table_ref)?; let query = format!( - "SELECT table_catalog, table_schema, table_name, definition FROM information_schema.views WHERE {where_clause}" + "SELECT table_catalog, table_schema, table_name, definition FROM {tables_table_ref} WHERE {where_clause}" ); let mut rewrite = DFParser::parse_sql(&query)?; @@ -3169,14 +3172,31 @@ FROM ( } /// Return true if there is a table provider available for "schema.table" - fn has_table(&self, schema: &str, table: &str) -> bool { - let tables_reference = TableReference::Partial { - schema: schema.into(), - table: table.into(), + fn resolve_info_table(&self, table: &str) -> Option { + let table_reference = if let Some(system_catalog) = + &self.context_provider.options().catalog.system_catalog + { + TableReference::Full { + catalog: system_catalog.deref().into(), + schema: "information_schema".into(), + table: table.into(), + } + } else { + TableReference::Partial { + schema: "information_schema".into(), + table: table.into(), + } }; - self.context_provider - .get_table_source(tables_reference) + + if self + .context_provider + .get_table_source(table_reference.clone()) .is_ok() + { + Some(table_reference) + } else { + None + } } fn validate_transaction_kind( diff --git a/datafusion/sqllogictest/test_files/information_schema.slt b/datafusion/sqllogictest/test_files/information_schema.slt index f6633a165df18..b34c772f618fe 100644 --- a/datafusion/sqllogictest/test_files/information_schema.slt +++ b/datafusion/sqllogictest/test_files/information_schema.slt @@ -213,6 +213,7 @@ datafusion.catalog.has_header true datafusion.catalog.information_schema true datafusion.catalog.location NULL datafusion.catalog.newlines_in_values false +datafusion.catalog.system_catalog NULL datafusion.execution.batch_size 8192 datafusion.execution.coalesce_batches true datafusion.execution.collect_statistics true @@ -375,6 +376,7 @@ datafusion.catalog.has_header true Default value for `format.has_header` for `CR datafusion.catalog.information_schema true Should DataFusion provide access to `information_schema` virtual tables for displaying schema information datafusion.catalog.location NULL Location scanned to load tables for `default` schema datafusion.catalog.newlines_in_values false Specifies whether newlines in (quoted) CSV values are supported. This is the default value for `format.newlines_in_values` for `CREATE EXTERNAL TABLE` if not specified explicitly in the statement. Parsing newlines in quoted values may be affected by execution behaviour such as parallel file scanning. Setting this to `true` ensures that newlines in values are parsed successfully, which may reduce performance. +datafusion.catalog.system_catalog NULL The system schema name - when set a global system and `information_schema` catalog will be available datafusion.execution.batch_size 8192 Default batch size while creating new batches, it's especially useful for buffer-in-memory batches since creating tiny batches would result in too much metadata memory consumption datafusion.execution.coalesce_batches true When set to true, record batches will be examined between each operator and small batches will be coalesced into larger batches. This is helpful when there are highly selective filters or joins that could produce tiny output batches. The target batch size is determined by the configuration setting datafusion.execution.collect_statistics true Should DataFusion collect statistics when first creating a table. Has no effect after the table is created. Defaults to true. diff --git a/datafusion/sqllogictest/test_files/information_schema_multiple_catalogs.slt b/datafusion/sqllogictest/test_files/information_schema_multiple_catalogs.slt index a7f71208882de..ae5a54902f23f 100644 --- a/datafusion/sqllogictest/test_files/information_schema_multiple_catalogs.slt +++ b/datafusion/sqllogictest/test_files/information_schema_multiple_catalogs.slt @@ -76,25 +76,24 @@ set datafusion.catalog.default_schema = my_other_schema; statement ok create table t3 as values(1); +query TTT rowsort +SELECT table_catalog, table_schema, table_name +FROM my_catalog.information_schema.tables +WHERE table_schema <> 'information_schema'; +---- +my_catalog my_schema t1 +my_catalog my_schema t2 + +query TTT rowsort +SELECT table_catalog, table_schema, table_name +FROM my_other_catalog.information_schema.tables +WHERE table_schema <> 'information_schema'; +---- +my_other_catalog my_other_schema t3 + query TTTT rowsort SELECT * from information_schema.tables; ---- -datafusion information_schema columns VIEW -datafusion information_schema df_settings VIEW -datafusion information_schema parameters VIEW -datafusion information_schema routines VIEW -datafusion information_schema schemata VIEW -datafusion information_schema tables VIEW -datafusion information_schema views VIEW -my_catalog information_schema columns VIEW -my_catalog information_schema df_settings VIEW -my_catalog information_schema parameters VIEW -my_catalog information_schema routines VIEW -my_catalog information_schema schemata 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 df_settings VIEW my_other_catalog information_schema parameters VIEW diff --git a/docs/source/user-guide/configs.md b/docs/source/user-guide/configs.md index ba8ea52bb0850..6537031c613a0 100644 --- a/docs/source/user-guide/configs.md +++ b/docs/source/user-guide/configs.md @@ -68,6 +68,7 @@ The following configuration settings are available: | datafusion.catalog.create_default_catalog_and_schema | true | Whether the default catalog and schema should be created automatically. | | datafusion.catalog.default_catalog | datafusion | The default catalog name - this impacts what SQL queries use if not specified | | datafusion.catalog.default_schema | public | The default schema name - this impacts what SQL queries use if not specified | +| datafusion.catalog.system_catalog | NULL | The system schema name - when set a global system and `information_schema` catalog will be available | | datafusion.catalog.information_schema | false | Should DataFusion provide access to `information_schema` virtual tables for displaying schema information | | datafusion.catalog.location | NULL | Location scanned to load tables for `default` schema | | datafusion.catalog.format | NULL | Type of `TableProvider` to use when loading `default` schema |