You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/08/22 04:36:54 UTC

(shardingsphere) branch master updated: Support parsing Oracle ALTER TABLE EXCHANGE PARTITION statement (#39544)

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

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new c38520ca710 Support parsing Oracle ALTER TABLE EXCHANGE PARTITION statement (#39544)
c38520ca710 is described below

commit c38520ca7104b12716a2806dfb9887c3a775bc87
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Sat Aug 22 12:36:48 2026 +0800

    Support parsing Oracle ALTER TABLE EXCHANGE PARTITION statement (#39544)
    
    - Add exchangePartitionTable rule to Oracle alterTablePartitioning grammar
    - Add ExchangePartitionDefinitionSegment and AlterTableStatement.exchangePartitionDefinitions
    - Visit exchange partition clause in OracleDDLStatementVisitor
    - Add parser IT cases for Oracle exchange partition, dblink INSERT and LOCK TABLE PARTITION FOR
    
    Fixes #27152
---
 ...an-sess_ca4aa60b-ba89-4cc1-875d-a5f70aa6617e.md | 73 ++++++++++++++++++++++
 .../src/main/antlr4/imports/oracle/DDLStatement.g4 |  5 ++
 .../statement/type/OracleDDLStatementVisitor.java  | 23 +++++++
 .../ExchangePartitionDefinitionSegment.java        | 40 ++++++++++++
 .../type/ddl/table/AlterTableStatement.java        |  7 ++-
 .../standard/type/AlterTableStatementAssert.java   | 20 ++++++
 .../ExpectedExchangePartitionDefinition.java       | 40 ++++++++++++
 .../table/AlterTableStatementTestCase.java         |  4 ++
 .../src/main/resources/case/ddl/alter-table.xml    | 23 +++++++
 .../parser/src/main/resources/case/dml/insert.xml  | 13 ++++
 .../it/parser/src/main/resources/case/tcl/lock.xml |  1 +
 .../resources/sql/supported/ddl/alter-table.xml    |  3 +
 .../main/resources/sql/supported/dml/insert.xml    |  1 +
 .../src/main/resources/sql/supported/lcl/lock.xml  |  1 +
 14 files changed, 253 insertions(+), 1 deletion(-)

diff --git a/.zcode/plans/plan-sess_ca4aa60b-ba89-4cc1-875d-a5f70aa6617e.md b/.zcode/plans/plan-sess_ca4aa60b-ba89-4cc1-875d-a5f70aa6617e.md
new file mode 100644
index 00000000000..a81cf341e1b
--- /dev/null
+++ b/.zcode/plans/plan-sess_ca4aa60b-ba89-4cc1-875d-a5f70aa6617e.md
@@ -0,0 +1,73 @@
+# Issue #27152 剩余实现计划:Oracle ALTER TABLE EXCHANGE PARTITION + 测试用例补齐
+
+## 目标(完整关闭 issue #27152 的剩余缺口)
+1. Oracle `ALTER TABLE t EXCHANGE PARTITION p WITH TABLE t2 [(INCLUDING|EXCLUDING) INDEXES] [(WITH|WITHOUT) VALIDATION]` 语法支持 + 语义建模
+2. 三组 Oracle parser 测试用例:alter-table exchange partition、insert dblink、lock table
+
+## 非目标
+- 不改 MySQL/Doris/Hive 的 EXCHANGE PARTITION(保持其现状纯语法覆盖)
+- 不为 INCLUDING/EXCLUDING INDEXES、WITH/WITHOUT VALIDATION、updateIndexClauses、parallelClause 建模(仅语法解析)
+- 不做 CASCADE 及多级分区高级子句
+- 不做任何远程 GitHub 写操作(issue 回复/关单/PR 由用户执行,最后提供 commit message 与回复草稿)
+- 不改 binder/rewriter/routing 下游(无消费需求)
+
+## Architecture Gate 报告(语义建模触及共享模型)
+- Owner:`parser/sql/statement/core` 的 `AlterTableStatement`(全方言共享)与 `segment/ddl/partition/` 目录(现有 4 个分区 segment 全在 core,即便仅单方言消费,放 core 对齐先例)
+- 兼容性:纯增量。新增 segment 类 + `AlterTableStatement` 加 `@Singular("exchangePartitionDefinition") Collection<ExchangePartitionDefinitionSegment>` 字段(该类为 Lombok `@Builder` 私有构造,public builder API 向后兼容)
+- 复用:`PartitionSegment`、`SimpleTableSegment`、`AlterDefinitionSegment`、`ExpectedPartition`、`PartitionAssert`、`AbstractExpectedSQLSegment`
+- 其余 10 个 `alterTablePartitioning` 分支的 visitor 行为保持不变(仍不产生 segment)
+
+## 语法设计(对齐 Oracle 官方语法与仓库现有分支模式)
+```antlr
+alterTablePartitioning
+    : ...(现有 11 分支)
+    | exchangePartitionTable
+    ;
+
+exchangePartitionTable
+    : EXCHANGE (partitionExtendedName | subpartitionExtendedName) WITH TABLE tableName
+      ((INCLUDING | EXCLUDING) INDEXES)? ((WITH | WITHOUT) VALIDATION)? (updateIndexClauses parallelClause?)?
+    ;
+```
+所需关键字(EXCHANGE/INCLUDING/EXCLUDING/VALIDATION/WITHOUT)均已在 `OracleKeyword.g4` 定义且为非保留字;`partitionExtendedName`、`subpartitionExtendedName`、`updateIndexClauses`、`parallelClause` 规则已存在。生成代码不入库,Maven 构建时自动重新生成。
+
+## 改动文件清单(13 个)
+生产(4):
+1. `parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/DDLStatement.g4` — 上述语法(~6 行)
+2. `parser/sql/statement/core/.../segment/ddl/partition/ExchangePartitionDefinitionSegment.java` — 新建(startIndex/stopIndex/partition(可空)/exchangeTable,实现 AlterDefinitionSegment,~40 行)
+3. `parser/sql/statement/core/.../statement/type/ddl/table/AlterTableStatement.java` — +1 字段
+4. `parser/sql/engine/dialect/oracle/.../type/OracleDDLStatementVisitor.java` — `visitAlterDefinitionClause` 增加对 `alterTablePartitioning` 的访问(仅 exchange 分支产生 segment);新增 `visitExchangePartitionTable` 与 `visitPartitionExtendedName`(`PARTITION name` → PartitionSegment;`PARTITION FOR (...)` → partition 为 null;SUBPARTITION 形式同样 partition 为 null)
+
+测试(9):
+5. `test/it/parser/src/main/resources/sql/supported/ddl/alter-table.xml` — +3 用例:basic、`PARTITION FOR (TO_DATE(...))` + INCLUDING INDEXES WITHOUT VALIDATION、EXCLUDING INDEXES WITH VALIDATION
+6. `test/it/parser/src/main/resources/case/ddl/alter-table.xml` — +3 期望(`<table>` + `<exchange-partition>` 子树:partition + exchange-table)
+7. `test/it/parser/.../jaxb/segment/impl/definition/ExpectedExchangePartitionDefinition.java` — 新建(照 `ExpectedAddPartitionDefinition` 模式:partition + exchange-table 子节点)
+8. `test/it/parser/.../AlterTableStatementTestCase.java` — +`@XmlElement(name = "exchange-partition")`
+9. `test/it/parser/.../asserts/.../AlterTableStatementAssert.java` — +断言方法(复用 PartitionAssert/TableAssert)
+10. `test/it/parser/src/main/resources/sql/supported/dml/insert.xml` — +1:`INSERT INTO remote_table@link SELECT * FROM local_table`(issue 原文)
+11. `test/it/parser/src/main/resources/case/dml/insert.xml` — +1 期望(table name=remote_table、stop-index 覆盖 `remote_table@link`,照 `select_with_table_dblink_oracle`/`insert_with_select_subquery` 先例)
+12. `test/it/parser/src/main/resources/sql/supported/lcl/lock.xml` — +1:`LOCK TABLE interval_sales PARTITION FOR (TO_DATE('01-JUN-2007','dd-MON-yyyy')) IN SHARE MODE`(issue 原文;Oracle `visitLock` 返回空 tables)
+13. `test/it/parser/src/main/resources/case/tcl/lock.xml` — +1 空 `<lock/>` 期望(照 `lock_instance_for_backup` 先例)
+
+## 实施步骤
+1. 记录 `git status --short` 基线(现有 3 个未跟踪 `__pycache__` 目录保持不动)
+2. 改 g4 语法 → 编译验证重新生成无冲突
+3. 新建 core segment + AlterTableStatement 字段
+4. Oracle visitor 三处改动
+5. test/it/parser 的 JAXB + assert 扩展
+6. 三组测试用例 XML(start/stop-index 按资源文件原文逐一计数)
+7. 分层验证(见下)
+8. Completion Loop:task-delta 审计、spotless/checkstyle、正确性自审(含 start/stop-index 复核)
+
+## 验证命令(按 token-efficiency 契约用 log-file wrapper,自底向上)
+1. `./mvnw -pl parser/sql/statement/core -DskipTests -Dspotless.skip=true install`
+2. `./mvnw -pl parser/sql/engine/dialect/oracle -DskipITs -Dspotless.skip=true test`
+3. `./mvnw -pl parser/sql/engine/dialect/oracle -DskipTests install`(供 IT 取到含新语法的构件;若 IT 仍拉到旧 SNAPSHOT 则改用 `-am` 并记录理由=reactor freshness)
+4. `./mvnw -pl test/it/parser -Dtest=org.apache.shardingsphere.test.it.sql.parser.oracle.InternalOracleParserIT -Dsurefire.failIfNoSpecifiedTests=false test`
+5. `./mvnw spotless:apply -Pcheck -T1C` → `./mvnw checkstyle:check -Pcheck -T1C`
+
+## 规模与风险
+- 预估新增 ~180 行(生产 ~90 / 测试 ~90),远低于 10,000 行任务上限
+- 风险 1:test/it/parser 取 parser SNAPSHOT 的方式(本地仓库 vs reactor)——步骤 3/4 编码时按实际裁剪
+- 风险 2:`PARTITION FOR` 形式 partition 为 null 的断言——期望节点省略 `<partition>` 子节点即可表达
+- 完成后提供 commit message(`Support ALTER TABLE EXCHANGE PARTITION parsing in Oracle (#27152)` 方向)与 issue 回复草稿,Git 提交与远程操作由用户执行
\ No newline at end of file
diff --git a/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/DDLStatement.g4 b/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/DDLStatement.g4
index 7152f1320eb..d576cb3c1ee 100644
--- a/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/DDLStatement.g4
+++ b/parser/sql/engine/dialect/oracle/src/main/antlr4/imports/oracle/DDLStatement.g4
@@ -1570,6 +1570,7 @@ alterTablePartitioning
     | dropTablePartition
     | renamePartitionSubpart
     | alterIntervalPartitioning
+    | exchangePartitionTable
     ;
 
 modifyTableDefaultAttrs
@@ -1754,6 +1755,10 @@ alterIntervalPartitioning
     : SET INTERVAL LP_ expr? RP_ | SET STORE IN LP_ tablespaceName (COMMA_ tablespaceName)* RP_
     ;
 
+exchangePartitionTable
+    : EXCHANGE (partitionExtendedName | subpartitionExtendedName) WITH TABLE tableName ((INCLUDING | EXCLUDING) INDEXES)? ((WITH | WITHOUT) VALIDATION)? (updateIndexClauses parallelClause?)?
+    ;
+
 partitionExtendedNames
     : (PARTITION | PARTITIONS) (partitionName | partitionForClauses) (COMMA_ (partitionName | partitionForClauses))*
     ;
diff --git a/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/type/OracleDDLStatementVisitor.java b/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/type/OracleDDLStatementVisitor.java
index b2e1a27bde4..c269a42b689 100644
--- a/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/type/OracleDDLStatementVisitor.java
+++ b/parser/sql/engine/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/engine/oracle/visitor/statement/type/OracleDDLStatementVisitor.java
@@ -162,6 +162,7 @@ import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.DropTy
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.DropViewContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.DynamicSqlStmtContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.ExceptionHandlerContext;
+import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.ExchangePartitionTableContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.FlashbackDatabaseContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.FlashbackTableContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.ForLoopStatementContext;
@@ -191,6 +192,7 @@ import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.OutOfL
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.OwnerContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.PackageNameContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.ParameterDeclarationContext;
+import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.PartitionExtendedNameContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.PlsqlBlockContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.PlsqlFunctionSourceContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.PlsqlPackageSourceContext;
@@ -228,6 +230,7 @@ import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.Variab
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.VarrayTypeSpecContext;
 import org.apache.shardingsphere.sql.parser.autogen.OracleStatementParser.CreateIndexTypeContext;
 import org.apache.shardingsphere.sql.parser.engine.oracle.visitor.statement.OracleStatementVisitor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dal.PartitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dal.VariableSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.AlterDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.CreateDefinitionSegment;
@@ -244,6 +247,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.constrain
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.index.IndexSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.index.IndexTypeSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.packages.PackageSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.ExchangePartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.routine.FunctionNameSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.routine.RoutineBodySegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.routine.ValidStatementSegment;
@@ -667,6 +671,8 @@ public final class OracleDDLStatementVisitor extends OracleStatementVisitor impl
                     result.dropConstraintDefinition((DropConstraintDefinitionSegment) each);
                 } else if (each instanceof ModifyCollectionRetrievalSegment) {
                     result.modifyCollectionRetrieval((ModifyCollectionRetrievalSegment) each);
+                } else if (each instanceof ExchangePartitionDefinitionSegment) {
+                    result.exchangePartitionDefinition((ExchangePartitionDefinitionSegment) each);
                 }
             }
         }
