You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@spark.apache.org by GitBox <gi...@apache.org> on 2019/04/10 01:18:27 UTC
[GitHub] [spark] eatoncys commented on a change in pull request #23010:
[SPARK-26012][SQL]Null and '' values should not cause dynamic partition
failure of string types
eatoncys commented on a change in pull request #23010: [SPARK-26012][SQL]Null and '' values should not cause dynamic partition failure of string types
URL: https://github.com/apache/spark/pull/23010#discussion_r273759651
##########
File path: sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/DataSourceStrategy.scala
##########
@@ -260,6 +261,75 @@ class FindDataSourceTable(sparkSession: SparkSession) extends Rule[LogicalPlan]
}
}
+/** A function that converts the empty string to null for partition values. */
+case class Empty2Null(child: Expression) extends UnaryExpression with String2StringExpression {
+ override def convert(v: UTF8String): UTF8String = if (v.numBytes() == 0) null else v
+ override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
+ nullSafeCodeGen(ctx, ev, c => {
+ val setIsNull = if (nullable) s"${ev.isNull} = true" else ""
+ s"""if ($c.numBytes() == 0) {
+ | $setIsNull;
+ | ${ev.value} = null;
+ |} else {
+ | ${ev.value} = $c;
+ |}""".stripMargin
+ })
+ }
+}
+
+/** A strategy that converts the empty string to null for partition values. */
+case class UpdateEmptyValueOfPartitionToNull(conf: SQLConf) extends Rule[LogicalPlan] {
+ private def updateQueryPlan(query: LogicalPlan,
+ partitionColumnNames: Seq[String]): LogicalPlan = {
+ val partitionColumns = partitionColumnNames.map { name =>
+ query.output.find(a => conf.resolver(a.name, name)).getOrElse {
+ throw new AnalysisException(
+ s"Unable to resolve $name given [${query.output.map(_.name).mkString(", ")}]")
+ }
+ }
+ val partitionSet = AttributeSet(partitionColumns)
+ var needConvert = false
+ val projectList: Seq[NamedExpression] = query.output.map {
+ case p if partitionSet.contains(p) && p.dataType == StringType && p.nullable =>
+ needConvert = true
+ Alias(Empty2Null(p), p.name)()
+ case attr => attr
+ }
+ if (needConvert) {
+ Project(projectList, query)
+ } else {
+ query
+ }
+ }
+
+ override def apply(plan: LogicalPlan): LogicalPlan = plan resolveOperatorsUp {
+ case i: InsertIntoHadoopFsRelationCommand =>
+ val actualQuery = updateQueryPlan(i.query, i.partitionColumns.map(_.name))
+ val partitionColumns = i.partitionColumns.map { col =>
+ actualQuery.output.find(a => conf.resolver(a.name, col.name)).getOrElse{
+ throw new AnalysisException(
+ s"Unable to resolve ${col.name} given [${i.output.map(_.name).mkString(", ")}]")
+ }
+ }
+ i.copy(partitionColumns = partitionColumns, query = actualQuery)
Review comment:
`CheckAnalysis` will fail if it not updated, because the attributes in `partitionColumns` is old.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
With regards,
Apache Git Services
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org