From 4872fcbef87c1fac262dee41f580e65b57f85fe0 Mon Sep 17 00:00:00 2001 From: Yuan Date: Thu, 5 Feb 2026 08:58:42 +0000 Subject: [PATCH 01/10] [VL] Enable Rank and DanseRank for WindowGroupLimitExec Signed-off-by: Yuan --- .../org/apache/gluten/backendsapi/velox/VeloxBackend.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxBackend.scala b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxBackend.scala index 8a1a343087f1..24d08a57920d 100644 --- a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxBackend.scala +++ b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxBackend.scala @@ -419,7 +419,7 @@ object VeloxBackendSettings extends BackendSettingsApi { override def supportWindowGroupLimitExec(rankLikeFunction: Expression): Boolean = { rankLikeFunction match { - case _: RowNumber => true + case _: RowNumber | _: Rank | _: DenseRank => true case _ => false } } From 22c53281cb9ad9ed3ff64c0227d4a8d793713962 Mon Sep 17 00:00:00 2001 From: Yuan Date: Sat, 7 Feb 2026 15:07:43 +0000 Subject: [PATCH 02/10] fix ut Signed-off-by: Yuan --- .../GlutenSQLWindowFunctionSuite.scala | 46 ++++++++++++++++++- .../GlutenSQLWindowFunctionSuite.scala | 46 ++++++++++++++++++- .../GlutenSQLWindowFunctionSuite.scala | 46 ++++++++++++++++++- 3 files changed, 135 insertions(+), 3 deletions(-) diff --git a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala index 4a87bac690e8..32e7e2c717c6 100644 --- a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala +++ b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala @@ -176,7 +176,51 @@ class GlutenSQLWindowFunctionSuite extends SQLWindowFunctionSuite with GlutenSQL ) ) assert( - !getExecutedPlan(df).exists { + getExecutedPlan(df).exists { + case _: WindowGroupLimitExecTransformer => true + case _ => false + } + ) + } + } + + testGluten("Filter on dense_rank") { + withTable("customer") { + val rdd = spark.sparkContext.parallelize(customerData) + val customerDF = spark.createDataFrame(rdd, customerSchema) + customerDF.createOrReplaceTempView("customer") + val query = + """ + |SELECT * from (SELECT + | c_custkey, + | c_acctbal, + | dense_rank() OVER ( + | PARTITION BY c_nationkey, + | "a" + | ORDER BY + | c_custkey, + | "a" + | ) AS rank + |FROM + | customer ORDER BY 1, 2) where rank <=2 + |""".stripMargin + val df = sql(query) + checkAnswer( + df, + Seq( + Row(4553, BigDecimal(638841L, 2), 1), + Row(4953, BigDecimal(603728L, 2), 1), + Row(9954, BigDecimal(758725L, 2), 1), + Row(35403, BigDecimal(603470L, 2), 2), + Row(35803, BigDecimal(528487L, 2), 1), + Row(61065, BigDecimal(728477L, 2), 1), + Row(95337, BigDecimal(91561L, 2), 2), + Row(127412, BigDecimal(462141L, 2), 2), + Row(148303, BigDecimal(430230L, 2), 2) + ) + ) + assert( + getExecutedPlan(df).exists { case _: WindowGroupLimitExecTransformer => true case _ => false } diff --git a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala index 7c803dd78d20..7515d45fca57 100644 --- a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala +++ b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala @@ -178,7 +178,51 @@ class GlutenSQLWindowFunctionSuite extends SQLWindowFunctionSuite with GlutenSQL ) ) assert( - !getExecutedPlan(df).exists { + getExecutedPlan(df).exists { + case _: WindowGroupLimitExecTransformer => true + case _ => false + } + ) + } + } + + testGluten("Filter on dense_rank") { + withTable("customer") { + val rdd = spark.sparkContext.parallelize(customerData) + val customerDF = spark.createDataFrame(rdd, customerSchema) + customerDF.createOrReplaceTempView("customer") + val query = + """ + |SELECT * from (SELECT + | c_custkey, + | c_acctbal, + | dense_rank() OVER ( + | PARTITION BY c_nationkey, + | "a" + | ORDER BY + | c_custkey, + | "a" + | ) AS rank + |FROM + | customer ORDER BY 1, 2) where rank <=2 + |""".stripMargin + val df = sql(query) + checkAnswer( + df, + Seq( + Row(4553, BigDecimal(638841L, 2), 1), + Row(4953, BigDecimal(603728L, 2), 1), + Row(9954, BigDecimal(758725L, 2), 1), + Row(35403, BigDecimal(603470L, 2), 2), + Row(35803, BigDecimal(528487L, 2), 1), + Row(61065, BigDecimal(728477L, 2), 1), + Row(95337, BigDecimal(91561L, 2), 2), + Row(127412, BigDecimal(462141L, 2), 2), + Row(148303, BigDecimal(430230L, 2), 2) + ) + ) + assert( + getExecutedPlan(df).exists { case _: WindowGroupLimitExecTransformer => true case _ => false } diff --git a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala index 7c803dd78d20..7515d45fca57 100644 --- a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala +++ b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenSQLWindowFunctionSuite.scala @@ -178,7 +178,51 @@ class GlutenSQLWindowFunctionSuite extends SQLWindowFunctionSuite with GlutenSQL ) ) assert( - !getExecutedPlan(df).exists { + getExecutedPlan(df).exists { + case _: WindowGroupLimitExecTransformer => true + case _ => false + } + ) + } + } + + testGluten("Filter on dense_rank") { + withTable("customer") { + val rdd = spark.sparkContext.parallelize(customerData) + val customerDF = spark.createDataFrame(rdd, customerSchema) + customerDF.createOrReplaceTempView("customer") + val query = + """ + |SELECT * from (SELECT + | c_custkey, + | c_acctbal, + | dense_rank() OVER ( + | PARTITION BY c_nationkey, + | "a" + | ORDER BY + | c_custkey, + | "a" + | ) AS rank + |FROM + | customer ORDER BY 1, 2) where rank <=2 + |""".stripMargin + val df = sql(query) + checkAnswer( + df, + Seq( + Row(4553, BigDecimal(638841L, 2), 1), + Row(4953, BigDecimal(603728L, 2), 1), + Row(9954, BigDecimal(758725L, 2), 1), + Row(35403, BigDecimal(603470L, 2), 2), + Row(35803, BigDecimal(528487L, 2), 1), + Row(61065, BigDecimal(728477L, 2), 1), + Row(95337, BigDecimal(91561L, 2), 2), + Row(127412, BigDecimal(462141L, 2), 2), + Row(148303, BigDecimal(430230L, 2), 2) + ) + ) + assert( + getExecutedPlan(df).exists { case _: WindowGroupLimitExecTransformer => true case _ => false } From 84b97d7393b69e5c0233cf81177ac50572e5f833 Mon Sep 17 00:00:00 2001 From: Yuan Date: Sat, 7 Feb 2026 15:35:05 +0000 Subject: [PATCH 03/10] fix ut Signed-off-by: Yuan --- .../utils/velox/VeloxTestSettings.scala | 2 + ...emoveRedundantWindowGroupLimitsSuite.scala | 51 ++++++++++++++++++- .../utils/velox/VeloxTestSettings.scala | 2 + ...emoveRedundantWindowGroupLimitsSuite.scala | 51 ++++++++++++++++++- .../utils/velox/VeloxTestSettings.scala | 2 + ...emoveRedundantWindowGroupLimitsSuite.scala | 51 ++++++++++++++++++- 6 files changed, 156 insertions(+), 3 deletions(-) diff --git a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala index f4427d7d43fb..b76a717e426e 100644 --- a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala +++ b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala @@ -960,6 +960,8 @@ class VeloxTestSettings extends BackendTestSettings { enableSuite[GlutenParquetFileMetadataStructRowIndexSuite] enableSuite[GlutenTableLocationSuite] enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] + // rewrite with Gluten test + .exclude("remove redundant WindowGroupLimits") enableSuite[GlutenSQLCollectLimitExecSuite] enableSuite[GlutenBatchEvalPythonExecSuite] // Replaced with other tests that check for native operations diff --git a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala index 9d819d2bd90f..455fa283b1cf 100644 --- a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala +++ b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala @@ -16,8 +16,57 @@ */ package org.apache.spark.sql.execution +import org.apache.gluten.execution.WindowGroupLimitExecTransformer + +import org.apache.spark.sql.DataFrame import org.apache.spark.sql.GlutenSQLTestsBaseTrait +import org.apache.spark.sql.functions.lit class GlutenRemoveRedundantWindowGroupLimitsSuite extends RemoveRedundantWindowGroupLimitsSuite - with GlutenSQLTestsBaseTrait {} + with GlutenSQLTestsBaseTrait { + private def checkNumWindowGroupLimits(df: DataFrame, count: Int): Unit = { + val plan = df.queryExecution.executedPlan + assert(collectWithSubqueries(plan) { + case exec: WindowGroupLimitExecTransformer => exec + }.length == count) + } + + private def checkWindowGroupLimits(query: String, count: Int): Unit = { + val df = sql(query) + checkNumWindowGroupLimits(df, count) + val result = df.collect() + checkAnswer(df, result) + } + + testGluten("remove redundant WindowGroupLimits") { + withTempView("t") { + spark.range(0, 100).withColumn("value", lit(1)).createOrReplaceTempView("t") + val query1 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (PARTITION BY id ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query1, 1) + + val query2 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query2, 2) + } + } +} diff --git a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala index 202705b6d1b5..f5c9d22db6ac 100644 --- a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala +++ b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala @@ -1122,6 +1122,8 @@ class VeloxTestSettings extends BackendTestSettings { enableSuite[GlutenParquetFileMetadataStructRowIndexSuite] enableSuite[GlutenTableLocationSuite] enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] + // rewrite with Gluten test + .exclude("remove redundant WindowGroupLimits") enableSuite[GlutenSQLCollectLimitExecSuite] // Generated suites for org.apache.spark.sql.execution.python // TODO: 4.x enableSuite[GlutenPythonDataSourceSuite] // 1 failure diff --git a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala index 9d819d2bd90f..455fa283b1cf 100644 --- a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala +++ b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala @@ -16,8 +16,57 @@ */ package org.apache.spark.sql.execution +import org.apache.gluten.execution.WindowGroupLimitExecTransformer + +import org.apache.spark.sql.DataFrame import org.apache.spark.sql.GlutenSQLTestsBaseTrait +import org.apache.spark.sql.functions.lit class GlutenRemoveRedundantWindowGroupLimitsSuite extends RemoveRedundantWindowGroupLimitsSuite - with GlutenSQLTestsBaseTrait {} + with GlutenSQLTestsBaseTrait { + private def checkNumWindowGroupLimits(df: DataFrame, count: Int): Unit = { + val plan = df.queryExecution.executedPlan + assert(collectWithSubqueries(plan) { + case exec: WindowGroupLimitExecTransformer => exec + }.length == count) + } + + private def checkWindowGroupLimits(query: String, count: Int): Unit = { + val df = sql(query) + checkNumWindowGroupLimits(df, count) + val result = df.collect() + checkAnswer(df, result) + } + + testGluten("remove redundant WindowGroupLimits") { + withTempView("t") { + spark.range(0, 100).withColumn("value", lit(1)).createOrReplaceTempView("t") + val query1 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (PARTITION BY id ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query1, 1) + + val query2 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query2, 2) + } + } +} diff --git a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala index a74142c95d96..e8f8dfa76253 100644 --- a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala +++ b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala @@ -1108,6 +1108,8 @@ class VeloxTestSettings extends BackendTestSettings { enableSuite[GlutenParquetFileMetadataStructRowIndexSuite] enableSuite[GlutenTableLocationSuite] enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] + // rewrite with Gluten test + .exclude("remove redundant WindowGroupLimits") enableSuite[GlutenSQLCollectLimitExecSuite] // Generated suites for org.apache.spark.sql.execution.python // TODO: 4.x enableSuite[GlutenPythonDataSourceSuite] diff --git a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala index 9d819d2bd90f..455fa283b1cf 100644 --- a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala +++ b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/GlutenRemoveRedundantWindowGroupLimitsSuite.scala @@ -16,8 +16,57 @@ */ package org.apache.spark.sql.execution +import org.apache.gluten.execution.WindowGroupLimitExecTransformer + +import org.apache.spark.sql.DataFrame import org.apache.spark.sql.GlutenSQLTestsBaseTrait +import org.apache.spark.sql.functions.lit class GlutenRemoveRedundantWindowGroupLimitsSuite extends RemoveRedundantWindowGroupLimitsSuite - with GlutenSQLTestsBaseTrait {} + with GlutenSQLTestsBaseTrait { + private def checkNumWindowGroupLimits(df: DataFrame, count: Int): Unit = { + val plan = df.queryExecution.executedPlan + assert(collectWithSubqueries(plan) { + case exec: WindowGroupLimitExecTransformer => exec + }.length == count) + } + + private def checkWindowGroupLimits(query: String, count: Int): Unit = { + val df = sql(query) + checkNumWindowGroupLimits(df, count) + val result = df.collect() + checkAnswer(df, result) + } + + testGluten("remove redundant WindowGroupLimits") { + withTempView("t") { + spark.range(0, 100).withColumn("value", lit(1)).createOrReplaceTempView("t") + val query1 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (PARTITION BY id ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query1, 1) + + val query2 = + """ + |SELECT * + |FROM ( + | SELECT id, rank() OVER w AS rn + | FROM t + | GROUP BY id + | WINDOW w AS (ORDER BY max(value)) + |) + |WHERE rn < 3 + |""".stripMargin + checkWindowGroupLimits(query2, 2) + } + } +} From 2475caff10fb824369f3e3d4e026f513ae4dc244 Mon Sep 17 00:00:00 2001 From: Yuan Date: Mon, 9 Feb 2026 22:28:51 +0000 Subject: [PATCH 04/10] fix window function name Signed-off-by: Yuan --- cpp/core/symbols.txt | 11 ++++ cpp/core/t.py | 60 +++++++++++++++++++ cpp/velox/substrait/SubstraitParser.cc | 16 +++++ cpp/velox/substrait/SubstraitParser.h | 11 ++++ cpp/velox/substrait/SubstraitToVeloxPlan.cc | 11 +++- .../WindowGroupLimitExecTransformer.scala | 21 ++++++- 6 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 cpp/core/symbols.txt create mode 100644 cpp/core/t.py diff --git a/cpp/core/symbols.txt b/cpp/core/symbols.txt new file mode 100644 index 000000000000..25b2bedf446e --- /dev/null +++ b/cpp/core/symbols.txt @@ -0,0 +1,11 @@ +# Exported symbols for macOS +# Generated from symbols.map + +_ZN8protobuf* +_ZN9substrait* +_gluten::* + +# Note: The following are hidden (local in GNU ld): +# *google::* (except protobuf) +# glog and gflags symbols + diff --git a/cpp/core/t.py b/cpp/core/t.py new file mode 100644 index 000000000000..fe8fcf7cbfaa --- /dev/null +++ b/cpp/core/t.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import re + +def convert_symbol_map_to_macos(map_file, output_file): + """Convert GNU ld version script to macOS exported_symbols_list""" + + with open(map_file, 'r') as f: + content = f.read() + + # Initialize symbol lists + exported_patterns = [] + + # Find global section + global_match = re.search(r'global:\s*(.*?)\s*local:', content, re.DOTALL) + if global_match: + global_section = global_match.group(1) + + # Extract C++ patterns + cpp_match = re.search(r'extern "C\+\+"\s*\{(.*?)\}', global_section, re.DOTALL) + if cpp_match: + cpp_content = cpp_match.group(1) + for line in cpp_content.split('\n'): + line = line.strip().rstrip(';') + if not line or line.startswith('#'): + continue + + # Convert patterns like "*protobuf::*" to macOS mangled form + if line.startswith('*'): + # Pattern: *protobuf::* + namespace = line[1:].split('::')[0] + # Convert to mangled C++ names + mangled = mangle_namespace(namespace) + exported_patterns.append(mangled + '*') + elif '::*' in line: + # Pattern: namespace::* + namespace = line.split('::')[0] + mangled = mangle_namespace(namespace) + exported_patterns.append(mangled + '*') + + # Write macOS symbol file + with open(output_file, 'w') as f: + f.write("# Exported symbols for macOS\n") + f.write("# Generated from " + map_file + "\n\n") + + for pattern in exported_patterns: + f.write(pattern + '\n') + + # Add note about what's hidden + f.write("\n# Note: The following are hidden (local in GNU ld):\n") + f.write("# *google::* (except protobuf)\n") + f.write("# glog and gflags symbols\n") + +def mangle_namespace(namespace): + """Convert namespace to mangled C++ name (simplified)""" + # This is a simplified mangling - real mangling is more complex + mangled = "_ZN" + str(len(namespace)) + namespace + return mangled + +if __name__ == "__main__": + convert_symbol_map_to_macos("symbols.map", "symbols.txt") diff --git a/cpp/velox/substrait/SubstraitParser.cc b/cpp/velox/substrait/SubstraitParser.cc index 2bc1dd71c301..e05ca6da7b1f 100644 --- a/cpp/velox/substrait/SubstraitParser.cc +++ b/cpp/velox/substrait/SubstraitParser.cc @@ -289,6 +289,22 @@ bool SubstraitParser::configSetInOptimization( return false; } +bool SubstraitParser::checkWindowFunction( + const ::substrait::extensions::AdvancedExtension& extension, + const std::string& config, + const std::string& targetFunction) { + if (extension.has_optimization()) { + google::protobuf::StringValue msg; + extension.optimization().UnpackTo(&msg); + std::size_t pos = msg.value().find(config); + if ((pos != std::string::npos) && (msg.value().size() >= targetFunction.size()) && + (msg.value().substr(pos + config.size(), targetFunction.size()) == targetFunction)) { + return true; + } + } + return false; +} + std::vector SubstraitParser::sigToTypes(const std::string& signature) { std::vector typeStrs = SubstraitParser::getSubFunctionTypes(signature); std::vector types; diff --git a/cpp/velox/substrait/SubstraitParser.h b/cpp/velox/substrait/SubstraitParser.h index f42d05b4a21c..a35a8972c0f0 100644 --- a/cpp/velox/substrait/SubstraitParser.h +++ b/cpp/velox/substrait/SubstraitParser.h @@ -93,6 +93,17 @@ class SubstraitParser { /// @return Whether the config is set as true. static bool configSetInOptimization(const ::substrait::extensions::AdvancedExtension&, const std::string& config); + /// @brief Return whether a config is set as true in AdvancedExtension + /// optimization. + /// @param extension Substrait advanced extension. + /// @param config the key string of a config. + /// @param target function + /// @return Whether the target function is correct. + static bool checkWindowFunction( + const ::substrait::extensions::AdvancedExtension&, + const std::string& config, + const std::string& targetFunction); + /// Extract input types from Substrait function signature. static std::vector sigToTypes(const std::string& functionSig); diff --git a/cpp/velox/substrait/SubstraitToVeloxPlan.cc b/cpp/velox/substrait/SubstraitToVeloxPlan.cc index b543dfa8ba89..cacde16a5474 100644 --- a/cpp/velox/substrait/SubstraitToVeloxPlan.cc +++ b/cpp/velox/substrait/SubstraitToVeloxPlan.cc @@ -1169,9 +1169,18 @@ core::PlanNodePtr SubstraitToVeloxPlanConverter::toVeloxPlan( childNode); } + auto windowFunc = core::TopNRowNumberNode::RankFunction::kRowNumber; + if (windowGroupLimitRel.has_advanced_extension()) { + if (SubstraitParser::checkWindowFunction(windowGroupLimitRel.advanced_extension(), "window_function=", "rank")){ + windowFunc = core::TopNRowNumberNode::RankFunction::kRank; + } else { + windowFunc = core::TopNRowNumberNode::RankFunction::kDenseRank; + } + } + return std::make_shared( nextPlanNodeId(), - core::TopNRowNumberNode::RankFunction::kRowNumber, + windowFunc, partitionKeys, sortingKeys, sortingOrders, diff --git a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala index 282e1b8e712f..27bc765047e4 100644 --- a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala +++ b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala @@ -17,16 +17,19 @@ package org.apache.gluten.execution import org.apache.gluten.backendsapi.BackendsApiManager +import org.apache.gluten.exception.GlutenNotSupportException import org.apache.gluten.expression.ExpressionConverter import org.apache.gluten.metrics.MetricsUpdater import org.apache.gluten.substrait.SubstraitContext +import org.apache.gluten.substrait.extensions.ExtensionBuilder import org.apache.gluten.substrait.rel.{RelBuilder, RelNode} -import org.apache.spark.sql.catalyst.expressions.{Ascending, Attribute, Expression, SortOrder} +import org.apache.spark.sql.catalyst.expressions.{Ascending, Attribute, DenseRank, Expression, Rank, RowNumber, SortOrder} import org.apache.spark.sql.catalyst.plans.physical.{AllTuples, ClusteredDistribution, Distribution, Partitioning} import org.apache.spark.sql.execution.SparkPlan import org.apache.spark.sql.execution.window.{GlutenFinal, GlutenPartial, GlutenWindowGroupLimitMode} +import com.google.protobuf.StringValue import io.substrait.proto.SortField import scala.collection.JavaConverters._ @@ -111,11 +114,27 @@ case class WindowGroupLimitExecTransformer( builder.build() }.asJava if (!validation) { + val windowFunction = rankLikeFunction match { + case _: RowNumber => "row_number" + case _: Rank => "rank" + case _: DenseRank => "dense_rank" + case _ => throw new GlutenNotSupportException(s"Unknow window function $rankLikeFunction") + } + val parametersStr = new StringBuffer("WindowGroupLimitParameters:") + parametersStr + .append("window_function=") + .append(windowFunction) + .append("\n") + val message = StringValue.newBuilder().setValue(parametersStr.toString).build() + val extensionNode = ExtensionBuilder.makeAdvancedExtension( + BackendsApiManager.getTransformerApiInstance.packPBMessage(message), + null) RelBuilder.makeWindowGroupLimitRel( input, partitionsExpressions, sortFieldList, limit, + extensionNode, context, operatorId) } else { From 8863fe405db1d36734f705fc0971810b5f6a9fea Mon Sep 17 00:00:00 2001 From: Yuan Date: Mon, 9 Feb 2026 22:32:02 +0000 Subject: [PATCH 05/10] remove extra files Signed-off-by: Yuan --- cpp/core/symbols.txt | 11 -------- cpp/core/t.py | 60 -------------------------------------------- 2 files changed, 71 deletions(-) delete mode 100644 cpp/core/symbols.txt delete mode 100644 cpp/core/t.py diff --git a/cpp/core/symbols.txt b/cpp/core/symbols.txt deleted file mode 100644 index 25b2bedf446e..000000000000 --- a/cpp/core/symbols.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Exported symbols for macOS -# Generated from symbols.map - -_ZN8protobuf* -_ZN9substrait* -_gluten::* - -# Note: The following are hidden (local in GNU ld): -# *google::* (except protobuf) -# glog and gflags symbols - diff --git a/cpp/core/t.py b/cpp/core/t.py deleted file mode 100644 index fe8fcf7cbfaa..000000000000 --- a/cpp/core/t.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -import re - -def convert_symbol_map_to_macos(map_file, output_file): - """Convert GNU ld version script to macOS exported_symbols_list""" - - with open(map_file, 'r') as f: - content = f.read() - - # Initialize symbol lists - exported_patterns = [] - - # Find global section - global_match = re.search(r'global:\s*(.*?)\s*local:', content, re.DOTALL) - if global_match: - global_section = global_match.group(1) - - # Extract C++ patterns - cpp_match = re.search(r'extern "C\+\+"\s*\{(.*?)\}', global_section, re.DOTALL) - if cpp_match: - cpp_content = cpp_match.group(1) - for line in cpp_content.split('\n'): - line = line.strip().rstrip(';') - if not line or line.startswith('#'): - continue - - # Convert patterns like "*protobuf::*" to macOS mangled form - if line.startswith('*'): - # Pattern: *protobuf::* - namespace = line[1:].split('::')[0] - # Convert to mangled C++ names - mangled = mangle_namespace(namespace) - exported_patterns.append(mangled + '*') - elif '::*' in line: - # Pattern: namespace::* - namespace = line.split('::')[0] - mangled = mangle_namespace(namespace) - exported_patterns.append(mangled + '*') - - # Write macOS symbol file - with open(output_file, 'w') as f: - f.write("# Exported symbols for macOS\n") - f.write("# Generated from " + map_file + "\n\n") - - for pattern in exported_patterns: - f.write(pattern + '\n') - - # Add note about what's hidden - f.write("\n# Note: The following are hidden (local in GNU ld):\n") - f.write("# *google::* (except protobuf)\n") - f.write("# glog and gflags symbols\n") - -def mangle_namespace(namespace): - """Convert namespace to mangled C++ name (simplified)""" - # This is a simplified mangling - real mangling is more complex - mangled = "_ZN" + str(len(namespace)) + namespace - return mangled - -if __name__ == "__main__": - convert_symbol_map_to_macos("symbols.map", "symbols.txt") From 674b13c0a21d82ba8ebd4a8fc3cb3d62dd5cbbcc Mon Sep 17 00:00:00 2001 From: Yuan Date: Wed, 11 Feb 2026 19:20:10 +0000 Subject: [PATCH 06/10] fix Signed-off-by: Yuan --- cpp/velox/substrait/SubstraitParser.cc | 2 +- cpp/velox/substrait/SubstraitParser.h | 1 - cpp/velox/substrait/SubstraitToVeloxPlan.cc | 2 +- .../gluten/execution/WindowGroupLimitExecTransformer.scala | 6 ++++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cpp/velox/substrait/SubstraitParser.cc b/cpp/velox/substrait/SubstraitParser.cc index e05ca6da7b1f..c67ad56f0932 100644 --- a/cpp/velox/substrait/SubstraitParser.cc +++ b/cpp/velox/substrait/SubstraitParser.cc @@ -291,8 +291,8 @@ bool SubstraitParser::configSetInOptimization( bool SubstraitParser::checkWindowFunction( const ::substrait::extensions::AdvancedExtension& extension, - const std::string& config, const std::string& targetFunction) { + const std::string config = "window_function="; if (extension.has_optimization()) { google::protobuf::StringValue msg; extension.optimization().UnpackTo(&msg); diff --git a/cpp/velox/substrait/SubstraitParser.h b/cpp/velox/substrait/SubstraitParser.h index a35a8972c0f0..d9ae646c32a2 100644 --- a/cpp/velox/substrait/SubstraitParser.h +++ b/cpp/velox/substrait/SubstraitParser.h @@ -101,7 +101,6 @@ class SubstraitParser { /// @return Whether the target function is correct. static bool checkWindowFunction( const ::substrait::extensions::AdvancedExtension&, - const std::string& config, const std::string& targetFunction); /// Extract input types from Substrait function signature. diff --git a/cpp/velox/substrait/SubstraitToVeloxPlan.cc b/cpp/velox/substrait/SubstraitToVeloxPlan.cc index cacde16a5474..d6aad9270634 100644 --- a/cpp/velox/substrait/SubstraitToVeloxPlan.cc +++ b/cpp/velox/substrait/SubstraitToVeloxPlan.cc @@ -1171,7 +1171,7 @@ core::PlanNodePtr SubstraitToVeloxPlanConverter::toVeloxPlan( auto windowFunc = core::TopNRowNumberNode::RankFunction::kRowNumber; if (windowGroupLimitRel.has_advanced_extension()) { - if (SubstraitParser::checkWindowFunction(windowGroupLimitRel.advanced_extension(), "window_function=", "rank")){ + if (SubstraitParser::checkWindowFunction(windowGroupLimitRel.advanced_extension(), "rank")){ windowFunc = core::TopNRowNumberNode::RankFunction::kRank; } else { windowFunc = core::TopNRowNumberNode::RankFunction::kDenseRank; diff --git a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala index 27bc765047e4..1accfb8b769e 100644 --- a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala +++ b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala @@ -28,6 +28,7 @@ import org.apache.spark.sql.catalyst.expressions.{Ascending, Attribute, DenseRan import org.apache.spark.sql.catalyst.plans.physical.{AllTuples, ClusteredDistribution, Distribution, Partitioning} import org.apache.spark.sql.execution.SparkPlan import org.apache.spark.sql.execution.window.{GlutenFinal, GlutenPartial, GlutenWindowGroupLimitMode} +import org.apache.spark.sql.internal.SQLConf import com.google.protobuf.StringValue import io.substrait.proto.SortField @@ -150,6 +151,11 @@ case class WindowGroupLimitExecTransformer( } override protected def doValidateInternal(): ValidationResult = { + if (SQLConf.get.usePartitionEvaluator) { + return ValidationResult.failed( + "WindowGroupLimitExec does not support partition evaluator," + + " please set " + s"${SQLConf.get.usePartitionEvaluator} to false and try again.") + } if (!BackendsApiManager.getSettings.supportWindowGroupLimitExec(rankLikeFunction)) { return ValidationResult .failed(s"Found unsupported rank like function: $rankLikeFunction") From 132071eace3f7eb7efff373716f6254b5dbfe403 Mon Sep 17 00:00:00 2001 From: Yuan Date: Thu, 12 Feb 2026 17:47:04 +0000 Subject: [PATCH 07/10] fix Signed-off-by: Yuan --- cpp/velox/substrait/SubstraitToVeloxPlan.cc | 2 +- .../gluten/execution/WindowGroupLimitExecTransformer.scala | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/cpp/velox/substrait/SubstraitToVeloxPlan.cc b/cpp/velox/substrait/SubstraitToVeloxPlan.cc index d6aad9270634..83099efddeea 100644 --- a/cpp/velox/substrait/SubstraitToVeloxPlan.cc +++ b/cpp/velox/substrait/SubstraitToVeloxPlan.cc @@ -1173,7 +1173,7 @@ core::PlanNodePtr SubstraitToVeloxPlanConverter::toVeloxPlan( if (windowGroupLimitRel.has_advanced_extension()) { if (SubstraitParser::checkWindowFunction(windowGroupLimitRel.advanced_extension(), "rank")){ windowFunc = core::TopNRowNumberNode::RankFunction::kRank; - } else { + } else if (SubstraitParser::checkWindowFunction(windowGroupLimitRel.advanced_extension(), "dense_rank")) { windowFunc = core::TopNRowNumberNode::RankFunction::kDenseRank; } } diff --git a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala index 1accfb8b769e..27bc765047e4 100644 --- a/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala +++ b/gluten-substrait/src/main/scala/org/apache/gluten/execution/WindowGroupLimitExecTransformer.scala @@ -28,7 +28,6 @@ import org.apache.spark.sql.catalyst.expressions.{Ascending, Attribute, DenseRan import org.apache.spark.sql.catalyst.plans.physical.{AllTuples, ClusteredDistribution, Distribution, Partitioning} import org.apache.spark.sql.execution.SparkPlan import org.apache.spark.sql.execution.window.{GlutenFinal, GlutenPartial, GlutenWindowGroupLimitMode} -import org.apache.spark.sql.internal.SQLConf import com.google.protobuf.StringValue import io.substrait.proto.SortField @@ -151,11 +150,6 @@ case class WindowGroupLimitExecTransformer( } override protected def doValidateInternal(): ValidationResult = { - if (SQLConf.get.usePartitionEvaluator) { - return ValidationResult.failed( - "WindowGroupLimitExec does not support partition evaluator," + - " please set " + s"${SQLConf.get.usePartitionEvaluator} to false and try again.") - } if (!BackendsApiManager.getSettings.supportWindowGroupLimitExec(rankLikeFunction)) { return ValidationResult .failed(s"Found unsupported rank like function: $rankLikeFunction") From 0a4b8844c856cc83c4346cea65f9e45e7050a469 Mon Sep 17 00:00:00 2001 From: Yuan Date: Fri, 13 Feb 2026 08:37:19 +0000 Subject: [PATCH 08/10] fix format Signed-off-by: Yuan --- cpp/velox/substrait/SubstraitParser.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cpp/velox/substrait/SubstraitParser.h b/cpp/velox/substrait/SubstraitParser.h index d9ae646c32a2..8131851ed094 100644 --- a/cpp/velox/substrait/SubstraitParser.h +++ b/cpp/velox/substrait/SubstraitParser.h @@ -96,12 +96,9 @@ class SubstraitParser { /// @brief Return whether a config is set as true in AdvancedExtension /// optimization. /// @param extension Substrait advanced extension. - /// @param config the key string of a config. /// @param target function - /// @return Whether the target function is correct. - static bool checkWindowFunction( - const ::substrait::extensions::AdvancedExtension&, - const std::string& targetFunction); + /// @return Whether the target function is match. + static bool checkWindowFunction(const ::substrait::extensions::AdvancedExtension&, const std::string& targetFunction); /// Extract input types from Substrait function signature. static std::vector sigToTypes(const std::string& functionSig); From fda04b76601365e176a5abdeac2c642fd263e41b Mon Sep 17 00:00:00 2001 From: Yuan Date: Fri, 13 Feb 2026 08:54:13 +0000 Subject: [PATCH 09/10] ignore ch ut Signed-off-by: Yuan --- .../gluten/utils/clickhouse/ClickHouseTestSettings.scala | 3 +++ .../gluten/utils/clickhouse/ClickHouseTestSettings.scala | 3 +++ .../gluten/utils/clickhouse/ClickHouseTestSettings.scala | 3 +++ 3 files changed, 9 insertions(+) diff --git a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index d325d8a6b9c0..ef0b50b8e543 100644 --- a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -2006,6 +2006,7 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH("SPLIT") enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] .excludeCH("remove redundant WindowGroupLimits") + .excludeCH("Gluten - remove redundant WindowGroupLimits") enableSuite[GlutenReplaceHashWithSortAggSuite] .exclude("replace partial hash aggregate with sort aggregate") .exclude("replace partial and final hash aggregate together with sort aggregate") @@ -2060,6 +2061,8 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH( "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") + .excludeCH("Gluten - Filter on rank") + .excludeCH("Gluten - Filter on dense rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite] diff --git a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index abccf9fe912d..f59df0438ec2 100644 --- a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -1982,6 +1982,7 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH("SPLIT") enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] .excludeCH("remove redundant WindowGroupLimits") + .excludeCH("Gluten - remove redundant WindowGroupLimits") enableSuite[GlutenReplaceHashWithSortAggSuite] .exclude("replace partial hash aggregate with sort aggregate") .exclude("replace partial and final hash aggregate together with sort aggregate") @@ -2036,6 +2037,8 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH( "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") + .excludeCH("Gluten - Filter on rank") + .excludeCH("Gluten - Filter on dense rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite] diff --git a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index abccf9fe912d..f59df0438ec2 100644 --- a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -1982,6 +1982,7 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH("SPLIT") enableSuite[GlutenRemoveRedundantWindowGroupLimitsSuite] .excludeCH("remove redundant WindowGroupLimits") + .excludeCH("Gluten - remove redundant WindowGroupLimits") enableSuite[GlutenReplaceHashWithSortAggSuite] .exclude("replace partial hash aggregate with sort aggregate") .exclude("replace partial and final hash aggregate together with sort aggregate") @@ -2036,6 +2037,8 @@ class ClickHouseTestSettings extends BackendTestSettings { .excludeCH( "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") + .excludeCH("Gluten - Filter on rank") + .excludeCH("Gluten - Filter on dense rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite] From 79dcbeb32ddda3d121bae0eff4d7ca02dfef9888 Mon Sep 17 00:00:00 2001 From: Yuan Date: Fri, 13 Feb 2026 11:45:46 +0000 Subject: [PATCH 10/10] fix Signed-off-by: Yuan --- .../apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala | 2 +- .../apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala | 2 +- .../apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index ef0b50b8e543..29d7534e8fae 100644 --- a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -2062,7 +2062,7 @@ class ClickHouseTestSettings extends BackendTestSettings { "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") .excludeCH("Gluten - Filter on rank") - .excludeCH("Gluten - Filter on dense rank") + .excludeCH("Gluten - Filter on dense_rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite] diff --git a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index f59df0438ec2..ec99089c324e 100644 --- a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -2038,7 +2038,7 @@ class ClickHouseTestSettings extends BackendTestSettings { "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") .excludeCH("Gluten - Filter on rank") - .excludeCH("Gluten - Filter on dense rank") + .excludeCH("Gluten - Filter on dense_rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite] diff --git a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala index f59df0438ec2..ec99089c324e 100644 --- a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala +++ b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala @@ -2038,7 +2038,7 @@ class ClickHouseTestSettings extends BackendTestSettings { "window function: multiple window expressions specified by range in a single expression") .excludeCH("Gluten - Filter on row number") .excludeCH("Gluten - Filter on rank") - .excludeCH("Gluten - Filter on dense rank") + .excludeCH("Gluten - Filter on dense_rank") enableSuite[GlutenSameResultSuite] enableSuite[GlutenSaveLoadSuite] enableSuite[GlutenScalaReflectionRelationSuite]