This is an automated email from the ASF dual-hosted git repository.
marong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git
The following commit(s) were added to refs/heads/main by this push:
new d5d2aca32a [GLUTEN-8332][VL] Support explode/posexplode/inline outer
(#10202)
d5d2aca32a is described below
commit d5d2aca32ab73b3e088ed9d930fb3ecf37698f72
Author: Rong Ma <[email protected]>
AuthorDate: Fri Jul 18 10:57:43 2025 +0100
[GLUTEN-8332][VL] Support explode/posexplode/inline outer (#10202)
---
.../gluten/execution/GenerateExecTransformer.scala | 105 +++++++--
.../gluten/execution/MiscOperatorSuite.scala | 256 ++++++++++++---------
cpp/velox/substrait/SubstraitToVeloxPlan.cc | 14 +-
.../gluten/utils/velox/VeloxTestSettings.scala | 1 +
.../GlutenGeneratorExpressionSuite.scala | 21 ++
.../gluten/utils/velox/VeloxTestSettings.scala | 1 +
.../GlutenGeneratorExpressionSuite.scala | 21 ++
.../gluten/utils/velox/VeloxTestSettings.scala | 1 +
.../GlutenGeneratorExpressionSuite.scala | 21 ++
.../gluten/utils/velox/VeloxTestSettings.scala | 1 +
.../GlutenGeneratorExpressionSuite.scala | 21 ++
11 files changed, 317 insertions(+), 146 deletions(-)
diff --git
a/backends-velox/src/main/scala/org/apache/gluten/execution/GenerateExecTransformer.scala
b/backends-velox/src/main/scala/org/apache/gluten/execution/GenerateExecTransformer.scala
index b5766c35c3..e2d257c0c0 100644
---
a/backends-velox/src/main/scala/org/apache/gluten/execution/GenerateExecTransformer.scala
+++
b/backends-velox/src/main/scala/org/apache/gluten/execution/GenerateExecTransformer.scala
@@ -28,7 +28,7 @@ import org.apache.gluten.utils.PullOutProjectHelper
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.execution.{GenerateExec, ProjectExec, SparkPlan}
import org.apache.spark.sql.execution.metric.SQLMetrics
-import org.apache.spark.sql.types.IntegerType
+import org.apache.spark.sql.types.{BooleanType, IntegerType}
import com.google.protobuf.StringValue
@@ -70,7 +70,7 @@ case class GenerateExecTransformer(
override protected def doGeneratorValidate(
generator: Generator,
outer: Boolean): ValidationResult = {
- if (!supportsGenerate(generator, outer)) {
+ if (!supportsGenerate(generator)) {
ValidationResult.failed(
s"Velox backend does not support this generator:
${generator.getClass.getSimpleName}" +
s", outer: $outer")
@@ -132,6 +132,12 @@ case class GenerateExecTransformer(
.append(if (isStack) "1" else "0")
.append("\n")
+ // isOuter: 1 for outer, 0 for inner.
+ parametersStr
+ .append("isOuter=")
+ .append(if (outer) "1" else "0")
+ .append("\n")
+
val injectProject = if (isStack) {
// We always need to inject a Project for stack because we organize
// stack's flat params into arrays, e.g. stack(2, 1, 2, 3) is
@@ -161,17 +167,12 @@ case class GenerateExecTransformer(
}
object GenerateExecTransformer {
- def supportsGenerate(generator: Generator, outer: Boolean): Boolean = {
- // TODO: supports outer and remove this param.
- if (outer) {
- false
- } else {
- generator match {
- case _: Inline | _: ExplodeBase | _: JsonTuple | _: Stack =>
- true
- case _ =>
- false
- }
+ def supportsGenerate(generator: Generator): Boolean = {
+ generator match {
+ case _: Inline | _: ExplodeBase | _: JsonTuple | _: Stack =>
+ true
+ case _ =>
+ false
}
}
}
@@ -179,7 +180,7 @@ object GenerateExecTransformer {
object PullOutGenerateProjectHelper extends PullOutProjectHelper {
val JSON_PATH_PREFIX = "$."
def pullOutPreProject(generate: GenerateExec): SparkPlan = {
- if (GenerateExecTransformer.supportsGenerate(generate.generator,
generate.outer)) {
+ if (GenerateExecTransformer.supportsGenerate(generate.generator)) {
generate.generator match {
case _: Inline | _: ExplodeBase =>
val expressionMap = new mutable.HashMap[Expression,
NamedExpression]()
@@ -277,7 +278,7 @@ object PullOutGenerateProjectHelper extends
PullOutProjectHelper {
}
def pullOutPostProject(generate: GenerateExec): SparkPlan = {
- if (GenerateExecTransformer.supportsGenerate(generate.generator,
generate.outer)) {
+ if (GenerateExecTransformer.supportsGenerate(generate.generator)) {
generate.generator match {
case PosExplode(_) =>
val originalOrdinal = generate.generatorOutput.head
@@ -287,22 +288,76 @@ object PullOutGenerateProjectHelper extends
PullOutProjectHelper {
originalOrdinal.exprId,
originalOrdinal.qualifier)
}
- val newGenerate =
- generate.copy(generatorOutput = generate.generatorOutput.tail :+
originalOrdinal)
- ProjectExec(
- (generate.requiredChildOutput :+ ordinal) ++
generate.generatorOutput.tail,
- newGenerate)
+
+ if (generate.outer) {
+ val isNullOrEmpty =
+ AttributeReference(generatePostAliasName, BooleanType, nullable
= true)()
+
+ val newOutput = (ordinal +: generate.generatorOutput.tail).map {
+ attr =>
+ val caseWhen = CaseWhen(
+ Seq((isNullOrEmpty, Literal(null, attr.dataType))),
+ attr
+ )
+ Alias(caseWhen, generatePostAliasName)(attr.exprId,
attr.qualifier)
+ }
+
+ // Reorder the generatorOutput to match the output from the Unnest
operator in Velox.
+ val newGenerate = generate.copy(generatorOutput =
+ generate.generatorOutput.tail :+ originalOrdinal :+
isNullOrEmpty)
+
+ ProjectExec(generate.requiredChildOutput ++ newOutput, newGenerate)
+ } else {
+ val newGenerate =
+ generate.copy(generatorOutput = generate.generatorOutput.tail :+
originalOrdinal)
+ ProjectExec(
+ (generate.requiredChildOutput :+ ordinal) ++
generate.generatorOutput.tail,
+ newGenerate)
+ }
case Inline(_) | JsonTupleExplode(_) =>
val unnestOutput = {
val struct = CreateStruct(generate.generatorOutput)
val alias = Alias(struct, generatePostAliasName)()
alias.toAttribute
}
- val newGenerate = generate.copy(generatorOutput = Seq(unnestOutput))
- val newOutput = generate.generatorOutput.zipWithIndex.map {
- case (attr, i) =>
- val getStructField = GetStructField(unnestOutput, i,
Some(attr.name))
- Alias(getStructField, generatePostAliasName)(attr.exprId,
attr.qualifier)
+ if (generate.outer) {
+ val isNullOrEmpty =
+ AttributeReference(generatePostAliasName, BooleanType, nullable
= true)()
+ val newGenerate = generate.copy(generatorOutput =
Seq(unnestOutput) :+ isNullOrEmpty)
+ val newOutput = generate.generatorOutput.zipWithIndex.map {
+ case (attr, i) =>
+ val getStructField = GetStructField(unnestOutput, i,
Some(attr.name))
+ val caseWhen = CaseWhen(
+ Seq((isNullOrEmpty, Literal(null, getStructField.dataType))),
+ getStructField
+ )
+ Alias(caseWhen, generatePostAliasName)(attr.exprId,
attr.qualifier)
+ }
+ ProjectExec(generate.requiredChildOutput ++ newOutput, newGenerate)
+ } else {
+ val newGenerate = generate.copy(generatorOutput =
Seq(unnestOutput))
+ val newOutput = generate.generatorOutput.zipWithIndex.map {
+ case (attr, i) =>
+ val getStructField = GetStructField(unnestOutput, i,
Some(attr.name))
+ Alias(getStructField, generatePostAliasName)(attr.exprId,
attr.qualifier)
+ }
+ ProjectExec(generate.requiredChildOutput ++ newOutput, newGenerate)
+ }
+ case Explode(_) if generate.outer =>
+ // Drop the last column of generatorOutput, which is the boolean
representing whether
+ // the null value is unnested from the input array/map (e.g.
array(1, null)), or the
+ // array/map itself is null or empty (e.g. array(), map(), null).
+ val isNullOrEmpty =
+ AttributeReference(generatePostAliasName, BooleanType, nullable =
true)()
+ val newGenerate =
+ generate.copy(generatorOutput = generate.generatorOutput :+
isNullOrEmpty)
+ val newOutput = generate.generatorOutput.map {
+ attr =>
+ val caseWhen = CaseWhen(
+ Seq((isNullOrEmpty, Literal(null, attr.dataType))),
+ attr
+ )
+ Alias(caseWhen, generatePostAliasName)(attr.exprId,
attr.qualifier)
}
ProjectExec(generate.requiredChildOutput ++ newOutput, newGenerate)
case _ => generate
diff --git
a/backends-velox/src/test/scala/org/apache/gluten/execution/MiscOperatorSuite.scala
b/backends-velox/src/test/scala/org/apache/gluten/execution/MiscOperatorSuite.scala
index 33b7f140fb..333267e838 100644
---
a/backends-velox/src/test/scala/org/apache/gluten/execution/MiscOperatorSuite.scala
+++
b/backends-velox/src/test/scala/org/apache/gluten/execution/MiscOperatorSuite.scala
@@ -930,81 +930,98 @@ class MiscOperatorSuite extends
VeloxWholeStageTransformerSuite with AdaptiveSpa
test("test explode/posexplode function") {
Seq("explode", "posexplode").foreach {
- func =>
- // Literal: func(literal)
- runQueryAndCompare(s"""
- |SELECT $func(array(1, 2, 3));
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- runQueryAndCompare(s"""
- |SELECT $func(map(1, 'a', 2, 'b'));
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- runQueryAndCompare(
- s"""
- |SELECT $func(array(map(1, 'a', 2, 'b'), map(3, 'c', 4, 'd'),
map(5, 'e', 6, 'f')));
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- runQueryAndCompare(s"""
- |SELECT $func(map(1, array(1, 2), 2, array(3,
4)));
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
+ f =>
+ Seq(true, false).foreach {
+ isOuter =>
+ val func = if (isOuter) s"${f}_outer" else f
+ // Literal: func(literal)
+ runQueryAndCompare(s"""
+ |SELECT $func(array(1, 2, 3));
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ runQueryAndCompare(s"""
+ |SELECT $func(map(1, 'a', 2, 'b'));
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ runQueryAndCompare(
+ s"""
+ |SELECT
+ | $func(array(map(1, 'a', 2, 'b'), map(3, 'c', 4, 'd'),
map(5, '', 6, null)));
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ runQueryAndCompare(s"""
+ |SELECT $func(map(1, array(1, 2), 2,
array(3, 4), 3, array()));
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
- // CreateArray/CreateMap: func(array(col)), func(map(k, v))
- withTempView("t1") {
- sql("""select * from values (1), (2), (3), (4)
- |as tbl(a)
+ // CreateArray/CreateMap: func(array(col)), func(map(k, v))
+ withTempView("t1") {
+ sql("""select * from values (1), (2), (3), (4), (null)
+ |as tbl(a)
""".stripMargin).createOrReplaceTempView("t1")
- runQueryAndCompare(s"""
- |SELECT $func(array(a)) from t1;
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- sql("""select * from values (1, 'a'), (2, 'b'), (3, null), (4, null)
- |as tbl(a, b)
+ runQueryAndCompare(s"""
+ |SELECT $func(array(a)) from t1;
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ sql("""select * from values (1, 'a'), (2, 'b'), (3, null), (4,
null)
+ |as tbl(a, b)
""".stripMargin).createOrReplaceTempView("t1")
- runQueryAndCompare(s"""
- |SELECT $func(map(a, b)) from t1;
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- }
+ runQueryAndCompare(s"""
+ |SELECT $func(map(a, b)) from t1;
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ }
- // AttributeReference: func(col)
- withTempView("t2") {
- sql("""select * from values
- | array(1, 2, 3),
- | array(4, null)
- |as tbl(a)
+ // AttributeReference: func(col)
+ withTempView("t2") {
+ sql("""select * from values
+ | array(1, 2, 3),
+ | array(4, null),
+ | array(),
+ | null
+ |as tbl(a)
""".stripMargin).createOrReplaceTempView("t2")
- runQueryAndCompare(s"""
- |SELECT $func(a) from t2;
- |""".stripMargin) {
- // No ProjectExecTransformer is introduced.
- checkSparkOperatorChainMatch[GenerateExecTransformer,
FilterExecTransformer]
- }
- sql("""select * from values
- | map(1, 'a', 2, 'b', 3, null),
- | map(4, null)
- |as tbl(a)
+ runQueryAndCompare(s"""
+ |SELECT $func(a) from t2;
+ |""".stripMargin) {
+ df =>
+ if (!isOuter) {
+ // No ProjectExecTransformer is introduced.
+ checkSparkOperatorChainMatch[GenerateExecTransformer,
FilterExecTransformer](df)
+ }
+ checkGlutenOperatorMatch[GenerateExecTransformer](df)
+ }
+ sql("""select * from values
+ | map(1, 'a', 2, 'b', 3, null),
+ | map(4, null),
+ | map(),
+ | null
+ |as tbl(a)
""".stripMargin).createOrReplaceTempView("t2")
- runQueryAndCompare(s"""
- |SELECT $func(a) from t2;
- |""".stripMargin) {
- // No ProjectExecTransformer is introduced.
- checkSparkOperatorChainMatch[GenerateExecTransformer,
FilterExecTransformer]
- }
+ runQueryAndCompare(s"""
+ |SELECT $func(a) from t2;
+ |""".stripMargin) {
+ df =>
+ if (!isOuter) {
+ // No ProjectExecTransformer is introduced.
+ checkSparkOperatorChainMatch[GenerateExecTransformer,
FilterExecTransformer](df)
+ }
+ checkGlutenOperatorMatch[GenerateExecTransformer](df)
+ }
- runQueryAndCompare(
- s"""
- |SELECT
$func(${VeloxDummyExpression.VELOX_DUMMY_EXPRESSION}(a)) from t2;
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
+ runQueryAndCompare(
+ s"""
+ |SELECT
$func(${VeloxDummyExpression.VELOX_DUMMY_EXPRESSION}(a)) from t2;
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ }
}
}
}
@@ -1047,58 +1064,67 @@ class MiscOperatorSuite extends
VeloxWholeStageTransformerSuite with AdaptiveSpa
}
test("test inline function") {
- // Literal: func(literal)
- runQueryAndCompare(s"""
- |SELECT inline(array(
- | named_struct('c1', 0, 'c2', 1),
- | named_struct('c1', 2, 'c2', null)));
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
+ Seq(true, false).foreach {
+ isOuter =>
+ val func = if (isOuter) "inline_outer" else "inline"
- // CreateArray: func(array(col))
- withTempView("t1") {
- sql("""SELECT * from values
- | (named_struct('c1', 0, 'c2', 1)),
- | (named_struct('c1', 2, 'c2', null)),
- | (null)
- |as tbl(a)
+ // Literal: func(literal)
+ runQueryAndCompare(s"""
+ |SELECT $func(array(
+ | named_struct('c1', 0, 'c2', 1),
+ | named_struct('c1', 2, 'c2', null)));
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+
+ // CreateArray: func(array(col))
+ withTempView("t1") {
+ sql("""SELECT * from values
+ | (named_struct('c1', 0, 'c2', 1)),
+ | (named_struct('c1', 2, 'c2', null)),
+ | (null)
+ |as tbl(a)
""".stripMargin).createOrReplaceTempView("t1")
- runQueryAndCompare(s"""
- |SELECT inline(array(a)) from t1;
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- }
+ runQueryAndCompare(s"""
+ |SELECT $func(array(a)) from t1;
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ }
- withTempView("t2") {
- sql("""SELECT * from values
- | array(
- | named_struct('c1', 0, 'c2', 1),
- | null,
- | named_struct('c1', 2, 'c2', 3)
- | ),
- | array(
- | null,
- | named_struct('c1', 0, 'c2', 1),
- | named_struct('c1', 2, 'c2', 3)
- | )
- |as tbl(a)
+ withTempView("t2") {
+ sql("""SELECT * from values
+ | array(
+ | named_struct('c1', 0, 'c2', 1),
+ | null,
+ | named_struct('c1', 2, 'c2', 3)
+ | ),
+ | array(
+ | null,
+ | named_struct('c1', 0, 'c2', 1),
+ | named_struct('c1', 2, 'c2', 3)
+ | ),
+ | array(),
+ | null
+ |as tbl(a)
""".stripMargin).createOrReplaceTempView("t2")
- runQueryAndCompare("""
- |SELECT inline(a) from t2;
- |""".stripMargin) {
- checkGlutenOperatorMatch[GenerateExecTransformer]
- }
- }
+ runQueryAndCompare(s"""
+ |SELECT $func(a) from t2;
+ |""".stripMargin) {
+ checkGlutenOperatorMatch[GenerateExecTransformer]
+ }
+ }
- // Fallback for array(struct(...), null) literal.
- runQueryAndCompare(s"""
- |SELECT inline(array(
- | named_struct('c1', 0, 'c2', 1),
- | named_struct('c1', 2, 'c2', null),
- | null));
- |""".stripMargin)(_)
+ // Fallback for array(struct(...), null) literal.
+ runQueryAndCompare(s"""
+ |SELECT $func(array(
+ | named_struct('c1', 0, 'c2', 1),
+ | named_struct('c1', 2, 'c2', null),
+ | null));
+ |""".stripMargin) {
+ checkSparkOperatorMatch[GenerateExec]
+ }
+ }
}
test("test multi-generate") {
diff --git a/cpp/velox/substrait/SubstraitToVeloxPlan.cc
b/cpp/velox/substrait/SubstraitToVeloxPlan.cc
index 2fe4b14a64..53f5b7b607 100644
--- a/cpp/velox/substrait/SubstraitToVeloxPlan.cc
+++ b/cpp/velox/substrait/SubstraitToVeloxPlan.cc
@@ -876,13 +876,15 @@ core::PlanNodePtr
SubstraitToVeloxPlanConverter::toVeloxPlan(const ::substrait::
}
std::optional<std::string> ordinalityName = std::nullopt;
- if (generateRel.has_advanced_extension() &&
-
SubstraitParser::configSetInOptimization(generateRel.advanced_extension(),
"isPosExplode=")) {
- ordinalityName = std::make_optional<std::string>("pos");
- }
-
- // TODO: allow to set this in SQL
std::optional<std::string> emptyUnnestValueName = std::nullopt;
+ if (generateRel.has_advanced_extension()) {
+ if
(SubstraitParser::configSetInOptimization(generateRel.advanced_extension(),
"isPosExplode=")) {
+ ordinalityName = std::make_optional<std::string>("pos");
+ }
+ if
(SubstraitParser::configSetInOptimization(generateRel.advanced_extension(),
"isOuter=")) {
+ emptyUnnestValueName = std::make_optional<std::string>("empty_unnest");
+ }
+ }
return std::make_shared<core::UnnestNode>(
nextPlanNodeId(), replicated, unnest, std::move(unnestNames),
ordinalityName, emptyUnnestValueName, childNode);
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 91dabb92a9..2344316242 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -230,6 +230,7 @@ class VeloxTestSettings extends BackendTestSettings {
.excludeGlutenTest("from_unixtime")
enableSuite[GlutenDecimalExpressionSuite]
enableSuite[GlutenDecimalPrecisionSuite]
+ enableSuite[GlutenGeneratorExpressionSuite]
enableSuite[GlutenStringFunctionsSuite]
enableSuite[GlutenRegexpExpressionsSuite]
enableSuite[GlutenNullExpressionsSuite]
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
new file mode 100644
index 0000000000..d1867936c1
--- /dev/null
+++
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.spark.sql.catalyst.expressions
+
+import org.apache.spark.sql.GlutenTestsTrait
+
+class GlutenGeneratorExpressionSuite extends GeneratorExpressionSuite with
GlutenTestsTrait {}
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index ceeaaa1de8..dd0121393b 100644
---
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -150,6 +150,7 @@ class VeloxTestSettings extends BackendTestSettings {
enableSuite[GlutenDecimalPrecisionSuite]
enableSuite[GlutenHashExpressionsSuite]
enableSuite[GlutenHigherOrderFunctionsSuite]
+ enableSuite[GlutenGeneratorExpressionSuite]
enableSuite[GlutenIntervalExpressionsSuite]
enableSuite[GlutenJsonExpressionsSuite]
// https://github.com/apache/incubator-gluten/issues/8102
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
new file mode 100644
index 0000000000..d1867936c1
--- /dev/null
+++
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.spark.sql.catalyst.expressions
+
+import org.apache.spark.sql.GlutenTestsTrait
+
+class GlutenGeneratorExpressionSuite extends GeneratorExpressionSuite with
GlutenTestsTrait {}
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 47e8935cfa..82ba127334 100644
---
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -143,6 +143,7 @@ class VeloxTestSettings extends BackendTestSettings {
.excludeGlutenTest("from_unixtime")
enableSuite[GlutenDecimalExpressionSuite]
enableSuite[GlutenDecimalPrecisionSuite]
+ enableSuite[GlutenGeneratorExpressionSuite]
enableSuite[GlutenHashExpressionsSuite]
enableSuite[GlutenHigherOrderFunctionsSuite]
enableSuite[GlutenIntervalExpressionsSuite]
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
new file mode 100644
index 0000000000..d1867936c1
--- /dev/null
+++
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.spark.sql.catalyst.expressions
+
+import org.apache.spark.sql.GlutenTestsTrait
+
+class GlutenGeneratorExpressionSuite extends GeneratorExpressionSuite with
GlutenTestsTrait {}
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 23c89362d5..90a731c9d4 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
@@ -143,6 +143,7 @@ class VeloxTestSettings extends BackendTestSettings {
.excludeGlutenTest("from_unixtime")
enableSuite[GlutenDecimalExpressionSuite]
enableSuite[GlutenDecimalPrecisionSuite]
+ enableSuite[GlutenGeneratorExpressionSuite]
enableSuite[GlutenHashExpressionsSuite]
enableSuite[GlutenHigherOrderFunctionsSuite]
enableSuite[GlutenIntervalExpressionsSuite]
diff --git
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
new file mode 100644
index 0000000000..d1867936c1
--- /dev/null
+++
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenGeneratorExpressionSuite.scala
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.spark.sql.catalyst.expressions
+
+import org.apache.spark.sql.GlutenTestsTrait
+
+class GlutenGeneratorExpressionSuite extends GeneratorExpressionSuite with
GlutenTestsTrait {}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]