mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-13 15:32:36 +00:00
feat!: upgrade DataFusion to 55 (#8555)
* feat!: upgrade DataFusion dependencies to 55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * refactor: migrate DataFusion 55 APIs Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: preserve table function planning behavior Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: preserve PostgreSQL query compatibility Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: preserve distributed execution plan behavior Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: cover DataFusion 55 behavior regressions Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: update DataFusion 55 SQLness expectations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: complete DataFusion 55 test API migration Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: address DataFusion 55 CI regressions Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: address remaining DataFusion 55 regressions Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: adapt latest base code to DataFusion 55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: normalize environment-specific DataFusion 55 plans Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: align final DataFusion 55 expectations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: isolate DataFusion 55 regression cases Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: preserve empty result schema in timestamp widening Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: preserve JSON source column order Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * chore: use released DataFusion 55 integrations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: adapt latest execution plan mock to DataFusion 55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: pin DataFusion recursive schema and date repairs Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix(promql): align dictionary temporality match keys Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: retain Greptime DataFusion fork behaviors on version 55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: restore ordinary function error expectations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: refresh distributed count compatibility plan Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix(query): adapt last-row cast hint to DataFusion 55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: refresh instant last-row empty results for Arrow 59 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * style: simplify DataFusion expression visitor imports Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: restore sorting and PostgreSQL column-order assertions Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix(function): restore primitive numeric coercion signatures Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * refactor(function): share geo integer signature types Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: cover timestamp widening overflow boundaries Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: fix decimal coercion regression imports Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix(function): preserve scalar count_hash NULL state semantics Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: simplify decimal clamp case type inference Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: retain historical count_hash wrapper result Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: restore timestamp widening equality and IN pruning Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: carry upstream aggregate dynamic filter correctness fix Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: carry upstream null and predicate simplification fixes Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: restore baseline JSON ordering expectations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: restore histogram JSON ordering expectations Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: refresh empty PromQL range result schemas Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: align native timestamp plan with DF55 decimal display Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: refresh native timestamp SQLness results for DF55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: regenerate NULL sample empty result headers for DF55 Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: use DF55 child replacement API in timestamp regressions Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: expose pushed scan dynamic filters to DF55 producers Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: encode string-backed PostgreSQL OID aliases in binary results Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: verify REGPROC binary and text over PostgreSQL protocol Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: register real PostgreSQL catalogs in server fixtures Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: complete DF55 expression inventories for custom query plans Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: correct RangeSelect expression fixture and column identities Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * ci: wait for Kafka WAL helper deployment rollout Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * test: update custom storage empty result headers Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: require exact row counts in scan statistics Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> * fix: suppress deprecated partition_statistics warning in test Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> --------- Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> Co-authored-by: Ning Sun <sunng@protonmail.com>
This commit is contained in:
@@ -33,15 +33,15 @@ use datafusion::functions_aggregate::count::Count;
|
||||
use datafusion::functions_aggregate::min_max::{Max, Min};
|
||||
use datafusion::optimizer::AnalyzerRule;
|
||||
use datafusion::optimizer::analyzer::type_coercion::TypeCoercion;
|
||||
use datafusion::physical_planner::create_aggregate_expr_and_maybe_filter;
|
||||
use datafusion_common::{Column, ScalarValue};
|
||||
use datafusion_expr::expr::{AggregateFunction, AggregateFunctionParams};
|
||||
use datafusion_expr::function::StateFieldsArgs;
|
||||
use datafusion_expr::physical_planning_context::PhysicalPlanningContext;
|
||||
use datafusion_expr::{
|
||||
Accumulator, Aggregate, AggregateUDF, AggregateUDFImpl, EmitTo, Expr, ExprSchemable,
|
||||
GroupsAccumulator, LogicalPlan, Signature,
|
||||
};
|
||||
use datafusion_physical_expr::aggregate::AggregateFunctionExpr;
|
||||
use datafusion_physical_expr::aggregate::{AggregateFunctionExpr, LoweredAggregateBuilder};
|
||||
use datatypes::arrow::datatypes::{DataType, Field};
|
||||
|
||||
use crate::aggrs::aggr_wrapper::fix_order::FixStateUdafOrderingAnalyzer;
|
||||
@@ -210,12 +210,25 @@ impl StateMergeHelper {
|
||||
lower_aggr_exprs.push(expr);
|
||||
|
||||
// then create the merge function using the physical expression of the original aggregate function
|
||||
let (original_phy_expr, _filter, _ordering) = create_aggregate_expr_and_maybe_filter(
|
||||
let (name, human_display) = match aggr_expr {
|
||||
Expr::Alias(alias) => (alias.name.clone(), aggr_expr.human_display().to_string()),
|
||||
Expr::AggregateFunction(_) => (
|
||||
aggr_expr.schema_name().to_string(),
|
||||
aggr_expr.human_display().to_string(),
|
||||
),
|
||||
_ => unreachable!("aggregate expression was validated above"),
|
||||
};
|
||||
let original_phy_expr = LoweredAggregateBuilder::new(
|
||||
aggr_expr,
|
||||
aggr.input.schema(),
|
||||
aggr.input.schema().as_arrow(),
|
||||
&Default::default(),
|
||||
)?;
|
||||
&PhysicalPlanningContext::default(),
|
||||
)
|
||||
.with_name(name)
|
||||
.with_human_display(human_display)
|
||||
.build()?
|
||||
.aggregate;
|
||||
|
||||
let merge_func = MergeWrapper::new(
|
||||
(*aggr_func.func).clone(),
|
||||
@@ -371,9 +384,6 @@ impl AggregateUDFImpl for StateWrapper {
|
||||
Ok(Box::new(StateGroupsAccum::new(inner, state_type)?))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
@@ -443,7 +453,7 @@ impl AggregateUDFImpl for StateWrapper {
|
||||
&self,
|
||||
statistics_args: &datafusion_expr::StatisticsArgs,
|
||||
) -> Option<ScalarValue> {
|
||||
let inner = self.inner().inner().as_any();
|
||||
let inner = self.inner().inner();
|
||||
// only count/min/max need special handling here, for getting result from statistics
|
||||
// the result of count/min/max is also the result of count_state so can return directly
|
||||
let can_use_stat = inner.is::<Count>() || inner.is::<Max>() || inner.is::<Min>();
|
||||
@@ -572,11 +582,10 @@ impl GroupsAccumulator for StateGroupsAccum {
|
||||
&mut self,
|
||||
values: &[ArrayRef],
|
||||
group_indices: &[usize],
|
||||
opt_filter: Option<&BooleanArray>,
|
||||
total_num_groups: usize,
|
||||
) -> datafusion_common::Result<()> {
|
||||
self.inner
|
||||
.merge_batch(values, group_indices, opt_filter, total_num_groups)
|
||||
.merge_batch(values, group_indices, total_num_groups)
|
||||
}
|
||||
|
||||
fn evaluate(&mut self, emit_to: EmitTo) -> datafusion_common::Result<ArrayRef> {
|
||||
@@ -596,10 +605,6 @@ impl GroupsAccumulator for StateGroupsAccum {
|
||||
self.inner.convert_to_state(values, opt_filter)
|
||||
}
|
||||
|
||||
fn supports_convert_to_state(&self) -> bool {
|
||||
self.inner.supports_convert_to_state()
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.inner.size()
|
||||
}
|
||||
@@ -800,10 +805,6 @@ impl AggregateUDFImpl for DeltaMergeWrapper {
|
||||
}))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@@ -973,9 +974,6 @@ impl AggregateUDFImpl for MergeWrapper {
|
||||
Ok(Box::new(MergeAccum::new(inner_accum, &fields)))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
@@ -144,7 +144,6 @@ fn rewrite_expr(
|
||||
let Some(old_state_wrapper) = aggregate_function
|
||||
.func
|
||||
.inner()
|
||||
.as_any()
|
||||
.downcast_ref::<StateWrapper>()
|
||||
else {
|
||||
return Ok(Transformed::no(Expr::AggregateFunction(aggregate_function)));
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::any::Any;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::{Context, Poll};
|
||||
@@ -25,6 +24,7 @@ use arrow::record_batch::RecordBatch;
|
||||
use arrow_schema::SchemaRef;
|
||||
use common_telemetry::init_default_ut_logging;
|
||||
use datafusion::catalog::{Session, TableProvider};
|
||||
use datafusion::common::tree_node::TreeNodeRecursion;
|
||||
use datafusion::datasource::DefaultTableSource;
|
||||
use datafusion::execution::{RecordBatchStream, SendableRecordBatchStream, TaskContext};
|
||||
use datafusion::functions_aggregate::average::avg_udaf;
|
||||
@@ -39,16 +39,16 @@ use datafusion::physical_planner::{DefaultPhysicalPlanner, PhysicalPlanner};
|
||||
use datafusion::prelude::SessionContext;
|
||||
use datafusion_common::arrow::array::AsArray;
|
||||
use datafusion_common::arrow::datatypes::{Float64Type, UInt64Type};
|
||||
use datafusion_common::{Column, TableReference};
|
||||
use datafusion_common::{Column, Result, TableReference};
|
||||
use datafusion_expr::expr::{AggregateFunction, NullTreatment};
|
||||
use datafusion_expr::function::AccumulatorArgs;
|
||||
use datafusion_expr::{
|
||||
Aggregate, AggregateUDFImpl, ColumnarValue, Expr, LogicalPlan, ScalarFunctionArgs, SortExpr,
|
||||
TableScan, TypeSignature, lit,
|
||||
TableScanBuilder, TypeSignature, lit,
|
||||
};
|
||||
use datafusion_physical_expr::aggregate::AggregateExprBuilder;
|
||||
use datafusion_physical_expr::expressions::{Column as PhysicalColumn, col, lit as physical_lit};
|
||||
use datafusion_physical_expr::{EquivalenceProperties, Partitioning};
|
||||
use datafusion_physical_expr::{EquivalenceProperties, Partitioning, PhysicalExpr};
|
||||
use futures::{Stream, StreamExt as _};
|
||||
use hyperloglogplus::HyperLogLog;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -97,10 +97,6 @@ impl ExecutionPlan for MockInputExec {
|
||||
"MockInputExec"
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn properties(&self) -> &Arc<PlanProperties> {
|
||||
&self.properties
|
||||
}
|
||||
@@ -109,6 +105,13 @@ impl ExecutionPlan for MockInputExec {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn apply_expressions(
|
||||
&self,
|
||||
_f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
|
||||
) -> Result<TreeNodeRecursion> {
|
||||
Ok(TreeNodeRecursion::Continue)
|
||||
}
|
||||
|
||||
fn with_new_children(
|
||||
self: Arc<Self>,
|
||||
_children: Vec<Arc<dyn ExecutionPlan>>,
|
||||
@@ -203,10 +206,6 @@ impl Default for DummyTableProvider {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TableProvider for DummyTableProvider {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn schema(&self) -> Arc<arrow_schema::Schema> {
|
||||
self.schema.clone()
|
||||
}
|
||||
@@ -237,14 +236,12 @@ fn dummy_table_scan() -> LogicalPlan {
|
||||
let table_provider = Arc::new(DummyTableProvider::default());
|
||||
let table_source = DefaultTableSource::new(table_provider);
|
||||
LogicalPlan::TableScan(
|
||||
TableScan::try_new(
|
||||
TableReference::bare("Number"),
|
||||
Arc::new(table_source),
|
||||
None,
|
||||
vec![],
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
TableScanBuilder::new(TableReference::bare("Number"), Arc::new(table_source))
|
||||
.with_projection(None)
|
||||
.with_filters(vec![])
|
||||
.with_fetch(None)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -252,14 +249,12 @@ fn dummy_table_scan_with_ts() -> LogicalPlan {
|
||||
let table_provider = Arc::new(DummyTableProvider::with_ts(None));
|
||||
let table_source = DefaultTableSource::new(table_provider);
|
||||
LogicalPlan::TableScan(
|
||||
TableScan::try_new(
|
||||
TableReference::bare("Number"),
|
||||
Arc::new(table_source),
|
||||
None,
|
||||
vec![],
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
TableScanBuilder::new(TableReference::bare("Number"), Arc::new(table_source))
|
||||
.with_projection(None)
|
||||
.with_filters(vec![])
|
||||
.with_fetch(None)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -381,10 +376,7 @@ async fn test_sum_udaf() {
|
||||
.create_physical_plan(&res.lower_state, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
let mut state_accum = aggr_func_expr.create_accumulator().unwrap();
|
||||
|
||||
@@ -414,10 +406,7 @@ async fn test_sum_udaf() {
|
||||
.create_physical_plan(&res.upper_merge, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
let mut merge_accum = aggr_func_expr.create_accumulator().unwrap();
|
||||
|
||||
@@ -543,10 +532,7 @@ async fn test_avg_udaf() {
|
||||
.create_physical_plan(&coerced_aggr_state_plan, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
let mut state_accum = aggr_func_expr.create_accumulator().unwrap();
|
||||
|
||||
@@ -582,10 +568,7 @@ async fn test_avg_udaf() {
|
||||
.create_physical_plan(&res.upper_merge, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
|
||||
let mut merge_accum = aggr_func_expr.create_accumulator().unwrap();
|
||||
@@ -703,10 +686,7 @@ async fn test_last_value_order_by_udaf() {
|
||||
.create_physical_plan(&fixed_aggr_state_plan, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_state_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
|
||||
let merge_input_fields = vec![Arc::new(Field::new(
|
||||
@@ -796,10 +776,7 @@ async fn test_last_value_order_by_udaf() {
|
||||
.create_physical_plan(&res.upper_merge, &ctx.state())
|
||||
.await
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan
|
||||
.as_any()
|
||||
.downcast_ref::<AggregateExec>()
|
||||
.unwrap();
|
||||
let aggr_exec = phy_aggr_merge_plan.downcast_ref::<AggregateExec>().unwrap();
|
||||
let aggr_func_expr = &aggr_exec.aggr_expr()[0];
|
||||
|
||||
let mut merge_accum = aggr_func_expr.create_accumulator().unwrap();
|
||||
@@ -900,7 +877,7 @@ fn test_avg_state_groups_accumulator_state_merge_evaluate() {
|
||||
.update_batch(&merged_values, &merged_group_indices, None, 3)
|
||||
.unwrap();
|
||||
merged_accum
|
||||
.merge_batch(&source_state, &[1, 2, 0], None, 3)
|
||||
.merge_batch(&source_state, &[1, 2, 0], 3)
|
||||
.unwrap();
|
||||
|
||||
let result = merged_accum.evaluate(EmitTo::All).unwrap();
|
||||
@@ -1324,14 +1301,12 @@ async fn test_udaf_correct_eval_result() {
|
||||
);
|
||||
let table_source = DefaultTableSource::new(Arc::new(table_provider));
|
||||
let logical_plan = LogicalPlan::TableScan(
|
||||
TableScan::try_new(
|
||||
test_table_ref.clone(),
|
||||
Arc::new(table_source),
|
||||
None,
|
||||
vec![],
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
TableScanBuilder::new(test_table_ref.clone(), Arc::new(table_source))
|
||||
.with_projection(None)
|
||||
.with_filters(vec![])
|
||||
.with_fetch(None)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let args = case.args;
|
||||
|
||||
@@ -123,7 +123,6 @@ impl UddSketchState {
|
||||
|
||||
fn downcast_accumulator_args(args: AccumulatorArgs) -> DfResult<(u32, f64)> {
|
||||
let bucket_size = match args.exprs[0]
|
||||
.as_any()
|
||||
.downcast_ref::<Literal>()
|
||||
.map(|lit| lit.value())
|
||||
{
|
||||
@@ -140,7 +139,6 @@ fn downcast_accumulator_args(args: AccumulatorArgs) -> DfResult<(u32, f64)> {
|
||||
};
|
||||
|
||||
let error_rate = match args.exprs[1]
|
||||
.as_any()
|
||||
.downcast_ref::<Literal>()
|
||||
.map(|lit| lit.value())
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ use std::sync::Arc;
|
||||
use ahash::RandomState;
|
||||
use datafusion_common::cast::as_list_array;
|
||||
use datafusion_common::error::Result;
|
||||
use datafusion_common::hash_utils::create_hashes;
|
||||
use datafusion_common::hash_utils::create_hashes_with_hasher;
|
||||
use datafusion_common::utils::SingleRowListArrayBuilder;
|
||||
use datafusion_common::{ScalarValue, internal_err, not_impl_err};
|
||||
use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
|
||||
@@ -74,10 +74,6 @@ pub struct CountHash {
|
||||
}
|
||||
|
||||
impl AggregateUDFImpl for CountHash {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"count_hash"
|
||||
}
|
||||
@@ -208,7 +204,7 @@ impl GroupsAccumulator for CountHashGroupAccumulator {
|
||||
let array = &values[0];
|
||||
self.batch_hashes.clear();
|
||||
self.batch_hashes.resize(array.len(), 0);
|
||||
let hashes = create_hashes(
|
||||
let hashes = create_hashes_with_hasher(
|
||||
&[ArrayRef::clone(array)],
|
||||
&self.random_state,
|
||||
&mut self.batch_hashes,
|
||||
@@ -279,7 +275,6 @@ impl GroupsAccumulator for CountHashGroupAccumulator {
|
||||
&mut self,
|
||||
values: &[ArrayRef],
|
||||
group_indices: &[usize],
|
||||
_opt_filter: Option<&BooleanArray>,
|
||||
total_num_groups: usize,
|
||||
) -> Result<()> {
|
||||
assert_eq!(
|
||||
@@ -364,10 +359,6 @@ impl GroupsAccumulator for CountHashGroupAccumulator {
|
||||
Ok(vec![Arc::new(list_array)])
|
||||
}
|
||||
|
||||
fn supports_convert_to_state(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
// Base size of the struct
|
||||
let mut size = size_of::<Self>();
|
||||
@@ -423,7 +414,7 @@ impl Accumulator for CountHashAccumulator {
|
||||
|
||||
self.batch_hashes.clear();
|
||||
self.batch_hashes.resize(arr.len(), 0);
|
||||
let hashes = create_hashes(
|
||||
let hashes = create_hashes_with_hasher(
|
||||
&[ArrayRef::clone(arr)],
|
||||
&self.random_state,
|
||||
&mut self.batch_hashes,
|
||||
@@ -522,6 +513,28 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_hash_accumulator_typed_null_state_merge() -> Result<()> {
|
||||
let typed_nulls = Arc::new(Int32Array::from(vec![None, None])) as ArrayRef;
|
||||
|
||||
let mut fresh = create_test_accumulator();
|
||||
fresh.update_batch(&[typed_nulls])?;
|
||||
let fresh_state = fresh.state()?;
|
||||
assert_eq!(fresh.evaluate()?, ScalarValue::Int64(Some(1)));
|
||||
|
||||
let persisted_state = Arc::new(
|
||||
SingleRowListArrayBuilder::new(Arc::new(UInt64Array::from(vec![0])) as ArrayRef)
|
||||
.build_list_array(),
|
||||
) as ArrayRef;
|
||||
let mut restored = create_test_accumulator();
|
||||
restored.merge_batch(&[persisted_state])?;
|
||||
|
||||
assert_eq!(restored.evaluate()?, fresh.evaluate()?);
|
||||
assert_eq!(restored.state()?, fresh_state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_hash_accumulator_merge() -> Result<()> {
|
||||
// Accumulator 1
|
||||
@@ -622,7 +635,7 @@ mod tests {
|
||||
// We will merge acc1's group 0 into acc2's group 0
|
||||
// and acc1's group 1 into acc2's group 2
|
||||
let merge_group_indices = vec![0, 2];
|
||||
acc2.merge_batch(&state1, &merge_group_indices, None, 3)?;
|
||||
acc2.merge_batch(&state1, &merge_group_indices, 3)?;
|
||||
|
||||
let result_array = acc2.evaluate(EmitTo::All)?;
|
||||
let result = result_array.as_any().downcast_ref::<Int64Array>().unwrap();
|
||||
|
||||
@@ -23,6 +23,33 @@ use datatypes::types::cast::cast;
|
||||
use datatypes::value::ValueRef;
|
||||
use snafu::{OptionExt, ResultExt};
|
||||
|
||||
/// Integer types accepted by geospatial function signatures.
|
||||
pub(crate) const INTEGER_TYPES: &[DataType] = &[
|
||||
DataType::Int8,
|
||||
DataType::Int16,
|
||||
DataType::Int32,
|
||||
DataType::Int64,
|
||||
DataType::UInt8,
|
||||
DataType::UInt16,
|
||||
DataType::UInt32,
|
||||
DataType::UInt64,
|
||||
];
|
||||
|
||||
/// Legacy primitive numeric signature types; Decimal values are coerced to `Float64`.
|
||||
pub(crate) const NUMERICS: &[DataType] = &[
|
||||
DataType::Int8,
|
||||
DataType::Int16,
|
||||
DataType::Int32,
|
||||
DataType::Int64,
|
||||
DataType::UInt8,
|
||||
DataType::UInt16,
|
||||
DataType::UInt32,
|
||||
DataType::UInt64,
|
||||
DataType::Float16,
|
||||
DataType::Float32,
|
||||
DataType::Float64,
|
||||
];
|
||||
|
||||
/// Create a function signature with oneof signatures of interleaving two arguments.
|
||||
pub(crate) fn one_of_sigs2(args1: Vec<DataType>, args2: Vec<DataType>) -> Signature {
|
||||
let mut sigs = Vec::with_capacity(args1.len() * args2.len());
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
//! When IQR = 0 (constant quartiles), returns 0.0 if value is on the fence,
|
||||
//! or +inf if value is outside.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
@@ -32,11 +31,11 @@ use std::sync::Arc;
|
||||
use arrow::array::{Array, ArrayRef, Float64Array};
|
||||
use arrow::datatypes::{DataType, Field, FieldRef};
|
||||
use datafusion_common::{DataFusionError, Result, ScalarValue};
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{PartitionEvaluator, Signature, Volatility, WindowUDFImpl};
|
||||
use datafusion_functions_window_common::field::WindowUDFFieldArgs;
|
||||
use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
|
||||
|
||||
use crate::helper::NUMERICS;
|
||||
use crate::scalars::anomaly::utils::{cast_to_f64, collect_window_values, percentile_sorted};
|
||||
|
||||
/// Minimum valid samples for IQR (linear-interpolated Q1 != Q3 is possible at n >= 3).
|
||||
@@ -56,10 +55,6 @@ impl AnomalyScoreIqr {
|
||||
}
|
||||
|
||||
impl WindowUDFImpl for AnomalyScoreIqr {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"anomaly_score_iqr"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
//! When MAD = 0 (majority-constant window), returns 0.0 if value equals
|
||||
//! median, or +inf otherwise.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
@@ -28,11 +27,11 @@ use std::sync::Arc;
|
||||
use arrow::array::{Array, ArrayRef, Float64Array};
|
||||
use arrow::datatypes::{DataType, Field, FieldRef};
|
||||
use datafusion_common::{DataFusionError, Result, ScalarValue};
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{PartitionEvaluator, Signature, Volatility, WindowUDFImpl};
|
||||
use datafusion_functions_window_common::field::WindowUDFFieldArgs;
|
||||
use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
|
||||
|
||||
use crate::helper::NUMERICS;
|
||||
use crate::scalars::anomaly::utils::{
|
||||
anomaly_ratio, cast_to_f64, collect_window_values, median_f64,
|
||||
};
|
||||
@@ -57,10 +56,6 @@ impl AnomalyScoreMad {
|
||||
}
|
||||
|
||||
impl WindowUDFImpl for AnomalyScoreMad {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"anomaly_score_mad"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
//! When stddev = 0 (constant window), returns 0.0 if value equals mean,
|
||||
//! or +inf otherwise.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
@@ -27,11 +26,11 @@ use std::sync::Arc;
|
||||
use arrow::array::{Array, ArrayRef, Float64Array};
|
||||
use arrow::datatypes::{DataType, Field, FieldRef};
|
||||
use datafusion_common::{DataFusionError, Result, ScalarValue};
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{PartitionEvaluator, Signature, Volatility, WindowUDFImpl};
|
||||
use datafusion_functions_window_common::field::WindowUDFFieldArgs;
|
||||
use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
|
||||
|
||||
use crate::helper::NUMERICS;
|
||||
use crate::scalars::anomaly::utils::{anomaly_ratio, cast_to_f64, collect_window_values};
|
||||
|
||||
/// Minimum valid samples for zscore (stddev requires n >= 2).
|
||||
@@ -51,10 +50,6 @@ impl AnomalyScoreZscore {
|
||||
}
|
||||
|
||||
impl WindowUDFImpl for AnomalyScoreZscore {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"anomaly_score_zscore"
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ use datafusion::arrow::array::{Array, AsArray, ListBuilder, StringViewBuilder};
|
||||
use datafusion::arrow::datatypes::{DataType, Field, Float64Type, UInt8Type};
|
||||
use datafusion::logical_expr::ColumnarValue;
|
||||
use datafusion_common::DataFusionError;
|
||||
use datafusion_expr::type_coercion::aggregates::INTEGERS;
|
||||
use datafusion_expr::{ScalarFunctionArgs, Signature, TypeSignature, Volatility};
|
||||
use geohash::Coord;
|
||||
use snafu::ResultExt;
|
||||
|
||||
use crate::function::{Function, extract_args};
|
||||
use crate::helper::INTEGER_TYPES;
|
||||
use crate::scalars::geo::helpers;
|
||||
|
||||
fn ensure_resolution_usize(v: u8) -> datafusion_common::Result<usize> {
|
||||
@@ -49,7 +49,7 @@ impl Default for GeohashFunction {
|
||||
fn default() -> Self {
|
||||
let mut signatures = Vec::new();
|
||||
for coord_type in &[DataType::Float32, DataType::Float64] {
|
||||
for resolution_type in INTEGERS {
|
||||
for resolution_type in INTEGER_TYPES {
|
||||
signatures.push(TypeSignature::Exact(vec![
|
||||
// latitude
|
||||
coord_type.clone(),
|
||||
@@ -146,7 +146,7 @@ impl Default for GeohashNeighboursFunction {
|
||||
fn default() -> Self {
|
||||
let mut signatures = Vec::new();
|
||||
for coord_type in &[DataType::Float32, DataType::Float64] {
|
||||
for resolution_type in INTEGERS {
|
||||
for resolution_type in INTEGER_TYPES {
|
||||
signatures.push(TypeSignature::Exact(vec![
|
||||
// latitude
|
||||
coord_type.clone(),
|
||||
|
||||
@@ -26,7 +26,6 @@ use datafusion::arrow::compute;
|
||||
use datafusion::arrow::datatypes::{Float64Type, Int64Type, UInt8Type, UInt64Type};
|
||||
use datafusion::logical_expr::ColumnarValue;
|
||||
use datafusion_common::{DataFusionError, ScalarValue};
|
||||
use datafusion_expr::type_coercion::aggregates::INTEGERS;
|
||||
use datafusion_expr::{ScalarFunctionArgs, Signature, TypeSignature, Volatility};
|
||||
use datatypes::arrow::datatypes::{DataType, Field};
|
||||
use derive_more::Display;
|
||||
@@ -34,6 +33,7 @@ use h3o::{CellIndex, LatLng, Resolution};
|
||||
use snafu::prelude::*;
|
||||
|
||||
use crate::function::{Function, extract_args};
|
||||
use crate::helper::INTEGER_TYPES;
|
||||
use crate::scalars::geo::helpers;
|
||||
|
||||
static CELL_TYPES: LazyLock<Vec<DataType>> =
|
||||
@@ -42,11 +42,11 @@ static CELL_TYPES: LazyLock<Vec<DataType>> =
|
||||
static COORDINATE_TYPES: LazyLock<Vec<DataType>> =
|
||||
LazyLock::new(|| vec![DataType::Float32, DataType::Float64]);
|
||||
|
||||
static RESOLUTION_TYPES: &[DataType] = INTEGERS;
|
||||
static RESOLUTION_TYPES: &[DataType] = INTEGER_TYPES;
|
||||
|
||||
static DISTANCE_TYPES: &[DataType] = INTEGERS;
|
||||
static DISTANCE_TYPES: &[DataType] = INTEGER_TYPES;
|
||||
|
||||
static POSITION_TYPES: &[DataType] = INTEGERS;
|
||||
static POSITION_TYPES: &[DataType] = INTEGER_TYPES;
|
||||
|
||||
/// Function that returns [h3] encoding cellid for a given geospatial coordinate.
|
||||
///
|
||||
|
||||
@@ -25,6 +25,7 @@ use s2::latlng::LatLng;
|
||||
use snafu::ensure;
|
||||
|
||||
use crate::function::{Function, extract_args};
|
||||
use crate::helper::INTEGER_TYPES;
|
||||
use crate::scalars::geo::helpers;
|
||||
use crate::scalars::geo::helpers::ensure_and_coerce;
|
||||
|
||||
@@ -34,7 +35,7 @@ static CELL_TYPES: LazyLock<Vec<DataType>> =
|
||||
static COORDINATE_TYPES: LazyLock<Vec<DataType>> =
|
||||
LazyLock::new(|| vec![DataType::Float32, DataType::Float64]);
|
||||
|
||||
static LEVEL_TYPES: &[DataType] = datafusion_expr::type_coercion::aggregates::INTEGERS;
|
||||
static LEVEL_TYPES: &[DataType] = INTEGER_TYPES;
|
||||
|
||||
/// Function that returns [s2] encoding cellid for a given geospatial coordinate.
|
||||
///
|
||||
|
||||
@@ -532,7 +532,7 @@ mod tests {
|
||||
use datafusion_common::arrow::datatypes::{Float64Type, Int64Type};
|
||||
use datatypes::extension::json::Json2ExtensionType;
|
||||
use datatypes::types::parse_string_to_jsonb;
|
||||
use serde_json::json;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -595,6 +595,34 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn assert_json_or_string_eq(actual: Option<&str>, expected: Option<&str>) {
|
||||
let is_json_container = |value: &str| {
|
||||
matches!(
|
||||
serde_json::from_str::<Value>(value),
|
||||
Ok(Value::Object(_) | Value::Array(_))
|
||||
)
|
||||
};
|
||||
|
||||
match (actual, expected) {
|
||||
(Some(actual), Some(expected))
|
||||
if is_json_container(actual) || is_json_container(expected) =>
|
||||
{
|
||||
let actual_value = serde_json::from_str::<Value>(actual).unwrap_or_else(|error| {
|
||||
panic!("failed to parse actual JSON result {actual:?}: {error}")
|
||||
});
|
||||
let expected_value =
|
||||
serde_json::from_str::<Value>(expected).unwrap_or_else(|error| {
|
||||
panic!("failed to parse expected JSON result {expected:?}: {error}")
|
||||
});
|
||||
assert_eq!(
|
||||
actual_value, expected_value,
|
||||
"JSON result mismatch: actual {actual:?}, expected {expected:?}"
|
||||
);
|
||||
}
|
||||
_ => assert_eq!(actual, expected),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_get_int() {
|
||||
let json_get_int = JsonGetInt::default();
|
||||
@@ -895,7 +923,7 @@ mod tests {
|
||||
let result = result.as_string_view();
|
||||
assert_eq!(1, result.len());
|
||||
let actual = result.is_valid(0).then(|| result.value(0));
|
||||
assert_eq!(actual, expect);
|
||||
assert_json_or_string_eq(actual, expect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,7 +1061,7 @@ mod tests {
|
||||
let result = result.as_string_view();
|
||||
assert_eq!(1, result.len());
|
||||
let actual = result.is_valid(0).then(|| result.value(0));
|
||||
assert_eq!(actual, expect);
|
||||
assert_json_or_string_eq(actual, expect);
|
||||
}
|
||||
|
||||
let json_strings = [
|
||||
|
||||
@@ -59,10 +59,8 @@ impl FunctionRewrite for JsonGetRewriter {
|
||||
// json_get(column, path, <data_type>)
|
||||
// )
|
||||
fn inject_type_from_cast_expr(cast: Cast) -> Result<Transformed<Expr>> {
|
||||
let Cast {
|
||||
expr,
|
||||
mut data_type,
|
||||
} = cast;
|
||||
let Cast { expr, field } = cast;
|
||||
let mut data_type = field.data_type().clone();
|
||||
|
||||
let mut json_get = match *expr {
|
||||
Expr::ScalarFunction(f)
|
||||
@@ -73,7 +71,7 @@ fn inject_type_from_cast_expr(cast: Cast) -> Result<Transformed<Expr>> {
|
||||
expr => {
|
||||
return Ok(Transformed::no(Expr::Cast(Cast {
|
||||
expr: Box::new(expr),
|
||||
data_type,
|
||||
field,
|
||||
})));
|
||||
}
|
||||
};
|
||||
@@ -204,10 +202,7 @@ mod tests {
|
||||
});
|
||||
|
||||
// Create a cast expression: json_get(...)::int8
|
||||
let cast_expr = Expr::Cast(Cast {
|
||||
expr: Box::new(json_expr),
|
||||
data_type: DataType::Int8,
|
||||
});
|
||||
let cast_expr = Expr::Cast(Cast::new(Box::new(json_expr), DataType::Int8));
|
||||
|
||||
// Apply the rewriter
|
||||
let result = rewriter.rewrite(cast_expr, &schema, &config).unwrap();
|
||||
@@ -279,10 +274,7 @@ mod tests {
|
||||
|
||||
// Create an arrow cast function: cast(json_get(...), 'Int64')
|
||||
// Note: ArrowCastFunc doesn't exist in this codebase, so this test uses a simple cast instead
|
||||
let arrow_cast_expr = Expr::Cast(Cast {
|
||||
expr: Box::new(json_get_expr),
|
||||
data_type: DataType::Int64,
|
||||
});
|
||||
let arrow_cast_expr = Expr::Cast(Cast::new(Box::new(json_get_expr), DataType::Int64));
|
||||
|
||||
// Apply the rewriter
|
||||
let result = rewriter.rewrite(arrow_cast_expr, &schema, &config).unwrap();
|
||||
|
||||
@@ -24,6 +24,7 @@ use datafusion::execution::SessionStateBuilder;
|
||||
use datafusion::logical_expr::{self, ColumnarValue, Expr, Volatility};
|
||||
use datafusion::physical_planner::{DefaultPhysicalPlanner, PhysicalPlanner};
|
||||
use datafusion_common::DataFusionError;
|
||||
use datafusion_expr::physical_planning_context::PhysicalPlanningContext;
|
||||
use datafusion_expr::{ScalarFunctionArgs, Signature};
|
||||
use datatypes::arrow::array::RecordBatch;
|
||||
use datatypes::arrow::datatypes::{DataType, Field};
|
||||
@@ -110,8 +111,12 @@ impl MatchesFunction {
|
||||
let input_schema = Self::input_schema();
|
||||
let session_state = SessionStateBuilder::new().with_default_features().build();
|
||||
let planner = DefaultPhysicalPlanner::default();
|
||||
let physical_expr =
|
||||
planner.create_physical_expr(&like_expr, &input_schema, &session_state)?;
|
||||
let physical_expr = planner.create_physical_expr(
|
||||
&like_expr,
|
||||
&input_schema,
|
||||
&session_state,
|
||||
&PhysicalPlanningContext::default(),
|
||||
)?;
|
||||
|
||||
let arrow_schema = Arc::new(input_schema.as_arrow().clone());
|
||||
let input_record_batch = RecordBatch::try_new(arrow_schema, vec![data_array]).unwrap();
|
||||
|
||||
@@ -19,10 +19,10 @@ use datafusion::arrow::array::{Array, ArrayRef, AsArray, PrimitiveArray};
|
||||
use datafusion::arrow::datatypes::DataType as ArrowDataType;
|
||||
use datafusion::logical_expr::{ColumnarValue, Volatility};
|
||||
use datafusion_common::{DataFusionError, ScalarValue, utils};
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{ScalarFunctionArgs, Signature};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::helper::NUMERICS;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClampFunction {
|
||||
@@ -338,7 +338,9 @@ mod test {
|
||||
|
||||
use arrow_schema::Field;
|
||||
use datafusion_common::config::ConfigOptions;
|
||||
use datatypes::arrow::array::{ArrayRef, Float64Array, Int64Array, UInt64Array};
|
||||
use datatypes::arrow::array::{
|
||||
ArrayRef, Decimal128Array, Float64Array, Int64Array, UInt64Array,
|
||||
};
|
||||
use datatypes::arrow_array::StringArray;
|
||||
|
||||
use super::*;
|
||||
@@ -370,6 +372,79 @@ mod test {
|
||||
impl_test_eval!(ClampMinFunction);
|
||||
impl_test_eval!(ClampMaxFunction);
|
||||
|
||||
fn decimal_array(values: Vec<i128>) -> ColumnarValue {
|
||||
ColumnarValue::Array(Arc::new(
|
||||
Decimal128Array::from(values)
|
||||
.with_precision_and_scale(10, 2)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
fn decimal_scalar(value: i128) -> ColumnarValue {
|
||||
ColumnarValue::Scalar(ScalarValue::Decimal128(Some(value), 10, 2))
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn evaluate_decimal(
|
||||
function: &dyn Function,
|
||||
args: Vec<ColumnarValue>,
|
||||
) -> datafusion_common::Result<ArrayRef> {
|
||||
let input_types = args
|
||||
.iter()
|
||||
.map(ColumnarValue::data_type)
|
||||
.collect::<Vec<_>>();
|
||||
let planned_types = datafusion_expr::type_coercion::functions::data_types(
|
||||
function.name(),
|
||||
&input_types,
|
||||
function.signature(),
|
||||
)?;
|
||||
let args = args
|
||||
.into_iter()
|
||||
.zip(planned_types)
|
||||
.map(|(arg, planned_type)| arg.cast_to(&planned_type, None))
|
||||
.collect::<datafusion_common::Result<Vec<_>>>()?;
|
||||
function
|
||||
.invoke_with_args(ScalarFunctionArgs {
|
||||
args,
|
||||
arg_fields: vec![],
|
||||
number_rows: 3,
|
||||
return_field: Arc::new(Field::new("x", ArrowDataType::Float64, false)),
|
||||
config_options: Arc::new(ConfigOptions::new()),
|
||||
})
|
||||
.and_then(|value| value.to_array(3))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clamp_decimal_coercion_executes_as_float64() {
|
||||
let test_cases = [
|
||||
(
|
||||
Box::new(ClampFunction::default()) as Box<dyn Function>,
|
||||
vec![
|
||||
decimal_array(vec![100, 300, 500]),
|
||||
decimal_scalar(200),
|
||||
decimal_scalar(400),
|
||||
],
|
||||
vec![2.0, 3.0, 4.0],
|
||||
),
|
||||
(
|
||||
Box::new(ClampMinFunction::default()),
|
||||
vec![decimal_array(vec![100, 300, 500]), decimal_scalar(200)],
|
||||
vec![2.0, 3.0, 5.0],
|
||||
),
|
||||
(
|
||||
Box::new(ClampMaxFunction::default()),
|
||||
vec![decimal_array(vec![100, 300, 500]), decimal_scalar(200)],
|
||||
vec![1.0, 2.0, 2.0],
|
||||
),
|
||||
];
|
||||
|
||||
for (function, args, expected) in test_cases {
|
||||
let result = evaluate_decimal(function.as_ref(), args).unwrap();
|
||||
let expected: ArrayRef = Arc::new(Float64Array::from(expected));
|
||||
assert_eq!(expected.as_ref(), result.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clamp_i64() {
|
||||
let inputs = [
|
||||
|
||||
@@ -18,10 +18,10 @@ use std::fmt::Display;
|
||||
use datafusion_common::arrow::compute;
|
||||
use datafusion_common::arrow::compute::kernels::numeric;
|
||||
use datafusion_common::arrow::datatypes::DataType;
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, Signature, Volatility};
|
||||
|
||||
use crate::function::{Function, extract_args};
|
||||
use crate::helper::NUMERICS;
|
||||
|
||||
const NAME: &str = "mod";
|
||||
|
||||
@@ -90,12 +90,74 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use arrow_schema::Field;
|
||||
use datafusion_common::ScalarValue;
|
||||
use datafusion_common::arrow::array::{
|
||||
AsArray, Float64Array, Int32Array, StringViewArray, UInt32Array,
|
||||
AsArray, Decimal128Array, Float64Array, Int32Array, StringViewArray, UInt32Array,
|
||||
};
|
||||
use datafusion_common::arrow::datatypes::{Float64Type, Int64Type, UInt64Type};
|
||||
|
||||
use super::*;
|
||||
fn decimal_array(values: Vec<i128>) -> ColumnarValue {
|
||||
ColumnarValue::Array(Arc::new(
|
||||
Decimal128Array::from(values)
|
||||
.with_precision_and_scale(10, 2)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
fn decimal_scalar(value: i128) -> ColumnarValue {
|
||||
ColumnarValue::Scalar(ScalarValue::Decimal128(Some(value), 10, 2))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn modulo_decimal_coercion_executes_as_float64() {
|
||||
let function = ModuloFunction::default();
|
||||
let test_cases = [
|
||||
(
|
||||
vec![decimal_array(vec![500, 600]), decimal_scalar(200)],
|
||||
vec![1.0, 0.0],
|
||||
),
|
||||
(
|
||||
vec![decimal_scalar(500), decimal_array(vec![200, 300])],
|
||||
vec![1.0, 2.0],
|
||||
),
|
||||
];
|
||||
|
||||
for (args, expected) in test_cases {
|
||||
let input_types = args
|
||||
.iter()
|
||||
.map(ColumnarValue::data_type)
|
||||
.collect::<Vec<_>>();
|
||||
let planned_types = datafusion_expr::type_coercion::functions::data_types(
|
||||
function.name(),
|
||||
&input_types,
|
||||
function.signature(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(vec![DataType::Float64; 2], planned_types);
|
||||
let args = args
|
||||
.into_iter()
|
||||
.zip(planned_types)
|
||||
.map(|(arg, planned_type)| arg.cast_to(&planned_type, None))
|
||||
.collect::<datafusion_common::Result<Vec<_>>>()
|
||||
.unwrap();
|
||||
let result = function
|
||||
.invoke_with_args(ScalarFunctionArgs {
|
||||
args,
|
||||
arg_fields: vec![],
|
||||
number_rows: 2,
|
||||
return_field: Arc::new(Field::new("x", DataType::Float64, false)),
|
||||
config_options: Arc::new(Default::default()),
|
||||
})
|
||||
.unwrap()
|
||||
.to_array(2)
|
||||
.unwrap();
|
||||
let result = result.as_primitive::<Float64Type>();
|
||||
assert_eq!(&Float64Array::from(expected), result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mod_function_signed() {
|
||||
let function = ModuloFunction::default();
|
||||
|
||||
@@ -18,11 +18,11 @@ use common_query::error;
|
||||
use datafusion::arrow::compute::kernels::numeric;
|
||||
use datafusion_common::arrow::compute::kernels::cast;
|
||||
use datafusion_common::arrow::datatypes::DataType;
|
||||
use datafusion_expr::type_coercion::aggregates::NUMERICS;
|
||||
use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, Signature, Volatility};
|
||||
use snafu::ResultExt;
|
||||
|
||||
use crate::function::{Function, extract_args};
|
||||
use crate::helper::NUMERICS;
|
||||
|
||||
/// generates rates from a sequence of adjacent data points.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -96,12 +96,13 @@ mod tests {
|
||||
let rate = RateFunction::default();
|
||||
assert_eq!("rate", rate.name());
|
||||
assert_eq!(DataType::Float64, rate.return_type(&[]).unwrap());
|
||||
assert!(matches!(rate.signature(),
|
||||
Signature {
|
||||
type_signature: TypeSignature::Uniform(2, valid_types),
|
||||
volatility: Volatility::Immutable,
|
||||
..
|
||||
} if valid_types == NUMERICS
|
||||
assert!(matches!(
|
||||
rate.signature(),
|
||||
Signature {
|
||||
type_signature: TypeSignature::Uniform(2, valid_types),
|
||||
volatility: Volatility::Immutable,
|
||||
..
|
||||
} if valid_types == NUMERICS
|
||||
));
|
||||
let values = vec![1.0, 3.0, 6.0];
|
||||
let ts = vec![0, 1, 2];
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
@@ -49,10 +48,6 @@ impl Hash for ScalarUdf {
|
||||
}
|
||||
|
||||
impl ScalarUDFImpl for ScalarUdf {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
self.function.name()
|
||||
}
|
||||
|
||||
@@ -389,6 +389,9 @@ impl PGCatalogFunction {
|
||||
registry.register(pg_catalog::create_pg_get_partition_ancestors_udf());
|
||||
registry.register(pg_catalog::quote_ident_udf::create_quote_ident_udf());
|
||||
registry.register(pg_catalog::quote_ident_udf::create_parse_ident_udf());
|
||||
// Register array bound UDFs used by pg_catalog views.
|
||||
registry.register(pg_catalog::array_bounds_udf::create_array_upper_udf());
|
||||
registry.register(pg_catalog::array_bounds_udf::create_array_lower_udf());
|
||||
registry.register_scalar(ObjDescriptionFunction::new());
|
||||
registry.register_scalar(ColDescriptionFunction::new());
|
||||
registry.register_scalar(ShobjDescriptionFunction::new());
|
||||
|
||||
Reference in New Issue
Block a user