Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d51c0dc
[SPARK-28227][SQL] Support TRANSFORM with aggregation
AngersZhuuuu Jul 13, 2020
5d85160
Fix UT
AngersZhuuuu Jul 14, 2020
dbb4d04
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Sep 4, 2020
2b8912e
Update SparkSqlParserSuite.scala
AngersZhuuuu Sep 4, 2020
d89afa9
Update SparkSqlParserSuite.scala
AngersZhuuuu Sep 4, 2020
b1cc739
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Dec 22, 2020
f2a640b
solve import
AngersZhuuuu Dec 22, 2020
b04909c
follow comment
AngersZhuuuu Dec 22, 2020
671711b
Update SQLQuerySuite.scala
AngersZhuuuu Dec 22, 2020
1a4262b
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Dec 23, 2020
8df104b
Fix UT
AngersZhuuuu Dec 23, 2020
1b4e0c1
update
AngersZhuuuu Dec 23, 2020
a85753f
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Dec 23, 2020
3eb8d11
follow commnet
AngersZhuuuu Jan 4, 2021
2e146c3
follow comment
AngersZhuuuu Jan 4, 2021
ca5a032
follow comment
AngersZhuuuu Jan 5, 2021
614f8f9
Add UT
AngersZhuuuu Jan 5, 2021
ee16a2f
follow comment
AngersZhuuuu Jan 5, 2021
9ef73b6
Update SparkSqlParserSuite.scala
AngersZhuuuu Jan 5, 2021
707f1e6
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Jan 6, 2021
327566f
Update transform.sql.out
AngersZhuuuu Jan 6, 2021
fa293cd
Update AstBuilder.scala
AngersZhuuuu Jan 6, 2021
a8233d4
Remove input parameters
AngersZhuuuu Jan 7, 2021
c3d423a
Revert "Remove input parameters"
AngersZhuuuu Jan 7, 2021
cf4085a
follow comment
AngersZhuuuu Jan 7, 2021
1278705
Merge branch 'master' into SPARK-28227-NEW
AngersZhuuuu Mar 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@ fromStatementBody
querySpecification
: transformClause
fromClause?
whereClause? #transformQuerySpecification
whereClause?
aggregationClause?
havingClause? #transformQuerySpecification
Comment thread
maropu marked this conversation as resolved.
Outdated
Comment thread
AngersZhuuuu marked this conversation as resolved.
Outdated
| selectClause
fromClause?
lateralView*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1363,12 +1363,7 @@ class Analyzer(
}
// If the script transformation input contains Stars, expand it.
Comment thread
AngersZhuuuu marked this conversation as resolved.
Outdated
case t: ScriptTransformation if containsStar(t.input) =>
t.copy(
input = t.input.flatMap {
case s: Star => s.expand(t.child, resolver)
case o => o :: Nil
}
)
t.copy(input = t.child.output)
Comment thread
AngersZhuuuu marked this conversation as resolved.
case g: Generate if containsStar(g.generator.children) =>
failAnalysis("Invalid usage of '*' in explode/json_tuple/UDTF")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
ctx,
ctx.transformClause,
ctx.whereClause,
ctx.aggregationClause,
ctx.havingClause,
plan
)
} else {
Expand Down Expand Up @@ -583,7 +585,13 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
val from = OneRowRelation().optional(ctx.fromClause) {
visitFromClause(ctx.fromClause)
}
withTransformQuerySpecification(ctx, ctx.transformClause, ctx.whereClause, from)
withTransformQuerySpecification(
ctx,
ctx.transformClause,
ctx.whereClause,
ctx.aggregationClause,
ctx.havingClause, from
Comment thread
AngersZhuuuu marked this conversation as resolved.
Outdated
)
}

