Skip to content
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2d762b4
plan exists subquery
AngersZhuuuu Nov 8, 2019
1c577bc
Update subquery.scala
AngersZhuuuu Nov 8, 2019
5fa971b
format import
AngersZhuuuu Nov 8, 2019
1401349
don;t collect executed rdd
AngersZhuuuu Nov 8, 2019
7b943aa
format code
AngersZhuuuu Nov 8, 2019
95e446d
Update predicates.scala
AngersZhuuuu Nov 10, 2019
20cda42
Update subquery.scala
AngersZhuuuu Nov 10, 2019
8e3ce4f
remove ExistsSubquery
AngersZhuuuu Nov 11, 2019
c290411
minimize cost
AngersZhuuuu Nov 11, 2019
866ddc7
follow comment
AngersZhuuuu Nov 11, 2019
3de0ecc
update import
AngersZhuuuu Nov 11, 2019
32f85c3
follow comment
AngersZhuuuu Nov 12, 2019
e47a757
Merge branch 'master' into SPARK-29800
AngersZhuuuu Nov 12, 2019
4c86605
remove broadcaset
AngersZhuuuu Nov 12, 2019
626e41f
Update subquery.scala
AngersZhuuuu Nov 13, 2019
ce76e0c
remove unused import
AngersZhuuuu Nov 13, 2019
4a4ca9b
Update subquery.scala
AngersZhuuuu Nov 13, 2019
88f804d
Merge branch 'master' into SPARK-29800
AngersZhuuuu Nov 21, 2019
7668bd6
ExistsSExec -> ExistsSubqueryExec
AngersZhuuuu Nov 25, 2019
a6b8485
Revert "Merge branch 'master' into SPARK-29800"
AngersZhuuuu Nov 25, 2019
34046be
follow comment
AngersZhuuuu Jan 2, 2020
4c6c04d
follow comment
AngersZhuuuu Jan 2, 2020
ac6a4d2
Update subquery.scala
AngersZhuuuu Jan 2, 2020
59162c6
Update finishAnalysis.scala
AngersZhuuuu Jan 2, 2020
89a1721
Update finishAnalysis.scala
AngersZhuuuu Jan 2, 2020
fb98b54
update
AngersZhuuuu Jan 2, 2020
67b4281
Update finishAnalysis.scala
AngersZhuuuu Jan 3, 2020
821ed40
Update finishAnalysis.scala
AngersZhuuuu Jan 3, 2020
e319fee
fix ut
AngersZhuuuu Jan 3, 2020
2c387f2
Update SubquerySuite.scala
AngersZhuuuu Jan 3, 2020
2aff8eb
Update SubquerySuite.scala
AngersZhuuuu Jan 3, 2020
2b7b417
Update CachedTableSuite.scala
AngersZhuuuu Jan 4, 2020
88fcdbf
Update CachedTableSuite.scala
AngersZhuuuu Jan 4, 2020
9f084ee
Merge branch 'master' into SPARK-29800
AngersZhuuuu Jan 4, 2020
8c6060a
Update CachedTableSuite.scala
AngersZhuuuu Jan 4, 2020
9a9d9d1
fix comment error
AngersZhuuuu Jan 5, 2020
173942d
follow comment
AngersZhuuuu Jan 6, 2020
26258b0
Update finishAnalysis.scala
AngersZhuuuu Jan 6, 2020
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 @@ -64,9 +64,10 @@ object SubqueryExpression {
/**
* Returns true when an expression contains an IN or EXISTS subquery and false otherwise.

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.

We should update the doc too.

*/
def hasInOrExistsSubquery(e: Expression): Boolean = {
def hasInOrCorrelatedExistsSubquery(e: Expression): Boolean = {
e.find {
case _: ListQuery | _: Exists => true
case _: ListQuery => true
case _: Exists if e.children.nonEmpty => true
case _ => false
}.isDefined
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ object RewritePredicateSubquery extends Rule[LogicalPlan] with PredicateHelper {
def apply(plan: LogicalPlan): LogicalPlan = plan transform {
case Filter(condition, child) =>
val (withSubquery, withoutSubquery) =
splitConjunctivePredicates(condition).partition(SubqueryExpression.hasInOrExistsSubquery)
splitConjunctivePredicates(condition)
.partition(SubqueryExpression.hasInOrCorrelatedExistsSubquery)
Comment on lines +99 to +100

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.

Unrelated change?

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.

Oh, nvm, I saw it.


// Construct the pruned filter condition.
val newFilter: LogicalPlan = withoutSubquery match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import scala.collection.mutable.ArrayBuffer
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.catalyst.{expressions, InternalRow}
import org.apache.spark.sql.catalyst.expressions.{AttributeSeq, CreateNamedStruct, Expression, ExprId, InSet, ListQuery, Literal, PlanExpression}
import org.apache.spark.sql.catalyst.expressions.{CreateNamedStruct, Expression, ExprId, InSet, ListQuery, Literal, PlanExpression}
import org.apache.spark.sql.catalyst.expressions.codegen.{CodegenContext, ExprCode}
import org.apache.spark.sql.catalyst.plans.logical.Project
import org.apache.spark.sql.catalyst.rules.Rule
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.types.{BooleanType, DataType, StructType}
Expand Down Expand Up @@ -171,6 +172,44 @@ case class InSubqueryExec(
}
}

/**
* The physical node of non-correlated EXISTS subquery.
*/
case class ExistsExec(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we name it ExistsSubquery or ExistsSubqueryExec?

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.

Can we name it ExistsSubquery or ExistsSubqueryExec?

Sure, update to ExistsSubqueryExec

plan: BaseSubqueryExec,
exprId: ExprId)
extends ExecSubqueryExpression {

@volatile private var result: Option[Boolean] = None

override def dataType: DataType = BooleanType
override def children: Seq[Expression] = Nil
override def nullable: Boolean = false
override def toString: String = s"EXISTS (${plan.simpleString(SQLConf.get.maxToStringFields)})"
override def withNewPlan(plan: BaseSubqueryExec): ExistsExec = copy(plan = plan)

override def semanticEquals(other: Expression): Boolean = other match {
case in: ExistsExec => plan.sameResult(in.plan)
case _ => false
}

def updateResult(): Unit = {
result = Some(plan.executeTake(1).length == 1)
}

def values(): Option[Boolean] = result

override def eval(input: InternalRow): Any = {
require(result.isDefined, s"$this has not finished")
result.get
}

override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

again, can we follow org.apache.spark.sql.execution.ScalarSubquery? The codegen is well implemented there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

as well as eval

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.

again, can we follow org.apache.spark.sql.execution.ScalarSubquery? The codegen is well implemented there.

Done

require(result.isDefined, s"$this has not finished")
Literal.create(result.get, dataType).doGenCode(ctx, ev)
}
}

/**
* Plans subqueries that are present in the given [[SparkPlan]].
*/
Expand All @@ -194,6 +233,9 @@ case class PlanSubqueries(sparkSession: SparkSession) extends Rule[SparkPlan] {
}
val executedPlan = new QueryExecution(sparkSession, query).executedPlan
InSubqueryExec(expr, SubqueryExec(s"subquery#${exprId.id}", executedPlan), exprId)
case expressions.Exists(sub, children, exprId) =>
Comment thread
cloud-fan marked this conversation as resolved.
Outdated
val executedPlan = new QueryExecution(sparkSession, Project(Nil, sub)).executedPlan

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we add the Project a bit earlier? This seems weird to add Project and do column pruning at planning time.

@AngersZhuuuu AngersZhuuuu Nov 12, 2019

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.

Can we add the Project a bit earlier? This seems weird to add Project and do column pruning at planning time.

add this in RewritePredicateSubquery?
Just tried. Not well, since we only handle non-correlated exists. Here is the best place to do this and won't impact other process.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about the rule OptimizeSubqueries?

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.

how about the rule OptimizeSubqueries?

correlated and non-correlated subqueries all match here, and we only handle non-correlated exists. If we add Project(Nil, ..) to here need to check subquery type.
For me, it ok to add it to here since it just handle one case and we can add comment here to explain why we do this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't it useful for correlated EXISTS as well? logically it's always valid to prune all columns from EXISTS subquery, no matter it's correlated or not.

@cloud-fan cloud-fan Nov 13, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about WHERE on a single table? it's a known problem that correlated subqueries may not work well in join condition.

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.

how about WHERE on a single table? it's a known problem that correlated subqueries may not work well in join condition.

It is ok with WHERE on a single table.
The case I show you is WHERE with correlated subquery, failed, not in join condition

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.

For correlated exists, convert to join, seems don't need to do this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK let's keep it here now. But I do think it's better to put logical optimization in logical rules.

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.

OK let's keep it here now. But I do think it's better to put logical optimization in logical rules.

Understand, but current code about subqueries disperse in different places and interacting with each other

ExistsExec(SubqueryExec(s"subquery#${exprId.id}", executedPlan), exprId)
}
}
}
Expand Down