mirror of
https://github.com/Canner/WrenAI.git
synced 2026-10-16 06:57:43 +08:00
deps(core): upgrade DataFusion to 55.1
parquet 58 pins thrift ^0.17, which carries CVE-2026-43868 (fixed in thrift 0.23). parquet 59 drops the thrift dependency, and DataFusion 55 is the first release on arrow/parquet 59, so the fix requires this upgrade. API migrations: TableReference moved out of datafusion::sql, as_any() is no longer part of the UDF/TableProvider traits, Cast/TryCast carry a field instead of a data type, and ContextProvider/Expr gained higher-order function support. Behaviour kept as before: - IN lists keep the string-preferring coercion. DataFusion now coerces them numerically, which would emit `CAST(str_col AS BIGINT) IN (1, 2)` and fail in the target database on non-numeric values. - UNION and CASE WHEN use the upstream split of comparison_coercion into type_union_coercion / get_coerce_type_for_case_when, matching their prior semantics. - wren-core-py lists only scalar/aggregate/window functions, since information_schema.routines now also reports table functions. The wasm release profile now uses fat LTO with one codegen unit; without it the binary grows past the 15 MB gzip budget checked in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d26ab6af5e
commit
ced74b4b1d
+3
-3
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Wren is an open-source semantic engine for MCP clients and AI agents. It translates SQL queries through a semantic layer (MDL — Modeling Definition Language) and executes them against 22+ data sources (PostgreSQL, BigQuery, Snowflake, Spark, etc.). The Rust engine is powered by Apache DataFusion (upstream, crates.io v53).
|
||||
Wren is an open-source semantic engine for MCP clients and AI agents. It translates SQL queries through a semantic layer (MDL — Modeling Definition Language) and executes them against 22+ data sources (PostgreSQL, BigQuery, Snowflake, Spark, etc.). The Rust engine is powered by Apache DataFusion (upstream, crates.io v55).
|
||||
|
||||
The previous WrenAI services (`wren-ui/`, `wren-ai-service/`, `wren-launcher/`, `docker/`, `deployment/`) were moved to the `legacy/v1` branch (tag `v1-final`) as of the wren-engine import. Active development is focused on the Open Context Engine.
|
||||
|
||||
@@ -83,7 +83,7 @@ Uses `uv` (not Poetry). `pyproject.toml` uses `hatchling` as build backend. Opti
|
||||
SQL query
|
||||
→ wren CLI / wren-core-py
|
||||
→ wren-core (Rust): MDL analysis → logical plan → optimization
|
||||
→ DataFusion (query planning, upstream crates.io v53)
|
||||
→ DataFusion (query planning, upstream crates.io v55)
|
||||
→ connector-specific SQL (Ibis / sqlglot)
|
||||
→ native execution on the target data source
|
||||
```
|
||||
@@ -110,7 +110,7 @@ SQL query
|
||||
- **Commits**: Conventional commits (`feat:`, `fix:`, `chore:`, `refactor:`, `test:`, `docs:`, `perf:`, `deps:`). Releases are automated via release-please with independent release lines per module.
|
||||
- **Rust**: format with `cargo fmt`, lint with `clippy -D warnings`, TOML with `taplo`.
|
||||
- **Python**: format and lint with `ruff` (line-length 88, target Python 3.11). Both `core/wren-core-py` and `core/wren` use uv.
|
||||
- **DataFusion**: upstream `datafusion` v53 from crates.io (no longer the Canner fork).
|
||||
- **DataFusion**: upstream `datafusion` v55 from crates.io (no longer the Canner fork).
|
||||
- **Snapshot testing**: wren-core uses `insta` for Rust snapshot tests.
|
||||
- **CI**: Per-module path-filtered workflows trigger only on changes inside that module.
|
||||
|
||||
|
||||
Generated
+319
-266
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ crate-type = ["cdylib"]
|
||||
pyo3 = { version = "0.29.0", features = ["extension-module", "abi3-py311"] }
|
||||
wren-core = { version = "0.4.0", path = "../wren-core/core", package = "wren-semantic-core" }
|
||||
wren-core-base = { path = "../wren-core-base", features = ["python-binding"] }
|
||||
datafusion-common = "53.0.0"
|
||||
datafusion-common = "55.1.0"
|
||||
base64 = "0.22.1"
|
||||
futures = "0.3.31"
|
||||
serde_json = "1.0.117"
|
||||
|
||||
@@ -635,6 +635,8 @@ impl PySessionContext {
|
||||
r.description
|
||||
FROM
|
||||
information_schema.routines r
|
||||
WHERE
|
||||
r.function_type IN ('SCALAR', 'AGGREGATE', 'WINDOW')
|
||||
"#;
|
||||
let batches = ctx
|
||||
.sql(sql)
|
||||
@@ -742,6 +744,7 @@ impl PySessionContext {
|
||||
inputs i ON r.specific_name = i.specific_name
|
||||
WHERE
|
||||
r.routine_name = '{}'
|
||||
AND r.function_type IN ('SCALAR', 'AGGREGATE', 'WINDOW')
|
||||
"#,
|
||||
function_name
|
||||
);
|
||||
|
||||
@@ -144,7 +144,7 @@ def test_read_function_list():
|
||||
path = "tests/functions.csv"
|
||||
session_context = SessionContext(manifest_str, path)
|
||||
functions = session_context.get_available_functions()
|
||||
assert len(functions) == 290
|
||||
assert len(functions) == 291
|
||||
|
||||
rewritten_sql = session_context.transform_sql(
|
||||
"SELECT add_two(c_custkey, c_custkey) FROM my_catalog.my_schema.customer"
|
||||
@@ -156,7 +156,7 @@ def test_read_function_list():
|
||||
|
||||
session_context = SessionContext(manifest_str, None)
|
||||
functions = session_context.get_available_functions()
|
||||
assert len(functions) == 283
|
||||
assert len(functions) == 284
|
||||
|
||||
|
||||
def test_get_available_functions():
|
||||
|
||||
@@ -4,7 +4,7 @@ Wren Engine compiled to WebAssembly for browser-native analytics. Runs SQL queri
|
||||
|
||||
## Why a Separate Crate?
|
||||
|
||||
Uses **upstream DataFusion** (crates.io v53), not the Canner fork. The WASM version executes queries directly via DataFusion — no SQL unparser or dialect transpilation needed. Kept outside the `wren-core/` workspace to avoid dependency conflicts.
|
||||
Uses **upstream DataFusion** (crates.io v55), not the Canner fork. The WASM version executes queries directly via DataFusion — no SQL unparser or dialect transpilation needed. Kept outside the `wren-core/` workspace to avoid dependency conflicts.
|
||||
|
||||
Shared code: `wren-core-base` (manifest types, no DataFusion dependency) and `wren-core` semantic layer (MDL analysis rules).
|
||||
|
||||
@@ -51,10 +51,10 @@ just clean # Remove pkg/, dist/, target/
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **DataFusion v53** (upstream, crates.io) — query engine, `default-features = false` + selected features
|
||||
- **Arrow v58.1** — `json` feature for JSON reader
|
||||
- **Parquet v58.1** — `snap` + `lz4` only (no zstd — requires C library, can't compile to WASM)
|
||||
- **object_store v0.13.1** — `aws` + `http` features for URL mode (HttpStore)
|
||||
- **DataFusion v55** (upstream, crates.io) — query engine, `default-features = false` + selected features
|
||||
- **Arrow v59.2** — `json` feature for JSON reader
|
||||
- **Parquet v59.2** — `snap` + `lz4` only (no zstd — requires C library, can't compile to WASM)
|
||||
- **object_store v0.13.2** — `aws` + `http` features for URL mode (HttpStore)
|
||||
- **wren-core** (path: `../wren-core/core`) — semantic layer, `default-features = false`
|
||||
- **wren-core-base** (path: `../wren-core-base`) — shared manifest types
|
||||
- **wasm-bindgen / js-sys / web-sys** — WASM ↔ JS bindings
|
||||
@@ -69,7 +69,7 @@ just clean # Remove pkg/, dist/, target/
|
||||
- **No SystemTime**: chrono `wasmbind` feature required
|
||||
- **getrandom**: All three major versions (0.2, 0.3, 0.4) in the dep tree need explicit WASM JS backend
|
||||
- **macOS build**: Needs LLVM (`brew install llvm`) for C deps — justfile handles env vars automatically
|
||||
- **Binary size**: ~68 MB raw / ~14 MB gzip (target: < 15 MB gzip)
|
||||
- **Binary size**: ~57 MB raw / ~14 MB gzip (target: < 15 MB gzip); the release profile uses fat LTO + `codegen-units = 1` to stay under budget
|
||||
|
||||
## npm Package (wren-core-wasm)
|
||||
|
||||
|
||||
Generated
+327
-264
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ path = "src/lib.rs"
|
||||
# DataFusion WITHOUT parquet feature — parquet's default-features=true in
|
||||
# DataFusion enables zstd (C library) which cannot compile to WASM.
|
||||
# We read Parquet bytes → Arrow RecordBatch → MemTable ourselves.
|
||||
datafusion = { version = "53", default-features = false, features = [
|
||||
datafusion = { version = "55.1", default-features = false, features = [
|
||||
"nested_expressions",
|
||||
"crypto_expressions",
|
||||
"datetime_expressions",
|
||||
@@ -30,16 +30,16 @@ datafusion = { version = "53", default-features = false, features = [
|
||||
"sql",
|
||||
] }
|
||||
|
||||
object_store = { version = "0.13.1", features = ["aws", "http"] }
|
||||
object_store = { version = "0.13.2", features = ["aws", "http"] }
|
||||
|
||||
# --- Arrow / Parquet ---
|
||||
arrow = { version = "58.1", default-features = false, features = [
|
||||
arrow = { version = "59.2", default-features = false, features = [
|
||||
"json",
|
||||
"csv",
|
||||
] }
|
||||
# No zstd: requires C library (zstd-sys) which cannot compile to WASM.
|
||||
# Snappy and LZ4 are pure Rust and WASM-compatible.
|
||||
parquet = { version = "58.1", default-features = false, features = [
|
||||
parquet = { version = "59.2", default-features = false, features = [
|
||||
"arrow",
|
||||
"snap",
|
||||
"lz4",
|
||||
@@ -89,3 +89,9 @@ getrandom_04 = { package = "getrandom", version = "0.4", features = [
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
|
||||
# Fat LTO with a single codegen unit keeps the binary under the 15 MB gzip
|
||||
# budget checked in CI (~14 MB vs ~16 MB without it on DataFusion 55).
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# wren-core
|
||||
|
||||
Rust semantic engine for Wren Engine. Handles MDL analysis, query planning, logical plan optimization, and SQL generation via Apache DataFusion (upstream, crates.io v53). Published to crates.io as `wren-semantic-core` (lib name `wren_core`).
|
||||
Rust semantic engine for Wren Engine. Handles MDL analysis, query planning, logical plan optimization, and SQL generation via Apache DataFusion (upstream, crates.io v55). Published to crates.io as `wren-semantic-core` (lib name `wren_core`).
|
||||
|
||||
## Workspace Layout
|
||||
|
||||
@@ -38,7 +38,7 @@ taplo fmt # Format Cargo.toml file
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **DataFusion**: upstream `datafusion` v53 from crates.io (no longer the Canner fork)
|
||||
- **DataFusion**: upstream `datafusion` v55 from crates.io (no longer the Canner fork)
|
||||
- **wren-core-base**: Shared manifest types from `../wren-core-base`
|
||||
|
||||
## Known Limitations
|
||||
|
||||
@@ -9,12 +9,12 @@ homepage = "https://getwren.ai"
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Canner/WrenAI"
|
||||
rust-version = "1.78"
|
||||
rust-version = "1.94"
|
||||
version = "0.4.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.89"
|
||||
datafusion = { version = "53", default-features = false, features = [
|
||||
datafusion = { version = "55.1", default-features = false, features = [
|
||||
"nested_expressions",
|
||||
"crypto_expressions",
|
||||
"datetime_expressions",
|
||||
@@ -24,7 +24,7 @@ datafusion = { version = "53", default-features = false, features = [
|
||||
"parquet",
|
||||
"sql",
|
||||
] }
|
||||
datafusion-session = { version = "53", default-features = false }
|
||||
datafusion-session = { version = "55.1", default-features = false }
|
||||
env_logger = "0.11.10"
|
||||
hashbrown = "0.17.0"
|
||||
insta = { version = "1.41.1" }
|
||||
|
||||
@@ -8,13 +8,13 @@ use datafusion::{
|
||||
arrow::datatypes::{DataType, SchemaRef},
|
||||
common::{
|
||||
config::ConfigOptions, file_options::file_type::FileType, plan_datafusion_err,
|
||||
plan_err, Result, Spans,
|
||||
plan_err, Result, Spans, TableReference,
|
||||
},
|
||||
error::DataFusionError,
|
||||
logical_expr::{
|
||||
builder::LogicalTableSource,
|
||||
planner::{ContextProvider, ExprPlanner, TypePlanner},
|
||||
AggregateUDF, ScalarUDF, TableSource, WindowUDF,
|
||||
AggregateUDF, HigherOrderUDF, ScalarUDF, TableSource, WindowUDF,
|
||||
},
|
||||
prelude::Expr,
|
||||
sql::{
|
||||
@@ -27,7 +27,6 @@ use datafusion::{
|
||||
},
|
||||
dialect::GenericDialect,
|
||||
},
|
||||
TableReference,
|
||||
},
|
||||
};
|
||||
use wren_core_base::mdl::RowLevelAccessControl;
|
||||
@@ -395,6 +394,14 @@ impl ContextProvider for RlacContextProvider {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn get_higher_order_meta(&self, name: &str) -> Option<Arc<HigherOrderUDF>> {
|
||||
self.session_state
|
||||
.read()
|
||||
.higher_order_functions()
|
||||
.get(name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn get_aggregate_meta(&self, name: &str) -> Option<Arc<AggregateUDF>> {
|
||||
self.session_state
|
||||
.read()
|
||||
@@ -428,6 +435,15 @@ impl ContextProvider for RlacContextProvider {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn higher_order_function_names(&self) -> Vec<String> {
|
||||
self.session_state
|
||||
.read()
|
||||
.higher_order_functions()
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn udaf_names(&self) -> Vec<String> {
|
||||
self.session_state
|
||||
.read()
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::mdl::{AnalyzedWrenMDL, Dataset, SessionStateRef};
|
||||
use datafusion::common::tree_node::{
|
||||
Transformed, TransformedResult, TreeNode, TreeNodeRecursion, TreeNodeRewriter,
|
||||
};
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{internal_err, plan_err, Column, DFSchemaRef, Result, Spans};
|
||||
use datafusion::config::ConfigOptions;
|
||||
use datafusion::error::DataFusionError;
|
||||
@@ -17,7 +18,6 @@ use datafusion::logical_expr::{
|
||||
Window,
|
||||
};
|
||||
use datafusion::optimizer::AnalyzerRule;
|
||||
use datafusion::sql::TableReference;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -17,12 +17,12 @@ use crate::DataFusionError;
|
||||
use datafusion::common::alias::AliasGenerator;
|
||||
use datafusion::common::config::ConfigOptions;
|
||||
use datafusion::common::tree_node::{Transformed, TransformedResult};
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{plan_err, Result};
|
||||
use datafusion::logical_expr::{col, ident, Extension, UserDefinedLogicalNodeCore};
|
||||
use datafusion::logical_expr::{Expr, LogicalPlan, LogicalPlanBuilder};
|
||||
use datafusion::optimizer::analyzer::AnalyzerRule;
|
||||
use datafusion::physical_plan::internal_err;
|
||||
use datafusion::sql::TableReference;
|
||||
|
||||
pub const SOURCE_ALIAS: &str = "__source";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::mdl::Dataset;
|
||||
use datafusion::common::internal_err;
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::error::Result;
|
||||
use datafusion::prelude::Expr;
|
||||
use datafusion::sql::TableReference;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ impl TimestampSimplify {
|
||||
} else {
|
||||
Arc::new(DFSchema::empty())
|
||||
};
|
||||
let info = SimplifyContext::default().with_schema(schema);
|
||||
let info = SimplifyContext::builder().with_schema(schema).build();
|
||||
|
||||
// Inputs have already been rewritten (due to bottom-up traversal handled by Optimizer)
|
||||
// Just need to rewrite our own expressions
|
||||
@@ -123,13 +123,13 @@ impl TreeNodeRewriter for ExprRewriter<'_> {
|
||||
match &expr {
|
||||
// we only simplify the cast expression for the literal value
|
||||
Expr::Cast(Cast {
|
||||
data_type,
|
||||
field,
|
||||
expr: sub_expr,
|
||||
})
|
||||
| Expr::TryCast(TryCast {
|
||||
data_type,
|
||||
field,
|
||||
expr: sub_expr,
|
||||
}) if is_timestamp(data_type)
|
||||
}) if is_timestamp(field.data_type())
|
||||
&& matches!(sub_expr.as_ref(), Expr::Literal(_, _)) =>
|
||||
{
|
||||
let original_name = self.name_preserver.save(&expr);
|
||||
|
||||
@@ -25,32 +25,35 @@ use datafusion::{
|
||||
common::{
|
||||
exec_err, internal_err, not_impl_err, plan_datafusion_err, plan_err,
|
||||
tree_node::{Transformed, TreeNode, TreeNodeRewriter},
|
||||
Column, DFSchema, DFSchemaRef,
|
||||
Column, DFSchema, DFSchemaRef, TableReference,
|
||||
},
|
||||
config::ConfigOptions,
|
||||
error::{DataFusionError, Result},
|
||||
logical_expr::{
|
||||
binary::{comparison_coercion, like_coercion, BinaryTypeCoercer},
|
||||
binary::{
|
||||
comparison_coercion, like_coercion, type_union_coercion, BinaryTypeCoercer,
|
||||
},
|
||||
expr::{
|
||||
self, AggregateFunction, AggregateFunctionParams, Alias, Exists, InList,
|
||||
InSubquery, ScalarFunction, SetComparison, WindowFunction,
|
||||
WindowFunctionParams,
|
||||
self, AggregateFunction, AggregateFunctionParams, Alias, Exists,
|
||||
HigherOrderFunction, InList, InSubquery, ScalarFunction, SetComparison,
|
||||
WindowFunction, WindowFunctionParams,
|
||||
},
|
||||
expr_rewriter::coerce_plan_expr_for_schema,
|
||||
expr_schema::cast_subquery,
|
||||
type_coercion::{
|
||||
functions::fields_with_udf,
|
||||
functions::{
|
||||
fields_with_udf, value_fields_with_higher_order_udf_and_lambdas,
|
||||
},
|
||||
is_datetime,
|
||||
other::{get_coerce_type_for_case_expression, get_coerce_type_for_list},
|
||||
other::{get_coerce_type_for_case_expression, get_coerce_type_for_case_when},
|
||||
},
|
||||
utils::merge_schema,
|
||||
Between, BinaryExpr, Case, ExprSchemable, Join, Like, Limit, LogicalPlan,
|
||||
Operator, Projection, Subquery, Union, WindowFunctionDefinition,
|
||||
Operator, Projection, Subquery, Union, ValueOrLambda, WindowFunctionDefinition,
|
||||
},
|
||||
optimizer::{utils::NamePreserver, AnalyzerRule},
|
||||
prelude::Expr,
|
||||
scalar::ScalarValue,
|
||||
sql::TableReference,
|
||||
};
|
||||
|
||||
use datafusion::logical_expr::{
|
||||
@@ -535,8 +538,15 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
|
||||
.iter()
|
||||
.map(|list_expr| list_expr.get_type(self.schema))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let result_type =
|
||||
get_coerce_type_for_list(&expr_data_type, &list_data_types);
|
||||
// DataFusion coerces IN lists with the numeric-preferring
|
||||
// `comparison_coercion`, which would cast a string column to a number
|
||||
// (`CAST(s AS BIGINT) IN (1, 2)`) and fail in the target database on
|
||||
// non-numeric values. Keep the string-preferring coercion instead.
|
||||
let result_type = list_data_types
|
||||
.iter()
|
||||
.try_fold(expr_data_type.clone(), |left_type, right_type| {
|
||||
type_union_coercion(&left_type, right_type)
|
||||
});
|
||||
match result_type {
|
||||
None => plan_err!(
|
||||
"Can not find compatible types to compare {expr_data_type:?} with {list_data_types:?}"
|
||||
@@ -641,6 +651,41 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
|
||||
},
|
||||
})))
|
||||
}
|
||||
Expr::HigherOrderFunction(HigherOrderFunction { func, args }) => {
|
||||
let current_fields = args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
Expr::Lambda(lambda) => Ok(ValueOrLambda::Lambda(
|
||||
lambda.body.to_field(self.schema)?.1,
|
||||
)),
|
||||
_ => Ok(ValueOrLambda::Value(arg.to_field(self.schema)?.1)),
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let new_fields = value_fields_with_higher_order_udf_and_lambdas(
|
||||
¤t_fields,
|
||||
func.as_ref(),
|
||||
)?;
|
||||
|
||||
let new_args = std::iter::zip(args, new_fields)
|
||||
.map(|(arg, new_field)| match (&arg, new_field) {
|
||||
(Expr::Lambda(_), ValueOrLambda::Lambda(_)) => Ok(arg),
|
||||
(Expr::Lambda(_), ValueOrLambda::Value(_)) => internal_err!(
|
||||
"value_fields_with_higher_order_udf returned a value for a lambda argument"
|
||||
),
|
||||
(_, ValueOrLambda::Value(new_field)) => {
|
||||
arg.cast_to(new_field.data_type(), self.schema)
|
||||
}
|
||||
(_, ValueOrLambda::Lambda(_)) => internal_err!(
|
||||
"value_fields_with_higher_order_udf returned a lambda for a value argument"
|
||||
),
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
Ok(Transformed::yes(Expr::HigherOrderFunction(
|
||||
HigherOrderFunction::new(func, new_args),
|
||||
)))
|
||||
}
|
||||
// TODO: remove the next line after `Expr::Wildcard` is removed
|
||||
#[expect(deprecated)]
|
||||
Expr::Alias(_)
|
||||
@@ -656,7 +701,9 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
|
||||
| Expr::Wildcard { .. }
|
||||
| Expr::GroupingSet(_)
|
||||
| Expr::Placeholder(_)
|
||||
| Expr::OuterReferenceColumn(_, _) => Ok(Transformed::no(expr)),
|
||||
| Expr::OuterReferenceColumn(_, _)
|
||||
| Expr::Lambda(_)
|
||||
| Expr::LambdaVariable(_) => Ok(Transformed::no(expr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -967,8 +1014,7 @@ fn coerce_case_expression(case: Case, schema: &DFSchema) -> Result<Case> {
|
||||
.iter()
|
||||
.map(|(when, _then)| when.get_type(schema))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let coerced_type =
|
||||
get_coerce_type_for_case_expression(&when_types, Some(case_type));
|
||||
let coerced_type = get_coerce_type_for_case_when(&when_types, case_type);
|
||||
coerced_type.ok_or_else(|| {
|
||||
plan_datafusion_err!(
|
||||
"Failed to coerce case ({case_type:?}) and when ({when_types:?}) \
|
||||
@@ -1072,7 +1118,7 @@ fn coerce_union_schema_with_schema(
|
||||
plan_schema.fields().iter()
|
||||
) {
|
||||
let coerced_type =
|
||||
comparison_coercion(union_datatype, plan_field.data_type()).ok_or_else(
|
||||
type_union_coercion(union_datatype, plan_field.data_type()).ok_or_else(
|
||||
|| {
|
||||
plan_datafusion_err!(
|
||||
"Incompatible inputs for Union: Previous inputs were \
|
||||
|
||||
@@ -13,6 +13,7 @@ use datafusion::common::types::{
|
||||
logical_binary, logical_boolean, logical_date, logical_float16, logical_float32,
|
||||
logical_float64, logical_string,
|
||||
};
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{plan_err, DFSchema, DFSchemaRef};
|
||||
use datafusion::datasource::DefaultTableSource;
|
||||
use datafusion::error::Result;
|
||||
@@ -22,7 +23,6 @@ use datafusion::logical_expr::{builder::LogicalTableSource, Expr, TableSource};
|
||||
use datafusion::logical_expr::{Coercion, TypeSignatureClass};
|
||||
use datafusion::sql::sqlparser::ast;
|
||||
use datafusion::sql::sqlparser::parser::Parser;
|
||||
use datafusion::sql::TableReference;
|
||||
use log::debug;
|
||||
use petgraph::dot::{Config, Dot};
|
||||
use petgraph::Graph;
|
||||
@@ -406,7 +406,10 @@ pub fn expr_to_columns(
|
||||
| Expr::InSubquery(_)
|
||||
| Expr::ScalarSubquery(_)
|
||||
| Expr::Wildcard { .. }
|
||||
| Expr::Placeholder(_) => {}
|
||||
| Expr::Placeholder(_)
|
||||
| Expr::HigherOrderFunction(_)
|
||||
| Expr::Lambda(_)
|
||||
| Expr::LambdaVariable(_) => {}
|
||||
Expr::SetComparison(_) => {}
|
||||
}
|
||||
Ok(TreeNodeRecursion::Continue)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
@@ -19,6 +18,7 @@ use datafusion::catalog::memory::{MemoryCatalogProvider, MemoryCatalogProviderLi
|
||||
use datafusion::catalog::CatalogProvider;
|
||||
use datafusion::catalog::CatalogProviderList;
|
||||
use datafusion::catalog::{MemorySchemaProvider, Session};
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{internal_err, Result};
|
||||
use datafusion::datasource::{TableProvider, TableType, ViewTable};
|
||||
use datafusion::execution::session_state::SessionStateBuilder;
|
||||
@@ -36,7 +36,6 @@ use datafusion::optimizer::{AnalyzerRule, OptimizerRule};
|
||||
use datafusion::physical_plan::ExecutionPlan;
|
||||
use datafusion::prelude::SessionContext;
|
||||
use datafusion::scalar::ScalarValue;
|
||||
use datafusion::sql::TableReference;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub type SessionPropertiesRef = Arc<HashMap<String, Option<String>>>;
|
||||
@@ -429,10 +428,6 @@ impl WrenDataSource {
|
||||
|
||||
#[async_trait]
|
||||
impl TableProvider for WrenDataSource {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn schema(&self) -> SchemaRef {
|
||||
self.schema.clone()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use datafusion::logical_expr::{
|
||||
Signature, TypeSignature, Volatility, WindowUDFImpl,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
@@ -288,10 +287,6 @@ fn build_document(func: &RemoteFunction) -> Documentation {
|
||||
}
|
||||
|
||||
impl ScalarUDFImpl for ByPassScalarUDF {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@@ -389,10 +384,6 @@ impl From<RemoteFunction> for ByPassAggregateUDF {
|
||||
}
|
||||
|
||||
impl AggregateUDFImpl for ByPassAggregateUDF {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@@ -470,10 +461,6 @@ impl From<RemoteFunction> for ByPassWindowFunction {
|
||||
}
|
||||
|
||||
impl WindowUDFImpl for ByPassWindowFunction {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::any::Any;
|
||||
|
||||
use datafusion::arrow::datatypes::DataType;
|
||||
use datafusion::common::not_impl_err;
|
||||
use datafusion::common::types::{
|
||||
@@ -53,10 +51,6 @@ impl ToCharFunc {
|
||||
}
|
||||
|
||||
impl ScalarUDFImpl for ToCharFunc {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"to_char"
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Display;
|
||||
use std::sync::Arc;
|
||||
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{internal_err, plan_err, Column};
|
||||
use datafusion::error::Result;
|
||||
use datafusion::sql::TableReference;
|
||||
use petgraph::Graph;
|
||||
|
||||
use crate::logical_plan::utils::from_qualified_name;
|
||||
@@ -388,9 +388,9 @@ fn get_dataset_link_revers_if_need(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{Column, Spans};
|
||||
use datafusion::error::Result;
|
||||
use datafusion::sql::TableReference;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::mdl::utils::{dequote_identifier, quoted, to_field};
|
||||
use crate::DataFusionError;
|
||||
use context::SessionPropertiesRef;
|
||||
use datafusion::arrow::datatypes::Field;
|
||||
use datafusion::common::TableReference;
|
||||
use datafusion::common::{internal_datafusion_err, plan_err};
|
||||
use datafusion::datasource::TableProvider;
|
||||
use datafusion::error::Result;
|
||||
@@ -24,7 +25,6 @@ use datafusion::sql::parser::DFParser;
|
||||
use datafusion::sql::sqlparser::ast::{Expr, ExprWithAlias, Ident};
|
||||
use datafusion::sql::sqlparser::dialect::dialect_from_str;
|
||||
use datafusion::sql::unparser::Unparser;
|
||||
use datafusion::sql::TableReference;
|
||||
pub use dataset::Dataset;
|
||||
use dialect::WrenDialect;
|
||||
use log::{debug, info, warn};
|
||||
@@ -944,6 +944,56 @@ mod test {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_in_list_mixed_string_numeric_prefers_string() -> Result<()> {
|
||||
let manifest = ManifestBuilder::new()
|
||||
.catalog("wren")
|
||||
.schema("test")
|
||||
.model(
|
||||
ModelBuilder::new("customer")
|
||||
.table_reference("customer")
|
||||
.column(ColumnBuilder::new("c_custkey", "int").build())
|
||||
.column(ColumnBuilder::new("c_name", "varchar").build())
|
||||
.build(),
|
||||
)
|
||||
.build();
|
||||
let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(
|
||||
manifest,
|
||||
Arc::new(HashMap::default()),
|
||||
Mode::Unparse,
|
||||
)?);
|
||||
let ctx = create_wren_ctx(None, analyzed_mdl.wren_mdl().data_source().as_ref());
|
||||
|
||||
let sql = "select c_custkey from customer where c_name in (1, 2)";
|
||||
let actual = mdl::transform_sql_with_ctx(
|
||||
&ctx,
|
||||
Arc::clone(&analyzed_mdl),
|
||||
&[],
|
||||
Arc::new(HashMap::new()),
|
||||
sql,
|
||||
)
|
||||
.await?;
|
||||
assert_snapshot!(actual,
|
||||
@"SELECT customer.c_custkey FROM (SELECT customer.c_custkey, customer.c_name FROM \
|
||||
(SELECT __source.c_custkey AS c_custkey, __source.c_name AS c_name FROM customer AS __source) AS customer) AS customer \
|
||||
WHERE customer.c_name IN (CAST(1 AS VARCHAR), CAST(2 AS VARCHAR))");
|
||||
|
||||
let sql = "select c_custkey from customer where c_custkey in ('1', '2')";
|
||||
let actual = mdl::transform_sql_with_ctx(
|
||||
&ctx,
|
||||
Arc::clone(&analyzed_mdl),
|
||||
&[],
|
||||
Arc::new(HashMap::new()),
|
||||
sql,
|
||||
)
|
||||
.await?;
|
||||
assert_snapshot!(actual,
|
||||
@"SELECT customer.c_custkey FROM (SELECT customer.c_custkey FROM \
|
||||
(SELECT __source.c_custkey AS c_custkey FROM customer AS __source) AS customer) AS customer \
|
||||
WHERE CAST(customer.c_custkey AS VARCHAR) IN ('1', '2')");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_remote_function() -> Result<()> {
|
||||
env_logger::init();
|
||||
@@ -1210,7 +1260,10 @@ mod test {
|
||||
.await.map_err(|e| {
|
||||
assert_snapshot!(
|
||||
e.to_string(),
|
||||
@"Schema error: No field named \"名字\". Valid fields are wren.test.artist.\"串接名字\"."
|
||||
@r#"
|
||||
Schema error: No field named "名字". Did you mean 'wren.test.artist."串接名字"'?
|
||||
Valid fields are wren.test.artist."串接名字".
|
||||
"#
|
||||
)
|
||||
});
|
||||
Ok(())
|
||||
@@ -3076,7 +3129,10 @@ mod test {
|
||||
panic!("whitelist_name is hidden, it should not be selected directly")
|
||||
}
|
||||
Err(e) => {
|
||||
assert_snapshot!(e.to_string(), @"Schema error: No field named whitelist_name. Valid fields are customer.c_custkey, customer.mock_id.")
|
||||
assert_snapshot!(e.to_string(), @r"
|
||||
Schema error: No field named whitelist_name.
|
||||
Valid fields are customer.c_custkey, customer.mock_id.
|
||||
")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3227,7 +3283,10 @@ mod test {
|
||||
Err(e) => {
|
||||
assert_snapshot!(
|
||||
e.to_string(),
|
||||
@"Schema error: No field named c_name. Valid fields are customer.c_custkey."
|
||||
@r"
|
||||
Schema error: No field named c_name.
|
||||
Valid fields are customer.c_custkey.
|
||||
"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5235,7 +5294,7 @@ mod test {
|
||||
use datafusion::catalog::memory::MemoryCatalogProvider;
|
||||
use datafusion::catalog::CatalogProvider;
|
||||
use datafusion::catalog::SchemaProvider;
|
||||
use datafusion::sql::TableReference;
|
||||
use datafusion::common::TableReference;
|
||||
use std::sync::Barrier;
|
||||
use std::thread;
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ select "Customer_id" from wrenai.public."Orders" where exists (select 1 from wre
|
||||
04eafb40a16989307464f27f1fed8907
|
||||
0732c0881c70ebcda536a4b14e9db106
|
||||
|
||||
query RIITRTTT
|
||||
query RIITRTTT rowsort
|
||||
select * from "Order_items" where "Order_id" in ('03c83b31dbc387f83f1b5579b53182fb', '08cbb1d4cd574b126569b208fd4b26ea')
|
||||
----
|
||||
14.68 1 1 03c83b31dbc387f83f1b5579b53182fb 119.8 a04087ab6a96ffa041f8a2701a72b616 2023/1/15 7:26 CA
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
query ?IR
|
||||
query TIR
|
||||
select
|
||||
cntrycode,
|
||||
count(*) as numcust,
|
||||
|
||||
Reference in New Issue
Block a user