override def visitRegularQuerySpecification(
Expand Down Expand Up @@ -638,7 +646,9 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
ctx: ParserRuleContext,
transformClause: TransformClauseContext,
whereClause: WhereClauseContext,
relation: LogicalPlan): LogicalPlan = withOrigin(ctx) {
aggregationClause: AggregationClauseContext,
havingClause: HavingClauseContext,
relation: LogicalPlan): LogicalPlan = withOrigin(ctx) {
// Add where.
val withFilter = relation.optionalMap(whereClause)(withWhereClause)

Expand All @@ -660,12 +670,39 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
AttributeReference("value", StringType)()), true)
}

val namedExpressions = expressions.map {
case e: NamedExpression => e
case e: Expression => UnresolvedAlias(e)
}

def createProject() = if (namedExpressions.nonEmpty) {
Project(namedExpressions, withFilter)
} else {
withFilter
}

val withProject = if (aggregationClause == null && havingClause != null) {
if (conf.getConf(SQLConf.LEGACY_HAVING_WITHOUT_GROUP_BY_AS_WHERE)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a new feature, we don't need to follow this config here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a new feature, we don't need to follow this config here?

We can follow this, why not, but I need to add test case about this.

// If the legacy conf is set, treat HAVING without GROUP BY as WHERE.
withHavingClause(havingClause, createProject())
} else {
// According to SQL standard, HAVING without GROUP BY means global aggregate.
withHavingClause(havingClause, Aggregate(Nil, namedExpressions, withFilter))
}
} else if (aggregationClause != null) {
val aggregate = withAggregationClause(aggregationClause, namedExpressions, withFilter)
aggregate.optionalMap(havingClause)(withHavingClause)
} else {
// When hitting this branch, `having` must be null.
createProject()
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part just copied from withSelectQuerySpecification? If so, could you share it between withSelectQuerySpecification and withTransformQuerySpecification?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part just copied from withSelectQuerySpecification? If so, could you share it between withSelectQuerySpecification and withTransformQuerySpecification?

Done


// Create the transform.
ScriptTransformation(
expressions,
Seq(UnresolvedStar(None)),
Comment thread
maropu marked this conversation as resolved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, on second thought, we cannot remove this param input from ScriptTransformation in this PR? Since the input exprs of the current ScriptTransformation implementaiton always coms from child's output, IIUC we don't need this param anymore?

@AngersZhuuuu AngersZhuuuu Jan 6, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, on second thought, we cannot remove this param input from ScriptTransformation in this PR? Since the input exprs of the current ScriptTransformation implementaiton always coms from child's output, IIUC we don't need this param anymore?

It looks like this. We can replace input with child.output directly. That's a really nice suggestion since current way(converting LogicalPlan it looks a little weird). If we remove this input parameter and add correct comment.
The whole process looks more natural. I have tried it in local, a new big diff. How about a new ticket about refactor this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Just removing the param causes a big diff? I'd like to remove the current the weird Analyzer code to handle the unresolved star in this PR though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Just removing the param causes a big diff? I'd like to remove the current the weird Analyzer code to handle the unresolved star in this PR though.

Yea, will change a lot of file. Compared to the origin code here(the current the weird Analyzer code) ,current change seem s not so weird, but for the whole process, really weird.

Create a ticket for this https://issues.apache.org/jira/browse/SPARK-34035
image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, will change a lot of file.

How many changed lines of codes there?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, will change a lot of file.

How many changed lines of codes there?

Nearly 100 lines, most of changes are about UT

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I don't have a strong opinion on it, so please follow other reviewer's comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I don't have a strong opinion on it, so please follow other reviewer's comment.

Yea

string(transformClause.script),
attributes,
withFilter,
withProject,
withScriptIOSchema(
ctx,
transformClause.inRowFormat,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,131 @@ abstract class SQLQuerySuiteBase extends QueryTest with SQLTestUtils with TestHi
}
}
}