@@ -694,10 +700,27 @@ public final class OracleDDLStatementVisitor extends OracleStatementVisitor impl
         if (null != ctx.constraintClauses()) {
             result.getValue().addAll(((CollectionValue<AddColumnDefinitionSegment>) visit(ctx.constraintClauses())).getValue());
         }
+        if (null != ctx.alterTablePartitioning() && null != ctx.alterTablePartitioning().exchangePartitionTable()) {
+            result.getValue().add((AlterDefinitionSegment) visit(ctx.alterTablePartitioning().exchangePartitionTable()));
+        }
         // TODO More alter definition parse
         return result;
     }
     
+    @Override
+    public ASTNode visitExchangePartitionTable(final ExchangePartitionTableContext ctx) {
+        PartitionSegment partition = null;
+        if (null != ctx.partitionExtendedName() && null != ctx.partitionExtendedName().partitionName()) {
+            partition = (PartitionSegment) visit(ctx.partitionExtendedName());
+        }
+        return new ExchangePartitionDefinitionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), partition, (SimpleTableSegment) visit(ctx.tableName()));
+    }
+    
+    @Override
+    public ASTNode visitPartitionExtendedName(final PartitionExtendedNameContext ctx) {
+        return new PartitionSegment(ctx.partitionName().getStart().getStartIndex(), ctx.partitionName().getStop().getStopIndex(), (IdentifierValue) visit(ctx.partitionName()));
+    }
+    
     @SuppressWarnings("unchecked")
     @Override
     public ASTNode visitColumnClauses(final ColumnClausesContext ctx) {
diff --git a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/ddl/partition/ExchangePartitionDefinitionSegment.java b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/ddl/partition/ExchangePartitionDefinitionSegment.java
new file mode 100644
index 00000000000..2df7be81d6c
--- /dev/null
+++ b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/ddl/partition/ExchangePartitionDefinitionSegment.java
@@ -0,0 +1,40 @@
+/*
+ * 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.shardingsphere.sql.parser.statement.core.segment.ddl.partition;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dal.PartitionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.AlterDefinitionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
+
+/**
+ * Exchange partition definition segment.
+ */
+@RequiredArgsConstructor
+@Getter
+public final class ExchangePartitionDefinitionSegment implements AlterDefinitionSegment {
+    
+    private final int startIndex;
+    
+    private final int stopIndex;
+    
+    private final PartitionSegment partition;
+    
+    private final SimpleTableSegment exchangeTable;
+}
diff --git a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/ddl/table/AlterTableStatement.java b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/ddl/table/AlterTableStatement.java
index df4629882b0..48a77b76a9a 100644
--- a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/ddl/table/AlterTableStatement.java
+++ b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/ddl/table/AlterTableStatement.java
@@ -42,6 +42,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.index.Ind
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.index.RenameIndexDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.AddPartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.AddPartitionsSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.ExchangePartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.ModifyPartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.RenamePartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.primary.DropPrimaryKeyDefinitionSegment;
@@ -146,6 +147,8 @@ public final class AlterTableStatement extends DDLStatement {
     
     private final Collection<ModifyPartitionDefinitionSegment> modifyPartitionDefinitions;
     
+    private final Collection<ExchangePartitionDefinitionSegment> exchangePartitionDefinitions;
+    
     private final SQLStatementAttributes attributes;
     
     @Builder
@@ -178,7 +181,8 @@ public final class AlterTableStatement extends DDLStatement {
                                 @Singular("renamePartitionDefinition") final Collection<RenamePartitionDefinitionSegment> renamePartitionDefinitions,
                                 @Singular("addPartitionDefinition") final Collection<AddPartitionDefinitionSegment> addPartitionDefinitions,
                                 @Singular("addPartitionsSegment") final Collection<AddPartitionsSegment> addPartitionsSegments,
-                                @Singular("modifyPartitionDefinition") final Collection<ModifyPartitionDefinitionSegment> modifyPartitionDefinitions) {
+                                @Singular("modifyPartitionDefinition") final Collection<ModifyPartitionDefinitionSegment> modifyPartitionDefinitions,
+                                @Singular("exchangePartitionDefinition") final Collection<ExchangePartitionDefinitionSegment> exchangePartitionDefinitions) {
         super(databaseType);
         this.table = table;
         this.renameTable = renameTable;
@@ -215,6 +219,7 @@ public final class AlterTableStatement extends DDLStatement {
         this.addPartitionDefinitions = addPartitionDefinitions;
         this.addPartitionsSegments = addPartitionsSegments;
         this.modifyPartitionDefinitions = modifyPartitionDefinitions;
+        this.exchangePartitionDefinitions = exchangePartitionDefinitions;
         attributes = new SQLStatementAttributes(new TableSQLStatementAttribute(getTables()), new AlterTableConstraintSQLStatementAttribute(), new AlterTableIndexSQLStatementAttribute());
     }
     
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/standard/type/AlterTableStatementAssert.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/standard/type/AlterTableStatementAssert.java
index 8f0e564ff7f..ebfc8e8a021 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/standard/type/AlterTableStatementAssert.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/standard/type/AlterTableStatementAssert.java
@@ -32,6 +32,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.constrain
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.index.RenameIndexDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.AddPartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.AddPartitionsSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.ExchangePartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.ModifyPartitionDefinitionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.PartitionValuesSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.partition.RenamePartitionDefinitionSegment;
@@ -78,6 +79,7 @@ import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedAddPartitionDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedModifyPartitionDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedRenamePartitionDefinition;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedExchangePartitionDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedAddRollupDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedDropRollupDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedOrderByColumnDefinition;
@@ -135,6 +137,7 @@ public final class AlterTableStatementAssert {
         assertRenamePartitionDefinitions(assertContext, actual, expected);
         assertAddPartitionDefinitions(assertContext, actual, expected);
         assertModifyPartitionDefinitions(assertContext, actual, expected);
+        assertExchangePartitionDefinitions(assertContext, actual, expected);
         assertAddPartitionsSegments(assertContext, actual, expected);
         assertConvertTable(assertContext, actual, expected);
         assertModifyCollectionRetrievalDefinitions(assertContext, actual, expected);
@@ -464,6 +467,23 @@ public final class AlterTableStatementAssert {
         }
     }
     
+    private static void assertExchangePartitionDefinitions(final SQLCaseAssertContext assertContext, final AlterTableStatement actual, final AlterTableStatementTestCase expected) {
+        assertThat(assertContext.getText("Exchange partition definitions size assertion error: "), actual.getExchangePartitionDefinitions().size(), is(expected.getExchangePartitions().size()));
+        int count = 0;
+        for (ExchangePartitionDefinitionSegment each : actual.getExchangePartitionDefinitions()) {
+            ExpectedExchangePartitionDefinition expectedExchangePartition = expected.getExchangePartitions().get(count);
+            if (null != each.getPartition()) {
+                assertNotNull(expectedExchangePartition.getPartition(), assertContext.getText("Expected partition should exist."));
+                PartitionAssert.assertIs(assertContext, each.getPartition(), expectedExchangePartition.getPartition());
+            } else {
+                assertNull(expectedExchangePartition.getPartition(), assertContext.getText("Expected partition should not exist."));
+            }
+            TableAssert.assertIs(assertContext, each.getExchangeTable(), expectedExchangePartition.getExchangeTable());
+            SQLSegmentAssert.assertIs(assertContext, each, expectedExchangePartition);
+            count++;
+        }
+    }
+    
     private static void assertAddPartitionDefinitions(final SQLCaseAssertContext assertContext, final AlterTableStatement actual, final AlterTableStatementTestCase expected) {
         assertThat(assertContext.getText("Add partition definitions size assertion error: "), actual.getAddPartitionDefinitions().size(), is(expected.getAddPartitions().size()));
         int count = 0;
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/definition/ExpectedExchangePartitionDefinition.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/definition/ExpectedExchangePartitionDefinition.java
new file mode 100644
index 00000000000..030e8148984
--- /dev/null
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/definition/ExpectedExchangePartitionDefinition.java
@@ -0,0 +1,40 @@
+/*
+ * 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.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.AbstractExpectedSQLSegment;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.index.ExpectedPartition;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.table.ExpectedSimpleTable;
+
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Expected exchange partition definition.
+ */
+@Getter
+@Setter
+public final class ExpectedExchangePartitionDefinition extends AbstractExpectedSQLSegment {
+    
+    @XmlElement(name = "partition")
+    private ExpectedPartition partition;
+    
+    @XmlElement(name = "exchange-table")
+    private ExpectedSimpleTable exchangeTable;
+}
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/table/AlterTableStatementTestCase.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/table/AlterTableStatementTestCase.java
index 649d2950af8..a24dca07951 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/table/AlterTableStatementTestCase.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/table/AlterTableStatementTestCase.java
@@ -39,6 +39,7 @@ import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedRenameRollupDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedReplaceTableDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedAddPartitionDefinition;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedExchangePartitionDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedModifyPartitionDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedEnableFeatureDefinition;
 import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.definition.ExpectedModifyEngineDefinition;
@@ -116,6 +117,9 @@ public final class AlterTableStatementTestCase extends SQLParserTestCase {
     @XmlElement(name = "modify-partition")
     private final List<ExpectedModifyPartitionDefinition> modifyPartitions = new LinkedList<>();
     
+    @XmlElement(name = "exchange-partition")
+    private final List<ExpectedExchangePartitionDefinition> exchangePartitions = new LinkedList<>();
+    
     @XmlElement(name = "drop-column")
     private final List<ExpectedColumn> dropColumns = new LinkedList<>();
     
diff --git a/test/it/parser/src/main/resources/case/ddl/alter-table.xml b/test/it/parser/src/main/resources/case/ddl/alter-table.xml
index 97f1d205a16..0c636c0d93e 100644
--- a/test/it/parser/src/main/resources/case/ddl/alter-table.xml
+++ b/test/it/parser/src/main/resources/case/ddl/alter-table.xml
@@ -2124,6 +2124,29 @@
         <table name="target_table" start-index="12" stop-index="23" />
     </alter-table>
 
+    <alter-table sql-case-id="alter_table_exchange_partition_oracle">
+        <table name="orders" start-index="12" stop-index="17" />
+        <exchange-partition start-index="19" stop-index="72">
+            <partition name="orders_q1" start-index="38" stop-index="46" />
+            <exchange-table name="orders_q1_copy" start-index="59" stop-index="72" />
+        </exchange-partition>
+    </alter-table>
+
+    <alter-table sql-case-id="alter_table_exchange_partition_for_oracle">
+        <table name="sales" start-index="12" stop-index="16" />
+        <exchange-partition start-index="18" stop-index="136">
+            <exchange-table name="sales_old" start-index="91" stop-index="99" />
+        </exchange-partition>
+    </alter-table>
+
+    <alter-table sql-case-id="alter_table_exchange_partition_excluding_index_oracle">
+        <table name="orders" start-index="12" stop-index="17" />
+        <exchange-partition start-index="19" stop-index="106">
+            <partition name="orders_q2" start-index="38" stop-index="46" />
+            <exchange-table name="orders_q2_copy" start-index="59" stop-index="72" />
+        </exchange-partition>
+    </alter-table>
+
     <alter-table sql-case-id="alter_table_set_partition_retention_basic">
         <table name="log_data" start-index="12" stop-index="19" />
     </alter-table>
diff --git a/test/it/parser/src/main/resources/case/dml/insert.xml b/test/it/parser/src/main/resources/case/dml/insert.xml
index 893ad3c940b..c98732c7d43 100644
--- a/test/it/parser/src/main/resources/case/dml/insert.xml
+++ b/test/it/parser/src/main/resources/case/dml/insert.xml
@@ -2274,6 +2274,19 @@
         </select>
     </insert>
 
+    <insert sql-case-id="insert_into_table_with_dblink">
+        <table name="remote_table" start-index="12" stop-index="28" />
+        <columns start-index="29" stop-index="29" />
+        <select>
+            <from>
+                <simple-table name="local_table" start-index="44" stop-index="54" />
+            </from>
+            <projections start-index="37" stop-index="37">
+                <shorthand-projection start-index="37" stop-index="37" />
+            </projections>
+        </select>
+    </insert>
+
     <insert sql-case-id="insert_with_multitable_element">
         <multi-table-insert-type value="ALL" />
         <multi-table-insert-into start-index="11" stop-index="231">
diff --git a/test/it/parser/src/main/resources/case/tcl/lock.xml b/test/it/parser/src/main/resources/case/tcl/lock.xml
index 1a715eb979b..e3d6f39ae27 100644
--- a/test/it/parser/src/main/resources/case/tcl/lock.xml
+++ b/test/it/parser/src/main/resources/case/tcl/lock.xml
@@ -108,4 +108,5 @@
     <lock sql-case-id="lock_hs1">
         <table name="hs1" start-index="5" stop-index="7" />
     </lock>
+    <lock sql-case-id="lock_table_partition_for_share_mode" />
 </sql-parser-test-cases>
diff --git a/test/it/parser/src/main/resources/sql/supported/ddl/alter-table.xml b/test/it/parser/src/main/resources/sql/supported/ddl/alter-table.xml
index 24d090fd64f..de3d81f71b6 100644
--- a/test/it/parser/src/main/resources/sql/supported/ddl/alter-table.xml
+++ b/test/it/parser/src/main/resources/sql/supported/ddl/alter-table.xml
@@ -316,6 +316,9 @@
     <sql-case id="alter_table_rename_partition_basic" value="ALTER TABLE sales PARTITION (dt='2025-07-01') RENAME TO PARTITION (dt='20250701')" db-types="Hive" />
     <sql-case id="alter_table_exchange_partition_basic" value="ALTER TABLE target_table EXCHANGE PARTITION (dt='2025-07-25') WITH TABLE source_table" db-types="Hive" />
     <sql-case id="alter_table_exchange_partition_multiple" value="ALTER TABLE target_table EXCHANGE PARTITION (dt='2025-07-25', region='asia') WITH TABLE source_table" db-types="Hive" />
+    <sql-case id="alter_table_exchange_partition_oracle" value="ALTER TABLE orders EXCHANGE PARTITION orders_q1 WITH TABLE orders_q1_copy" db-types="Oracle" />
+    <sql-case id="alter_table_exchange_partition_for_oracle" value="ALTER TABLE sales EXCHANGE PARTITION FOR (TO_DATE('01-JAN-2020','dd-MON-yyyy')) WITH TABLE sales_old INCLUDING INDEXES WITHOUT VALIDATION" db-types="Oracle" />
+    <sql-case id="alter_table_exchange_partition_excluding_index_oracle" value="ALTER TABLE orders EXCHANGE PARTITION orders_q2 WITH TABLE orders_q2_copy EXCLUDING INDEXES WITH VALIDATION" db-types="Oracle" />
     <sql-case id="alter_table_set_partition_retention_basic" value="ALTER TABLE log_data SET TBLPROPERTIES ('partition.retention.period'='30d')" db-types="Hive" />
     <sql-case id="alter_table_set_partition_retention_with_discover" value="ALTER TABLE log_data SET TBLPROPERTIES ('partition.retention.period'='7d', 'discover.partitions'='true')" db-types="Hive" />
     <sql-case id="msck_check_table" value="MSCK TABLE sales" db-types="Hive" />
diff --git a/test/it/parser/src/main/resources/sql/supported/dml/insert.xml b/test/it/parser/src/main/resources/sql/supported/dml/insert.xml
index 32e90ae94ae..45c09ecbd31 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/insert.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/insert.xml
@@ -91,6 +91,7 @@
     <sql-case id="insert_without_columns" value="INSERT INTO departments VALUES (280, 'Recreation', 121, 1700)" db-types="Oracle" />
     <sql-case id="insert_with_dml_table_expr_select" value="INSERT INTO (SELECT employee_id, last_name, email, hire_date, job_id, salary, commission_pct FROM employees) VALUES (207, 'Gregory', 'pgregory@example.com', sysdate, 'PU_CLERK', 1.2E3, NULL)" db-types="Oracle" />
     <sql-case id="insert_with_select_subquery" value="INSERT INTO bonuses SELECT employee_id, salary*1.1 FROM employees WHERE commission_pct > 0.25" db-types="Oracle" />
+    <sql-case id="insert_into_table_with_dblink" value="INSERT INTO remote_table@link SELECT * FROM local_table" db-types="Oracle" />
     <sql-case id="insert_with_multitable_element" value="INSERT ALL INTO sales (prod_id, cust_id, time_id, amount) VALUES (product_id, customer_id, weekly_start_date, sales_sun) INTO sales (prod_id, cust_id, time_id, amount) VALUES (product_id, customer_id, sales_mon, weekly_start_date+1) SELECT product_id, customer_id, weekly_start_date, sales_sun, sales_mon FROM sales_input_table" db-types="Oracle" />
     <sql-case id="insert_all_with_multi_table_with_conditional_when" value="INSERT ALL WHEN order_total &lt;= 100000 THEN INTO small_orders WHEN order_total > 1000000 AND order_total &lt;= 200000 THEN INTO medium_orders WHEN order_total > 200000 THEN INTO large_orders SELECT order_id, order_total, sales_rep_id, customer_id FROM orders" db-types="Oracle" />
     <sql-case id="insert_all_with_multi_table_with_conditional_when_with_conditional_else" value="INSERT ALL WHEN order_total &lt;= 100000 THEN INTO small_orders WHEN order_total > 100000 AND order_total &lt;= 200000 THEN INTO medium_orders ELSE INTO large_orders SELECT order_id, order_total, sales_rep_id, customer_id FROM orders" db-types="Oracle" />
diff --git a/test/it/parser/src/main/resources/sql/supported/lcl/lock.xml b/test/it/parser/src/main/resources/sql/supported/lcl/lock.xml
index 812b2999754..029bf334366 100644
--- a/test/it/parser/src/main/resources/sql/supported/lcl/lock.xml
+++ b/test/it/parser/src/main/resources/sql/supported/lcl/lock.xml
@@ -47,4 +47,5 @@
     <sql-case id="lock_table_share_update_exclusive_nowait" value="LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE NOWAIT;" db-types="PostgreSQL" />
     <sql-case id="lock_table_share_update_exclusive" value="LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE;" db-types="PostgreSQL" />
     <sql-case id="lock_hs1" value="LOCK hs1;" db-types="PostgreSQL" />
+    <sql-case id="lock_table_partition_for_share_mode" value="LOCK TABLE interval_sales PARTITION FOR (TO_DATE('01-JUN-2007','dd-MON-yyyy')) IN SHARE MODE" db-types="Oracle" />
 </sql-cases>