This is an automated email from the ASF dual-hosted git repository.
jayzhan 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 48a1754b33 Introduce user defined SQL planner API (#11180)
48a1754b33 is described below
commit 48a1754b33e983a8201ca3fefa36136fa44f0c55
Author: Jay Zhan <[email protected]>
AuthorDate: Tue Jul 2 08:00:21 2024 +0800
Introduce user defined SQL planner API (#11180)
* Prototype user defined sql planner might look like
* at arrow
Signed-off-by: jayzhan211 <[email protected]>
* get field
Signed-off-by: jayzhan211 <[email protected]>
* cleanup
Signed-off-by: jayzhan211 <[email protected]>
* plan array literal
Signed-off-by: jayzhan211 <[email protected]>
* move to functions-array
Signed-off-by: jayzhan211 <[email protected]>
* license
Signed-off-by: jayzhan211 <[email protected]>
* cleanup
Signed-off-by: jayzhan211 <[email protected]>
* fix
Signed-off-by: jayzhan211 <[email protected]>
* change nested array test
Signed-off-by: jayzhan211 <[email protected]>
* address comment
Signed-off-by: jayzhan211 <[email protected]>
* fix stack overflow issue
Signed-off-by: jayzhan211 <[email protected]>
* upd cli
Signed-off-by: jayzhan211 <[email protected]>
* fix doc
Signed-off-by: jayzhan211 <[email protected]>
* fix doc
Signed-off-by: jayzhan211 <[email protected]>
* Rename PlannerResult::Simplified to PlannerResult::Planned
* Update comments and add Debug/Clone impls
---------
Signed-off-by: jayzhan211 <[email protected]>
Co-authored-by: Andrew Lamb <[email protected]>
---
datafusion-cli/Cargo.lock | 100 ++++++-------
datafusion/core/src/execution/session_state.rs | 30 +++-
datafusion/expr/src/lib.rs | 2 +
datafusion/expr/src/planner.rs | 146 +++++++++++++++++++
datafusion/functions-array/src/lib.rs | 2 +-
datafusion/functions-array/src/planner.rs | 161 +++++++++++++++++++++
datafusion/sql/src/expr/mod.rs | 191 ++++---------------------
datafusion/sql/src/expr/value.rs | 31 ++--
datafusion/sql/src/planner.rs | 74 +++-------
9 files changed, 454 insertions(+), 283 deletions(-)
diff --git a/datafusion-cli/Cargo.lock b/datafusion-cli/Cargo.lock
index 28312fee79..5fc8dbcfdf 100644
--- a/datafusion-cli/Cargo.lock
+++ b/datafusion-cli/Cargo.lock
@@ -387,7 +387,7 @@ checksum =
"c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -757,9 +757,9 @@ checksum =
"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake2"
@@ -875,9 +875,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.99"
+version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
+checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
dependencies = [
"jobserver",
"libc",
@@ -981,7 +981,7 @@ version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
dependencies = [
- "strum 0.26.2",
+ "strum 0.26.3",
"strum_macros 0.26.4",
"unicode-width",
]
@@ -1099,7 +1099,7 @@ source =
"registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
dependencies = [
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -1262,7 +1262,7 @@ dependencies = [
"paste",
"serde_json",
"sqlparser",
- "strum 0.26.2",
+ "strum 0.26.3",
"strum_macros 0.26.4",
]
@@ -1426,7 +1426,7 @@ dependencies = [
"log",
"regex",
"sqlparser",
- "strum 0.26.2",
+ "strum 0.26.3",
]
[[package]]
@@ -1504,9 +1504,9 @@ checksum =
"fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "either"
-version = "1.12.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "endian-type"
@@ -1685,7 +1685,7 @@ checksum =
"87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -2253,9 +2253,9 @@ checksum =
"4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libmimalloc-sys"
-version = "0.1.38"
+version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
dependencies = [
"cc",
"libc",
@@ -2267,7 +2267,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"libc",
]
@@ -2289,9 +2289,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.21"
+version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lz4_flex"
@@ -2331,9 +2331,9 @@ checksum =
"78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mimalloc"
-version = "0.1.42"
+version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
dependencies = [
"libmimalloc-sys",
]
@@ -2406,9 +2406,9 @@ dependencies = [
[[package]]
name = "num-bigint"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -2482,9 +2482,9 @@ dependencies = [
[[package]]
name = "object"
-version = "0.36.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
+checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
dependencies = [
"memchr",
]
@@ -2698,7 +2698,7 @@ checksum =
"2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -2912,7 +2912,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
]
[[package]]
@@ -3095,7 +3095,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -3264,7 +3264,7 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -3310,14 +3310,14 @@ checksum =
"500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
name = "serde_json"
-version = "1.0.117"
+version = "1.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
dependencies = [
"itoa",
"ryu",
@@ -3445,7 +3445,7 @@ checksum =
"01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3474,9 +3474,9 @@ checksum =
"290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum"
-version = "0.26.2"
+version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
+checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros 0.26.4",
]
@@ -3491,7 +3491,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3504,14 +3504,14 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
name = "subtle"
-version = "2.6.0"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
@@ -3526,9 +3526,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.67"
+version = "2.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
+checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
dependencies = [
"proc-macro2",
"quote",
@@ -3591,7 +3591,7 @@ checksum =
"46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3646,9 +3646,9 @@ dependencies = [
[[package]]
name = "tinyvec"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
dependencies = [
"tinyvec_macros",
]
@@ -3686,7 +3686,7 @@ checksum =
"5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3783,7 +3783,7 @@ checksum =
"34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3828,7 +3828,7 @@ checksum =
"f03ca4cb38206e2bef0700092660bb74d696f808514dae47fa1467cbfe26e96e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
@@ -3907,9 +3907,9 @@ checksum =
"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
-version = "1.8.0"
+version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
dependencies = [
"getrandom",
"serde",
@@ -3982,7 +3982,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
"wasm-bindgen-shared",
]
@@ -4016,7 +4016,7 @@ checksum =
"e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4281,7 +4281,7 @@ checksum =
"15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.67",
+ "syn 2.0.68",
]
[[package]]
diff --git a/datafusion/core/src/execution/session_state.rs
b/datafusion/core/src/execution/session_state.rs
index 0b880ddbf8..ac94ee61fc 100644
--- a/datafusion/core/src/execution/session_state.rs
+++ b/datafusion/core/src/execution/session_state.rs
@@ -605,7 +605,7 @@ impl SessionState {
}
}
- let query = SqlToRel::new_with_options(&provider,
self.get_parser_options());
+ let query = self.build_sql_query_planner(&provider);
query.statement_to_plan(statement)
}
@@ -658,8 +658,7 @@ impl SessionState {
tables: HashMap::new(),
};
- let query = SqlToRel::new_with_options(&provider,
self.get_parser_options());
-
+ let query = self.build_sql_query_planner(&provider);
query.sql_to_expr(sql_expr, df_schema, &mut PlannerContext::new())
}
@@ -943,6 +942,31 @@ impl SessionState {
let udtf = self.table_functions.remove(name);
Ok(udtf.map(|x| x.function().clone()))
}
+
+ fn build_sql_query_planner<'a, S>(&self, provider: &'a S) -> SqlToRel<'a,
S>
+ where
+ S: ContextProvider,
+ {
+ let query = SqlToRel::new_with_options(provider,
self.get_parser_options());
+
+ // register crate of array expressions (if enabled)
+ #[cfg(feature = "array_expressions")]
+ {
+ let array_planner =
+
Arc::new(functions_array::planner::ArrayFunctionPlanner::default()) as _;
+
+ let field_access_planner =
+
Arc::new(functions_array::planner::FieldAccessPlanner::default()) as _;
+
+ query
+ .with_user_defined_planner(array_planner)
+ .with_user_defined_planner(field_access_planner)
+ }
+ #[cfg(not(feature = "array_expressions"))]
+ {
+ query
+ }
+ }
}
struct SessionContextProvider<'a> {
diff --git a/datafusion/expr/src/lib.rs b/datafusion/expr/src/lib.rs
index 89ee94f9f8..5f1d3c9d5c 100644
--- a/datafusion/expr/src/lib.rs
+++ b/datafusion/expr/src/lib.rs
@@ -48,6 +48,7 @@ pub mod function;
pub mod groups_accumulator;
pub mod interval_arithmetic;
pub mod logical_plan;
+pub mod planner;
pub mod registry;
pub mod simplify;
pub mod sort_properties;
@@ -81,6 +82,7 @@ pub use partition_evaluator::PartitionEvaluator;
pub use signature::{
ArrayFunctionSignature, Signature, TypeSignature, Volatility,
TIMEZONE_WILDCARD,
};
+pub use sqlparser;
pub use table_source::{TableProviderFilterPushDown, TableSource, TableType};
pub use udaf::{AggregateExt, AggregateUDF, AggregateUDFImpl, ReversedUDAF};
pub use udf::{ScalarUDF, ScalarUDFImpl};
diff --git a/datafusion/expr/src/planner.rs b/datafusion/expr/src/planner.rs
new file mode 100644
index 0000000000..1febfbec7e
--- /dev/null
+++ b/datafusion/expr/src/planner.rs
@@ -0,0 +1,146 @@
+// 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.
+
+//! [`ContextProvider`] and [`UserDefinedSQLPlanner`] APIs to customize SQL
query planning
+
+use std::sync::Arc;
+
+use arrow::datatypes::{DataType, SchemaRef};
+use datafusion_common::{
+ config::ConfigOptions, file_options::file_type::FileType, not_impl_err,
DFSchema,
+ Result, TableReference,
+};
+
+use crate::{AggregateUDF, Expr, GetFieldAccess, ScalarUDF, TableSource,
WindowUDF};
+
+/// Provides the `SQL` query planner meta-data about tables and
+/// functions referenced in SQL statements, without a direct dependency on
other
+/// DataFusion structures
+pub trait ContextProvider {
+ /// Getter for a datasource
+ fn get_table_source(&self, name: TableReference) -> Result<Arc<dyn
TableSource>>;
+
+ fn get_file_type(&self, _ext: &str) -> Result<Arc<dyn FileType>> {
+ not_impl_err!("Registered file types are not supported")
+ }
+
+ /// Getter for a table function
+ fn get_table_function_source(
+ &self,
+ _name: &str,
+ _args: Vec<Expr>,
+ ) -> Result<Arc<dyn TableSource>> {
+ not_impl_err!("Table Functions are not supported")
+ }
+
+ /// This provides a worktable (an intermediate table that is used to store
the results of a CTE during execution)
+ /// We don't directly implement this in the logical plan's ['SqlToRel`]
+ /// because the sql code needs access to a table that contains
execution-related types that can't be a direct dependency
+ /// of the sql crate (namely, the `CteWorktable`).
+ /// The [`ContextProvider`] provides a way to "hide" this dependency.
+ fn create_cte_work_table(
+ &self,
+ _name: &str,
+ _schema: SchemaRef,
+ ) -> Result<Arc<dyn TableSource>> {
+ not_impl_err!("Recursive CTE is not implemented")
+ }
+
+ /// Getter for a UDF description
+ fn get_function_meta(&self, name: &str) -> Option<Arc<ScalarUDF>>;
+ /// Getter for a UDAF description
+ fn get_aggregate_meta(&self, name: &str) -> Option<Arc<AggregateUDF>>;
+ /// Getter for a UDWF
+ fn get_window_meta(&self, name: &str) -> Option<Arc<WindowUDF>>;
+ /// Getter for system/user-defined variable type
+ fn get_variable_type(&self, variable_names: &[String]) -> Option<DataType>;
+
+ /// Get configuration options
+ fn options(&self) -> &ConfigOptions;
+
+ /// Get all user defined scalar function names
+ fn udf_names(&self) -> Vec<String>;
+
+ /// Get all user defined aggregate function names
+ fn udaf_names(&self) -> Vec<String>;
+
+ /// Get all user defined window function names
+ fn udwf_names(&self) -> Vec<String>;
+}
+
+/// This trait allows users to customize the behavior of the SQL planner
+pub trait UserDefinedSQLPlanner {
+ /// Plan the binary operation between two expressions, returns
OriginalBinaryExpr if not possible
+ fn plan_binary_op(
+ &self,
+ expr: RawBinaryExpr,
+ _schema: &DFSchema,
+ ) -> Result<PlannerResult<RawBinaryExpr>> {
+ Ok(PlannerResult::Original(expr))
+ }
+
+ /// Plan the field access expression, returns OriginalFieldAccessExpr if
not possible
+ fn plan_field_access(
+ &self,
+ expr: RawFieldAccessExpr,
+ _schema: &DFSchema,
+ ) -> Result<PlannerResult<RawFieldAccessExpr>> {
+ Ok(PlannerResult::Original(expr))
+ }
+
+ // Plan the array literal, returns OriginalArray if not possible
+ fn plan_array_literal(
+ &self,
+ exprs: Vec<Expr>,
+ _schema: &DFSchema,
+ ) -> Result<PlannerResult<Vec<Expr>>> {
+ Ok(PlannerResult::Original(exprs))
+ }
+}
+
+/// An operator with two arguments to plan
+///
+/// Note `left` and `right` are DataFusion [`Expr`]s but the `op` is the SQL
AST
+/// operator.
+///
+/// This structure is used by [`UserDefinedSQLPlanner`] to plan operators with
+/// custom expressions.
+#[derive(Debug, Clone)]
+pub struct RawBinaryExpr {
+ pub op: sqlparser::ast::BinaryOperator,
+ pub left: Expr,
+ pub right: Expr,
+}
+
+/// An expression with GetFieldAccess to plan
+///
+/// This structure is used by [`UserDefinedSQLPlanner`] to plan operators with
+/// custom expressions.
+#[derive(Debug, Clone)]
+pub struct RawFieldAccessExpr {
+ pub field_access: GetFieldAccess,
+ pub expr: Expr,
+}
+
+/// Result of planning a raw expr with [`UserDefinedSQLPlanner`]
+#[derive(Debug, Clone)]
+pub enum PlannerResult<T> {
+ /// The raw expression was successfully planned as a new [`Expr`]
+ Planned(Expr),
+ /// The raw expression could not be planned, and is returned unmodified
+ Original(T),
+}
diff --git a/datafusion/functions-array/src/lib.rs
b/datafusion/functions-array/src/lib.rs
index 543b7a6027..814127be80 100644
--- a/datafusion/functions-array/src/lib.rs
+++ b/datafusion/functions-array/src/lib.rs
@@ -39,6 +39,7 @@ pub mod extract;
pub mod flatten;
pub mod length;
pub mod make_array;
+pub mod planner;
pub mod position;
pub mod range;
pub mod remove;
@@ -50,7 +51,6 @@ pub mod set_ops;
pub mod sort;
pub mod string;
pub mod utils;
-
use datafusion_common::Result;
use datafusion_execution::FunctionRegistry;
use datafusion_expr::ScalarUDF;
diff --git a/datafusion/functions-array/src/planner.rs
b/datafusion/functions-array/src/planner.rs
new file mode 100644
index 0000000000..f33ee56582
--- /dev/null
+++ b/datafusion/functions-array/src/planner.rs
@@ -0,0 +1,161 @@
+// 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.
+
+//! SQL planning extensions like [`ArrayFunctionPlanner`] and
[`FieldAccessPlanner`]
+
+use datafusion_common::{utils::list_ndims, DFSchema, Result};
+use datafusion_expr::{
+ planner::{PlannerResult, RawBinaryExpr, RawFieldAccessExpr,
UserDefinedSQLPlanner},
+ sqlparser, AggregateFunction, Expr, ExprSchemable, GetFieldAccess,
+};
+use datafusion_functions::expr_fn::get_field;
+
+use crate::{
+ array_has::array_has_all,
+ expr_fn::{array_append, array_concat, array_prepend},
+ extract::{array_element, array_slice},
+ make_array::make_array,
+};
+
+#[derive(Default)]
+pub struct ArrayFunctionPlanner {}
+
+impl UserDefinedSQLPlanner for ArrayFunctionPlanner {
+ fn plan_binary_op(
+ &self,
+ expr: RawBinaryExpr,
+ schema: &DFSchema,
+ ) -> Result<PlannerResult<RawBinaryExpr>> {
+ let RawBinaryExpr { op, left, right } = expr;
+
+ if op == sqlparser::ast::BinaryOperator::StringConcat {
+ let left_type = left.get_type(schema)?;
+ let right_type = right.get_type(schema)?;
+ let left_list_ndims = list_ndims(&left_type);
+ let right_list_ndims = list_ndims(&right_type);
+
+ // Rewrite string concat operator to function based on types
+ // if we get list || list then we rewrite it to array_concat()
+ // if we get list || non-list then we rewrite it to array_append()
+ // if we get non-list || list then we rewrite it to array_prepend()
+ // if we get string || string then we rewrite it to concat()
+
+ // We determine the target function to rewrite based on the list
n-dimension, the check is not exact but sufficient.
+ // The exact validity check is handled in the actual function, so
even if there is 3d list appended with 1d list, it is also fine to rewrite.
+ if left_list_ndims + right_list_ndims == 0 {
+ // TODO: concat function ignore null, but string concat takes
null into consideration
+ // we can rewrite it to concat if we can configure the
behaviour of concat function to the one like `string concat operator`
+ } else if left_list_ndims == right_list_ndims {
+ return Ok(PlannerResult::Planned(array_concat(vec![left,
right])));
+ } else if left_list_ndims > right_list_ndims {
+ return Ok(PlannerResult::Planned(array_append(left, right)));
+ } else if left_list_ndims < right_list_ndims {
+ return Ok(PlannerResult::Planned(array_prepend(left, right)));
+ }
+ } else if matches!(
+ op,
+ sqlparser::ast::BinaryOperator::AtArrow
+ | sqlparser::ast::BinaryOperator::ArrowAt
+ ) {
+ let left_type = left.get_type(schema)?;
+ let right_type = right.get_type(schema)?;
+ let left_list_ndims = list_ndims(&left_type);
+ let right_list_ndims = list_ndims(&right_type);
+ // if both are list
+ if left_list_ndims > 0 && right_list_ndims > 0 {
+ if op == sqlparser::ast::BinaryOperator::AtArrow {
+ // array1 @> array2 -> array_has_all(array1, array2)
+ return Ok(PlannerResult::Planned(array_has_all(left,
right)));
+ } else {
+ // array1 <@ array2 -> array_has_all(array2, array1)
+ return Ok(PlannerResult::Planned(array_has_all(right,
left)));
+ }
+ }
+ }
+
+ Ok(PlannerResult::Original(RawBinaryExpr { op, left, right }))
+ }
+
+ fn plan_array_literal(
+ &self,
+ exprs: Vec<Expr>,
+ _schema: &DFSchema,
+ ) -> Result<PlannerResult<Vec<Expr>>> {
+ Ok(PlannerResult::Planned(make_array(exprs)))
+ }
+}
+
+#[derive(Default)]
+pub struct FieldAccessPlanner {}
+
+impl UserDefinedSQLPlanner for FieldAccessPlanner {
+ fn plan_field_access(
+ &self,
+ expr: RawFieldAccessExpr,
+ _schema: &DFSchema,
+ ) -> Result<PlannerResult<RawFieldAccessExpr>> {
+ let RawFieldAccessExpr { expr, field_access } = expr;
+
+ match field_access {
+ // expr["field"] => get_field(expr, "field")
+ GetFieldAccess::NamedStructField { name } => {
+ Ok(PlannerResult::Planned(get_field(expr, name)))
+ }
+ // expr[idx] ==> array_element(expr, idx)
+ GetFieldAccess::ListIndex { key: index } => {
+ match expr {
+ // Special case for array_agg(expr)[index] to
NTH_VALUE(expr, index)
+ Expr::AggregateFunction(agg_func) if
is_array_agg(&agg_func) => {
+ Ok(PlannerResult::Planned(Expr::AggregateFunction(
+ datafusion_expr::expr::AggregateFunction::new(
+ AggregateFunction::NthValue,
+ agg_func
+ .args
+ .into_iter()
+ .chain(std::iter::once(*index))
+ .collect(),
+ agg_func.distinct,
+ agg_func.filter,
+ agg_func.order_by,
+ agg_func.null_treatment,
+ ),
+ )))
+ }
+ _ => Ok(PlannerResult::Planned(array_element(expr,
*index))),
+ }
+ }
+ // expr[start, stop, stride] ==> array_slice(expr, start, stop,
stride)
+ GetFieldAccess::ListRange {
+ start,
+ stop,
+ stride,
+ } => Ok(PlannerResult::Planned(array_slice(
+ expr,
+ *start,
+ *stop,
+ Some(*stride),
+ ))),
+ }
+ }
+}
+
+fn is_array_agg(agg_func: &datafusion_expr::expr::AggregateFunction) -> bool {
+ agg_func.func_def
+ == datafusion_expr::expr::AggregateFunctionDefinition::BuiltIn(
+ AggregateFunction::ArrayAgg,
+ )
+}
diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index b1182b35ec..786ea288fa 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -17,7 +17,8 @@
use arrow_schema::DataType;
use arrow_schema::TimeUnit;
-use datafusion_common::utils::list_ndims;
+use datafusion_expr::planner::PlannerResult;
+use datafusion_expr::planner::RawFieldAccessExpr;
use sqlparser::ast::{CastKind, Expr as SQLExpr, Subscript, TrimWhereField,
Value};
use datafusion_common::{
@@ -27,8 +28,8 @@ use datafusion_common::{
use datafusion_expr::expr::InList;
use datafusion_expr::expr::ScalarFunction;
use datafusion_expr::{
- lit, AggregateFunction, Between, BinaryExpr, Cast, Expr, ExprSchemable,
- GetFieldAccess, Like, Literal, Operator, TryCast,
+ lit, Between, BinaryExpr, Cast, Expr, ExprSchemable, GetFieldAccess, Like,
Literal,
+ Operator, TryCast,
};
use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
@@ -52,7 +53,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
) -> Result<Expr> {
enum StackEntry {
SQLExpr(Box<SQLExpr>),
- Operator(Operator),
+ Operator(sqlparser::ast::BinaryOperator),
}
// Virtual stack machine to convert SQLExpr to Expr
@@ -69,7 +70,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
SQLExpr::BinaryOp { left, op, right } => {
// Note the order that we push the entries to the
stack
// is important. We want to visit the left node
first.
- let op = self.parse_sql_binary_op(op)?;
stack.push(StackEntry::Operator(op));
stack.push(StackEntry::SQLExpr(right));
stack.push(StackEntry::SQLExpr(left));
@@ -100,91 +100,28 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
fn build_logical_expr(
&self,
- op: Operator,
+ op: sqlparser::ast::BinaryOperator,
left: Expr,
right: Expr,
schema: &DFSchema,
) -> Result<Expr> {
- // Rewrite string concat operator to function based on types
- // if we get list || list then we rewrite it to array_concat()
- // if we get list || non-list then we rewrite it to array_append()
- // if we get non-list || list then we rewrite it to array_prepend()
- // if we get string || string then we rewrite it to concat()
- if op == Operator::StringConcat {
- let left_type = left.get_type(schema)?;
- let right_type = right.get_type(schema)?;
- let left_list_ndims = list_ndims(&left_type);
- let right_list_ndims = list_ndims(&right_type);
-
- // We determine the target function to rewrite based on the list
n-dimension, the check is not exact but sufficient.
- // The exact validity check is handled in the actual function, so
even if there is 3d list appended with 1d list, it is also fine to rewrite.
- if left_list_ndims + right_list_ndims == 0 {
- // TODO: concat function ignore null, but string concat takes
null into consideration
- // we can rewrite it to concat if we can configure the
behaviour of concat function to the one like `string concat operator`
- } else if left_list_ndims == right_list_ndims {
- if let Some(udf) =
self.context_provider.get_function_meta("array_concat")
- {
- return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![left, right],
- )));
- } else {
- return internal_err!("array_concat not found");
+ // try extension planers
+ let mut binary_expr = datafusion_expr::planner::RawBinaryExpr { op,
left, right };
+ for planner in self.planners.iter() {
+ match planner.plan_binary_op(binary_expr, schema)? {
+ PlannerResult::Planned(expr) => {
+ return Ok(expr);
}
- } else if left_list_ndims > right_list_ndims {
- if let Some(udf) =
self.context_provider.get_function_meta("array_append")
- {
- return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![left, right],
- )));
- } else {
- return internal_err!("array_append not found");
- }
- } else if left_list_ndims < right_list_ndims {
- if let Some(udf) =
- self.context_provider.get_function_meta("array_prepend")
- {
- return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![left, right],
- )));
- } else {
- return internal_err!("array_prepend not found");
- }
- }
- } else if matches!(op, Operator::AtArrow | Operator::ArrowAt) {
- let left_type = left.get_type(schema)?;
- let right_type = right.get_type(schema)?;
- let left_list_ndims = list_ndims(&left_type);
- let right_list_ndims = list_ndims(&right_type);
- // if both are list
- if left_list_ndims > 0 && right_list_ndims > 0 {
- if let Some(udf) =
- self.context_provider.get_function_meta("array_has_all")
- {
- // array1 @> array2 -> array_has_all(array1, array2)
- if op == Operator::AtArrow {
- return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![left, right],
- )));
- // array1 <@ array2 -> array_has_all(array2, array1)
- } else {
- return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![right, left],
- )));
- }
- } else {
- return internal_err!("array_has_all not found");
+ PlannerResult::Original(expr) => {
+ binary_expr = expr;
}
}
}
+ let datafusion_expr::planner::RawBinaryExpr { op, left, right } =
binary_expr;
Ok(Expr::BinaryExpr(BinaryExpr::new(
Box::new(left),
- op,
+ self.parse_sql_binary_op(op)?,
Box::new(right),
)))
}
@@ -272,7 +209,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
let expr =
self.sql_expr_to_logical_expr(*expr, schema,
planner_context)?;
- let get_field_access = match *subscript {
+ let field_access = match *subscript {
Subscript::Index { index } => {
// index can be a name, in which case it is a named
field access
match index {
@@ -341,7 +278,17 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
};
- self.plan_field_access(expr, get_field_access)
+ let mut field_access_expr = RawFieldAccessExpr { expr,
field_access };
+ for planner in self.planners.iter() {
+ match planner.plan_field_access(field_access_expr,
schema)? {
+ PlannerResult::Planned(expr) => return Ok(expr),
+ PlannerResult::Original(expr) => {
+ field_access_expr = expr;
+ }
+ }
+ }
+
+ not_impl_err!("GetFieldAccess not supported by
UserDefinedExtensionPlanners: {field_access_expr:?}")
}
SQLExpr::CompoundIdentifier(ids) => {
@@ -676,36 +623,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
}
- /// 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,
@@ -991,58 +908,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
let args = vec![fullstr, substr];
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(fun, args)))
}
-
- /// Given an expression and the field to access, creates a new expression
for accessing that field
- fn plan_field_access(
- &self,
- expr: Expr,
- get_field_access: GetFieldAccess,
- ) -> Result<Expr> {
- 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(
- udf,
- vec![expr, lit(name)],
- )))
- } else {
- internal_err!("get_field not found")
- }
- }
- // expr[idx] ==> array_element(expr, idx)
- GetFieldAccess::ListIndex { key } => {
- // 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(
- udf,
- vec![expr, *key],
- )))
- } else {
- internal_err!("get_field not found")
- }
- }
- // expr[start, stop, stride] ==> array_slice(expr, start, stop,
stride)
- GetFieldAccess::ListRange {
- start,
- stop,
- stride,
- } => {
- if let Some(udf) =
self.context_provider.get_function_meta("array_slice")
- {
- Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
- udf,
- vec![expr, *start, *stop, *stride],
- )))
- } else {
- internal_err!("array_slice not found")
- }
- }
- }
- }
}
#[cfg(test)]
diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs
index fa95fc2e05..5cd6ffc687 100644
--- a/datafusion/sql/src/expr/value.rs
+++ b/datafusion/sql/src/expr/value.rs
@@ -20,9 +20,10 @@ use
arrow::compute::kernels::cast_utils::parse_interval_month_day_nano;
use arrow::datatypes::DECIMAL128_MAX_PRECISION;
use arrow_schema::DataType;
use datafusion_common::{
- not_impl_err, plan_err, DFSchema, DataFusionError, Result, ScalarValue,
+ internal_err, not_impl_err, plan_err, DFSchema, DataFusionError, Result,
ScalarValue,
};
-use datafusion_expr::expr::{BinaryExpr, Placeholder, ScalarFunction};
+use datafusion_expr::expr::{BinaryExpr, Placeholder};
+use datafusion_expr::planner::PlannerResult;
use datafusion_expr::{lit, Expr, Operator};
use log::debug;
use sqlparser::ast::{BinaryOperator, Expr as SQLExpr, Interval, Value};
@@ -130,6 +131,8 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
)))
}
+ // IMPORTANT: Keep sql_array_literal's function body small to prevent
stack overflow
+ // This function is recursively called, potentially leading to deep call
stacks.
pub(super) fn sql_array_literal(
&self,
elements: Vec<SQLExpr>,
@@ -142,13 +145,25 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
})
.collect::<Result<Vec<_>>>()?;
- if let Some(udf) =
self.context_provider.get_function_meta("make_array") {
- Ok(Expr::ScalarFunction(ScalarFunction::new_udf(udf, values)))
- } else {
- not_impl_err!(
- "array_expression featrue is disable, So should implement
make_array UDF by yourself"
- )
+ self.try_plan_array_literal(values, schema)
+ }
+
+ fn try_plan_array_literal(
+ &self,
+ values: Vec<Expr>,
+ schema: &DFSchema,
+ ) -> Result<Expr> {
+ let mut exprs = values;
+ for planner in self.planners.iter() {
+ match planner.plan_array_literal(exprs, schema)? {
+ PlannerResult::Planned(expr) => {
+ return Ok(expr);
+ }
+ PlannerResult::Original(values) => exprs = values,
+ }
}
+
+ internal_err!("Expected a simplified result, but none was found")
}
/// Convert a SQL interval expression to a DataFusion logical plan
diff --git a/datafusion/sql/src/planner.rs b/datafusion/sql/src/planner.rs
index 00f2212006..443cd64a94 100644
--- a/datafusion/sql/src/planner.rs
+++ b/datafusion/sql/src/planner.rs
@@ -21,17 +21,15 @@ use std::sync::Arc;
use std::vec;
use arrow_schema::*;
-use datafusion_common::file_options::file_type::FileType;
use datafusion_common::{
field_not_found, internal_err, plan_datafusion_err, DFSchemaRef,
SchemaError,
};
-use datafusion_expr::WindowUDF;
+use datafusion_expr::planner::UserDefinedSQLPlanner;
use sqlparser::ast::TimezoneInfo;
use sqlparser::ast::{ArrayElemTypeDef, ExactNumberInfo};
use sqlparser::ast::{ColumnDef as SQLColumnDef, ColumnOption};
use sqlparser::ast::{DataType as SQLDataType, Ident, ObjectName, TableAlias};
-use datafusion_common::config::ConfigOptions;
use datafusion_common::TableReference;
use datafusion_common::{
not_impl_err, plan_err, unqualified_field_not_found, DFSchema,
DataFusionError,
@@ -39,64 +37,11 @@ use datafusion_common::{
};
use datafusion_expr::logical_plan::{LogicalPlan, LogicalPlanBuilder};
use datafusion_expr::utils::find_column_exprs;
-use datafusion_expr::TableSource;
-use datafusion_expr::{col, AggregateUDF, Expr, ScalarUDF};
+use datafusion_expr::{col, Expr};
use crate::utils::make_decimal_type;
-/// The ContextProvider trait allows the query planner to obtain meta-data
about tables and
-/// functions referenced in SQL statements
-pub trait ContextProvider {
- /// Getter for a datasource
- fn get_table_source(&self, name: TableReference) -> Result<Arc<dyn
TableSource>>;
-
- fn get_file_type(&self, _ext: &str) -> Result<Arc<dyn FileType>> {
- not_impl_err!("Registered file types are not supported")
- }
-
- /// Getter for a table function
- fn get_table_function_source(
- &self,
- _name: &str,
- _args: Vec<Expr>,
- ) -> Result<Arc<dyn TableSource>> {
- not_impl_err!("Table Functions are not supported")
- }
-
- /// This provides a worktable (an intermediate table that is used to store
the results of a CTE during execution)
- /// We don't directly implement this in the logical plan's ['SqlToRel`]
- /// because the sql code needs access to a table that contains
execution-related types that can't be a direct dependency
- /// of the sql crate (namely, the `CteWorktable`).
- /// The [`ContextProvider`] provides a way to "hide" this dependency.
- fn create_cte_work_table(
- &self,
- _name: &str,
- _schema: SchemaRef,
- ) -> Result<Arc<dyn TableSource>> {
- not_impl_err!("Recursive CTE is not implemented")
- }
-
- /// Getter for a UDF description
- fn get_function_meta(&self, name: &str) -> Option<Arc<ScalarUDF>>;
- /// Getter for a UDAF description
- fn get_aggregate_meta(&self, name: &str) -> Option<Arc<AggregateUDF>>;
- /// Getter for a UDWF
- fn get_window_meta(&self, name: &str) -> Option<Arc<WindowUDF>>;
- /// Getter for system/user-defined variable type
- fn get_variable_type(&self, variable_names: &[String]) -> Option<DataType>;
-
- /// Get configuration options
- fn options(&self) -> &ConfigOptions;
-
- /// Get all user defined scalar function names
- fn udf_names(&self) -> Vec<String>;
-
- /// Get all user defined aggregate function names
- fn udaf_names(&self) -> Vec<String>;
-
- /// Get all user defined window function names
- fn udwf_names(&self) -> Vec<String>;
-}
+pub use datafusion_expr::planner::ContextProvider;
/// SQL parser options
#[derive(Debug)]
@@ -241,6 +186,8 @@ pub struct SqlToRel<'a, S: ContextProvider> {
pub(crate) context_provider: &'a S,
pub(crate) options: ParserOptions,
pub(crate) normalizer: IdentNormalizer,
+ /// user defined planner extensions
+ pub(crate) planners: Vec<Arc<dyn UserDefinedSQLPlanner>>,
}
impl<'a, S: ContextProvider> SqlToRel<'a, S> {
@@ -249,13 +196,24 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
Self::new_with_options(context_provider, ParserOptions::default())
}
+ /// add an user defined planner
+ pub fn with_user_defined_planner(
+ mut self,
+ planner: Arc<dyn UserDefinedSQLPlanner>,
+ ) -> Self {
+ self.planners.push(planner);
+ self
+ }
+
/// Create a new query planner
pub fn new_with_options(context_provider: &'a S, options: ParserOptions)
-> Self {
let normalize = options.enable_ident_normalization;
+
SqlToRel {
context_provider,
options,
normalizer: IdentNormalizer::new(normalize),
+ planners: vec![],
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]