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:
Jax Liu
2026-09-24 16:12:27 +08:00
co-authored by Claude Opus 5.5
parent d26ab6af5e
commit ced74b4b1d
24 changed files with 837 additions and 612 deletions
+3 -3
View File
@@ -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.
+319 -266
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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"
+3
View File
@@ -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():
+6 -6
View File
@@ -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)
+327 -264
View File
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -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
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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(
&current_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 -6
View File
@@ -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 -2
View File
@@ -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;
+64 -5
View File
@@ -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,