From c779379cdb34bc909fc9aac0f6aea977302eec79 Mon Sep 17 00:00:00 2001 From: jackylee-ch Date: Sun, 6 Sep 2026 23:58:50 +0800 Subject: [PATCH 1/2] fix: reject groups accumulator for distinct bitwise aggregates --- .../functions-aggregate/src/bit_and_or_xor.rs | 4 ++-- .../sqllogictest/test_files/aggregate.slt | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/datafusion/functions-aggregate/src/bit_and_or_xor.rs b/datafusion/functions-aggregate/src/bit_and_or_xor.rs index d730a6c1cb3eb..2829c0a881817 100644 --- a/datafusion/functions-aggregate/src/bit_and_or_xor.rs +++ b/datafusion/functions-aggregate/src/bit_and_or_xor.rs @@ -290,8 +290,8 @@ impl AggregateUDFImpl for BitwiseOperation { } } - fn groups_accumulator_supported(&self, _args: AccumulatorArgs) -> bool { - true + fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool { + !args.is_distinct } fn create_groups_accumulator( diff --git a/datafusion/sqllogictest/test_files/aggregate.slt b/datafusion/sqllogictest/test_files/aggregate.slt index 565298217617b..365074270d0ca 100644 --- a/datafusion/sqllogictest/test_files/aggregate.slt +++ b/datafusion/sqllogictest/test_files/aggregate.slt @@ -5123,6 +5123,28 @@ ORDER BY tag 33 11 NULL 33 11 NULL 33 11 NULL B +# bit_xor(DISTINCT) with GROUP BY must not use the non-distinct groups +# accumulator: for tag A, 5 ^ 5 ^ 9 = 9 but 5 ^ 9 = 12. +statement ok +create table bit_distinct (c SMALLINT, tag varchar) as values + (5, 'A'), (5, 'A'), (9, 'A'), (33, 'B'), (33, 'B'); + +query IIT +SELECT bit_xor(DISTINCT c), count(c), tag FROM bit_distinct GROUP BY tag ORDER BY tag; +---- +12 3 A +33 2 B + +query IT +SELECT bit_xor(DISTINCT c) FILTER (WHERE c > 4), tag FROM bit_distinct GROUP BY tag ORDER BY tag; +---- +12 A +33 B + +statement ok +drop table bit_distinct; + + # bit_and_i32 statement ok create table t (c int) as values (4), (7), (15); From 423078155eee54289d94f57c7162c644ce77a54e Mon Sep 17 00:00:00 2001 From: jackylee-ch Date: Mon, 7 Sep 2026 21:31:32 +0800 Subject: [PATCH 2/2] fix: limit the groups accumulator rejection to bit_xor(DISTINCT) AND and OR are idempotent, so DISTINCT does not change their result and they keep the vectorized path. --- datafusion/functions-aggregate/src/bit_and_or_xor.rs | 3 ++- datafusion/sqllogictest/test_files/aggregate.slt | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/datafusion/functions-aggregate/src/bit_and_or_xor.rs b/datafusion/functions-aggregate/src/bit_and_or_xor.rs index 2829c0a881817..92212b328ee7d 100644 --- a/datafusion/functions-aggregate/src/bit_and_or_xor.rs +++ b/datafusion/functions-aggregate/src/bit_and_or_xor.rs @@ -291,7 +291,8 @@ impl AggregateUDFImpl for BitwiseOperation { } fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool { - !args.is_distinct + // DISTINCT only changes the result of XOR; AND and OR are idempotent + !(args.is_distinct && self.operation == BitwiseOperationType::Xor) } fn create_groups_accumulator( diff --git a/datafusion/sqllogictest/test_files/aggregate.slt b/datafusion/sqllogictest/test_files/aggregate.slt index 365074270d0ca..1dfdf2e989b66 100644 --- a/datafusion/sqllogictest/test_files/aggregate.slt +++ b/datafusion/sqllogictest/test_files/aggregate.slt @@ -5124,7 +5124,8 @@ ORDER BY tag # bit_xor(DISTINCT) with GROUP BY must not use the non-distinct groups -# accumulator: for tag A, 5 ^ 5 ^ 9 = 9 but 5 ^ 9 = 12. +# accumulator: for tag A, 5 ^ 5 ^ 9 = 9 but 5 ^ 9 = 12. AND and OR are +# idempotent, so they keep the groups accumulator. statement ok create table bit_distinct (c SMALLINT, tag varchar) as values (5, 'A'), (5, 'A'), (9, 'A'), (33, 'B'), (33, 'B'); @@ -5141,6 +5142,12 @@ SELECT bit_xor(DISTINCT c) FILTER (WHERE c > 4), tag FROM bit_distinct GROUP BY 12 A 33 B +query IIIT +SELECT bit_and(DISTINCT c), bit_or(DISTINCT c), count(c), tag FROM bit_distinct GROUP BY tag ORDER BY tag; +---- +1 13 3 A +33 33 2 B + statement ok drop table bit_distinct;