1818package org .apache .kyuubi .engine .spark .operation
1919
2020import java .lang .{Boolean => JBoolean }
21+ import java .util .concurrent .{TimeoutException , TimeUnit }
2122
2223import com .fasterxml .jackson .databind .ObjectMapper
2324import com .fasterxml .jackson .module .scala .DefaultScalaModule
@@ -33,11 +34,12 @@ import org.apache.kyuubi.KyuubiSQLException
3334import org .apache .kyuubi .config .KyuubiConf .{LINEAGE_PARSER_PLUGIN_PROVIDER , OPERATION_PLAN_ONLY_EXCLUDES , OPERATION_PLAN_ONLY_OUT_STYLE }
3435import org .apache .kyuubi .engine .spark .KyuubiSparkUtil .getSessionConf
3536import org .apache .kyuubi .engine .spark .operation .PlanOnlyStatement ._
36- import org .apache .kyuubi .operation .{AnalyzeMode , ArrayFetchIterator , ExecutionMode , IterableFetchIterator , JsonStyle , LineageMode , OperationHandle , OptimizeMode , OptimizeWithStatsMode , ParseMode , PhysicalMode , PlainStyle , PlanOnlyMode , PlanOnlyStyle , UnknownMode , UnknownStyle }
37+ import org .apache .kyuubi .operation .{AnalyzeMode , ArrayFetchIterator , ExecutionMode , IterableFetchIterator , JsonStyle , LineageMode , OperationHandle , OperationState , OptimizeMode , OptimizeWithStatsMode , ParseMode , PhysicalMode , PlainStyle , PlanOnlyMode , PlanOnlyStyle , UnknownMode , UnknownStyle }
3738import org .apache .kyuubi .operation .PlanOnlyMode .{notSupportedModeError , unknownModeError }
3839import org .apache .kyuubi .operation .PlanOnlyStyle .{notSupportedStyleError , unknownStyleError }
3940import org .apache .kyuubi .operation .log .OperationLog
4041import org .apache .kyuubi .session .Session
42+ import org .apache .kyuubi .util .ThreadUtils
4143import org .apache .kyuubi .util .reflect .DynMethods
4244
4345/**
@@ -47,6 +49,7 @@ class PlanOnlyStatement(
4749 session : Session ,
4850 override val statement : String ,
4951 mode : PlanOnlyMode ,
52+ queryTimeout : Long ,
5053 override protected val handle : OperationHandle )
5154 extends SparkOperation (session) {
5255
@@ -76,27 +79,48 @@ class PlanOnlyStatement(
7679
7780 override protected def runInternal (): Unit =
7881 try {
79- withLocalProperties {
80- SQLConf .withExistingConf(spark.sessionState.conf) {
81- val parsed = spark.sessionState.sqlParser.parsePlan(statement)
82- parsed match {
83- case cmd if planExcludes.contains(cmd.getClass.getSimpleName) =>
84- result = spark.sql(statement)
85- iter = new ArrayFetchIterator (result.collect())
86-
87- case plan => style match {
88- case PlainStyle => explainWithPlainStyle(plan)
89- case JsonStyle => explainWithJsonStyle(plan)
90- case UnknownStyle => unknownStyleError(style)
91- case other => throw notSupportedStyleError(other, " Spark SQL" )
92- }
93- }
82+ if (queryTimeout > 0 ) {
83+ val timeoutExecutor =
84+ ThreadUtils .newDaemonSingleThreadScheduledExecutor(" query-timeout-thread" , false )
85+ val future = timeoutExecutor.submit(new Runnable {
86+ override def run (): Unit = doParsePlan()
87+ })
88+ try {
89+ future.get(queryTimeout, TimeUnit .SECONDS )
90+ } catch {
91+ case _ : TimeoutException =>
92+ future.cancel(true )
93+ cleanup(OperationState .TIMEOUT )
94+ } finally {
95+ timeoutExecutor.shutdownNow()
9496 }
97+ } else {
98+ doParsePlan()
9599 }
96100 } catch {
97101 onError()
98102 }
99103
104+ private def doParsePlan (): Unit = {
105+ withLocalProperties {
106+ SQLConf .withExistingConf(spark.sessionState.conf) {
107+ val parsed = spark.sessionState.sqlParser.parsePlan(statement)
108+ parsed match {
109+ case cmd if planExcludes.contains(cmd.getClass.getSimpleName) =>
110+ result = spark.sql(statement)
111+ iter = new ArrayFetchIterator (result.collect())
112+
113+ case plan => style match {
114+ case PlainStyle => explainWithPlainStyle(plan)
115+ case JsonStyle => explainWithJsonStyle(plan)
116+ case UnknownStyle => unknownStyleError(style)
117+ case other => throw notSupportedStyleError(other, " Spark SQL" )
118+ }
119+ }
120+ }
121+ }
122+ }
123+
100124 private def explainWithPlainStyle (plan : LogicalPlan ): Unit = {
101125 mode match {
102126 case ParseMode =>
0 commit comments