Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion vortex-duckdb/src/convert/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,12 @@ impl Display for PushedAggregate {

impl PushedAggregate {
pub fn build(self, dtype: DType) -> VortexResult<Box<dyn DynAccumulator>> {
let opts = NumericalAggregateOpts::default();
let opts = if dtype.is_float() {
// duckdb treats nan as a real value, vortex defaults skip nans
NumericalAggregateOpts::include_nans()
} else {
NumericalAggregateOpts::default()
};
Ok(match self {
Self::Min => Box::new(Accumulator::try_new(Min, opts, dtype)?),
Self::Max => Box::new(Accumulator::try_new(Max, opts, dtype)?),
Expand Down
12 changes: 6 additions & 6 deletions vortex-duckdb/src/table_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,14 +672,14 @@ pub fn pushdown_projection_aggregates(
return Ok(false);
};

// TODO(myrrc): DuckDB treats NaN as a normal value ordered greater than
// everything which is substandard. vortex aggregations just skip nan.
// don't push aggregations on floats until resolved
// duckdb's min() returns nan only when every value is nan.
// vortex's min() either ignores or counts nans.
// See slt/duckdb/nan_aggregates.slt.
let projection_id_usize: usize = projection_id.as_();
if bind_data.column_fields[projection_id_usize]
.dtype
.is_float()
if aggregate == PushedAggregate::Min
&& bind_data.column_fields[projection_id_usize]
.dtype
.is_float()
{
return Ok(false);
}
Expand Down
6 changes: 0 additions & 6 deletions vortex-sqllogictest/slt/aggregates_edge_cases.slt
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ SELECT count(x), sum(x), min(x), max(x), avg(x) FROM '${WORK_DIR}/f-posinf.vorte
----
3 Infinity 1 Infinity Infinity

onlyif duckdb
query TT
EXPLAIN SELECT sum(x) FROM '${WORK_DIR}/f-null.vortex';
----
<REGEX>:.*UNGROUPED_AGGREGATE.*

query IRRRR
SELECT count(x), sum(x), min(x), max(x), avg(x) FROM '${WORK_DIR}/f-null.vortex';
----
Expand Down
156 changes: 148 additions & 8 deletions vortex-sqllogictest/slt/duckdb/nan_aggregates.slt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright the Vortex contributors

# Vortex aggregate pushdown must match DuckDB's NaN semantics
# Vortex aggregate pushdown must match DuckDB's float semantics:
# NULL is skipped, NaN is a real value

include ../setup.slt.no

Expand All @@ -17,22 +18,161 @@ COPY (

query TT
EXPLAIN
SELECT count(*), count(x), sum(x), min(x), max(x), avg(x)
SELECT count(*), count(x), sum(x), max(x), avg(x)
FROM '${WORK_DIR}/nan-agg.vortex';
----
<!REGEX>:.*UNGROUPED_AGGREGATE.*

query IIRRR
SELECT count(*), count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/nan-agg.vortex';
----
4 3 NaN NaN NaN

query TT
EXPLAIN
SELECT min(x) FROM '${WORK_DIR}/nan-agg.vortex';
----
<REGEX>:.*UNGROUPED_AGGREGATE.*

query II
SELECT count(*), count(x) FROM '${WORK_DIR}/nan-agg.vortex';
query R
SELECT min(x) FROM '${WORK_DIR}/nan-agg.vortex';
----
4 3
1

query RR
SELECT sum(x), avg(x) FROM '${WORK_DIR}/nan-agg.vortex';
query TT
EXPLAIN
SELECT min(x), max(x) FROM '${WORK_DIR}/nan-agg.vortex';
----
NaN NaN
<REGEX>:.*UNGROUPED_AGGREGATE.*

query RR
SELECT min(x), max(x) FROM '${WORK_DIR}/nan-agg.vortex';
----
1 NaN

statement ok
COPY (
SELECT * FROM (VALUES ('-nan'::DOUBLE), (1.0), (2.0)) AS t(x)
) TO '${WORK_DIR}/neg-nan.vortex';

query IRR
SELECT count(x), sum(x), max(x) FROM '${WORK_DIR}/neg-nan.vortex';
----
3 NaN NaN

statement ok
COPY (
SELECT * FROM (VALUES ('nan'::DOUBLE), ('nan'::DOUBLE)) AS t(x)
) TO '${WORK_DIR}/all-nan.vortex';

query IRRR
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/all-nan.vortex';
----
2 NaN NaN NaN

query R
SELECT min(x) FROM '${WORK_DIR}/all-nan.vortex';
----
NaN

statement ok
COPY (
SELECT * FROM (VALUES
('inf'::DOUBLE),
('-inf'::DOUBLE),
(0.0),
(CAST(NULL AS DOUBLE))
) AS t(x)
) TO '${WORK_DIR}/mixed-inf.vortex';

query IRRR
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/mixed-inf.vortex';
----
3 NaN Infinity NaN

query R
SELECT min(x) FROM '${WORK_DIR}/mixed-inf.vortex';
----
-Infinity

statement ok
COPY (
SELECT * FROM (VALUES ('inf'::DOUBLE), (1.0), (2.0)) AS t(x)
) TO '${WORK_DIR}/pos-inf.vortex';

query RRR
SELECT sum(x), max(x), avg(x) FROM '${WORK_DIR}/pos-inf.vortex';
----
Infinity Infinity Infinity

statement ok
COPY (
SELECT * FROM (VALUES ('-inf'::DOUBLE), (1.0), (2.0)) AS t(x)
) TO '${WORK_DIR}/neg-inf.vortex';

query RR
SELECT sum(x), avg(x) FROM '${WORK_DIR}/neg-inf.vortex';
----
-Infinity -Infinity

statement ok
COPY (
SELECT * FROM (VALUES (CAST(NULL AS DOUBLE)), (CAST(NULL AS DOUBLE))) AS t(x)
) TO '${WORK_DIR}/all-null.vortex';

query IRR
SELECT count(x), sum(x), max(x) FROM '${WORK_DIR}/all-null.vortex';
----
0 0 NULL

query R
SELECT min(x) FROM '${WORK_DIR}/all-null.vortex';
----
NULL

statement ok
COPY (SELECT i::DOUBLE AS x FROM generate_series(1, 2000) t(i)) TO '${WORK_DIR}/big-plain.vortex';

query TT
EXPLAIN
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/big-plain.vortex';
----
<!REGEX>:.*UNGROUPED_AGGREGATE.*

query IRRR
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/big-plain.vortex';
----
2000 2001000 2000 1000.5

query R
SELECT min(x) FROM '${WORK_DIR}/big-plain.vortex';
----
1

statement ok
COPY (SELECT CASE WHEN i = 1000 THEN 'nan'::DOUBLE ELSE i::DOUBLE END AS x
FROM generate_series(1, 2000) t(i)) TO '${WORK_DIR}/big-nan.vortex';

query IRRR
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/big-nan.vortex';
----
2000 NaN NaN NaN

query R
SELECT min(x) FROM '${WORK_DIR}/big-nan.vortex';
----
1

statement ok
COPY (SELECT CASE WHEN i = 1 THEN 'inf'::DOUBLE ELSE i::DOUBLE END AS x
FROM generate_series(1, 2000) t(i)) TO '${WORK_DIR}/big-inf.vortex';

query IRRR
SELECT count(x), sum(x), max(x), avg(x) FROM '${WORK_DIR}/big-inf.vortex';
----
2000 Infinity Infinity Infinity

query R
SELECT min(x) FROM '${WORK_DIR}/big-inf.vortex';
----
2
Loading