You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ch...@apache.org on 2022/05/23 03:13:27 UTC

[incubator-kyuubi] branch master updated: [KYUUBI #2706] Spark extensions support Spark-3.3

This is an automated email from the ASF dual-hosted git repository.

chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new 85cbea400 [KYUUBI #2706] Spark extensions support Spark-3.3
85cbea400 is described below

commit 85cbea400c7c6103fb5b5bed362145419b73535c
Author: Fu Chen <cf...@gmail.com>
AuthorDate: Mon May 23 11:13:18 2022 +0800

    [KYUUBI #2706] Spark extensions support Spark-3.3
    
    ### _Why are the changes needed?_
    
    to close #2706
    
    Spark extensions support Spark-3.3, part of #2620
    
    ### _How was this patch tested?_
    - [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible
    
    - [ ] Add screenshots for manual tests if appropriate
    
    - [x] [Run test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests) locally before make a pull request
    
    Closes #2707 from cfmcgrady/kyuubi-2706.
    
    Closes #2706
    
    0b07b6e4 [Fu Chen] spark extensions support spark 3.3
    
    Authored-by: Fu Chen <cf...@gmail.com>
    Signed-off-by: Cheng Pan <ch...@apache.org>
---
 .../kyuubi/sql/KyuubiSparkSQLCommonExtension.scala |   0
 .../apache/kyuubi/sql/KyuubiSparkSQLParser.scala   |   0
 .../sql/InsertShuffleNodeBeforeJoinSuite.scala     |  19 +++
 .../scala/org/apache/spark/sql/ZorderSuite.scala   |  22 +++
 .../kyuubi/sql/KyuubiSparkSQLCommonExtension.scala |   0
 .../apache/kyuubi/sql/KyuubiSparkSQLParser.scala   |   0
 .../sql/InsertShuffleNodeBeforeJoinSuite.scala     |  19 +++
 .../scala/org/apache/spark/sql/ZorderSuite.scala   |  22 +++
 .../spark/kyuubi-extension-spark-3-3/pom.xml       | 159 ++++++++++++++++++
 .../apache/kyuubi/sql/DropIgnoreNonexistent.scala  |  54 +++++++
 .../kyuubi/sql/KyuubiSparkSQLCommonExtension.scala |   0
 .../kyuubi/sql/KyuubiSparkSQLExtension.scala       |  43 +++++
 .../apache/kyuubi/sql/KyuubiSparkSQLParser.scala   |   8 +
 .../apache/kyuubi/sql/RebalanceBeforeWriting.scala |  59 +++++++
 .../sql/watchdog/ForcedMaxOutputRowsRule.scala     |  46 ++++++
 .../src/test/resources/log4j.properties            |  44 +++++
 .../spark/sql/DropIgnoreNonexistentSuite.scala     |  53 ++++++
 .../spark/sql/FinalStageConfigIsolationSuite.scala | 179 +++++++++++++++++++++
 .../sql/InsertShuffleNodeBeforeJoinSuite.scala     |  19 +++
 .../spark/sql/RebalanceBeforeWritingSuite.scala    | 179 +++++++++++++++++++++
 .../scala/org/apache/spark/sql/WatchDogSuite.scala |  20 +++
 .../scala/org/apache/spark/sql/ZorderSuite.scala   |  22 +++
 .../kyuubi/sql/watchdog/MaxPartitionStrategy.scala |   4 +-
 ... => InsertShuffleNodeBeforeJoinSuiteBase.scala} |   2 +-
 .../{ZorderSuite.scala => ZorderSuiteBase.scala}   |   6 +-
 pom.xml                                            |   4 +
 26 files changed, 977 insertions(+), 6 deletions(-)

diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala b/extensions/spark/kyuubi-extension-spark-3-1/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
similarity index 100%
copy from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
copy to extensions/spark/kyuubi-extension-spark-3-1/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala b/extensions/spark/kyuubi-extension-spark-3-1/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
similarity index 100%
copy from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
copy to extensions/spark/kyuubi-extension-spark-3-1/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
diff --git a/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala b/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
new file mode 100644
index 000000000..f0d384657
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
@@ -0,0 +1,19 @@
+/*
+ * 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.spark.sql
+
+class InsertShuffleNodeBeforeJoinSuite extends InsertShuffleNodeBeforeJoinSuiteBase
diff --git a/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/ZorderSuite.scala b/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
new file mode 100644
index 000000000..fd04e27db
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-1/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.spark.sql
+
+class ZorderWithCodegenEnabledSuite extends ZorderWithCodegenEnabledSuiteBase {}
+
+class ZorderWithCodegenDisabledSuite extends ZorderWithCodegenDisabledSuiteBase {}
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala b/extensions/spark/kyuubi-extension-spark-3-2/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
similarity index 100%
copy from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
copy to extensions/spark/kyuubi-extension-spark-3-2/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala b/extensions/spark/kyuubi-extension-spark-3-2/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
similarity index 100%
copy from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
copy to extensions/spark/kyuubi-extension-spark-3-2/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
diff --git a/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala b/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
new file mode 100644
index 000000000..f0d384657
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
@@ -0,0 +1,19 @@
+/*
+ * 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.spark.sql
+
+class InsertShuffleNodeBeforeJoinSuite extends InsertShuffleNodeBeforeJoinSuiteBase
diff --git a/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/ZorderSuite.scala b/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
new file mode 100644
index 000000000..fd04e27db
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-2/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.spark.sql
+
+class ZorderWithCodegenEnabledSuite extends ZorderWithCodegenEnabledSuiteBase {}
+
+class ZorderWithCodegenDisabledSuite extends ZorderWithCodegenDisabledSuiteBase {}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/pom.xml b/extensions/spark/kyuubi-extension-spark-3-3/pom.xml
new file mode 100644
index 000000000..16f8f213a
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.kyuubi</groupId>
+        <artifactId>kyuubi-parent</artifactId>
+        <version>1.6.0-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>kyuubi-extension-spark-3-3_2.12</artifactId>
+    <name>Kyuubi Dev Spark Extensions (for Spark 3.3)</name>
+    <packaging>jar</packaging>
+    <url>https://kyuubi.apache.org/</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.kyuubi</groupId>
+            <artifactId>kyuubi-extension-spark-common_${scala.binary.version}</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.kyuubi</groupId>
+            <artifactId>kyuubi-extension-spark-common_${scala.binary.version}</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.scala-lang</groupId>
+            <artifactId>scala-library</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.spark</groupId>
+            <artifactId>spark-sql_${scala.binary.version}</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.spark</groupId>
+            <artifactId>spark-hive_${scala.binary.version}</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-client-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.spark</groupId>
+            <artifactId>spark-core_${scala.binary.version}</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.spark</groupId>
+            <artifactId>spark-catalyst_${scala.binary.version}</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.scalatestplus</groupId>
+            <artifactId>scalacheck-1-15_${scala.binary.version}</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.spark</groupId>
+            <artifactId>spark-sql_${scala.binary.version}</artifactId>
+            <version>${spark.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-client-runtime</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!--
+          Spark requires `commons-collections` and `commons-io` but got them from transitive
+          dependencies of `hadoop-client`. As we are using Hadoop Shaded Client, we need add
+          them explicitly. See more details at SPARK-33212.
+          -->
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <outputDirectory>target/scala-${scala.binary.version}/classes</outputDirectory>
+        <testOutputDirectory>target/scala-${scala.binary.version}/test-classes</testOutputDirectory>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <configuration>
+                    <shadedArtifactAttached>false</shadedArtifactAttached>
+                    <artifactSet>
+                        <includes>
+                            <include>org.apache.kyuubi:kyuubi-extension-spark-common_${scala.binary.version}</include>
+                        </includes>
+                    </artifactSet>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/DropIgnoreNonexistent.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/DropIgnoreNonexistent.scala
new file mode 100644
index 000000000..e740554d4
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/DropIgnoreNonexistent.scala
@@ -0,0 +1,54 @@
+/*
+ * 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.kyuubi.sql
+
+import org.apache.spark.sql.SparkSession
+import org.apache.spark.sql.catalyst.analysis.{UnresolvedFunc, UnresolvedRelation, UnresolvedTableOrView, UnresolvedView}
+import org.apache.spark.sql.catalyst.plans.logical.{DropFunction, DropNamespace, DropTable, DropView, LogicalPlan, NoopCommand, UncacheTable}
+import org.apache.spark.sql.catalyst.rules.Rule
+import org.apache.spark.sql.execution.command.{AlterTableDropPartitionCommand, DropTableCommand}
+
+import org.apache.kyuubi.sql.KyuubiSQLConf._
+
+case class DropIgnoreNonexistent(session: SparkSession) extends Rule[LogicalPlan] {
+
+  override def apply(plan: LogicalPlan): LogicalPlan = {
+    if (conf.getConf(DROP_IGNORE_NONEXISTENT)) {
+      plan match {
+        case i @ AlterTableDropPartitionCommand(_, _, false, _, _) =>
+          i.copy(ifExists = true)
+        case i @ DropTableCommand(_, false, _, _) =>
+          i.copy(ifExists = true)
+        case i @ DropNamespace(_, false, _) =>
+          i.copy(ifExists = true)
+        // like: org.apache.spark.sql.catalyst.analysis.ResolveCommandsWithIfExists
+        case DropTable(u: UnresolvedTableOrView, false, _) =>
+          NoopCommand("DROP TABLE", u.multipartIdentifier)
+        case DropView(u: UnresolvedView, false) =>
+          NoopCommand("DROP VIEW", u.multipartIdentifier)
+        case UncacheTable(u: UnresolvedRelation, false, _) =>
+          NoopCommand("UNCACHE TABLE", u.multipartIdentifier)
+        case DropFunction(u: UnresolvedFunc, false) =>
+          NoopCommand("DROP FUNCTION", u.multipartIdentifier)
+        case _ => plan
+      }
+    } else {
+      plan
+    }
+  }
+
+}
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
similarity index 100%
rename from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
rename to extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLCommonExtension.scala
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLExtension.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLExtension.scala
new file mode 100644
index 000000000..ef9da41be
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLExtension.scala
@@ -0,0 +1,43 @@
+/*
+ * 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.kyuubi.sql
+
+import org.apache.spark.sql.SparkSessionExtensions
+
+import org.apache.kyuubi.sql.watchdog.{ForcedMaxOutputRowsRule, MaxPartitionStrategy}
+
+// scalastyle:off line.size.limit
+/**
+ * Depend on Spark SQL Extension framework, we can use this extension follow steps
+ *   1. move this jar into $SPARK_HOME/jars
+ *   2. add config into `spark-defaults.conf`: `spark.sql.extensions=org.apache.kyuubi.sql.KyuubiSparkSQLExtension`
+ */
+// scalastyle:on line.size.limit
+class KyuubiSparkSQLExtension extends (SparkSessionExtensions => Unit) {
+  override def apply(extensions: SparkSessionExtensions): Unit = {
+    KyuubiSparkSQLCommonExtension.injectCommonExtensions(extensions)
+
+    extensions.injectPostHocResolutionRule(RebalanceBeforeWritingDatasource)
+    extensions.injectPostHocResolutionRule(RebalanceBeforeWritingHive)
+    extensions.injectPostHocResolutionRule(DropIgnoreNonexistent)
+
+    // watchdog extension
+    extensions.injectOptimizerRule(ForcedMaxOutputRowsRule)
+    extensions.injectPlannerStrategy(MaxPartitionStrategy)
+  }
+}
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
similarity index 95%
rename from extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
rename to extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
index 2f12a82e2..af1711ebb 100644
--- a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/KyuubiSparkSQLParser.scala
@@ -100,6 +100,14 @@ abstract class KyuubiSparkSQLParserBase extends ParserInterface {
   override def parseDataType(sqlText: String): DataType = {
     delegate.parseDataType(sqlText)
   }
+
+  /**
+   * This functions was introduced since spark-3.3, for more details, please see
+   * https://github.com/apache/spark/pull/34543
+   */
+  override def parseQuery(sqlText: String): LogicalPlan = {
+    delegate.parseQuery(sqlText)
+  }
 }
 
 class SparkKyuubiSparkSQLParser(
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/RebalanceBeforeWriting.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/RebalanceBeforeWriting.scala
new file mode 100644
index 000000000..8f7f17c4a
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/RebalanceBeforeWriting.scala
@@ -0,0 +1,59 @@
+/*
+ * 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.kyuubi.sql
+
+import org.apache.spark.sql.SparkSession
+import org.apache.spark.sql.catalyst.expressions.Attribute
+import org.apache.spark.sql.catalyst.plans.logical._
+
+trait RepartitionBuilderWithRebalance extends RepartitionBuilder {
+  override def buildRepartition(
+      dynamicPartitionColumns: Seq[Attribute],
+      query: LogicalPlan): LogicalPlan = {
+    RebalancePartitions(dynamicPartitionColumns, query)
+  }
+
+  override def canInsertRepartitionByExpression(plan: LogicalPlan): Boolean = {
+    super.canInsertRepartitionByExpression(plan) && {
+      plan match {
+        case _: RebalancePartitions => false
+        case _ => true
+      }
+    }
+  }
+}
+
+/**
+ * For datasource table, there two commands can write data to table
+ * 1. InsertIntoHadoopFsRelationCommand
+ * 2. CreateDataSourceTableAsSelectCommand
+ * This rule add a RebalancePartitions node between write and query
+ */
+case class RebalanceBeforeWritingDatasource(session: SparkSession)
+  extends RepartitionBeforeWritingDatasourceBase
+  with RepartitionBuilderWithRebalance {}
+
+/**
+ * For Hive table, there two commands can write data to table
+ * 1. InsertIntoHiveTable
+ * 2. CreateHiveTableAsSelectCommand
+ * This rule add a RebalancePartitions node between write and query
+ */
+case class RebalanceBeforeWritingHive(session: SparkSession)
+  extends RepartitionBeforeWritingHiveBase
+  with RepartitionBuilderWithRebalance {}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/watchdog/ForcedMaxOutputRowsRule.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/watchdog/ForcedMaxOutputRowsRule.scala
new file mode 100644
index 000000000..a3d990b10
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/main/scala/org/apache/kyuubi/sql/watchdog/ForcedMaxOutputRowsRule.scala
@@ -0,0 +1,46 @@
+/*
+ * 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.kyuubi.sql.watchdog
+
+import org.apache.spark.sql.SparkSession
+import org.apache.spark.sql.catalyst.plans.logical.{Aggregate, CommandResult, LogicalPlan, Union, WithCTE}
+import org.apache.spark.sql.execution.command.DataWritingCommand
+
+case class ForcedMaxOutputRowsRule(sparkSession: SparkSession) extends ForcedMaxOutputRowsBase {
+
+  override protected def isChildAggregate(a: Aggregate): Boolean = false
+
+  override protected def canInsertLimitInner(p: LogicalPlan): Boolean = p match {
+    case WithCTE(plan, _) => this.canInsertLimitInner(plan)
+    case plan: LogicalPlan => plan match {
+        case Union(children, _, _) => !children.exists {
+            case _: DataWritingCommand => true
+            case p: CommandResult if p.commandLogicalPlan.isInstanceOf[DataWritingCommand] => true
+            case _ => false
+          }
+        case _ => super.canInsertLimitInner(plan)
+      }
+  }
+
+  override protected def canInsertLimit(p: LogicalPlan, maxOutputRowsOpt: Option[Int]): Boolean = {
+    p match {
+      case WithCTE(plan, _) => this.canInsertLimit(plan, maxOutputRowsOpt)
+      case _ => super.canInsertLimit(p, maxOutputRowsOpt)
+    }
+  }
+}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/resources/log4j.properties b/extensions/spark/kyuubi-extension-spark-3-3/src/test/resources/log4j.properties
new file mode 100644
index 000000000..68d6a0e66
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/resources/log4j.properties
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootLogger=INFO, CA, FA
+
+# Console Appender
+log4j.appender.CA=org.apache.log4j.ConsoleAppender
+log4j.appender.CA.layout=org.apache.log4j.PatternLayout
+log4j.appender.CA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p %c: %m%n
+log4j.appender.CA.Threshold = FATAL
+
+# File Appender
+log4j.appender.FA=org.apache.log4j.FileAppender
+log4j.appender.FA.append=false
+log4j.appender.FA.file=target/unit-tests.log
+log4j.appender.FA.layout=org.apache.log4j.PatternLayout
+log4j.appender.FA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %t %p %c{2}: %m%n
+
+# Set the logger level of File Appender to WARN
+log4j.appender.FA.Threshold = DEBUG
+
+# SPARK-34128: Suppress undesirable TTransportException warnings involved in THRIFT-4805
+log4j.appender.CA.filter.1=org.apache.log4j.varia.StringMatchFilter
+log4j.appender.CA.filter.1.StringToMatch=Thrift error occurred during processing of message
+log4j.appender.CA.filter.1.AcceptOnMatch=false
+
+log4j.appender.FA.filter.1=org.apache.log4j.varia.StringMatchFilter
+log4j.appender.FA.filter.1.StringToMatch=Thrift error occurred during processing of message
+log4j.appender.FA.filter.1.AcceptOnMatch=false
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/DropIgnoreNonexistentSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/DropIgnoreNonexistentSuite.scala
new file mode 100644
index 000000000..ff1326894
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/DropIgnoreNonexistentSuite.scala
@@ -0,0 +1,53 @@
+/*
+ * 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.spark.sql
+
+import org.apache.spark.sql.catalyst.plans.logical.{DropNamespace, NoopCommand}
+import org.apache.spark.sql.execution.command._
+
+import org.apache.kyuubi.sql.KyuubiSQLConf
+
+class DropIgnoreNonexistentSuite extends KyuubiSparkSQLExtensionTest {
+
+  test("drop ignore nonexistent") {
+    withSQLConf(KyuubiSQLConf.DROP_IGNORE_NONEXISTENT.key -> "true") {
+      // drop nonexistent database
+      val df1 = sql("DROP DATABASE nonexistent_database")
+      assert(df1.queryExecution.analyzed.asInstanceOf[DropNamespace].ifExists == true)
+
+      // drop nonexistent table
+      val df2 = sql("DROP TABLE nonexistent_table")
+      assert(df2.queryExecution.analyzed.isInstanceOf[NoopCommand])
+
+      // drop nonexistent view
+      val df3 = sql("DROP VIEW nonexistent_view")
+      assert(df3.queryExecution.analyzed.isInstanceOf[NoopCommand])
+
+      // drop nonexistent function
+      val df4 = sql("DROP FUNCTION nonexistent_function")
+      assert(df4.queryExecution.analyzed.isInstanceOf[NoopCommand])
+
+      // drop nonexistent PARTITION
+      withTable("test") {
+        sql("CREATE TABLE IF NOT EXISTS test(i int) PARTITIONED BY (p int)")
+        val df5 = sql("ALTER TABLE test DROP PARTITION (p = 1)")
+        assert(df5.queryExecution.analyzed
+          .asInstanceOf[AlterTableDropPartitionCommand].ifExists == true)
+      }
+    }
+  }
+}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/FinalStageConfigIsolationSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/FinalStageConfigIsolationSuite.scala
new file mode 100644
index 000000000..0f983dc6c
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/FinalStageConfigIsolationSuite.scala
@@ -0,0 +1,179 @@
+/*
+ * 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.spark.sql
+
+import org.apache.spark.sql.execution.adaptive.{AQEShuffleReadExec, QueryStageExec}
+import org.apache.spark.sql.internal.SQLConf
+
+import org.apache.kyuubi.sql.{FinalStageConfigIsolation, KyuubiSQLConf}
+
+class FinalStageConfigIsolationSuite extends KyuubiSparkSQLExtensionTest {
+  override protected def beforeAll(): Unit = {
+    super.beforeAll()
+    setupData()
+  }
+
+  test("final stage config set reset check") {
+    withSQLConf(
+      KyuubiSQLConf.FINAL_STAGE_CONFIG_ISOLATION.key -> "true",
+      "spark.sql.finalStage.adaptive.coalescePartitions.minPartitionNum" -> "1",
+      "spark.sql.finalStage.adaptive.advisoryPartitionSizeInBytes" -> "100") {
+      // use loop to double check final stage config doesn't affect the sql query each other
+      (1 to 3).foreach { _ =>
+        sql("SELECT COUNT(*) FROM VALUES(1) as t(c)").collect()
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.previousStage.adaptive.coalescePartitions.minPartitionNum") ===
+          FinalStageConfigIsolation.INTERNAL_UNSET_CONFIG_TAG)
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.adaptive.coalescePartitions.minPartitionNum") ===
+          "1")
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.finalStage.adaptive.coalescePartitions.minPartitionNum") ===
+          "1")
+
+        // 64MB
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.previousStage.adaptive.advisoryPartitionSizeInBytes") ===
+          "67108864b")
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.adaptive.advisoryPartitionSizeInBytes") ===
+          "100")
+        assert(spark.sessionState.conf.getConfString(
+          "spark.sql.finalStage.adaptive.advisoryPartitionSizeInBytes") ===
+          "100")
+      }
+
+      sql("SET spark.sql.adaptive.advisoryPartitionSizeInBytes=1")
+      assert(spark.sessionState.conf.getConfString(
+        "spark.sql.adaptive.advisoryPartitionSizeInBytes") ===
+        "1")
+      assert(!spark.sessionState.conf.contains(
+        "spark.sql.previousStage.adaptive.advisoryPartitionSizeInBytes"))
+
+      sql("SET a=1")
+      assert(spark.sessionState.conf.getConfString("a") === "1")
+
+      sql("RESET spark.sql.adaptive.coalescePartitions.minPartitionNum")
+      assert(!spark.sessionState.conf.contains(
+        "spark.sql.adaptive.coalescePartitions.minPartitionNum"))
+      assert(!spark.sessionState.conf.contains(
+        "spark.sql.previousStage.adaptive.coalescePartitions.minPartitionNum"))
+
+      sql("RESET a")
+      assert(!spark.sessionState.conf.contains("a"))
+    }
+  }
+
+  test("final stage config isolation") {
+    def checkPartitionNum(
+        sqlString: String,
+        previousPartitionNum: Int,
+        finalPartitionNum: Int): Unit = {
+      val df = sql(sqlString)
+      df.collect()
+      val shuffleReaders = collect(df.queryExecution.executedPlan) {
+        case customShuffleReader: AQEShuffleReadExec => customShuffleReader
+      }
+      assert(shuffleReaders.nonEmpty)
+      // reorder stage by stage id to ensure we get the right stage
+      val sortedShuffleReaders = shuffleReaders.sortWith {
+        case (s1, s2) =>
+          s1.child.asInstanceOf[QueryStageExec].id < s2.child.asInstanceOf[QueryStageExec].id
+      }
+      if (sortedShuffleReaders.length > 1) {
+        assert(sortedShuffleReaders.head.partitionSpecs.length === previousPartitionNum)
+      }
+      assert(sortedShuffleReaders.last.partitionSpecs.length === finalPartitionNum)
+      assert(df.rdd.partitions.length === finalPartitionNum)
+    }
+
+    withSQLConf(
+      SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "-1",
+      SQLConf.COALESCE_PARTITIONS_MIN_PARTITION_NUM.key -> "1",
+      SQLConf.SHUFFLE_PARTITIONS.key -> "3",
+      KyuubiSQLConf.FINAL_STAGE_CONFIG_ISOLATION.key -> "true",
+      "spark.sql.adaptive.advisoryPartitionSizeInBytes" -> "1",
+      "spark.sql.adaptive.coalescePartitions.minPartitionSize" -> "1",
+      "spark.sql.finalStage.adaptive.advisoryPartitionSizeInBytes" -> "10000000") {
+
+      // use loop to double check final stage config doesn't affect the sql query each other
+      (1 to 3).foreach { _ =>
+        checkPartitionNum(
+          "SELECT c1, count(*) FROM t1 GROUP BY c1",
+          1,
+          1)
+
+        checkPartitionNum(
+          "SELECT c2, count(*) FROM (SELECT c1, count(*) as c2 FROM t1 GROUP BY c1) GROUP BY c2",
+          3,
+          1)
+
+        checkPartitionNum(
+          "SELECT t1.c1, count(*) FROM t1 JOIN t2 ON t1.c2 = t2.c2 GROUP BY t1.c1",
+          3,
+          1)
+
+        checkPartitionNum(
+          """
+            | SELECT /*+ REPARTITION */
+            | t1.c1, count(*) FROM t1
+            | JOIN t2 ON t1.c2 = t2.c2
+            | JOIN t3 ON t1.c1 = t3.c1
+            | GROUP BY t1.c1
+            |""".stripMargin,
+          3,
+          1)
+
+        // one shuffle reader
+        checkPartitionNum(
+          """
+            | SELECT /*+ BROADCAST(t1) */
+            | t1.c1, t2.c2 FROM t1
+            | JOIN t2 ON t1.c2 = t2.c2
+            | DISTRIBUTE BY c1
+            |""".stripMargin,
+          1,
+          1)
+
+        // test ReusedExchange
+        checkPartitionNum(
+          """
+            |SELECT /*+ REPARTITION */ t0.c2 FROM (
+            |SELECT t1.c1, (count(*) + c1) as c2 FROM t1 GROUP BY t1.c1
+            |) t0 JOIN (
+            |SELECT t1.c1, (count(*) + c1) as c2 FROM t1 GROUP BY t1.c1
+            |) t1 ON t0.c2 = t1.c2
+            |""".stripMargin,
+          3,
+          1)
+
+        // one shuffle reader
+        checkPartitionNum(
+          """
+            |SELECT t0.c1 FROM (
+            |SELECT t1.c1 FROM t1 GROUP BY t1.c1
+            |) t0 JOIN (
+            |SELECT t1.c1 FROM t1 GROUP BY t1.c1
+            |) t1 ON t0.c1 = t1.c1
+            |""".stripMargin,
+          1,
+          1)
+      }
+    }
+  }
+}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
new file mode 100644
index 000000000..f0d384657
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
@@ -0,0 +1,19 @@
+/*
+ * 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.spark.sql
+
+class InsertShuffleNodeBeforeJoinSuite extends InsertShuffleNodeBeforeJoinSuiteBase
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/RebalanceBeforeWritingSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/RebalanceBeforeWritingSuite.scala
new file mode 100644
index 000000000..f1a27cdb8
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/RebalanceBeforeWritingSuite.scala
@@ -0,0 +1,179 @@
+/*
+ * 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.spark.sql
+
+import org.apache.spark.sql.catalyst.expressions.Attribute
+import org.apache.spark.sql.catalyst.plans.logical.RebalancePartitions
+import org.apache.spark.sql.hive.HiveUtils
+import org.apache.spark.sql.hive.execution.OptimizedCreateHiveTableAsSelectCommand
+
+import org.apache.kyuubi.sql.KyuubiSQLConf
+
+class RebalanceBeforeWritingSuite extends KyuubiSparkSQLExtensionTest {
+  test("check rebalance exists") {
+    def check(df: DataFrame, expectedRebalanceNum: Int = 1): Unit = {
+      assert(
+        df.queryExecution.analyzed.collect {
+          case r: RebalancePartitions => r
+        }.size == expectedRebalanceNum)
+    }
+
+    // It's better to set config explicitly in case of we change the default value.
+    withSQLConf(KyuubiSQLConf.INSERT_REPARTITION_BEFORE_WRITE.key -> "true") {
+      Seq("USING PARQUET", "").foreach { storage =>
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage PARTITIONED BY (c2 string)")
+          check(sql("INSERT INTO TABLE tmp1 PARTITION(c2='a') " +
+            "SELECT * FROM VALUES(1),(2) AS t(c1)"))
+        }
+
+        withTable("tmp1", "tmp2") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage PARTITIONED BY (c2 string)")
+          sql(s"CREATE TABLE tmp2 (c1 int) $storage PARTITIONED BY (c2 string)")
+          check(
+            sql(
+              """FROM VALUES(1),(2)
+                |INSERT INTO TABLE tmp1 PARTITION(c2='a') SELECT *
+                |INSERT INTO TABLE tmp2 PARTITION(c2='a') SELECT *
+                |""".stripMargin),
+            2)
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage")
+          check(sql("INSERT INTO TABLE tmp1 SELECT * FROM VALUES(1),(2),(3) AS t(c1)"))
+        }
+
+        withTable("tmp1", "tmp2") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage")
+          sql(s"CREATE TABLE tmp2 (c1 int) $storage")
+          check(
+            sql(
+              """FROM VALUES(1),(2),(3)
+                |INSERT INTO TABLE tmp1 SELECT *
+                |INSERT INTO TABLE tmp2 SELECT *
+                |""".stripMargin),
+            2)
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 $storage AS SELECT * FROM VALUES(1),(2),(3) AS t(c1)")
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 $storage PARTITIONED BY(c2) AS " +
+            s"SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)")
+        }
+      }
+    }
+  }
+
+  test("check rebalance does not exists") {
+    def check(df: DataFrame): Unit = {
+      assert(
+        df.queryExecution.analyzed.collect {
+          case r: RebalancePartitions => r
+        }.isEmpty)
+    }
+
+    withSQLConf(KyuubiSQLConf.INSERT_REPARTITION_BEFORE_WRITE.key -> "true") {
+      // test no write command
+      check(sql("SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)"))
+      check(sql("SELECT count(*) FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)"))
+
+      // test not supported plan
+      withTable("tmp1") {
+        sql(s"CREATE TABLE tmp1 (c1 int) PARTITIONED BY (c2 string)")
+        check(sql("INSERT INTO TABLE tmp1 PARTITION(c2) " +
+          "SELECT /*+ repartition(10) */ * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)"))
+        check(sql("INSERT INTO TABLE tmp1 PARTITION(c2) " +
+          "SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2) ORDER BY c1"))
+        check(sql("INSERT INTO TABLE tmp1 PARTITION(c2) " +
+          "SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2) LIMIT 10"))
+      }
+    }
+
+    withSQLConf(KyuubiSQLConf.INSERT_REPARTITION_BEFORE_WRITE.key -> "false") {
+      Seq("USING PARQUET", "").foreach { storage =>
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage PARTITIONED BY (c2 string)")
+          check(sql("INSERT INTO TABLE tmp1 PARTITION(c2) " +
+            "SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)"))
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage")
+          check(sql("INSERT INTO TABLE tmp1 SELECT * FROM VALUES(1),(2),(3) AS t(c1)"))
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 $storage AS SELECT * FROM VALUES(1),(2),(3) AS t(c1)")
+        }
+
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 $storage PARTITIONED BY(c2) AS " +
+            s"SELECT * FROM VALUES(1, 'a'),(2, 'b') AS t(c1, c2)")
+        }
+      }
+    }
+  }
+
+  test("test dynamic partition write") {
+    def checkRepartitionExpression(df: DataFrame): Unit = {
+      assert(df.queryExecution.analyzed.collect {
+        case r: RebalancePartitions if r.partitionExpressions.size == 1 =>
+          assert(r.partitionExpressions.head.asInstanceOf[Attribute].name === "c2")
+          r
+      }.size == 1)
+    }
+
+    withSQLConf(
+      KyuubiSQLConf.INSERT_REPARTITION_BEFORE_WRITE.key -> "true",
+      KyuubiSQLConf.DYNAMIC_PARTITION_INSERTION_REPARTITION_NUM.key -> "2") {
+      Seq("USING PARQUET", "").foreach { storage =>
+        withTable("tmp1") {
+          sql(s"CREATE TABLE tmp1 (c1 int) $storage PARTITIONED BY (c2 string)")
+          checkRepartitionExpression(sql("INSERT INTO TABLE tmp1 SELECT 1 as c1, 'a' as c2 "))
+        }
+
+        withTable("tmp1") {
+          checkRepartitionExpression(
+            sql("CREATE TABLE tmp1 PARTITIONED BY(C2) SELECT 1 as c1, 'a' as c2 "))
+        }
+      }
+    }
+  }
+
+  test("OptimizedCreateHiveTableAsSelectCommand") {
+    withSQLConf(
+      HiveUtils.CONVERT_METASTORE_PARQUET.key -> "true",
+      HiveUtils.CONVERT_METASTORE_CTAS.key -> "true") {
+      withTable("t") {
+        val df = sql(s"CREATE TABLE t STORED AS parquet AS SELECT 1 as a")
+        val ctas = df.queryExecution.analyzed.collect {
+          case _: OptimizedCreateHiveTableAsSelectCommand => true
+        }
+        assert(ctas.size == 1)
+        val repartition = df.queryExecution.analyzed.collect {
+          case _: RebalancePartitions => true
+        }
+        assert(repartition.size == 1)
+      }
+    }
+  }
+}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/WatchDogSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/WatchDogSuite.scala
new file mode 100644
index 000000000..957089340
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/WatchDogSuite.scala
@@ -0,0 +1,20 @@
+/*
+ * 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.spark.sql
+
+class WatchDogSuite extends WatchDogSuiteBase {}
diff --git a/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/ZorderSuite.scala b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
new file mode 100644
index 000000000..fd04e27db
--- /dev/null
+++ b/extensions/spark/kyuubi-extension-spark-3-3/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.spark.sql
+
+class ZorderWithCodegenEnabledSuite extends ZorderWithCodegenEnabledSuiteBase {}
+
+class ZorderWithCodegenDisabledSuite extends ZorderWithCodegenDisabledSuiteBase {}
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/watchdog/MaxPartitionStrategy.scala b/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/watchdog/MaxPartitionStrategy.scala
index d724c7e40..61ab07adf 100644
--- a/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/watchdog/MaxPartitionStrategy.scala
+++ b/extensions/spark/kyuubi-extension-spark-common/src/main/scala/org/apache/kyuubi/sql/watchdog/MaxPartitionStrategy.scala
@@ -97,7 +97,7 @@ case class MaxPartitionStrategy(session: SparkSession)
                 _),
               _,
               _,
-              _)) if fsRelation.partitionSchemaOption.isDefined =>
+              _)) if fsRelation.partitionSchema.nonEmpty =>
         val (partitionKeyFilters, dataFilter) =
           getPartitionKeyFiltersAndDataFilters(
             fsRelation.sparkSession,
@@ -130,7 +130,7 @@ case class MaxPartitionStrategy(session: SparkSession)
                 _),
               _,
               _,
-              _)) if fsRelation.partitionSchemaOption.isDefined =>
+              _)) if fsRelation.partitionSchema.nonEmpty =>
         val (partitionKeyFilters, _) =
           getPartitionKeyFiltersAndDataFilters(
             fsRelation.sparkSession,
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala b/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuiteBase.scala
similarity index 97%
rename from extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
rename to extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuiteBase.scala
index e66f2cee5..c657dee49 100644
--- a/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuite.scala
+++ b/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/InsertShuffleNodeBeforeJoinSuiteBase.scala
@@ -23,7 +23,7 @@ import org.apache.spark.sql.internal.{SQLConf, StaticSQLConf}
 
 import org.apache.kyuubi.sql.KyuubiSQLConf
 
-class InsertShuffleNodeBeforeJoinSuite extends KyuubiSparkSQLExtensionTest {
+trait InsertShuffleNodeBeforeJoinSuiteBase extends KyuubiSparkSQLExtensionTest {
   override protected def beforeAll(): Unit = {
     super.beforeAll()
     setupData()
diff --git a/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuite.scala b/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuiteBase.scala
similarity index 99%
rename from extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
rename to extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuiteBase.scala
index 9eea2957f..b24533e69 100644
--- a/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuite.scala
+++ b/extensions/spark/kyuubi-extension-spark-common/src/test/scala/org/apache/spark/sql/ZorderSuiteBase.scala
@@ -31,7 +31,7 @@ import org.apache.spark.sql.types._
 import org.apache.kyuubi.sql.{KyuubiSQLConf, KyuubiSQLExtensionException}
 import org.apache.kyuubi.sql.zorder.{OptimizeZorderCommandBase, Zorder, ZorderBytesUtils}
 
-trait ZorderSuite extends KyuubiSparkSQLExtensionTest with ExpressionEvalHelper {
+trait ZorderSuiteBase extends KyuubiSparkSQLExtensionTest with ExpressionEvalHelper {
   override def sparkConf(): SparkConf = {
     super.sparkConf()
       .set(
@@ -654,7 +654,7 @@ trait ZorderSuite extends KyuubiSparkSQLExtensionTest with ExpressionEvalHelper
   }
 }
 
-class ZorderWithCodegenEnabledSuite extends ZorderSuite {
+trait ZorderWithCodegenEnabledSuiteBase extends ZorderSuiteBase {
   override def sparkConf(): SparkConf = {
     val conf = super.sparkConf
     conf.set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "true")
@@ -662,7 +662,7 @@ class ZorderWithCodegenEnabledSuite extends ZorderSuite {
   }
 }
 
-class ZorderWithCodegenDisabledSuite extends ZorderSuite {
+trait ZorderWithCodegenDisabledSuiteBase extends ZorderSuiteBase {
   override def sparkConf(): SparkConf = {
     val conf = super.sparkConf
     conf.set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
diff --git a/pom.xml b/pom.xml
index 852b098c8..9772b3ad9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1943,6 +1943,7 @@
             <modules>
                 <module>extensions/spark/kyuubi-extension-spark-common</module>
                 <module>extensions/spark/kyuubi-extension-spark-3-1</module>
+                <module>extensions/spark/kyuubi-spark-authz</module>
             </modules>
         </profile>
 
@@ -1960,6 +1961,7 @@
             </properties>
             <modules>
                 <module>extensions/spark/kyuubi-extension-spark-common</module>
+                <module>extensions/spark/kyuubi-spark-authz</module>
                 <module>extensions/spark/kyuubi-extension-spark-3-2</module>
             </modules>
         </profile>
@@ -1985,6 +1987,8 @@
                 <maven.plugin.scalatest.exclude.tags>org.apache.kyuubi.tags.ExtendedSQLTest,org.apache.kyuubi.tags.DeltaTest,org.apache.kyuubi.tags.IcebergTest,org.apache.kyuubi.tags.HudiTest</maven.plugin.scalatest.exclude.tags>
             </properties>
             <modules>
+                <module>extensions/spark/kyuubi-extension-spark-common</module>
+                <module>extensions/spark/kyuubi-extension-spark-3-3</module>
                 <module>extensions/spark/kyuubi-spark-connector-kudu</module>
             </modules>
         </profile>