Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions datafusion/core/tests/sql/information_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,41 @@ async fn information_schema_show_tables_no_information_schema() {
assert_eq!(err.to_string(), "Error during planning: SHOW TABLES is not supported unless information_schema is enabled");
}

#[tokio::test]
async fn information_schema_describe_table() {
let ctx =
SessionContext::with_config(SessionConfig::new().with_information_schema(true));

let sql = "CREATE OR REPLACE TABLE y AS VALUES (1,2),(3,4);";
ctx.sql(sql).await.unwrap();

let sql_all = "describe y;";
let results_all = execute_to_batches(&ctx, sql_all).await;

let expected = vec![
"+-------------+-----------+-------------+",
"| column_name | data_type | is_nullable |",
"+-------------+-----------+-------------+",
"| column1 | Int64 | YES |",
"| column2 | Int64 | YES |",
"+-------------+-----------+-------------+",
];

assert_batches_eq!(expected, &results_all);
}

#[tokio::test]
async fn information_schema_describe_table_not_exists() {
let ctx = SessionContext::with_config(SessionConfig::new());

let sql_all = "describe table;";
let err = plan_and_collect(&ctx, sql_all).await.unwrap_err();
assert_eq!(
err.to_string(),
"Error during planning: 'datafusion.public.table' not found"
);
}

#[tokio::test]
async fn information_schema_show_tables() {
let ctx =
Expand Down
24 changes: 24 additions & 0 deletions datafusion/sql/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ pub struct CreateExternalTable {
pub if_not_exists: bool,
}

/// DataFusion extension DDL for `DESCRIBE TABLE`
#[derive(Debug, Clone, PartialEq)]
pub struct DescribeTable {
/// Table name
pub table_name: String,
}

/// DataFusion Statement representations.
///
/// Tokens parsed by `DFParser` are converted into these values.
Expand All @@ -78,6 +85,8 @@ pub enum Statement {
Statement(Box<SQLStatement>),
/// Extension: `CREATE EXTERNAL TABLE`
CreateExternalTable(CreateExternalTable),
/// Extension: `DESCRIBE TABLE`
DescribeTable(DescribeTable),
}

/// SQL Parser
Expand Down Expand Up @@ -155,6 +164,12 @@ impl<'a> DFParser<'a> {
// use custom parsing
self.parse_create()
}
Keyword::DESCRIBE => {
// move one token forward
self.parser.next_token();
// use custom parsing
self.parse_describe()
}
_ => {
// use the native parser
Ok(Statement::Statement(Box::from(
Expand All @@ -172,6 +187,15 @@ impl<'a> DFParser<'a> {
}
}

pub fn parse_describe(&mut self) -> Result<Statement, ParserError> {
let table_name = self.parser.parse_object_name()?;

let des = DescribeTable {
table_name: table_name.to_string(),
};
Ok(Statement::DescribeTable(des))
}

/// Parse a SQL CREATE statement
pub fn parse_create(&mut self) -> Result<Statement, ParserError> {
if self.parser.parse_keyword(Keyword::EXTERNAL) {
Expand Down
28 changes: 27 additions & 1 deletion datafusion/sql/src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

//! SQL Query Planner (produces logical plan from SQL AST)

use crate::parser::{CreateExternalTable, Statement as DFStatement};
use crate::parser::{CreateExternalTable, DescribeTable, Statement as DFStatement};
use arrow::datatypes::*;
use datafusion_common::ToDFSchema;
use datafusion_expr::expr_rewriter::normalize_col;
Expand Down Expand Up @@ -139,6 +139,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
match statement {
DFStatement::CreateExternalTable(s) => self.external_table_to_plan(s),
DFStatement::Statement(s) => self.sql_statement_to_plan(*s),
DFStatement::DescribeTable(s) => self.describe_table_to_plan(s),
}
}

Expand Down Expand Up @@ -353,6 +354,31 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
}

pub fn describe_table_to_plan(
&self,
statement: DescribeTable,
) -> Result<LogicalPlan> {
let table_name = statement.table_name;
let table_ref: TableReference = table_name.as_str().into();

// check if table_name exists
if let Err(e) = self.schema_provider.get_table_provider(table_ref) {
return Err(e);
}

if self.has_table("information_schema", "tables") {
let sql = format!("SELECT column_name, data_type, is_nullable \
FROM information_schema.columns WHERE table_name = '{table_name}';");
let mut rewrite = DFParser::parse_sql(&sql[..])?;
self.statement_to_plan(rewrite.pop_front().unwrap())
} else {
Err(DataFusionError::Plan(
"DESCRIBE TABLE is not supported unless information_schema is enabled"
.to_string(),
))
}
}

/// Generate a logical plan from a CREATE EXTERNAL TABLE statement
pub fn external_table_to_plan(
&self,
Expand Down