This is an automated email from the ASF dual-hosted git repository.
hongze 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 86449d0c8 [CORE][VL] Add OffloadProject to offload project having
input_file_name's support considered (#6200)
86449d0c8 is described below
commit 86449d0c87437002d6d2636e079420cacb258d82
Author: 高阳阳 <[email protected]>
AuthorDate: Fri Jun 28 16:22:37 2024 +0800
[CORE][VL] Add OffloadProject to offload project having input_file_name's
support considered (#6200)
---
.../gluten/backendsapi/velox/VeloxBackend.scala | 2 +
.../backendsapi/velox/VeloxIteratorApi.scala | 8 --
.../backendsapi/velox/VeloxSparkPlanExecApi.scala | 7 +-
.../extension/InputFileNameReplaceRule.scala | 155 ---------------------
.../execution/ScalarFunctionsValidateSuite.scala | 10 +-
.../gluten/backendsapi/BackendSettingsApi.scala | 1 +
.../extension/columnar/MiscColumnarRules.scala | 2 +-
.../extension/columnar/OffloadSingleNode.scala | 141 ++++++++++++++++++-
.../columnar/enumerated/EnumeratedTransform.scala | 1 +
.../columnar/enumerated/RasOffloadProject.scala | 18 ++-
.../spark/sql/GlutenColumnExpressionSuite.scala | 38 ++++-
.../spark/sql/GlutenColumnExpressionSuite.scala | 38 ++++-
.../spark/sql/GlutenColumnExpressionSuite.scala | 38 ++++-
.../spark/sql/GlutenColumnExpressionSuite.scala | 38 ++++-
.../scala/org/apache/gluten/GlutenConfig.scala | 11 --
.../gluten/sql/shims/spark32/Spark32Shims.scala | 11 +-
.../gluten/sql/shims/spark33/Spark33Shims.scala | 3 +
.../gluten/sql/shims/spark34/Spark34Shims.scala | 5 +-
.../gluten/sql/shims/spark35/Spark35Shims.scala | 5 +-
19 files changed, 326 insertions(+), 206 deletions(-)
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 158be10f4..82b45f2d4 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
@@ -303,6 +303,8 @@ object VeloxBackendSettings extends BackendSettingsApi {
override def supportNativeRowIndexColumn(): Boolean = true
+ override def supportNativeInputFileRelatedExpr(): Boolean = true
+
override def supportExpandExec(): Boolean = true
override def supportSortExec(): Boolean = true
diff --git
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxIteratorApi.scala
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxIteratorApi.scala
index 22862156c..613e53945 100644
---
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxIteratorApi.scala
+++
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxIteratorApi.scala
@@ -19,7 +19,6 @@ package org.apache.gluten.backendsapi.velox
import org.apache.gluten.GlutenNumaBindingInfo
import org.apache.gluten.backendsapi.IteratorApi
import org.apache.gluten.execution._
-import org.apache.gluten.extension.InputFileNameReplaceRule
import org.apache.gluten.metrics.IMetrics
import org.apache.gluten.sql.shims.SparkShimLoader
import org.apache.gluten.substrait.plan.PlanNode
@@ -134,13 +133,6 @@ class VeloxIteratorApi extends IteratorApi with Logging {
}
val metadataColumn =
SparkShimLoader.getSparkShims.generateMetadataColumns(file,
metadataColumnNames)
- metadataColumn.put(InputFileNameReplaceRule.replacedInputFileName,
file.filePath.toString)
- metadataColumn.put(
- InputFileNameReplaceRule.replacedInputFileBlockStart,
- file.start.toString)
- metadataColumn.put(
- InputFileNameReplaceRule.replacedInputFileBlockLength,
- file.length.toString)
metadataColumns.add(metadataColumn)
val partitionColumn = new JHashMap[String, String]()
for (i <- 0 until file.partitionValues.numFields) {
diff --git
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
index b48da1568..ed69a5893 100644
---
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
+++
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
@@ -806,12 +806,7 @@ class VeloxSparkPlanExecApi extends SparkPlanExecApi {
* @return
*/
override def genExtendedColumnarValidationRules(): List[SparkSession =>
Rule[SparkPlan]] = {
- val buf: ListBuffer[SparkSession => Rule[SparkPlan]] =
- ListBuffer(BloomFilterMightContainJointRewriteRule.apply,
ArrowScanReplaceRule.apply)
- if (GlutenConfig.getConf.enableInputFileNameReplaceRule) {
- buf += InputFileNameReplaceRule.apply
- }
- buf.result
+ List(BloomFilterMightContainJointRewriteRule.apply,
ArrowScanReplaceRule.apply)
}
/**
diff --git
a/backends-velox/src/main/scala/org/apache/gluten/extension/InputFileNameReplaceRule.scala
b/backends-velox/src/main/scala/org/apache/gluten/extension/InputFileNameReplaceRule.scala
deleted file mode 100644
index cd3f50d8e..000000000
---
a/backends-velox/src/main/scala/org/apache/gluten/extension/InputFileNameReplaceRule.scala
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.gluten.extension
-
-import org.apache.spark.sql.SparkSession
-import org.apache.spark.sql.catalyst.expressions.{AttributeReference,
Expression, InputFileBlockLength, InputFileBlockStart, InputFileName,
NamedExpression}
-import org.apache.spark.sql.catalyst.rules.Rule
-import org.apache.spark.sql.execution.{FileSourceScanExec, ProjectExec,
SparkPlan}
-import org.apache.spark.sql.execution.datasources.parquet.ParquetFileFormat
-import org.apache.spark.sql.execution.datasources.v2.BatchScanExec
-import org.apache.spark.sql.execution.datasources.v2.parquet.ParquetScan
-import org.apache.spark.sql.types.{LongType, StringType}
-
-object InputFileNameReplaceRule {
- val replacedInputFileName = "$input_file_name$"
- val replacedInputFileBlockStart = "$input_file_block_start$"
- val replacedInputFileBlockLength = "$input_file_block_length$"
-}
-
-case class InputFileNameReplaceRule(spark: SparkSession) extends
Rule[SparkPlan] {
- import InputFileNameReplaceRule._
-
- private def isInputFileName(expr: Expression): Boolean = {
- expr match {
- case _: InputFileName => true
- case _ => false
- }
- }
-
- private def isInputFileBlockStart(expr: Expression): Boolean = {
- expr match {
- case _: InputFileBlockStart => true
- case _ => false
- }
- }
-
- private def isInputFileBlockLength(expr: Expression): Boolean = {
- expr match {
- case _: InputFileBlockLength => true
- case _ => false
- }
- }
-
- override def apply(plan: SparkPlan): SparkPlan = {
- val replacedExprs = scala.collection.mutable.Map[String,
AttributeReference]()
-
- def hasParquetScan(plan: SparkPlan): Boolean = {
- plan match {
- case fileScan: FileSourceScanExec
- if fileScan.relation.fileFormat.isInstanceOf[ParquetFileFormat] =>
- true
- case batchScan: BatchScanExec =>
- batchScan.scan match {
- case _: ParquetScan => true
- case _ => false
- }
- case _ => plan.children.exists(hasParquetScan)
- }
- }
-
- def mayNeedConvert(expr: Expression): Boolean = {
- expr match {
- case e if isInputFileName(e) => true
- case s if isInputFileBlockStart(s) => true
- case l if isInputFileBlockLength(l) => true
- case other => other.children.exists(mayNeedConvert)
- }
- }
-
- def doConvert(expr: Expression): Expression = {
- expr match {
- case e if isInputFileName(e) =>
- replacedExprs.getOrElseUpdate(
- replacedInputFileName,
- AttributeReference(replacedInputFileName, StringType, true)())
- case s if isInputFileBlockStart(s) =>
- replacedExprs.getOrElseUpdate(
- replacedInputFileBlockStart,
- AttributeReference(replacedInputFileBlockStart, LongType, true)()
- )
- case l if isInputFileBlockLength(l) =>
- replacedExprs.getOrElseUpdate(
- replacedInputFileBlockLength,
- AttributeReference(replacedInputFileBlockLength, LongType, true)()
- )
- case other =>
- other.withNewChildren(other.children.map(child => doConvert(child)))
- }
- }
-
- def ensureChildOutputHasNewAttrs(plan: SparkPlan): SparkPlan = {
- plan match {
- case _ @ProjectExec(projectList, child) =>
- var newProjectList = projectList
- for ((_, newAttr) <- replacedExprs) {
- if (!newProjectList.exists(attr => attr.exprId == newAttr.exprId))
{
- newProjectList = newProjectList :+ newAttr.toAttribute
- }
- }
- val newChild = ensureChildOutputHasNewAttrs(child)
- ProjectExec(newProjectList, newChild)
- case f: FileSourceScanExec =>
- var newOutput = f.output
- for ((_, newAttr) <- replacedExprs) {
- if (!newOutput.exists(attr => attr.exprId == newAttr.exprId)) {
- newOutput = newOutput :+ newAttr.toAttribute
- }
- }
- f.copy(output = newOutput)
-
- case b: BatchScanExec =>
- var newOutput = b.output
- for ((_, newAttr) <- replacedExprs) {
- if (!newOutput.exists(attr => attr.exprId == newAttr.exprId)) {
- newOutput = newOutput :+ newAttr
- }
- }
- b.copy(output = newOutput)
- case other =>
- val newChildren = other.children.map(ensureChildOutputHasNewAttrs)
- other.withNewChildren(newChildren)
- }
- }
-
- def replaceInputFileNameInProject(plan: SparkPlan): SparkPlan = {
- plan match {
- case _ @ProjectExec(projectList, child)
- if projectList.exists(mayNeedConvert) && hasParquetScan(plan) =>
- val newProjectList = projectList.map {
- expr => doConvert(expr).asInstanceOf[NamedExpression]
- }
- val newChild =
replaceInputFileNameInProject(ensureChildOutputHasNewAttrs(child))
- ProjectExec(newProjectList, newChild)
- case other =>
- val newChildren = other.children.map(replaceInputFileNameInProject)
- other.withNewChildren(newChildren)
- }
- }
- replaceInputFileNameInProject(plan)
- }
-}
diff --git
a/backends-velox/src/test/scala/org/apache/gluten/execution/ScalarFunctionsValidateSuite.scala
b/backends-velox/src/test/scala/org/apache/gluten/execution/ScalarFunctionsValidateSuite.scala
index a2baf95ec..bd32a799c 100644
---
a/backends-velox/src/test/scala/org/apache/gluten/execution/ScalarFunctionsValidateSuite.scala
+++
b/backends-velox/src/test/scala/org/apache/gluten/execution/ScalarFunctionsValidateSuite.scala
@@ -645,13 +645,9 @@ class ScalarFunctionsValidateSuite extends
FunctionsValidateTest {
}
test("Test input_file_name function") {
- withSQLConf(
- "spark.gluten.sql.enableInputFileNameReplaceRule" -> "true"
- ) {
- runQueryAndCompare("""SELECT input_file_name(), l_orderkey
- | from lineitem limit 100""".stripMargin) {
- checkGlutenOperatorMatch[ProjectExecTransformer]
- }
+ runQueryAndCompare("""SELECT input_file_name(), l_orderkey
+ | from lineitem limit 100""".stripMargin) {
+ checkGlutenOperatorMatch[ProjectExecTransformer]
}
}
diff --git
a/gluten-core/src/main/scala/org/apache/gluten/backendsapi/BackendSettingsApi.scala
b/gluten-core/src/main/scala/org/apache/gluten/backendsapi/BackendSettingsApi.scala
index b132366e6..50292839b 100644
---
a/gluten-core/src/main/scala/org/apache/gluten/backendsapi/BackendSettingsApi.scala
+++
b/gluten-core/src/main/scala/org/apache/gluten/backendsapi/BackendSettingsApi.scala
@@ -44,6 +44,7 @@ trait BackendSettingsApi {
def supportNativeWrite(fields: Array[StructField]): Boolean = true
def supportNativeMetadataColumns(): Boolean = false
def supportNativeRowIndexColumn(): Boolean = false
+ def supportNativeInputFileRelatedExpr(): Boolean = false
def supportExpandExec(): Boolean = false
def supportSortExec(): Boolean = false
diff --git
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/MiscColumnarRules.scala
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/MiscColumnarRules.scala
index 8ed2137f4..15fc8bea7 100644
---
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/MiscColumnarRules.scala
+++
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/MiscColumnarRules.scala
@@ -30,7 +30,7 @@ object MiscColumnarRules {
object TransformPreOverrides {
def apply(): TransformPreOverrides = {
TransformPreOverrides(
- List(OffloadFilter()),
+ List(OffloadProject(), OffloadFilter()),
List(
OffloadOthers(),
OffloadAggregate(),
diff --git
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/OffloadSingleNode.scala
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/OffloadSingleNode.scala
index 75da28e30..11db0bc1f 100644
---
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/OffloadSingleNode.scala
+++
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/OffloadSingleNode.scala
@@ -26,15 +26,19 @@ import org.apache.gluten.utils.{LogLevelUtil, PlanUtil}
import org.apache.spark.api.python.EvalPythonExecTransformer
import org.apache.spark.internal.Logging
+import org.apache.spark.sql.catalyst.expressions.{Attribute,
AttributeReference, Expression, InputFileBlockLength, InputFileBlockStart,
InputFileName, NamedExpression}
import org.apache.spark.sql.execution._
import org.apache.spark.sql.execution.aggregate.{HashAggregateExec,
ObjectHashAggregateExec, SortAggregateExec}
import org.apache.spark.sql.execution.datasources.WriteFilesExec
-import org.apache.spark.sql.execution.datasources.v2.BatchScanExec
+import org.apache.spark.sql.execution.datasources.v2.{BatchScanExec,
DataSourceV2ScanExecBase}
import org.apache.spark.sql.execution.exchange.{BroadcastExchangeExec,
ShuffleExchangeExec}
import org.apache.spark.sql.execution.joins._
import org.apache.spark.sql.execution.python.{ArrowEvalPythonExec,
BatchEvalPythonExec}
import org.apache.spark.sql.execution.window.{WindowExec,
WindowGroupLimitExecShim}
import org.apache.spark.sql.hive.HiveTableScanExecTransformer
+import org.apache.spark.sql.types.{LongType, StringType}
+
+import scala.collection.mutable.Map
/**
* Converts a vanilla Spark plan node into Gluten plan node. Gluten plan is
supposed to be executed
@@ -181,7 +185,138 @@ case class OffloadJoin() extends OffloadSingleNode with
LogLevelUtil {
case other => other
}
}
+}
+
+case class OffloadProject() extends OffloadSingleNode with LogLevelUtil {
+ private def containsInputFileRelatedExpr(expr: Expression): Boolean = {
+ expr match {
+ case _: InputFileName | _: InputFileBlockStart | _: InputFileBlockLength
=> true
+ case _ => expr.children.exists(containsInputFileRelatedExpr)
+ }
+ }
+
+ private def rewriteExpr(
+ expr: Expression,
+ replacedExprs: Map[String, AttributeReference]): Expression = {
+ expr match {
+ case _: InputFileName =>
+ replacedExprs.getOrElseUpdate(
+ expr.prettyName,
+ AttributeReference(expr.prettyName, StringType, false)())
+ case _: InputFileBlockStart =>
+ replacedExprs.getOrElseUpdate(
+ expr.prettyName,
+ AttributeReference(expr.prettyName, LongType, false)())
+ case _: InputFileBlockLength =>
+ replacedExprs.getOrElseUpdate(
+ expr.prettyName,
+ AttributeReference(expr.prettyName, LongType, false)())
+ case other =>
+ other.withNewChildren(other.children.map(child => rewriteExpr(child,
replacedExprs)))
+ }
+ }
+
+ private def addMetadataCol(
+ plan: SparkPlan,
+ replacedExprs: Map[String, AttributeReference]): SparkPlan = {
+ def genNewOutput(output: Seq[Attribute]): Seq[Attribute] = {
+ var newOutput = output
+ for ((_, newAttr) <- replacedExprs) {
+ if (!newOutput.exists(attr => attr.exprId == newAttr.exprId)) {
+ newOutput = newOutput :+ newAttr
+ }
+ }
+ newOutput
+ }
+ def genNewProjectList(projectList: Seq[NamedExpression]):
Seq[NamedExpression] = {
+ var newProjectList = projectList
+ for ((_, newAttr) <- replacedExprs) {
+ if (!newProjectList.exists(attr => attr.exprId == newAttr.exprId)) {
+ newProjectList = newProjectList :+ newAttr.toAttribute
+ }
+ }
+ newProjectList
+ }
+
+ plan match {
+ case f: FileSourceScanExec =>
+ f.copy(output = genNewOutput(f.output))
+ case f: FileSourceScanExecTransformer =>
+ f.copy(output = genNewOutput(f.output))
+ case b: BatchScanExec =>
+ b.copy(output =
genNewOutput(b.output).asInstanceOf[Seq[AttributeReference]])
+ case b: BatchScanExecTransformer =>
+ b.copy(output =
genNewOutput(b.output).asInstanceOf[Seq[AttributeReference]])
+ case p @ ProjectExec(projectList, child) =>
+ p.copy(genNewProjectList(projectList), addMetadataCol(child,
replacedExprs))
+ case p @ ProjectExecTransformer(projectList, child) =>
+ p.copy(genNewProjectList(projectList), addMetadataCol(child,
replacedExprs))
+ case _ => plan.withNewChildren(plan.children.map(addMetadataCol(_,
replacedExprs)))
+ }
+ }
+
+ private def tryOffloadProjectExecWithInputFileRelatedExprs(
+ projectExec: ProjectExec): SparkPlan = {
+ def findScanNodes(plan: SparkPlan): Seq[SparkPlan] = {
+ plan.collect {
+ case f @ (_: FileSourceScanExec | _: AbstractFileSourceScanExec |
+ _: DataSourceV2ScanExecBase) =>
+ f
+ }
+ }
+ val addHint = AddTransformHintRule()
+ val newProjectList =
projectExec.projectList.filterNot(containsInputFileRelatedExpr)
+ val newProjectExec = ProjectExec(newProjectList, projectExec.child)
+ addHint.apply(newProjectExec)
+ if (TransformHints.isNotTransformable(newProjectExec)) {
+ // Project is still not transformable after remove `input_file_name`
expressions.
+ projectExec
+ } else {
+ // the project with `input_file_name` expression should have at most
+ // one data source, reference:
+ //
https://github.com/apache/spark/blob/e459674127e7b21e2767cc62d10ea6f1f941936c
+ //
/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala#L506
+ val leafScans = findScanNodes(projectExec)
+ assert(leafScans.size <= 1)
+ if (leafScans.isEmpty ||
TransformHints.isNotTransformable(leafScans(0))) {
+ // It means
+ // 1. projectExec has `input_file_name` but no scan child.
+ // 2. It has scan child node but the scan node fallback.
+ projectExec
+ } else {
+ val replacedExprs = scala.collection.mutable.Map[String,
AttributeReference]()
+ val newProjectList = projectExec.projectList.map {
+ expr => rewriteExpr(expr,
replacedExprs).asInstanceOf[NamedExpression]
+ }
+ val newChild = addMetadataCol(projectExec.child, replacedExprs)
+ logDebug(
+ s"Columnar Processing for ${projectExec.getClass} with " +
+ s"ProjectList ${projectExec.projectList} is currently supported.")
+ ProjectExecTransformer(newProjectList, newChild)
+ }
+ }
+ }
+ private def genProjectExec(projectExec: ProjectExec): SparkPlan = {
+ if (
+ TransformHints.isNotTransformable(projectExec) &&
+ BackendsApiManager.getSettings.supportNativeInputFileRelatedExpr() &&
+ projectExec.projectList.exists(containsInputFileRelatedExpr)
+ ) {
+ tryOffloadProjectExecWithInputFileRelatedExprs(projectExec)
+ } else if (TransformHints.isNotTransformable(projectExec)) {
+ projectExec
+ } else {
+ logDebug(s"Columnar Processing for ${projectExec.getClass} is currently
supported.")
+ ProjectExecTransformer(projectExec.projectList, projectExec.child)
+ }
+ }
+
+ override def offload(plan: SparkPlan): SparkPlan = plan match {
+ case p: ProjectExec =>
+ genProjectExec(p)
+ case other => other
+ }
}
// Filter transformation.
@@ -261,10 +396,6 @@ object OffloadOthers {
case plan: CoalesceExec =>
logDebug(s"Columnar Processing for ${plan.getClass} is currently
supported.")
ColumnarCoalesceExec(plan.numPartitions, plan.child)
- case plan: ProjectExec =>
- val columnarChild = plan.child
- logDebug(s"Columnar Processing for ${plan.getClass} is currently
supported.")
- ProjectExecTransformer(plan.projectList, columnarChild)
case plan: SortAggregateExec =>
logDebug(s"Columnar Processing for ${plan.getClass} is currently
supported.")
HashAggregateExecBaseTransformer.from(plan) {
diff --git
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/EnumeratedTransform.scala
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/EnumeratedTransform.scala
index 9a54a1014..30e4c0a79 100644
---
a/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/EnumeratedTransform.scala
+++
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/EnumeratedTransform.scala
@@ -54,6 +54,7 @@ case class EnumeratedTransform(session: SparkSession,
outputsColumnar: Boolean)
RasOffload.from[BaseJoinExec](OffloadJoin()).toRule,
RasOffloadHashAggregate.toRule,
RasOffloadFilter.toRule,
+ RasOffloadProject.toRule,
RasOffload.from[DataSourceV2ScanExecBase](OffloadOthers()).toRule,
RasOffload.from[DataSourceScanExec](OffloadOthers()).toRule,
RasOffload
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/RasOffloadProject.scala
similarity index 60%
copy from
gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
copy to
gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/RasOffloadProject.scala
index edd2a5a96..0bbf57499 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
+++
b/gluten-core/src/main/scala/org/apache/gluten/extension/columnar/enumerated/RasOffloadProject.scala
@@ -14,6 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.spark.sql
+package org.apache.gluten.extension.columnar.enumerated
-class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {}
+import org.apache.gluten.execution.ProjectExecTransformer
+
+import org.apache.spark.sql.execution.{ProjectExec, SparkPlan}
+
+object RasOffloadProject extends RasOffload {
+ override def offload(node: SparkPlan): SparkPlan = node match {
+ case ProjectExec(projectList, child) =>
+ ProjectExecTransformer(projectList, child)
+ case other =>
+ other
+ }
+
+ override def typeIdentifier(): RasOffload.TypeIdentifier =
+ RasOffload.TypeIdentifier.of[ProjectExec]
+}
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
index edd2a5a96..a4b530e63 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
+++
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
@@ -16,4 +16,40 @@
*/
package org.apache.spark.sql
-class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {}
+import org.apache.spark.sql.execution.ProjectExec
+import org.apache.spark.sql.functions.{expr, input_file_name}
+import org.apache.spark.sql.types.{ArrayType, IntegerType, StringType,
StructField, StructType}
+
+class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {
+ testGluten("input_file_name with scan is fallback") {
+ withTempPath {
+ dir =>
+ val rawData = Seq(
+ Row(1, "Alice", Seq(Row(Seq(1, 2, 3)))),
+ Row(2, "Bob", Seq(Row(Seq(4, 5)))),
+ Row(3, "Charlie", Seq(Row(Seq(6, 7, 8, 9))))
+ )
+ val schema = StructType(
+ Array(
+ StructField("id", IntegerType, nullable = false),
+ StructField("name", StringType, nullable = false),
+ StructField(
+ "nested_column",
+ ArrayType(
+ StructType(Array(
+ StructField("array_in_struct", ArrayType(IntegerType),
nullable = true)
+ ))),
+ nullable = true)
+ ))
+ val data: DataFrame =
spark.createDataFrame(sparkContext.parallelize(rawData), schema)
+ data.write.parquet(dir.getCanonicalPath)
+
+ val q =
+ spark.read.parquet(dir.getCanonicalPath).select(input_file_name(),
expr("nested_column"))
+ val firstRow = q.head()
+ assert(firstRow.getString(0).contains(dir.toURI.getPath))
+ val project = q.queryExecution.executedPlan.collect { case p:
ProjectExec => p }
+ assert(project.size == 1)
+ }
+ }
+}
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
index edd2a5a96..a4b530e63 100644
---
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
+++
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
@@ -16,4 +16,40 @@
*/
package org.apache.spark.sql
-class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {}
+import org.apache.spark.sql.execution.ProjectExec
+import org.apache.spark.sql.functions.{expr, input_file_name}
+import org.apache.spark.sql.types.{ArrayType, IntegerType, StringType,
StructField, StructType}
+
+class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {
+ testGluten("input_file_name with scan is fallback") {
+ withTempPath {
+ dir =>
+ val rawData = Seq(
+ Row(1, "Alice", Seq(Row(Seq(1, 2, 3)))),
+ Row(2, "Bob", Seq(Row(Seq(4, 5)))),
+ Row(3, "Charlie", Seq(Row(Seq(6, 7, 8, 9))))
+ )
+ val schema = StructType(
+ Array(
+ StructField("id", IntegerType, nullable = false),
+ StructField("name", StringType, nullable = false),
+ StructField(
+ "nested_column",
+ ArrayType(
+ StructType(Array(
+ StructField("array_in_struct", ArrayType(IntegerType),
nullable = true)
+ ))),
+ nullable = true)
+ ))
+ val data: DataFrame =
spark.createDataFrame(sparkContext.parallelize(rawData), schema)
+ data.write.parquet(dir.getCanonicalPath)
+
+ val q =
+ spark.read.parquet(dir.getCanonicalPath).select(input_file_name(),
expr("nested_column"))
+ val firstRow = q.head()
+ assert(firstRow.getString(0).contains(dir.toURI.getPath))
+ val project = q.queryExecution.executedPlan.collect { case p:
ProjectExec => p }
+ assert(project.size == 1)
+ }
+ }
+}
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
index edd2a5a96..a4b530e63 100644
---
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
+++
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
@@ -16,4 +16,40 @@
*/
package org.apache.spark.sql
-class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {}
+import org.apache.spark.sql.execution.ProjectExec
+import org.apache.spark.sql.functions.{expr, input_file_name}
+import org.apache.spark.sql.types.{ArrayType, IntegerType, StringType,
StructField, StructType}
+
+class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {
+ testGluten("input_file_name with scan is fallback") {
+ withTempPath {
+ dir =>
+ val rawData = Seq(
+ Row(1, "Alice", Seq(Row(Seq(1, 2, 3)))),
+ Row(2, "Bob", Seq(Row(Seq(4, 5)))),
+ Row(3, "Charlie", Seq(Row(Seq(6, 7, 8, 9))))
+ )
+ val schema = StructType(
+ Array(
+ StructField("id", IntegerType, nullable = false),
+ StructField("name", StringType, nullable = false),
+ StructField(
+ "nested_column",
+ ArrayType(
+ StructType(Array(
+ StructField("array_in_struct", ArrayType(IntegerType),
nullable = true)
+ ))),
+ nullable = true)
+ ))
+ val data: DataFrame =
spark.createDataFrame(sparkContext.parallelize(rawData), schema)
+ data.write.parquet(dir.getCanonicalPath)
+
+ val q =
+ spark.read.parquet(dir.getCanonicalPath).select(input_file_name(),
expr("nested_column"))
+ val firstRow = q.head()
+ assert(firstRow.getString(0).contains(dir.toURI.getPath))
+ val project = q.queryExecution.executedPlan.collect { case p:
ProjectExec => p }
+ assert(project.size == 1)
+ }
+ }
+}
diff --git
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
index edd2a5a96..8a28c4e98 100644
---
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
+++
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenColumnExpressionSuite.scala
@@ -16,4 +16,40 @@
*/
package org.apache.spark.sql
-class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {}
+import org.apache.spark.sql.execution.ProjectExec
+import org.apache.spark.sql.functions.{expr, input_file_name}
+import org.apache.spark.sql.types._
+
+class GlutenColumnExpressionSuite extends ColumnExpressionSuite with
GlutenSQLTestsTrait {
+ testGluten("input_file_name with scan is fallback") {
+ withTempPath {
+ dir =>
+ val rawData = Seq(
+ Row(1, "Alice", Seq(Row(Seq(1, 2, 3)))),
+ Row(2, "Bob", Seq(Row(Seq(4, 5)))),
+ Row(3, "Charlie", Seq(Row(Seq(6, 7, 8, 9))))
+ )
+ val schema = StructType(
+ Array(
+ StructField("id", IntegerType, nullable = false),
+ StructField("name", StringType, nullable = false),
+ StructField(
+ "nested_column",
+ ArrayType(
+ StructType(Array(
+ StructField("array_in_struct", ArrayType(IntegerType),
nullable = true)
+ ))),
+ nullable = true)
+ ))
+ val data: DataFrame =
spark.createDataFrame(sparkContext.parallelize(rawData), schema)
+ data.write.parquet(dir.getCanonicalPath)
+
+ val q =
+ spark.read.parquet(dir.getCanonicalPath).select(input_file_name(),
expr("nested_column"))
+ val firstRow = q.head()
+ assert(firstRow.getString(0).contains(dir.toURI.getPath))
+ val project = q.queryExecution.executedPlan.collect { case p:
ProjectExec => p }
+ assert(project.size == 1)
+ }
+ }
+}
diff --git a/shims/common/src/main/scala/org/apache/gluten/GlutenConfig.scala
b/shims/common/src/main/scala/org/apache/gluten/GlutenConfig.scala
index 58b99a7f3..ec80ba86a 100644
--- a/shims/common/src/main/scala/org/apache/gluten/GlutenConfig.scala
+++ b/shims/common/src/main/scala/org/apache/gluten/GlutenConfig.scala
@@ -37,7 +37,6 @@ case class GlutenNumaBindingInfo(
class GlutenConfig(conf: SQLConf) extends Logging {
import GlutenConfig._
- def enableInputFileNameReplaceRule: Boolean =
conf.getConf(INPUT_FILE_NAME_REPLACE_RULE_ENABLED)
def enableAnsiMode: Boolean = conf.ansiEnabled
def enableGluten: Boolean = conf.getConf(GLUTEN_ENABLED)
@@ -767,16 +766,6 @@ object GlutenConfig {
.booleanConf
.createWithDefault(GLUTEN_ENABLE_BY_DEFAULT)
- val INPUT_FILE_NAME_REPLACE_RULE_ENABLED =
- buildConf("spark.gluten.sql.enableInputFileNameReplaceRule")
- .internal()
- .doc(
- "Experimental: This config apply for velox backend to specify whether
to enable " +
- "inputFileNameReplaceRule to support offload input_file_name " +
- "expression to native.")
- .booleanConf
- .createWithDefault(false)
-
// FIXME the option currently controls both JVM and native validation
against a Substrait plan.
val NATIVE_VALIDATION_ENABLED =
buildConf("spark.gluten.sql.enable.native.validation")
diff --git
a/shims/spark32/src/main/scala/org/apache/gluten/sql/shims/spark32/Spark32Shims.scala
b/shims/spark32/src/main/scala/org/apache/gluten/sql/shims/spark32/Spark32Shims.scala
index b9c37ef3d..b036d6dd9 100644
---
a/shims/spark32/src/main/scala/org/apache/gluten/sql/shims/spark32/Spark32Shims.scala
+++
b/shims/spark32/src/main/scala/org/apache/gluten/sql/shims/spark32/Spark32Shims.scala
@@ -27,7 +27,7 @@ import org.apache.spark.sql.{AnalysisException, SparkSession}
import org.apache.spark.sql.catalyst.InternalRow
import org.apache.spark.sql.catalyst.catalog.BucketSpec
import org.apache.spark.sql.catalyst.csv.CSVOptions
-import org.apache.spark.sql.catalyst.expressions.{Attribute,
AttributeReference, BinaryExpression, Expression}
+import org.apache.spark.sql.catalyst.expressions.{Attribute,
AttributeReference, BinaryExpression, Expression, InputFileBlockLength,
InputFileBlockStart, InputFileName}
import
org.apache.spark.sql.catalyst.expressions.aggregate.TypedImperativeAggregate
import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
import org.apache.spark.sql.catalyst.plans.physical.{Distribution,
HashClusteredDistribution}
@@ -236,8 +236,13 @@ class Spark32Shims extends SparkShims {
override def generateMetadataColumns(
file: PartitionedFile,
- metadataColumnNames: Seq[String]): JMap[String, String] =
- new JHashMap[String, String]()
+ metadataColumnNames: Seq[String]): JMap[String, String] = {
+ val metadataColumn = new JHashMap[String, String]()
+ metadataColumn.put(InputFileName().prettyName, file.filePath)
+ metadataColumn.put(InputFileBlockStart().prettyName, file.start.toString)
+ metadataColumn.put(InputFileBlockLength().prettyName, file.length.toString)
+ metadataColumn
+ }
def getAnalysisExceptionPlan(ae: AnalysisException): Option[LogicalPlan] = {
ae.plan
diff --git
a/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
b/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
index d6292b46c..8b12c2642 100644
---
a/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
+++
b/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
@@ -231,6 +231,9 @@ class Spark33Shims extends SparkShims {
case _ =>
}
}
+ metadataColumn.put(InputFileName().prettyName, file.filePath)
+ metadataColumn.put(InputFileBlockStart().prettyName, file.start.toString)
+ metadataColumn.put(InputFileBlockLength().prettyName, file.length.toString)
metadataColumn
}
diff --git
a/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
b/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
index c718f4ed2..420be8511 100644
---
a/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
+++
b/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
@@ -240,8 +240,9 @@ class Spark34Shims extends SparkShims {
case _ =>
}
}
-
- // TODO: row_index metadata support
+ metadataColumn.put(InputFileName().prettyName, file.filePath.toString)
+ metadataColumn.put(InputFileBlockStart().prettyName, file.start.toString)
+ metadataColumn.put(InputFileBlockLength().prettyName, file.length.toString)
metadataColumn
}
diff --git
a/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
b/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
index f6feae01a..8ac8d323e 100644
---
a/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
+++
b/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
@@ -238,8 +238,9 @@ class Spark35Shims extends SparkShims {
case _ =>
}
}
-
- // TODO row_index metadata support
+ metadataColumn.put(InputFileName().prettyName, file.filePath.toString)
+ metadataColumn.put(InputFileBlockStart().prettyName, file.start.toString)
+ metadataColumn.put(InputFileBlockLength().prettyName, file.length.toString)
metadataColumn
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]