This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new f05458678b build(deps): upgrade sqlparser to 0.47.0 (#10392)
f05458678b is described below
commit f05458678b94332982b545f54d587c31bcd58c7e
Author: tison <[email protected]>
AuthorDate: Fri Jun 7 04:37:16 2024 +0800
build(deps): upgrade sqlparser to 0.47.0 (#10392)
* build(deps): upgrade sqlparser to 0.46.0
Signed-off-by: tison <[email protected]>
* function and cast fixups
* catchup refactors
Signed-off-by: tison <[email protected]>
* try migrate json expr
Signed-off-by: tison <[email protected]>
* Update for changes in sqlparser
* Update dependencies
* handle zero argument form
* fmt
* fixup more
Signed-off-by: tison <[email protected]>
* fixup more
Signed-off-by: tison <[email protected]>
* try use jmhain's branch
Signed-off-by: tison <[email protected]>
* fix compile FunctionArgumentClause exhausted
Signed-off-by: tison <[email protected]>
* fix compile set multi vars
Signed-off-by: tison <[email protected]>
* fix compile new string values
Signed-off-by: tison <[email protected]>
* fix compile set multi vars
Signed-off-by: tison <[email protected]>
* fix compile Subscript
Signed-off-by: tison <[email protected]>
* cargo fmt
Signed-off-by: tison <[email protected]>
* revert workaround on values
Signed-off-by: tison <[email protected]>
* Rework field access
* update lock
* fix doc
* try catchup new sqlparser version
Signed-off-by: tison <[email protected]>
* fixup timezone expr
Signed-off-by: tison <[email protected]>
* fixup params
Signed-off-by: tison <[email protected]>
* lock
Signed-off-by: tison <[email protected]>
* Update to sqlparser 0.47.0
* Update rust stack size on windows
* Revert "Update rust stack size on windows"
This reverts commit b5743d55a3992880a8d5c6f359c28aa4fa17c98a.
* Add test + support for `$$` function definition
* Disable failing windows CI test
* fmt
* simplify test
* fmt
---------
Signed-off-by: tison <[email protected]>
Co-authored-by: Joey Hain <[email protected]>
Co-authored-by: Andrew Lamb <[email protected]>
---
Cargo.toml | 2 +-
datafusion-cli/Cargo.lock | 4 +-
datafusion-cli/tests/cli_integration.rs | 2 +
datafusion-examples/examples/function_factory.rs | 2 +-
.../user_defined/user_defined_scalar_functions.rs | 112 +++++-
datafusion/expr/src/logical_plan/ddl.rs | 25 +-
datafusion/expr/src/logical_plan/mod.rs | 4 +-
datafusion/sql/src/expr/binary_op.rs | 2 +
datafusion/sql/src/expr/function.rs | 128 ++++++-
datafusion/sql/src/expr/json_access.rs | 31 --
datafusion/sql/src/expr/mod.rs | 397 ++++++++-------------
datafusion/sql/src/expr/value.rs | 1 +
datafusion/sql/src/parser.rs | 7 +-
datafusion/sql/src/planner.rs | 2 +-
datafusion/sql/src/select.rs | 16 +-
datafusion/sql/src/statement.rs | 150 ++++----
datafusion/sql/src/unparser/ast.rs | 2 +
datafusion/sql/src/unparser/expr.rs | 40 ++-
datafusion/sqllogictest/test_files/aggregate.slt | 2 +-
datafusion/sqllogictest/test_files/errors.slt | 2 +-
20 files changed, 502 insertions(+), 429 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 54f2f203fc..ccd54d7d25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -111,7 +111,7 @@ rand = "0.8"
regex = "1.8"
rstest = "0.21.0"
serde_json = "1"
-sqlparser = { version = "0.45.0", features = ["visitor"] }
+sqlparser = { version = "0.47", features = ["visitor"] }
tempfile = "3"
thiserror = "1.0.44"
tokio = { version = "1.36", features = ["macros", "rt", "sync"] }
diff --git a/datafusion-cli/Cargo.lock b/datafusion-cli/Cargo.lock
index b165070c60..c4a447d133 100644
--- a/datafusion-cli/Cargo.lock
+++ b/datafusion-cli/Cargo.lock
@@ -3261,9 +3261,9 @@ checksum =
"6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "sqlparser"
-version = "0.45.0"
+version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7bbffee862a796d67959a89859d6b1046bb5016d63e23835ad0da182777bbe0"
+checksum = "295e9930cd7a97e58ca2a070541a3ca502b17f5d1fa7157376d0fabd85324f25"
dependencies = [
"log",
"sqlparser_derive",
diff --git a/datafusion-cli/tests/cli_integration.rs
b/datafusion-cli/tests/cli_integration.rs
index 119a0aa39d..27cabf15af 100644
--- a/datafusion-cli/tests/cli_integration.rs
+++ b/datafusion-cli/tests/cli_integration.rs
@@ -28,6 +28,8 @@ fn init() {
let _ = env_logger::try_init();
}
+// Disabled due to https://github.com/apache/datafusion/issues/10793
+#[cfg(not(target_family = "windows"))]
#[rstest]
#[case::exec_from_commands(
["--command", "select 1", "--format", "json", "-q"],
diff --git a/datafusion-examples/examples/function_factory.rs
b/datafusion-examples/examples/function_factory.rs
index d61c19af47..f57b3bf604 100644
--- a/datafusion-examples/examples/function_factory.rs
+++ b/datafusion-examples/examples/function_factory.rs
@@ -212,7 +212,7 @@ impl TryFrom<CreateFunction> for ScalarFunctionWrapper {
name: definition.name,
expr: definition
.params
- .return_
+ .function_body
.expect("Expression has to be defined!"),
return_type: definition
.return_type
diff --git
a/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs
b/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs
index 2d98b7f80f..a81fc9159e 100644
--- a/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs
+++ b/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs
@@ -28,14 +28,17 @@ use datafusion_common::cast::{as_float64_array,
as_int32_array};
use datafusion_common::tree_node::{Transformed, TreeNode};
use datafusion_common::{
assert_batches_eq, assert_batches_sorted_eq, assert_contains, exec_err,
internal_err,
- not_impl_err, plan_err, DataFusionError, ExprSchema, Result, ScalarValue,
+ not_impl_err, plan_err, DFSchema, DataFusionError, ExprSchema, Result,
ScalarValue,
};
-use datafusion_execution::runtime_env::{RuntimeConfig, RuntimeEnv};
use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
use datafusion_expr::{
- Accumulator, ColumnarValue, CreateFunction, ExprSchemable,
LogicalPlanBuilder,
- ScalarUDF, ScalarUDFImpl, Signature, Volatility,
+ Accumulator, ColumnarValue, CreateFunction, CreateFunctionBody,
ExprSchemable,
+ LogicalPlanBuilder, OperateFunctionArg, ScalarUDF, ScalarUDFImpl,
Signature,
+ Volatility,
};
+use datafusion_functions_array::range::range_udf;
+use parking_lot::Mutex;
+use sqlparser::ast::Ident;
/// test that casting happens on udfs.
/// c11 is f32, but `custom_sqrt` requires f64. Casting happens but the
logical plan and
@@ -828,7 +831,7 @@ impl TryFrom<CreateFunction> for ScalarFunctionWrapper {
name: definition.name,
expr: definition
.params
- .return_
+ .function_body
.expect("Expression has to be defined!"),
return_type: definition
.return_type
@@ -852,15 +855,7 @@ impl TryFrom<CreateFunction> for ScalarFunctionWrapper {
#[tokio::test]
async fn create_scalar_function_from_sql_statement() -> Result<()> {
let function_factory = Arc::new(CustomFunctionFactory::default());
- let runtime_config = RuntimeConfig::new();
- let runtime_environment = RuntimeEnv::new(runtime_config)?;
-
- let session_config = SessionConfig::new();
- let state =
- SessionState::new_with_config_rt(session_config,
Arc::new(runtime_environment))
- .with_function_factory(function_factory.clone());
-
- let ctx = SessionContext::new_with_state(state);
+ let ctx =
SessionContext::new().with_function_factory(function_factory.clone());
let options = SQLOptions::new().with_allow_ddl(false);
let sql = r#"
@@ -926,6 +921,95 @@ async fn create_scalar_function_from_sql_statement() ->
Result<()> {
Ok(())
}
+/// Saves whatever is passed to it as a scalar function
+#[derive(Debug, Default)]
+struct RecordingFunctonFactory {
+ calls: Mutex<Vec<CreateFunction>>,
+}
+
+impl RecordingFunctonFactory {
+ fn new() -> Self {
+ Self::default()
+ }
+
+ /// return all the calls made to the factory
+ fn calls(&self) -> Vec<CreateFunction> {
+ self.calls.lock().clone()
+ }
+}
+
+#[async_trait::async_trait]
+impl FunctionFactory for RecordingFunctonFactory {
+ async fn create(
+ &self,
+ _state: &SessionState,
+ statement: CreateFunction,
+ ) -> Result<RegisterFunction> {
+ self.calls.lock().push(statement);
+
+ let udf = range_udf();
+ Ok(RegisterFunction::Scalar(udf))
+ }
+}
+
+#[tokio::test]
+async fn create_scalar_function_from_sql_statement_postgres_syntax() ->
Result<()> {
+ let function_factory = Arc::new(RecordingFunctonFactory::new());
+ let ctx =
SessionContext::new().with_function_factory(function_factory.clone());
+
+ let sql = r#"
+ CREATE FUNCTION strlen(name TEXT)
+ RETURNS int LANGUAGE plrust AS
+ $$
+ Ok(Some(name.unwrap().len() as i32))
+ $$;
+ "#;
+
+ let body = "
+ Ok(Some(name.unwrap().len() as i32))
+ ";
+
+ match ctx.sql(sql).await {
+ Ok(_) => {}
+ Err(e) => {
+ panic!("Error creating function: {}", e);
+ }
+ }
+
+ // verify that the call was passed through
+ let calls = function_factory.calls();
+ let schema = DFSchema::try_from(Schema::empty())?;
+ assert_eq!(calls.len(), 1);
+ let call = &calls[0];
+ let expected = CreateFunction {
+ or_replace: false,
+ temporary: false,
+ name: "strlen".into(),
+ args: Some(vec![OperateFunctionArg {
+ name: Some(Ident {
+ value: "name".into(),
+ quote_style: None,
+ }),
+ data_type: DataType::Utf8,
+ default_expr: None,
+ }]),
+ return_type: Some(DataType::Int32),
+ params: CreateFunctionBody {
+ language: Some(Ident {
+ value: "plrust".into(),
+ quote_style: None,
+ }),
+ behavior: None,
+ function_body: Some(lit(body)),
+ },
+ schema: Arc::new(schema),
+ };
+
+ assert_eq!(call, &expected);
+
+ Ok(())
+}
+
fn create_udf_context() -> SessionContext {
let ctx = SessionContext::new();
// register a custom UDF
diff --git a/datafusion/expr/src/logical_plan/ddl.rs
b/datafusion/expr/src/logical_plan/ddl.rs
index 4538ff52c0..45ddbafecf 100644
--- a/datafusion/expr/src/logical_plan/ddl.rs
+++ b/datafusion/expr/src/logical_plan/ddl.rs
@@ -341,29 +341,8 @@ pub struct CreateFunctionBody {
pub language: Option<Ident>,
/// IMMUTABLE | STABLE | VOLATILE
pub behavior: Option<Volatility>,
- /// AS 'definition'
- pub as_: Option<DefinitionStatement>,
- /// RETURN expression
- pub return_: Option<Expr>,
-}
-
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
-pub enum DefinitionStatement {
- SingleQuotedDef(String),
- DoubleDollarDef(String),
-}
-
-impl From<sqlparser::ast::FunctionDefinition> for DefinitionStatement {
- fn from(value: sqlparser::ast::FunctionDefinition) -> Self {
- match value {
- sqlparser::ast::FunctionDefinition::SingleQuotedDef(s) => {
- Self::SingleQuotedDef(s)
- }
- sqlparser::ast::FunctionDefinition::DoubleDollarDef(s) => {
- Self::DoubleDollarDef(s)
- }
- }
- }
+ /// RETURN or AS function body
+ pub function_body: Option<Expr>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
diff --git a/datafusion/expr/src/logical_plan/mod.rs
b/datafusion/expr/src/logical_plan/mod.rs
index 034440643e..8928f70cd5 100644
--- a/datafusion/expr/src/logical_plan/mod.rs
+++ b/datafusion/expr/src/logical_plan/mod.rs
@@ -30,8 +30,8 @@ pub use builder::{
};
pub use ddl::{
CreateCatalog, CreateCatalogSchema, CreateExternalTable, CreateFunction,
- CreateFunctionBody, CreateMemoryTable, CreateView, DdlStatement,
DefinitionStatement,
- DropCatalogSchema, DropFunction, DropTable, DropView, OperateFunctionArg,
+ CreateFunctionBody, CreateMemoryTable, CreateView, DdlStatement,
DropCatalogSchema,
+ DropFunction, DropTable, DropView, OperateFunctionArg,
};
pub use dml::{DmlStatement, WriteOp};
pub use plan::{
diff --git a/datafusion/sql/src/expr/binary_op.rs
b/datafusion/sql/src/expr/binary_op.rs
index 0d37742e5b..fcb57e8a82 100644
--- a/datafusion/sql/src/expr/binary_op.rs
+++ b/datafusion/sql/src/expr/binary_op.rs
@@ -51,6 +51,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
BinaryOperator::PGBitwiseShiftRight =>
Ok(Operator::BitwiseShiftRight),
BinaryOperator::PGBitwiseShiftLeft =>
Ok(Operator::BitwiseShiftLeft),
BinaryOperator::StringConcat => Ok(Operator::StringConcat),
+ BinaryOperator::ArrowAt => Ok(Operator::ArrowAt),
+ BinaryOperator::AtArrow => Ok(Operator::AtArrow),
_ => not_impl_err!("Unsupported SQL binary operator {op:?}"),
}
}
diff --git a/datafusion/sql/src/expr/function.rs
b/datafusion/sql/src/expr/function.rs
index 81a9b4b772..ea460cb3ef 100644
--- a/datafusion/sql/src/expr/function.rs
+++ b/datafusion/sql/src/expr/function.rs
@@ -30,7 +30,9 @@ use datafusion_expr::{
BuiltInWindowFunction,
};
use sqlparser::ast::{
- Expr as SQLExpr, Function as SQLFunction, FunctionArg, FunctionArgExpr,
WindowType,
+ DuplicateTreatment, Expr as SQLExpr, Function as SQLFunction, FunctionArg,
+ FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
FunctionArguments,
+ NullTreatment, ObjectName, OrderByExpr, WindowType,
};
use std::str::FromStr;
use strum::IntoEnumIterator;
@@ -79,6 +81,120 @@ fn find_closest_match(candidates: Vec<String>, target:
&str) -> String {
.expect("No candidates provided.") // Panic if `candidates` argument
is empty
}
+/// Arguments to for a function call extracted from the SQL AST
+#[derive(Debug)]
+struct FunctionArgs {
+ /// Function name
+ name: ObjectName,
+ /// Argument expressions
+ args: Vec<FunctionArg>,
+ /// ORDER BY clause, if any
+ order_by: Vec<OrderByExpr>,
+ /// OVER clause, if any
+ over: Option<WindowType>,
+ /// FILTER clause, if any
+ filter: Option<Box<SQLExpr>>,
+ /// NULL treatment clause, if any
+ null_treatment: Option<NullTreatment>,
+ /// DISTINCT
+ distinct: bool,
+}
+
+impl FunctionArgs {
+ fn try_new(function: SQLFunction) -> Result<Self> {
+ let SQLFunction {
+ name,
+ args,
+ over,
+ filter,
+ mut null_treatment,
+ within_group,
+ } = function;
+
+ // Handle no argument form (aka `current_time` as opposed to
`current_time()`)
+ let FunctionArguments::List(args) = args else {
+ return Ok(Self {
+ name,
+ args: vec![],
+ order_by: vec![],
+ over,
+ filter,
+ null_treatment,
+ distinct: false,
+ });
+ };
+
+ let FunctionArgumentList {
+ duplicate_treatment,
+ args,
+ clauses,
+ } = args;
+
+ let distinct = match duplicate_treatment {
+ Some(DuplicateTreatment::Distinct) => true,
+ Some(DuplicateTreatment::All) => false,
+ None => false,
+ };
+
+ // Pull out argument handling
+ let mut order_by = None;
+ for clause in clauses {
+ match clause {
+ FunctionArgumentClause::IgnoreOrRespectNulls(nt) => {
+ if null_treatment.is_some() {
+ return not_impl_err!(
+ "Calling {name}: Duplicated null treatment clause"
+ );
+ }
+ null_treatment = Some(nt);
+ }
+ FunctionArgumentClause::OrderBy(oby) => {
+ if order_by.is_some() {
+ return not_impl_err!("Calling {name}: Duplicated ORDER
BY clause in function arguments");
+ }
+ order_by = Some(oby);
+ }
+ FunctionArgumentClause::Limit(limit) => {
+ return not_impl_err!(
+ "Calling {name}: LIMIT not supported in function
arguments: {limit}"
+ )
+ }
+ FunctionArgumentClause::OnOverflow(overflow) => {
+ return not_impl_err!(
+ "Calling {name}: ON OVERFLOW not supported in function
arguments: {overflow}"
+ )
+ }
+ FunctionArgumentClause::Having(having) => {
+ return not_impl_err!(
+ "Calling {name}: HAVING not supported in function
arguments: {having}"
+ )
+ }
+ FunctionArgumentClause::Separator(sep) => {
+ return not_impl_err!(
+ "Calling {name}: SEPARATOR not supported in function
arguments: {sep}"
+ )
+ }
+ }
+ }
+
+ if !within_group.is_empty() {
+ return not_impl_err!("WITHIN GROUP is not supported yet:
{within_group:?}");
+ }
+
+ let order_by = order_by.unwrap_or_default();
+
+ Ok(Self {
+ name,
+ args,
+ order_by,
+ over,
+ filter,
+ null_treatment,
+ distinct,
+ })
+ }
+}
+
impl<'a, S: ContextProvider> SqlToRel<'a, S> {
pub(super) fn sql_function_to_expr(
&self,
@@ -86,16 +202,16 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
- let SQLFunction {
+ let function_args = FunctionArgs::try_new(function)?;
+ let FunctionArgs {
name,
args,
+ order_by,
over,
- distinct,
filter,
null_treatment,
- special: _, // true if not called with trailing parens
- order_by,
- } = function;
+ distinct,
+ } = function_args;
// If function is a window function (it has an OVER clause),
// it shouldn't have ordering requirement as function argument
diff --git a/datafusion/sql/src/expr/json_access.rs
b/datafusion/sql/src/expr/json_access.rs
deleted file mode 100644
index b24482f882..0000000000
--- a/datafusion/sql/src/expr/json_access.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 crate::planner::{ContextProvider, SqlToRel};
-use datafusion_common::{not_impl_err, Result};
-use datafusion_expr::Operator;
-use sqlparser::ast::JsonOperator;
-
-impl<'a, S: ContextProvider> SqlToRel<'a, S> {
- pub(crate) fn parse_sql_json_access(&self, op: JsonOperator) ->
Result<Operator> {
- match op {
- JsonOperator::AtArrow => Ok(Operator::AtArrow),
- JsonOperator::ArrowAt => Ok(Operator::ArrowAt),
- _ => not_impl_err!("Unsupported SQL json operator {op:?}"),
- }
- }
-}
diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index d34aa4cec5..8b64ccfb52 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -17,18 +17,16 @@
use arrow_schema::DataType;
use arrow_schema::TimeUnit;
-use sqlparser::ast::{ArrayAgg, Expr as SQLExpr, JsonOperator, TrimWhereField,
Value};
-use sqlparser::parser::ParserError::ParserError;
+use sqlparser::ast::{CastKind, Expr as SQLExpr, Subscript, TrimWhereField,
Value};
use datafusion_common::{
internal_datafusion_err, internal_err, not_impl_err, plan_err, DFSchema,
Result,
ScalarValue,
};
-use datafusion_expr::expr::AggregateFunctionDefinition;
use datafusion_expr::expr::InList;
use datafusion_expr::expr::ScalarFunction;
use datafusion_expr::{
- col, expr, lit, AggregateFunction, Between, BinaryExpr, Cast, Expr,
ExprSchemable,
+ lit, AggregateFunction, Between, BinaryExpr, Cast, Expr, ExprSchemable,
GetFieldAccess, Like, Literal, Operator, TryCast,
};
@@ -38,7 +36,6 @@ mod binary_op;
mod function;
mod grouping_set;
mod identifier;
-mod json_access;
mod order_by;
mod subquery;
mod substring;
@@ -76,16 +73,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
stack.push(StackEntry::SQLExpr(right));
stack.push(StackEntry::SQLExpr(left));
}
- SQLExpr::JsonAccess {
- left,
- operator,
- right,
- } => {
- let op = self.parse_sql_json_access(operator)?;
- stack.push(StackEntry::Operator(op));
- stack.push(StackEntry::SQLExpr(right));
- stack.push(StackEntry::SQLExpr(left));
- }
_ => {
let expr = self.sql_expr_to_logical_expr_internal(
*sql_expr,
@@ -190,62 +177,85 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
self.sql_identifier_to_expr(id, schema, planner_context)
}
- SQLExpr::MapAccess { column, keys } => {
- if let SQLExpr::Identifier(id) = *column {
- let keys = keys.into_iter().map(|mak| mak.key).collect();
- self.plan_indexed(
- col(self.normalizer.normalize(id)),
- keys,
- schema,
- planner_context,
- )
- } else {
- not_impl_err!(
- "map access requires an identifier, found column
{column} instead"
- )
- }
+ SQLExpr::MapAccess { .. } => {
+ not_impl_err!("Map Access")
}
- SQLExpr::ArrayIndex { obj, indexes } => {
- fn is_unsupported(expr: &SQLExpr) -> bool {
- matches!(expr, SQLExpr::JsonAccess { .. })
- }
- fn simplify_array_index_expr(expr: Expr, index: Expr) ->
(Expr, bool) {
- match &expr {
- Expr::AggregateFunction(agg_func) if agg_func.func_def
==
datafusion_expr::expr::AggregateFunctionDefinition::BuiltIn(AggregateFunction::ArrayAgg)
=> {
- let mut new_args = agg_func.args.clone();
- new_args.push(index.clone());
-
(Expr::AggregateFunction(datafusion_expr::expr::AggregateFunction::new(
- datafusion_expr::AggregateFunction::NthValue,
- new_args,
- agg_func.distinct,
- agg_func.filter.clone(),
- agg_func.order_by.clone(),
- agg_func.null_treatment,
- )), true)
- },
- _ => (expr, false),
- }
- }
+ // <expr>["foo"], <expr>[4] or <expr>[4:5]
+ SQLExpr::Subscript { expr, subscript } => {
let expr =
- self.sql_expr_to_logical_expr(*obj, schema,
planner_context)?;
- if indexes.len() > 1 || is_unsupported(&indexes[0]) {
- return self.plan_indexed(expr, indexes, schema,
planner_context);
- }
- let (new_expr, changed) = simplify_array_index_expr(
- expr,
- self.sql_expr_to_logical_expr(
- indexes[0].clone(),
- schema,
- planner_context,
- )?,
- );
+ self.sql_expr_to_logical_expr(*expr, schema,
planner_context)?;
- if changed {
- Ok(new_expr)
- } else {
- self.plan_indexed(new_expr, indexes, schema,
planner_context)
- }
+ let get_field_access = match *subscript {
+ Subscript::Index { index } => {
+ // index can be a name, in which case it is a named
field access
+ match index {
+ SQLExpr::Value(
+ Value::SingleQuotedString(s)
+ | Value::DoubleQuotedString(s),
+ ) => GetFieldAccess::NamedStructField {
+ name: ScalarValue::from(s),
+ },
+ SQLExpr::JsonAccess { .. } => {
+ return not_impl_err!("JsonAccess");
+ }
+ // otherwise treat like a list index
+ _ => GetFieldAccess::ListIndex {
+ key: Box::new(self.sql_expr_to_logical_expr(
+ index,
+ schema,
+ planner_context,
+ )?),
+ },
+ }
+ }
+ Subscript::Slice {
+ lower_bound,
+ upper_bound,
+ stride,
+ } => {
+ // Means access like [:2]
+ let lower_bound = if let Some(lower_bound) =
lower_bound {
+ self.sql_expr_to_logical_expr(
+ lower_bound,
+ schema,
+ planner_context,
+ )
+ } else {
+ not_impl_err!("Slice subscript requires a lower
bound")
+ }?;
+
+ // means access like [2:]
+ let upper_bound = if let Some(upper_bound) =
upper_bound {
+ self.sql_expr_to_logical_expr(
+ upper_bound,
+ schema,
+ planner_context,
+ )
+ } else {
+ not_impl_err!("Slice subscript requires an upper
bound")
+ }?;
+
+ // stride, default to 1
+ let stride = if let Some(stride) = stride {
+ self.sql_expr_to_logical_expr(
+ stride,
+ schema,
+ planner_context,
+ )?
+ } else {
+ lit(1i64)
+ };
+
+ GetFieldAccess::ListRange {
+ start: Box::new(lower_bound),
+ stop: Box::new(upper_bound),
+ stride: Box::new(stride),
+ }
+ }
+ };
+
+ self.plan_field_access(expr, get_field_access)
}
SQLExpr::CompoundIdentifier(ids) => {
@@ -267,6 +277,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
),
SQLExpr::Cast {
+ kind: CastKind::Cast | CastKind::DoubleColon,
expr,
data_type,
format,
@@ -296,7 +307,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
Ok(Expr::Cast(Cast::new(Box::new(expr), dt)))
}
- SQLExpr::TryCast {
+ SQLExpr::Cast {
+ kind: CastKind::TryCast | CastKind::SafeCast,
expr,
data_type,
format,
@@ -497,10 +509,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
planner_context,
),
- SQLExpr::AggregateExpressionWithFilter { expr, filter } => {
- self.sql_agg_with_filter_to_expr(*expr, *filter, schema,
planner_context)
- }
-
SQLExpr::Function(function) => {
self.sql_function_to_expr(function, schema, planner_context)
}
@@ -552,10 +560,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
self.parse_scalar_subquery(*subquery, schema, planner_context)
}
- SQLExpr::ArrayAgg(array_agg) => {
- self.parse_array_agg(array_agg, schema, planner_context)
- }
-
SQLExpr::Struct { values, fields } => {
self.parse_struct(values, fields, schema, planner_context)
}
@@ -571,12 +575,51 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
schema,
planner_context,
)?),
- DataType::Timestamp(TimeUnit::Nanosecond,
Some(time_zone.into())),
+ match *time_zone {
+ SQLExpr::Value(Value::SingleQuotedString(s)) => {
+ DataType::Timestamp(TimeUnit::Nanosecond,
Some(s.into()))
+ }
+ _ => {
+ return not_impl_err!(
+ "Unsupported ast node in sqltorel: {time_zone:?}"
+ )
+ }
+ },
))),
_ => not_impl_err!("Unsupported ast node in sqltorel: {sql:?}"),
}
}
+ /// Simplifies an expression like `ARRAY_AGG(expr)[index]` to
`NTH_VALUE(expr, index)`
+ ///
+ /// returns Some(Expr) if the expression was simplified, otherwise None
+ /// TODO: this should likely be done in ArrayAgg::simplify when it is
moved to a UDAF
+ fn simplify_array_index_expr(expr: &Expr, index: &Expr) -> Option<Expr> {
+ fn is_array_agg(agg_func: &datafusion_expr::expr::AggregateFunction)
-> bool {
+ agg_func.func_def
+ == datafusion_expr::expr::AggregateFunctionDefinition::BuiltIn(
+ AggregateFunction::ArrayAgg,
+ )
+ }
+ match expr {
+ Expr::AggregateFunction(agg_func) if is_array_agg(agg_func) => {
+ let mut new_args = agg_func.args.clone();
+ new_args.push(index.clone());
+ Some(Expr::AggregateFunction(
+ datafusion_expr::expr::AggregateFunction::new(
+ AggregateFunction::NthValue,
+ new_args,
+ agg_func.distinct,
+ agg_func.filter.clone(),
+ agg_func.order_by.clone(),
+ agg_func.null_treatment,
+ ),
+ ))
+ }
+ _ => None,
+ }
+ }
+
/// Parses a struct(..) expression
fn parse_struct(
&self,
@@ -679,55 +722,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
)))
}
- fn parse_array_agg(
- &self,
- array_agg: ArrayAgg,
- input_schema: &DFSchema,
- planner_context: &mut PlannerContext,
- ) -> Result<Expr> {
- // Some dialects have special syntax for array_agg. DataFusion only
supports it like a function.
- let ArrayAgg {
- distinct,
- expr,
- order_by,
- limit,
- within_group,
- } = array_agg;
- let order_by = if let Some(order_by) = order_by {
- Some(self.order_by_to_sort_expr(
- &order_by,
- input_schema,
- planner_context,
- true,
- None,
- )?)
- } else {
- None
- };
-
- if let Some(limit) = limit {
- return not_impl_err!("LIMIT not supported in ARRAY_AGG: {limit}");
- }
-
- if within_group {
- return not_impl_err!("WITHIN GROUP not supported in ARRAY_AGG");
- }
-
- let args =
- vec![self.sql_expr_to_logical_expr(*expr, input_schema,
planner_context)?];
-
- // next, aggregate built-ins
- Ok(Expr::AggregateFunction(expr::AggregateFunction::new(
- AggregateFunction::ArrayAgg,
- args,
- distinct,
- None,
- order_by,
- None,
- )))
- // see if we can rewrite it into NTH-VALUE
- }
-
fn sql_in_list_to_expr(
&self,
expr: SQLExpr,
@@ -754,7 +748,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
negated: bool,
expr: SQLExpr,
pattern: SQLExpr,
- escape_char: Option<char>,
+ escape_char: Option<String>,
schema: &DFSchema,
planner_context: &mut PlannerContext,
case_insensitive: bool,
@@ -764,6 +758,14 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
if pattern_type != DataType::Utf8 && pattern_type != DataType::Null {
return plan_err!("Invalid pattern in LIKE expression");
}
+ let escape_char = if let Some(char) = escape_char {
+ if char.len() != 1 {
+ return plan_err!("Invalid escape character in LIKE
expression");
+ }
+ Some(char.chars().next().unwrap())
+ } else {
+ None
+ };
Ok(Expr::Like(Like::new(
negated,
Box::new(self.sql_expr_to_logical_expr(expr, schema,
planner_context)?),
@@ -778,7 +780,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
negated: bool,
expr: SQLExpr,
pattern: SQLExpr,
- escape_char: Option<char>,
+ escape_char: Option<String>,
schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
@@ -787,6 +789,14 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
if pattern_type != DataType::Utf8 && pattern_type != DataType::Null {
return plan_err!("Invalid pattern in SIMILAR TO expression");
}
+ let escape_char = if let Some(char) = escape_char {
+ if char.len() != 1 {
+ return plan_err!("Invalid escape character in SIMILAR TO
expression");
+ }
+ Some(char.chars().next().unwrap())
+ } else {
+ None
+ };
Ok(Expr::SimilarTo(Like::new(
negated,
Box::new(self.sql_expr_to_logical_expr(expr, schema,
planner_context)?),
@@ -895,132 +905,14 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
let args = vec![fullstr, substr];
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(fun, args)))
}
- fn sql_agg_with_filter_to_expr(
- &self,
- expr: SQLExpr,
- filter: SQLExpr,
- schema: &DFSchema,
- planner_context: &mut PlannerContext,
- ) -> Result<Expr> {
- match self.sql_expr_to_logical_expr(expr, schema, planner_context)? {
- Expr::AggregateFunction(expr::AggregateFunction {
- func_def: AggregateFunctionDefinition::BuiltIn(fun),
- args,
- distinct,
- order_by,
- null_treatment,
- filter: None, // filter is passed in
- }) => Ok(Expr::AggregateFunction(expr::AggregateFunction::new(
- fun,
- args,
- distinct,
- Some(Box::new(self.sql_expr_to_logical_expr(
- filter,
- schema,
- planner_context,
- )?)),
- order_by,
- null_treatment,
- ))),
- Expr::AggregateFunction(..) => {
- internal_err!("Expected null filter clause in aggregate
function")
- }
- _ => internal_err!(
- "AggregateExpressionWithFilter expression was not an
AggregateFunction"
- ),
- }
- }
- fn plan_indices(
- &self,
- expr: SQLExpr,
- schema: &DFSchema,
- planner_context: &mut PlannerContext,
- ) -> Result<GetFieldAccess> {
- let field = match expr.clone() {
- SQLExpr::Value(
- Value::SingleQuotedString(s) | Value::DoubleQuotedString(s),
- ) => GetFieldAccess::NamedStructField {
- name: ScalarValue::from(s),
- },
- SQLExpr::JsonAccess {
- left,
- operator: JsonOperator::Colon,
- right,
- } => {
- let (start, stop, stride) = if let SQLExpr::JsonAccess {
- left: l,
- operator: JsonOperator::Colon,
- right: r,
- } = *left
- {
- let start = Box::new(self.sql_expr_to_logical_expr(
- *l,
- schema,
- planner_context,
- )?);
- let stop = Box::new(self.sql_expr_to_logical_expr(
- *r,
- schema,
- planner_context,
- )?);
- let stride = Box::new(self.sql_expr_to_logical_expr(
- *right,
- schema,
- planner_context,
- )?);
- (start, stop, stride)
- } else {
- let start = Box::new(self.sql_expr_to_logical_expr(
- *left,
- schema,
- planner_context,
- )?);
- let stop = Box::new(self.sql_expr_to_logical_expr(
- *right,
- schema,
- planner_context,
- )?);
- let stride =
Box::new(Expr::Literal(ScalarValue::Int64(Some(1))));
- (start, stop, stride)
- };
- GetFieldAccess::ListRange {
- start,
- stop,
- stride,
- }
- }
- _ => GetFieldAccess::ListIndex {
- key: Box::new(self.sql_expr_to_logical_expr(
- expr,
- schema,
- planner_context,
- )?),
- },
- };
-
- Ok(field)
- }
-
- fn plan_indexed(
+ /// Given an expression and the field to access, creates a new expression
for accessing that field
+ fn plan_field_access(
&self,
expr: Expr,
- mut keys: Vec<SQLExpr>,
- schema: &DFSchema,
- planner_context: &mut PlannerContext,
+ get_field_access: GetFieldAccess,
) -> Result<Expr> {
- let indices = keys.pop().ok_or_else(|| {
- ParserError("Internal error: Missing index key
expression".to_string())
- })?;
-
- let expr = if !keys.is_empty() {
- self.plan_indexed(expr, keys, schema, planner_context)?
- } else {
- expr
- };
-
- let field = self.plan_indices(indices, schema, planner_context)?;
- match field {
+ match get_field_access {
GetFieldAccess::NamedStructField { name } => {
if let Some(udf) =
self.context_provider.get_function_meta("get_field") {
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
@@ -1033,7 +925,10 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
// expr[idx] ==> array_element(expr, idx)
GetFieldAccess::ListIndex { key } => {
- if let Some(udf) =
+ // Special case for array_agg(expr)[index] to NTH_VALUE(expr,
index)
+ if let Some(simplified) =
Self::simplify_array_index_expr(&expr, &key) {
+ Ok(simplified)
+ } else if let Some(udf) =
self.context_provider.get_function_meta("array_element")
{
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs
index 25857db839..fa95fc2e05 100644
--- a/datafusion/sql/src/expr/value.rs
+++ b/datafusion/sql/src/expr/value.rs
@@ -50,6 +50,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
plan_err!("Invalid HexStringLiteral '{s}'")
}
}
+ Value::DollarQuotedString(s) => Ok(lit(s.value)),
Value::EscapedStringLiteral(s) => Ok(lit(s)),
_ => plan_err!("Unsupported Value '{value:?}'"),
}
diff --git a/datafusion/sql/src/parser.rs b/datafusion/sql/src/parser.rs
index d09317271d..bbc3a52f07 100644
--- a/datafusion/sql/src/parser.rs
+++ b/datafusion/sql/src/parser.rs
@@ -493,7 +493,8 @@ impl<'a> DFParser<'a> {
pub fn parse_option_value(&mut self) -> Result<Value, ParserError> {
let next_token = self.parser.next_token();
match next_token.token {
- Token::Word(Word { value, .. }) =>
Ok(Value::UnQuotedString(value)),
+ // e.g. things like "snappy" or "gzip" that may be keywords
+ Token::Word(word) => Ok(Value::SingleQuotedString(word.value)),
Token::SingleQuotedString(s) => Ok(Value::SingleQuotedString(s)),
Token::DoubleQuotedString(s) => Ok(Value::DoubleQuotedString(s)),
Token::EscapedStringLiteral(s) =>
Ok(Value::EscapedStringLiteral(s)),
@@ -1139,7 +1140,7 @@ mod tests {
unbounded: false,
options: vec![
("k1".into(), Value::SingleQuotedString("v1".into())),
- ("k2".into(), Value::UnQuotedString("v2".into())),
+ ("k2".into(), Value::SingleQuotedString("v2".into())),
],
constraints: vec![],
});
@@ -1462,7 +1463,7 @@ mod tests {
),
(
"format.compression".to_string(),
- Value::UnQuotedString("snappy".to_string()),
+ Value::SingleQuotedString("snappy".to_string()),
),
];
diff --git a/datafusion/sql/src/planner.rs b/datafusion/sql/src/planner.rs
index ed9d347225..0f04281aa2 100644
--- a/datafusion/sql/src/planner.rs
+++ b/datafusion/sql/src/planner.rs
@@ -367,7 +367,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
pub(crate) fn convert_data_type(&self, sql_type: &SQLDataType) ->
Result<DataType> {
match sql_type {
SQLDataType::Array(ArrayElemTypeDef::AngleBracket(inner_sql_type))
- |
SQLDataType::Array(ArrayElemTypeDef::SquareBracket(inner_sql_type)) => {
+ |
SQLDataType::Array(ArrayElemTypeDef::SquareBracket(inner_sql_type, _)) => {
// Arrays may be multi-dimensional.
let inner_data_type = self.convert_data_type(inner_sql_type)?;
Ok(DataType::new_list(inner_data_type, true))
diff --git a/datafusion/sql/src/select.rs b/datafusion/sql/src/select.rs
index e8e016bf09..0fa266e4e0 100644
--- a/datafusion/sql/src/select.rs
+++ b/datafusion/sql/src/select.rs
@@ -39,8 +39,8 @@ use datafusion_expr::{
Expr, Filter, GroupingSet, LogicalPlan, LogicalPlanBuilder, Partitioning,
};
use sqlparser::ast::{
- Distinct, Expr as SQLExpr, GroupByExpr, OrderByExpr, ReplaceSelectItem,
- WildcardAdditionalOptions, WindowType,
+ Distinct, Expr as SQLExpr, GroupByExpr, NamedWindowExpr, OrderByExpr,
+ ReplaceSelectItem, WildcardAdditionalOptions, WindowType,
};
use sqlparser::ast::{NamedWindowDefinition, Select, SelectItem,
TableWithJoins};
@@ -508,6 +508,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
opt_except: _opt_except,
opt_rename,
opt_replace: _opt_replace,
+ opt_ilike: _opt_ilike,
} = options;
if opt_rename.is_some() {
@@ -707,10 +708,17 @@ fn match_window_definitions(
}
| SelectItem::UnnamedExpr(SQLExpr::Function(f)) = proj
{
- for NamedWindowDefinition(window_ident, window_spec) in
named_windows.iter() {
+ for NamedWindowDefinition(window_ident, window_expr) in
named_windows.iter() {
if let Some(WindowType::NamedWindow(ident)) = &f.over {
if ident.eq(window_ident) {
- f.over =
Some(WindowType::WindowSpec(window_spec.clone()))
+ f.over = Some(match window_expr {
+ NamedWindowExpr::NamedWindow(ident) => {
+ WindowType::NamedWindow(ident.clone())
+ }
+ NamedWindowExpr::WindowSpec(spec) => {
+ WindowType::WindowSpec(spec.clone())
+ }
+ })
}
}
}
diff --git a/datafusion/sql/src/statement.rs b/datafusion/sql/src/statement.rs
index 13d2e05661..d10956efb6 100644
--- a/datafusion/sql/src/statement.rs
+++ b/datafusion/sql/src/statement.rs
@@ -54,10 +54,10 @@ use datafusion_expr::{
};
use sqlparser::ast;
use sqlparser::ast::{
- Assignment, ColumnDef, CreateTableOptions, DescribeAlias, Expr as SQLExpr,
Expr,
- FromTable, Ident, ObjectName, ObjectType, Query, SchemaName, SetExpr,
- ShowCreateObject, ShowStatementFilter, Statement, TableConstraint,
TableFactor,
- TableWithJoins, TransactionMode, UnaryOperator, Value,
+ Assignment, ColumnDef, CreateTableOptions, Delete, DescribeAlias, Expr as
SQLExpr,
+ Expr, FromTable, Ident, Insert, ObjectName, ObjectType,
OneOrManyWithParens, Query,
+ SchemaName, SetExpr, ShowCreateObject, ShowStatementFilter, Statement,
+ TableConstraint, TableFactor, TableWithJoins, TransactionMode,
UnaryOperator, Value,
};
use sqlparser::parser::ParserError::ParserError;
@@ -65,6 +65,30 @@ fn ident_to_string(ident: &Ident) -> String {
normalize_ident(ident.to_owned())
}
+fn value_to_string(value: &Value) -> Option<String> {
+ match value {
+ Value::SingleQuotedString(s) => Some(s.to_string()),
+ Value::DollarQuotedString(s) => Some(s.to_string()),
+ Value::Number(_, _) | Value::Boolean(_) => Some(value.to_string()),
+ Value::DoubleQuotedString(_)
+ | Value::EscapedStringLiteral(_)
+ | Value::NationalStringLiteral(_)
+ | Value::SingleQuotedByteStringLiteral(_)
+ | Value::DoubleQuotedByteStringLiteral(_)
+ | Value::TripleSingleQuotedString(_)
+ | Value::TripleDoubleQuotedString(_)
+ | Value::TripleSingleQuotedByteStringLiteral(_)
+ | Value::TripleDoubleQuotedByteStringLiteral(_)
+ | Value::SingleQuotedRawStringLiteral(_)
+ | Value::DoubleQuotedRawStringLiteral(_)
+ | Value::TripleSingleQuotedRawStringLiteral(_)
+ | Value::TripleDoubleQuotedRawStringLiteral(_)
+ | Value::HexStringLiteral(_)
+ | Value::Null
+ | Value::Placeholder(_) => None,
+ }
+}
+
fn object_name_to_string(object_name: &ObjectName) -> String {
object_name
.0
@@ -212,9 +236,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
Statement::SetVariable {
local,
hivevar,
- variable,
+ variables,
value,
- } => self.set_variable_to_plan(local, hivevar, &variable, value),
+ } => self.set_variable_to_plan(local, hivevar, &variables, value),
Statement::CreateTable {
query,
@@ -405,18 +429,19 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
ObjectType::Schema => {
let name = match name {
- TableReference::Bare { table } =>
Ok(SchemaReference::Bare { schema: table } ) ,
- TableReference::Partial { schema, table } =>
Ok(SchemaReference::Full { schema: table,catalog: schema }),
+ TableReference::Bare { table } =>
Ok(SchemaReference::Bare { schema: table }),
+ TableReference::Partial { schema, table } =>
Ok(SchemaReference::Full { schema: table, catalog: schema }),
TableReference::Full { catalog: _, schema: _,
table: _ } => {
Err(ParserError("Invalid schema specifier (has
3 parts)".to_string()))
- },
+ }
}?;
Ok(LogicalPlan::Ddl(DdlStatement::DropCatalogSchema(DropCatalogSchema {
name,
if_exists,
cascade,
schema: DFSchemaRef::new(DFSchema::empty()),
- })))},
+ })))
+ }
_ => not_impl_err!(
"Only `DROP TABLE/VIEW/SCHEMA ...` statement is
supported currently"
),
@@ -463,7 +488,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
filter,
} => self.show_columns_to_plan(extended, full, table_name, filter),
- Statement::Insert {
+ Statement::Insert(Insert {
or,
into,
table_name,
@@ -480,7 +505,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
replace_into,
priority,
insert_alias,
- } => {
+ }) => {
if or.is_some() {
plan_err!("Inserts with or clauses not supported")?;
}
@@ -537,7 +562,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
self.update_to_plan(table, assignments, from, selection)
}
- Statement::Delete {
+ Statement::Delete(Delete {
tables,
using,
selection,
@@ -545,7 +570,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
from,
order_by,
limit,
- } => {
+ }) => {
if !tables.is_empty() {
plan_err!("DELETE <TABLE> not supported")?;
}
@@ -652,7 +677,10 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
name,
args,
return_type,
- params,
+ function_body,
+ behavior,
+ language,
+ ..
} => {
let return_type = match return_type {
Some(t) => Some(self.convert_data_type(&t)?),
@@ -702,9 +730,13 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
let mut planner_context = PlannerContext::new()
.with_prepare_param_data_types(arg_types.unwrap_or_default());
- let result_expression = match params.return_ {
+ let function_body = match function_body {
Some(r) => Some(self.sql_to_expr(
- r,
+ match r {
+ ast::CreateFunctionBody::AsBeforeOptions(expr) =>
expr,
+ ast::CreateFunctionBody::AsAfterOptions(expr) =>
expr,
+ ast::CreateFunctionBody::Return(expr) => expr,
+ },
&DFSchema::empty(),
&mut planner_context,
)?),
@@ -712,14 +744,13 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
};
let params = CreateFunctionBody {
- language: params.language,
- behavior: params.behavior.map(|b| match b {
+ language,
+ behavior: behavior.map(|b| match b {
ast::FunctionBehavior::Immutable =>
Volatility::Immutable,
ast::FunctionBehavior::Stable => Volatility::Stable,
ast::FunctionBehavior::Volatile =>
Volatility::Volatile,
}),
- as_: params.as_.map(|m| m.into()),
- return_: result_expression,
+ function_body,
};
let statement = DdlStatement::CreateFunction(CreateFunction {
@@ -851,22 +882,11 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
let mut options = HashMap::new();
for (key, value) in statement.options {
- let value_string = match value {
- Value::SingleQuotedString(s) => s.to_string(),
- Value::DollarQuotedString(s) => s.to_string(),
- Value::UnQuotedString(s) => s.to_string(),
- Value::Number(_, _) | Value::Boolean(_) => value.to_string(),
- Value::DoubleQuotedString(_)
- | Value::EscapedStringLiteral(_)
- | Value::NationalStringLiteral(_)
- | Value::SingleQuotedByteStringLiteral(_)
- | Value::DoubleQuotedByteStringLiteral(_)
- | Value::RawStringLiteral(_)
- | Value::HexStringLiteral(_)
- | Value::Null
- | Value::Placeholder(_) => {
+ let value_string = match value_to_string(&value) {
+ None => {
return plan_err!("Unsupported Value in COPY statement {}",
value);
}
+ Some(v) => v,
};
if !(&key.contains('.')) {
// If config does not belong to any namespace, assume it is
@@ -886,9 +906,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
} else {
let e = || {
DataFusionError::Configuration(
- "Format not explicitly set and unable to get file extension!
Use STORED AS to define file format."
- .to_string(),
- )
+ "Format not explicitly set and unable to get file
extension! Use STORED AS to define file format."
+ .to_string(),
+ )
};
// try to infer file format from file extension
let extension: &str = &Path::new(&statement.target)
@@ -987,25 +1007,11 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
return plan_err!("Option {key} is specified multiple times");
}
- let value_string = match value {
- Value::SingleQuotedString(s) => s.to_string(),
- Value::DollarQuotedString(s) => s.to_string(),
- Value::UnQuotedString(s) => s.to_string(),
- Value::Number(_, _) | Value::Boolean(_) => value.to_string(),
- Value::DoubleQuotedString(_)
- | Value::EscapedStringLiteral(_)
- | Value::NationalStringLiteral(_)
- | Value::SingleQuotedByteStringLiteral(_)
- | Value::DoubleQuotedByteStringLiteral(_)
- | Value::RawStringLiteral(_)
- | Value::HexStringLiteral(_)
- | Value::Null
- | Value::Placeholder(_) => {
- return plan_err!(
- "Unsupported Value in CREATE EXTERNAL TABLE statement
{}",
- value
- );
- }
+ let Some(value_string) = value_to_string(&value) else {
+ return plan_err!(
+ "Unsupported Value in CREATE EXTERNAL TABLE statement {}",
+ value
+ );
};
if !(&key.contains('.')) {
@@ -1147,8 +1153,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
&self,
local: bool,
hivevar: bool,
- variable: &ObjectName,
- value: Vec<sqlparser::ast::Expr>,
+ variables: &OneOrManyWithParens<ObjectName>,
+ value: Vec<Expr>,
) -> Result<LogicalPlan> {
if local {
return not_impl_err!("LOCAL is not supported");
@@ -1158,7 +1164,14 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
return not_impl_err!("HIVEVAR is not supported");
}
- let variable = object_name_to_string(variable);
+ let variable = match variables {
+ OneOrManyWithParens::One(v) => object_name_to_string(v),
+ OneOrManyWithParens::Many(vs) => {
+ return not_impl_err!(
+ "SET only supports single variable assignment: {vs:?}"
+ );
+ }
+ };
let mut variable_lower = variable.to_lowercase();
if variable_lower == "timezone" || variable_lower == "time.zone" {
@@ -1169,22 +1182,11 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
// parse value string from Expr
let value_string = match &value[0] {
SQLExpr::Identifier(i) => ident_to_string(i),
- SQLExpr::Value(v) => match v {
- Value::SingleQuotedString(s) => s.to_string(),
- Value::DollarQuotedString(s) => s.to_string(),
- Value::Number(_, _) | Value::Boolean(_) => v.to_string(),
- Value::DoubleQuotedString(_)
- | Value::UnQuotedString(_)
- | Value::EscapedStringLiteral(_)
- | Value::NationalStringLiteral(_)
- | Value::SingleQuotedByteStringLiteral(_)
- | Value::DoubleQuotedByteStringLiteral(_)
- | Value::RawStringLiteral(_)
- | Value::HexStringLiteral(_)
- | Value::Null
- | Value::Placeholder(_) => {
+ SQLExpr::Value(v) => match value_to_string(v) {
+ None => {
return plan_err!("Unsupported Value {}", value[0]);
}
+ Some(v) => v,
},
// for capture signed number e.g. +8, -8
SQLExpr::UnaryOp { op, expr } => match op {
diff --git a/datafusion/sql/src/unparser/ast.rs
b/datafusion/sql/src/unparser/ast.rs
index d39d583d89..908e54e5fa 100644
--- a/datafusion/sql/src/unparser/ast.rs
+++ b/datafusion/sql/src/unparser/ast.rs
@@ -261,6 +261,8 @@ impl SelectBuilder {
named_window: self.named_window.clone(),
qualify: self.qualify.clone(),
value_table_mode: self.value_table_mode,
+ connect_by: None,
+ window_before_qualify: false,
})
}
fn create_empty() -> Self {
diff --git a/datafusion/sql/src/unparser/expr.rs
b/datafusion/sql/src/unparser/expr.rs
index 3efbe2ace6..024fd99b51 100644
--- a/datafusion/sql/src/unparser/expr.rs
+++ b/datafusion/sql/src/unparser/expr.rs
@@ -130,13 +130,15 @@ impl Unparser<'_> {
value: func_name.to_string(),
quote_style: None,
}]),
- args,
+ args:
ast::FunctionArguments::List(ast::FunctionArgumentList {
+ duplicate_treatment: None,
+ args,
+ clauses: vec![],
+ }),
filter: None,
null_treatment: None,
over: None,
- distinct: false,
- special: false,
- order_by: vec![],
+ within_group: vec![],
}))
}
Expr::Between(Between {
@@ -201,6 +203,7 @@ impl Unparser<'_> {
Expr::Cast(Cast { expr, data_type }) => {
let inner_expr = self.expr_to_sql(expr)?;
Ok(ast::Expr::Cast {
+ kind: ast::CastKind::Cast,
expr: Box::new(inner_expr),
data_type: self.arrow_dtype_to_ast_dtype(data_type)?,
format: None,
@@ -257,13 +260,15 @@ impl Unparser<'_> {
value: func_name.to_string(),
quote_style: None,
}]),
- args,
+ args:
ast::FunctionArguments::List(ast::FunctionArgumentList {
+ duplicate_treatment: None,
+ args,
+ clauses: vec![],
+ }),
filter: None,
null_treatment: None,
over,
- distinct: false,
- special: false,
- order_by: vec![],
+ within_group: vec![],
}))
}
Expr::SimilarTo(Like {
@@ -283,7 +288,7 @@ impl Unparser<'_> {
negated: *negated,
expr: Box::new(self.expr_to_sql(expr)?),
pattern: Box::new(self.expr_to_sql(pattern)?),
- escape_char: *escape_char,
+ escape_char: escape_char.map(|c| c.to_string()),
}),
Expr::AggregateFunction(agg) => {
let func_name = agg.func_def.name();
@@ -298,13 +303,17 @@ impl Unparser<'_> {
value: func_name.to_string(),
quote_style: None,
}]),
- args,
+ args:
ast::FunctionArguments::List(ast::FunctionArgumentList {
+ duplicate_treatment: agg
+ .distinct
+ .then_some(ast::DuplicateTreatment::Distinct),
+ args,
+ clauses: vec![],
+ }),
filter,
null_treatment: None,
over: None,
- distinct: agg.distinct,
- special: false,
- order_by: vec![],
+ within_group: vec![],
}))
}
Expr::ScalarSubquery(subq) => {
@@ -414,7 +423,8 @@ impl Unparser<'_> {
}
Expr::TryCast(TryCast { expr, data_type }) => {
let inner_expr = self.expr_to_sql(expr)?;
- Ok(ast::Expr::TryCast {
+ Ok(ast::Expr::Cast {
+ kind: ast::CastKind::TryCast,
expr: Box::new(inner_expr),
data_type: self.arrow_dtype_to_ast_dtype(data_type)?,
format: None,
@@ -729,6 +739,7 @@ impl Unparser<'_> {
))?;
Ok(ast::Expr::Cast {
+ kind: ast::CastKind::Cast,
expr:
Box::new(ast::Expr::Value(ast::Value::SingleQuotedString(
date.to_string(),
))),
@@ -751,6 +762,7 @@ impl Unparser<'_> {
))?;
Ok(ast::Expr::Cast {
+ kind: ast::CastKind::Cast,
expr:
Box::new(ast::Expr::Value(ast::Value::SingleQuotedString(
datetime.to_string(),
))),
diff --git a/datafusion/sqllogictest/test_files/aggregate.slt
b/datafusion/sqllogictest/test_files/aggregate.slt
index 56ec034257..b08d584673 100644
--- a/datafusion/sqllogictest/test_files/aggregate.slt
+++ b/datafusion/sqllogictest/test_files/aggregate.slt
@@ -194,7 +194,7 @@ select array_sort(c1), array_sort(c2) from (
statement ok
drop table array_agg_distinct_list_table;
-statement error This feature is not implemented: LIMIT not supported in
ARRAY_AGG: 1
+statement error This feature is not implemented: Calling array_agg: LIMIT not
supported in function arguments: 1
SELECT array_agg(c13 LIMIT 1) FROM aggregate_test_100
diff --git a/datafusion/sqllogictest/test_files/errors.slt
b/datafusion/sqllogictest/test_files/errors.slt
index b74b2fe60f..e930af107f 100644
--- a/datafusion/sqllogictest/test_files/errors.slt
+++ b/datafusion/sqllogictest/test_files/errors.slt
@@ -46,7 +46,7 @@ statement error DataFusion error: Arrow error: Cast error:
Cannot cast string 'c
SELECT CAST(c1 AS INT) FROM aggregate_test_100
# aggregation_with_bad_arguments
-statement error DataFusion error: Error during planning: No function matches
the given name and argument types 'COUNT\(\)'. You might need to add explicit
type casts.\n\tCandidate functions:\n\tCOUNT\(Any, .., Any\)
+statement error DataFusion error: SQL error: ParserError\("Expected an
expression:, found: \)"\)
SELECT COUNT(DISTINCT) FROM aggregate_test_100
# query_cte_incorrect
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]