You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/09/17 02:19:31 UTC
(datafusion-comet) branch main updated: feat: add BinaryType support for SortMergeJoin (#5928)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git
The following commit(s) were added to refs/heads/main by this push:
new 58ab5f618e feat: add BinaryType support for SortMergeJoin (#5928)
58ab5f618e is described below
commit 58ab5f618e1e715dee06165424672fdd820cafe4
Author: Alexey <xh...@gmail.com>
AuthorDate: Wed Sep 16 23:36:56 2026 +0000
feat: add BinaryType support for SortMergeJoin (#5928)
* add BinaryType support for SortMergeJoin
* Update spark/src/main/scala/org/apache/spark/sql/comet/operators.scala
Co-authored-by: Peter Lee <pe...@gmail.com>
---------
Co-authored-by: Peter Lee <pe...@gmail.com>
Co-authored-by: Chao Sun <su...@apache.org>
---
.../org/apache/spark/sql/comet/operators.scala | 5 +-
.../sql-tests/join/sort_merge_join_binary.sql | 172 +++++++++++++++++++++
2 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/spark/src/main/scala/org/apache/spark/sql/comet/operators.scala b/spark/src/main/scala/org/apache/spark/sql/comet/operators.scala
index 6329509307..84751acfde 100644
--- a/spark/src/main/scala/org/apache/spark/sql/comet/operators.scala
+++ b/spark/src/main/scala/org/apache/spark/sql/comet/operators.scala
@@ -44,7 +44,7 @@ import org.apache.spark.sql.execution.aggregate.{BaseAggregateExec, HashAggregat
import org.apache.spark.sql.execution.exchange.ReusedExchangeExec
import org.apache.spark.sql.execution.joins.{BroadcastHashJoinExec, BroadcastNestedLoopJoinExec, HashJoin, ShuffledHashJoinExec, SortMergeJoinExec}
import org.apache.spark.sql.execution.metric.{SQLMetric, SQLMetrics}
-import org.apache.spark.sql.types.{ArrayType, BooleanType, ByteType, DataType, DateType, DecimalType, DoubleType, FloatType, IntegerType, LongType, MapType, ShortType, StringType, StructField, StructType, TimestampNTZType, TimestampType}
+import org.apache.spark.sql.types.{ArrayType, BinaryType, BooleanType, ByteType, DataType, DateType, DecimalType, DoubleType, FloatType, IntegerType, LongType, MapType, ShortType, StringType, StructField, StructType, TimestampNTZType, TimestampType}
import org.apache.spark.sql.vectorized.ColumnarBatch
import org.apache.spark.unsafe.Platform
import org.apache.spark.util.SerializableConfiguration
@@ -3014,7 +3014,8 @@ object CometSortMergeJoinExec extends CometOperatorSerde[SortMergeJoinExec] {
private def supportedSortMergeJoinEqualType(dataType: DataType): Boolean = dataType match {
case st: StringType if isStringCollationType(st) => false
case _: ByteType | _: ShortType | _: IntegerType | _: LongType | _: FloatType |
- _: DoubleType | _: StringType | _: DateType | _: DecimalType | _: BooleanType =>
+ _: DoubleType | _: StringType | _: BinaryType | _: DateType | _: DecimalType |
+ _: BooleanType =>
true
case TimestampNTZType | _: TimestampType => true
case _ => false
diff --git a/spark/src/test/resources/sql-tests/join/sort_merge_join_binary.sql b/spark/src/test/resources/sql-tests/join/sort_merge_join_binary.sql
new file mode 100644
index 0000000000..16e5bce220
--- /dev/null
+++ b/spark/src/test/resources/sql-tests/join/sort_merge_join_binary.sql
@@ -0,0 +1,172 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+-- Tests for SortMergeJoin on BinaryType keys.
+--
+-- SortMergeJoin advances two sorted streams, so the sort order of the inputs must agree with the
+-- join key comparator. Spark orders binary with `TypeUtils.compareBinary` (unsigned byte, shorter
+-- prefix first) and Arrow orders `Binary` by byte slice, which is the same order. A disagreement
+-- between the sort and the comparator drops matches silently rather than failing, so the keys below
+-- separate the two candidate orderings: under signed comparison 0x80 and 0xff would sort before
+-- 0x00.
+
+-- Force a sort merge join: neither side may be broadcast, and hash joins are not preferred.
+-- Config: spark.sql.autoBroadcastJoinThreshold=-1
+-- Config: spark.sql.adaptive.autoBroadcastJoinThreshold=-1
+-- Config: spark.sql.join.preferSortMergeJoin=true
+-- More than one sorted stream per side.
+-- Config: spark.sql.shuffle.partitions=3
+-- Smaller than the row count of the smj_bin_wide_* tables, so their key groups span batches and
+-- the join compares keys across batch boundaries.
+-- Config: spark.comet.batchSize=16
+-- Decides whether the scan hands the join a plain or a dictionary-backed binary array.
+-- ConfigMatrix: parquet.enable.dictionary=false,true
+
+-- ============================================================
+-- Setup
+-- ============================================================
+
+-- 16-byte keys, the shape a UUID stored as binary takes. Rows 'a' and 'c' share a key, so a key
+-- group with more than one row on the left is covered.
+statement
+CREATE TABLE smj_bin_left(name STRING, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_left VALUES
+ ('a', X'000102030405060708090a0b0c0d0e0f'),
+ ('b', X'0102030405060708090a0b0c0d0e0f10'),
+ ('c', X'000102030405060708090a0b0c0d0e0f')
+
+statement
+CREATE TABLE smj_bin_right(name STRING, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_right VALUES
+ ('a', X'000102030405060708090a0b0c0d0e0f'),
+ ('d', X'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff')
+
+-- Keys that separate unsigned from signed byte ordering, plus an empty key and a shared prefix.
+-- Both sides carry the same keys, so every key must find its match.
+statement
+CREATE TABLE smj_bin_ord_left(id INT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_ord_left VALUES
+ (0, CAST('' AS BINARY)),
+ (1, X'00'),
+ (2, X'01'),
+ (3, X'0100'),
+ (4, X'7f'),
+ (5, X'80'),
+ (6, X'ff'),
+ (7, X'ff00')
+
+statement
+CREATE TABLE smj_bin_ord_right(id INT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_ord_right VALUES
+ (100, CAST('' AS BINARY)),
+ (200, X'00'),
+ (300, X'01'),
+ (400, X'0100'),
+ (500, X'7f'),
+ (600, X'80'),
+ (700, X'ff'),
+ (800, X'ff00')
+
+-- NULL keys on both sides.
+statement
+CREATE TABLE smj_bin_null_left(id INT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_null_left VALUES (1, X'0a0b'), (2, CAST(NULL AS BINARY)), (3, X'0c0d')
+
+statement
+CREATE TABLE smj_bin_null_right(id INT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_null_right VALUES (10, X'0a0b'), (20, CAST(NULL AS BINARY)), (30, X'1112')
+
+-- Few distinct keys over many rows: with the batch size above, every key group spans many batches.
+-- The 0x80 suffix keeps the high-byte ordering in play here too.
+statement
+CREATE TABLE smj_bin_wide_left(id BIGINT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_wide_left
+SELECT id, unhex(concat(lpad(hex(id % 3), 2, '0'), '80')) FROM range(200)
+
+statement
+CREATE TABLE smj_bin_wide_right(id BIGINT, bkey BINARY) USING parquet
+
+statement
+INSERT INTO smj_bin_wide_right
+SELECT id * 10, unhex(concat(lpad(hex(id % 4), 2, '0'), '80')) FROM range(200)
+
+-- ============================================================
+-- Join types over a binary key
+-- ============================================================
+
+query
+SELECT * FROM smj_bin_left l JOIN smj_bin_right r ON l.bkey = r.bkey
+
+query
+SELECT * FROM smj_bin_left l LEFT OUTER JOIN smj_bin_right r ON l.bkey = r.bkey
+
+query
+SELECT * FROM smj_bin_left l RIGHT OUTER JOIN smj_bin_right r ON l.bkey = r.bkey
+
+query
+SELECT * FROM smj_bin_left l FULL OUTER JOIN smj_bin_right r ON l.bkey = r.bkey
+
+query
+SELECT * FROM smj_bin_left l LEFT SEMI JOIN smj_bin_right r ON l.bkey = r.bkey
+
+query
+SELECT * FROM smj_bin_left l LEFT ANTI JOIN smj_bin_right r ON l.bkey = r.bkey
+
+-- Composite key: the binary key is only part of the ordering, and ('c', ...) no longer matches.
+query
+SELECT * FROM smj_bin_left l JOIN smj_bin_right r ON l.name = r.name AND l.bkey = r.bkey
+
+-- ============================================================
+-- Byte ordering
+-- ============================================================
+
+query
+SELECT * FROM smj_bin_ord_left l JOIN smj_bin_ord_right r ON l.bkey = r.bkey
+
+-- ============================================================
+-- NULL keys
+-- ============================================================
+
+-- Inner join: NULL = NULL must not match.
+query
+SELECT * FROM smj_bin_null_left l JOIN smj_bin_null_right r ON l.bkey = r.bkey
+
+-- Full outer join: the NULL-keyed row on each side surfaces as unmatched.
+query
+SELECT * FROM smj_bin_null_left l FULL OUTER JOIN smj_bin_null_right r ON l.bkey = r.bkey
+
+-- ============================================================
+-- Key groups spanning batches
+-- ============================================================
+
+query
+SELECT COUNT(*), SUM(l.id), MAX(r.id)
+FROM smj_bin_wide_left l JOIN smj_bin_wide_right r ON l.bkey = r.bkey
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org