test("SPARK-28227: test script transform with aggregation") {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the tests into SQLQueryTestSuite?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the tests into SQLQueryTestSuite?

This should wait for #29085, since currently we can't use script transform in sql/core

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maropu
UT have been moved to transform.sql

assume(TestUtils.testCommandAvailable("/bin/bash"))
val data1 = (1 to 1000).map { i => (i, i, i) }
data1.toDF("d1", "d2", "d3").createOrReplaceTempView("script_trans")

// without aggregation
assert(0 === sql(
s"""SELECT TRANSFORM ( d2, d1, CAST(d3 AS STRING) )
|USING 'cat 1>&2' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100""".stripMargin).count())

// number as column
assert(0 === sql(
s"""SELECT TRANSFORM ( 1,2,3 )
|USING 'cat 1>&2' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100""".stripMargin).count())

// number as column output
checkAnswer(sql(
"""
|SELECT TRANSFORM(1,2)
|USING 'cat' AS (a INT,b INT)
|FROM script_trans
|LIMIT 1
""".stripMargin), Row(1, 2))

// without aggregation with complex function
assert(0 === sql(
s"""SELECT TRANSFORM (
|d2 AS d5,
|d1,
|CASE
| WHEN d3 > 100 THEN 1
| WHEN d3 < 100 THEN 2
|ELSE 3 END )
|USING 'cat 1>&2' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100""".stripMargin).count())

// binary operator function
assert(0 === sql(
s"""SELECT TRANSFORM ( d2, d1, d3 + 1 )
|USING 'cat 1>&2' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100""".stripMargin).count())

// test all start
assert(100 === sql(
s"""SELECT TRANSFORM (*)
|USING 'cat' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100""".stripMargin).count())

// with aggregation and complex function
assert(100 === sql(
s"""SELECT TRANSFORM ( d2 AS d4, MAX(d1) as maxd1, CAST(SUM(d3) AS STRING))
|USING 'cat' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100
|GROUP BY d2""".stripMargin).count())


assert(100 === sql(
s"""SELECT TRANSFORM (d2, MAX(d1) as maxd1, CAST(sum(d3) AS STRING))
|USING 'cat' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100
|GROUP BY d2""".stripMargin).count())

assert(0 === sql(
s"""SELECT TRANSFORM (d2, MAX(d1) as maxd1, CAST(SUM(d3) AS STRING))
|USING 'cat 1>&2' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100
|GROUP BY d2
|HAVING maxd1 > 0""".stripMargin).count())

assert(90 === sql(
s"""SELECT TRANSFORM (d2, MAX(d1) as maxd1, CAST(SUM(d3) AS STRING))
|USING 'cat' AS (a,b,c)
|FROM script_trans
|WHERE d1 <= 100
|GROUP BY d2
|HAVING max(d1) > 10""".stripMargin).count())

val data2 = (1 to 5).map { i => (i, i) }
data2.toDF("key", "value").createOrReplaceTempView("test")
checkAnswer(
sql(
"""FROM
|(FROM test SELECT TRANSFORM(key, value) USING 'cat' AS (`thing1` INT, thing2 STRING)) t
|SELECT thing1 + 1
""".stripMargin), (2 to 6).map(i => Row(i)))

val data3 = (1 to 5).map { i => (i % 2, i) }
data3.toDF("key", "value").createOrReplaceTempView("test")
checkAnswer(
sql(
"""FROM
|(SELECT TRANSFORM(key, SUM(value) value)
|USING 'cat' AS (`thing1` INT, thing2 STRING)
|FROM test
|GROUP BY key
|) t
|SELECT (thing2 + 1) AS result
""".stripMargin).sort("result"), Array(7, 10).map(i => Row(i)))

checkAnswer(
sql(
"""
|MAP k / 10 USING 'cat' AS (one) FROM (SELECT 10 AS k)
""".stripMargin
), Row("1.0"))

checkAnswer(
sql(
"""
|FROM (SELECT 1 AS key, 100 AS value) src
|MAP src.*, src.key, CAST(src.key / 10 AS INT), CAST(src.key % 10 AS INT), src.value
|USING 'cat' AS (k, v, tkey, ten, one, tvalue)
""".stripMargin), Row("1", "100", "1", "0", "1", "100"))
}
}

@SlowHiveTest
Expand Down