From 0544e5e1a871e1b5f6182577a52d008e5695df68 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Fri, 30 Aug 2024 17:51:45 +0200 Subject: [PATCH] Implement SHOW FUNCTIONS --- datafusion/core/tests/sql/mod.rs | 1 + datafusion/core/tests/sql/show.rs | 45 +++++++++++++++++++++++++++++++ datafusion/sql/src/statement.rs | 43 ++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 datafusion/core/tests/sql/show.rs diff --git a/datafusion/core/tests/sql/mod.rs b/datafusion/core/tests/sql/mod.rs index dc9d04786021..601e7b4d1b81 100644 --- a/datafusion/core/tests/sql/mod.rs +++ b/datafusion/core/tests/sql/mod.rs @@ -62,6 +62,7 @@ pub mod explain_analyze; pub mod joins; mod path_partition; pub mod select; +pub mod show; mod sql_api; async fn register_aggregate_csv_by_sql(ctx: &SessionContext) { diff --git a/datafusion/core/tests/sql/show.rs b/datafusion/core/tests/sql/show.rs new file mode 100644 index 000000000000..dceeb11a7580 --- /dev/null +++ b/datafusion/core/tests/sql/show.rs @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use super::*; +use std::collections::HashSet; + +#[tokio::test] +async fn test_show_functions() { + let ctx = SessionContext::new(); + let result = execute(&ctx, "SHOW FUNCTIONS").await; + println!("{:?}", result); + assert!(!result.is_empty(), "result is empty"); + let names: HashSet = result + .into_iter() + .map(|mut row| { + assert_eq!(row.len(), 1); + row.remove(0) + }) + .collect(); + [ + "array_distinct", + "to_unixtime", + "covar_pop", + "max", + "concat", + ] + .into_iter() + .for_each(|name| { + assert!(names.contains(name), "{} not found in {:?}", name, names); + }); +} diff --git a/datafusion/sql/src/statement.rs b/datafusion/sql/src/statement.rs index 3dfc379b039a..d755767d0013 100644 --- a/datafusion/sql/src/statement.rs +++ b/datafusion/sql/src/statement.rs @@ -15,7 +15,8 @@ // specific language governing permissions and limitations // under the License. -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::iter; use std::path::Path; use std::str::FromStr; use std::sync::Arc; @@ -29,7 +30,7 @@ use crate::planner::{ }; use crate::utils::normalize_ident; -use arrow_schema::{DataType, Fields}; +use arrow_schema::{DataType, Field, Fields, Schema}; use datafusion_common::parsers::CompressionTypeVariant; use datafusion_common::{ exec_err, not_impl_err, plan_datafusion_err, plan_err, schema_err, @@ -43,7 +44,7 @@ use datafusion_expr::logical_plan::builder::project; use datafusion_expr::logical_plan::DdlStatement; use datafusion_expr::utils::expr_to_columns; use datafusion_expr::{ - cast, col, Analyze, CreateCatalog, CreateCatalogSchema, + cast, col, lit, Analyze, BuiltInWindowFunction, CreateCatalog, CreateCatalogSchema, CreateExternalTable as PlanCreateExternalTable, CreateFunction, CreateFunctionBody, CreateIndex as PlanCreateIndex, CreateMemoryTable, CreateView, DescribeTable, DmlStatement, DropCatalogSchema, DropFunction, DropTable, DropView, EmptyRelation, @@ -51,7 +52,7 @@ use datafusion_expr::{ OperateFunctionArg, PlanType, Prepare, SetVariable, SortExpr, Statement as PlanStatement, ToStringifiedPlan, TransactionAccessMode, TransactionConclusion, TransactionEnd, TransactionIsolationLevel, TransactionStart, - Volatility, WriteOp, + Values, Volatility, WriteOp, }; use sqlparser::ast; use sqlparser::ast::{ @@ -62,6 +63,7 @@ use sqlparser::ast::{ TableWithJoins, TransactionMode, UnaryOperator, Value, }; use sqlparser::parser::ParserError::ParserError; +use strum::IntoEnumIterator; fn ident_to_string(ident: &Ident) -> String { normalize_ident(ident.to_owned()) @@ -469,6 +471,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { filter, } => self.show_columns_to_plan(extended, full, table_name, filter), + Statement::ShowFunctions { filter } => self.show_functions_to_plan(filter), + Statement::Insert(Insert { or, into, @@ -1588,6 +1592,37 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { self.statement_to_plan(rewrite.pop_front().unwrap()) // length of rewrite is 1 } + fn show_functions_to_plan( + &self, + filter: Option, + ) -> Result { + if filter.is_some() { + // See https://github.com/sqlparser-rs/sqlparser-rs/issues/1399 before adding support for filter. + return plan_err!("SHOW FUNCTIONS with WHERE or LIKE is not supported"); + } + + let names = iter::empty::() + .chain(self.context_provider.udf_names()) + .chain(self.context_provider.udaf_names()) + .chain(BuiltInWindowFunction::iter().map(|func| func.to_string())) + .chain(self.context_provider.udwf_names()) + // TODO list table functions + .map(|name| name.to_lowercase()) + .collect::>(); + + Ok(LogicalPlan::Values(Values { + schema: Arc::new( + DFSchema::try_from(Schema::new(vec![Field::new( + "function_name", + DataType::Utf8, + false, + )])) + .unwrap(), + ), + values: names.into_iter().map(|name| vec![lit(name)]).collect(), + })) + } + /// 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 {