This is an automated email from the ASF dual-hosted git repository.
zhangyv pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push:
new 9b97559e03 feature: support Oracle Batch Insert (#7675)
9b97559e03 is described below
commit 9b97559e032585d0d8c896273125f1d8fea476b5
Author: maple <[email protected]>
AuthorDate: Fri Oct 17 15:27:17 2025 +0800
feature: support Oracle Batch Insert (#7675)
---
changes/en-us/2.x.md | 1 +
changes/zh-cn/2.x.md | 1 +
.../druid/DruidSQLRecognizerFactoryImpl.java | 10 +
.../druid/oracle/OracleMultiInsertRecognizer.java | 264 ++++++++++++++
.../oracle/OracleOperateRecognizerHolder.java | 4 +
.../druid/DruidSQLRecognizerFactoryTest.java | 146 +++++---
.../oracle/OracleMultiInsertRecognizerTest.java | 404 +++++++++++++++++++++
7 files changed, 783 insertions(+), 47 deletions(-)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 83fdc753a5..ecbabfb7b7 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -27,6 +27,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#7559](https://github.com/apache/incubator-seata/pull/7559)] Introduce
Cleanup API for TableMetaRefreshHolder Instance
- [[#7669](https://github.com/apache/incubator-seata/pull/7669)] add support
for Jackson serialization and deserialization of PostgreSQL array types
- [[#7664](https://github.com/apache/incubator-seata/pull/7664)] support
shentongdatabase XA mode
+- [[#7675](https://github.com/apache/incubator-seata/pull/7675)] support
Oracle Batch Insert
- [[#7663](https://github.com/apache/incubator-seata/pull/7663)] add Java 25
support in CI configuration files
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index cb247e0fc6..dc6c1a8459 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -27,6 +27,7 @@
- [[#7559](https://github.com/apache/incubator-seata/pull/7559)] 为
TableMetaRefreshHolder 实例引入清理 API
- [[#7669](https://github.com/apache/incubator-seata/pull/7669)] 添加对 Jackson
序列化和反序列化 PostgreSQL 数组类型的支持
- [[#7664](https://github.com/apache/incubator-seata/pull/7565)] 支持神通数据库的XA模式
+- [[#7675](https://github.com/apache/incubator-seata/pull/7675)] 支持Oracle批量插入
- [[#7663](https://github.com/apache/incubator-seata/pull/7663)]
支持java25版本的CI流水綫
diff --git
a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryImpl.java
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryImpl.java
index 02d8ada742..90820f84fc 100644
---
a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryImpl.java
+++
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryImpl.java
@@ -23,10 +23,12 @@ import
com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLReplaceStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
+import
com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import org.apache.seata.common.exception.NotSupportYetException;
import org.apache.seata.common.util.CollectionUtils;
import org.apache.seata.sqlparser.SQLRecognizer;
import org.apache.seata.sqlparser.SQLRecognizerFactory;
+import org.apache.seata.sqlparser.druid.oracle.OracleOperateRecognizerHolder;
import java.util.ArrayList;
import java.util.List;
@@ -72,6 +74,14 @@ class DruidSQLRecognizerFactoryImpl implements
SQLRecognizerFactory {
recognizer = recognizerHolder.getDeleteRecognizer(sql,
sqlStatement);
} else if (sqlStatement instanceof SQLSelectStatement) {
recognizer =
recognizerHolder.getSelectForUpdateRecognizer(sql, sqlStatement);
+ } else if (sqlStatement instanceof OracleMultiInsertStatement) {
+ OracleMultiInsertStatement stmt = (OracleMultiInsertStatement)
sqlStatement;
+ if (stmt.getOption() ==
OracleMultiInsertStatement.Option.FIRST) {
+ throw new NotSupportYetException("INSERT FIRST not
supported yet");
+ }
+ // Use specialized methods to handle Oracle bulk inserts
+ recognizer =
+ ((OracleOperateRecognizerHolder)
recognizerHolder).getMultiInsertRecognizer(sql, sqlStatement);
}
// When recognizer is null, it indicates that recognizerHolder
cannot allocate unsupported syntax, like
diff --git
a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizer.java
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizer.java
new file mode 100644
index 0000000000..08a445c722
--- /dev/null
+++
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizer.java
@@ -0,0 +1,264 @@
+/*
+ * 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.seata.sqlparser.druid.oracle;
+
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.SQLStatement;
+import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
+import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
+import com.alibaba.druid.sql.ast.expr.SQLNullExpr;
+import com.alibaba.druid.sql.ast.expr.SQLSequenceExpr;
+import com.alibaba.druid.sql.ast.expr.SQLValuableExpr;
+import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
+import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
+import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
+import
com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
+import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor;
+import org.apache.seata.common.exception.NotSupportYetException;
+import org.apache.seata.common.util.CollectionUtils;
+import org.apache.seata.sqlparser.SQLInsertRecognizer;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.NotPlaceholderExpr;
+import org.apache.seata.sqlparser.struct.Null;
+import org.apache.seata.sqlparser.struct.SqlMethodExpr;
+import org.apache.seata.sqlparser.struct.SqlSequenceExpr;
+import org.apache.seata.sqlparser.util.ColumnUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Oracle Multi Insert Recognizer for INSERT ALL statements
+ */
+public class OracleMultiInsertRecognizer extends BaseOracleRecognizer
implements SQLInsertRecognizer {
+
+ private final OracleMultiInsertStatement ast;
+ private String validatedTableName;
+ private List<String> validatedColumns;
+
+ public OracleMultiInsertRecognizer(String originalSQL, SQLStatement ast) {
+ super(originalSQL);
+ this.ast = (OracleMultiInsertStatement) ast;
+ validateSingleTableConsistency();
+ }
+
+ /**
+ * Verifies that all entries are in the same table and have consistent
column definitions.
+ * <p>
+ * If validation fails (e.g., conditional clauses are present, or
table/column consistency is violated),
+ * this method throws a {@link NotSupportYetException}.
+ * <p>
+ * Upon successful validation, initializes {@code validatedTableName} and
{@code validatedColumns}
+ * with the consistent table name and column definitions.
+ */
+ private void validateSingleTableConsistency() {
+ if (CollectionUtils.isEmpty(ast.getEntries())) {
+ return;
+ }
+
+ String firstTableName = null;
+ List<String> firstColumns = null;
+
+ for (OracleMultiInsertStatement.Entry entry : ast.getEntries()) {
+ // Check whether conditional insertion is included. It is not
supported yet.
+ if (entry instanceof
OracleMultiInsertStatement.ConditionalInsertClause) {
+ throw new NotSupportYetException(
+ "Oracle Multi Insert with conditional clauses
(WHEN...THEN) is not supported yet. " + "SQL: "
+ + getOriginalSQL());
+ }
+ if (entry instanceof OracleMultiInsertStatement.InsertIntoClause) {
+ OracleMultiInsertStatement.InsertIntoClause insertClause =
+ (OracleMultiInsertStatement.InsertIntoClause) entry;
+
+ // Get the current table name
+ String currentTableName = getTableNameFromClause(insertClause);
+ if (currentTableName == null) {
+ continue;
+ }
+
+ // Get current column information
+ List<String> currentColumns =
getColumnsFromClause(insertClause);
+
+ if (firstTableName == null) {
+ firstTableName = currentTableName;
+ firstColumns = currentColumns;
+ } else {
+ // Check whether the table names are consistent
+ if (!firstTableName.equalsIgnoreCase(currentTableName)) {
+ throw new NotSupportYetException(
+ "Oracle Multi Insert with different tables is
not supported yet. " + "Found tables: "
+ + firstTableName + " and " +
currentTableName + ". SQL: "
+ + getOriginalSQL());
+ }
+
+ // Check that column definitions are consistent
+ if (!Objects.equals(firstColumns, currentColumns)) {
+ throw new NotSupportYetException(
+ "Oracle Multi Insert with different column
definitions is not supported yet. "
+ + "Table: " + firstTableName + ". SQL:
" + getOriginalSQL());
+ }
+ }
+ }
+ }
+
+ this.validatedTableName = firstTableName;
+ this.validatedColumns = firstColumns;
+ }
+
+ private String
getTableNameFromClause(OracleMultiInsertStatement.InsertIntoClause
insertClause) {
+ if (insertClause.getTableSource() != null) {
+ StringBuilder sb = new StringBuilder();
+ OracleOutputVisitor visitor = new OracleOutputVisitor(sb) {
+ @Override
+ public boolean visit(SQLExprTableSource x) {
+ printTableSourceExpr(x.getExpr());
+ return false;
+ }
+ };
+ visitor.visit(insertClause.getTableSource());
+ return sb.toString();
+ }
+ return null;
+ }
+
+ private List<String>
getColumnsFromClause(OracleMultiInsertStatement.InsertIntoClause insertClause) {
+ if (CollectionUtils.isEmpty(insertClause.getColumns())) {
+ return Collections.emptyList();
+ }
+
+ List<String> columns = new ArrayList<>();
+ for (SQLExpr expr : insertClause.getColumns()) {
+ if (expr instanceof SQLIdentifierExpr) {
+ columns.add(((SQLIdentifierExpr) expr).getName());
+ } else {
+ // Handling non-standard column names
+ wrapSQLParsingException(expr);
+ }
+ }
+ return columns;
+ }
+
+ @Override
+ public SQLType getSQLType() {
+ return SQLType.INSERT;
+ }
+
+ @Override
+ public String getTableName() {
+ return validatedTableName;
+ }
+
+ @Override
+ public String getTableAlias() {
+ if (!CollectionUtils.isEmpty(ast.getEntries())) {
+ OracleMultiInsertStatement.Entry firstEntry =
ast.getEntries().get(0);
+ if (firstEntry instanceof
OracleMultiInsertStatement.InsertIntoClause) {
+ OracleMultiInsertStatement.InsertIntoClause insertClause =
+ (OracleMultiInsertStatement.InsertIntoClause)
firstEntry;
+ if (insertClause.getTableSource() != null) {
+ return insertClause.getTableSource().getAlias();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List<String> getInsertColumns() {
+ return validatedColumns;
+ }
+
+ @Override
+ public boolean insertColumnsIsEmpty() {
+ return CollectionUtils.isEmpty(validatedColumns);
+ }
+
+ @Override
+ public List<List<Object>> getInsertRows(Collection<Integer>
primaryKeyIndex) {
+ List<List<Object>> allRows = new ArrayList<>();
+
+ if (!CollectionUtils.isEmpty(ast.getEntries())) {
+ for (OracleMultiInsertStatement.Entry entry : ast.getEntries()) {
+ if (entry instanceof
OracleMultiInsertStatement.InsertIntoClause) {
+ OracleMultiInsertStatement.InsertIntoClause insertClause =
+ (OracleMultiInsertStatement.InsertIntoClause)
entry;
+
+ if
(!CollectionUtils.isEmpty(insertClause.getValuesList())) {
+ for (SQLInsertStatement.ValuesClause valuesClause :
insertClause.getValuesList()) {
+ List<SQLExpr> exprs = valuesClause.getValues();
+ List<Object> row = new ArrayList<>(exprs.size());
+ allRows.add(row);
+
+ for (int i = 0, len = exprs.size(); i < len; i++) {
+ SQLExpr expr = exprs.get(i);
+ if (expr instanceof SQLNullExpr) {
+ row.add(Null.get());
+ } else if (expr instanceof SQLValuableExpr) {
+ row.add(((SQLValuableExpr)
expr).getValue());
+ } else if (expr instanceof SQLVariantRefExpr) {
+ row.add(((SQLVariantRefExpr)
expr).getName());
+ } else if (expr instanceof
SQLMethodInvokeExpr) {
+ row.add(SqlMethodExpr.get());
+ } else if (expr instanceof SQLSequenceExpr) {
+ SQLSequenceExpr sequenceExpr =
(SQLSequenceExpr) expr;
+ String sequence =
sequenceExpr.getSequence().getSimpleName();
+ String function =
sequenceExpr.getFunction().name;
+ row.add(new SqlSequenceExpr(sequence,
function));
+ } else {
+ if (primaryKeyIndex.contains(i)) {
+ wrapSQLParsingException(expr);
+ }
+ row.add(NotPlaceholderExpr.get());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return allRows;
+ }
+
+ @Override
+ public List<String> getInsertParamsValue() {
+ return null;
+ }
+
+ @Override
+ public List<String> getDuplicateKeyUpdate() {
+ return null;
+ }
+
+ @Override
+ public List<String> getInsertColumnsUnEscape() {
+ List<String> insertColumns = getInsertColumns();
+ return ColumnUtils.delEscape(insertColumns, getDbType());
+ }
+
+ @Override
+ protected SQLStatement getAst() {
+ return ast;
+ }
+
+ @Override
+ public boolean isSqlSyntaxSupports() {
+ return true;
+ }
+}
diff --git
a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleOperateRecognizerHolder.java
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleOperateRecognizerHolder.java
index 6fe36ed8f7..50d8714155 100644
---
a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleOperateRecognizerHolder.java
+++
b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleOperateRecognizerHolder.java
@@ -52,4 +52,8 @@ public class OracleOperateRecognizerHolder implements
SQLOperateRecognizerHolder
}
return null;
}
+
+ public SQLRecognizer getMultiInsertRecognizer(String sql, SQLStatement
ast) {
+ return new OracleMultiInsertRecognizer(sql, ast);
+ }
}
diff --git
a/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryTest.java
b/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryTest.java
index 741e411d90..2d283beb69 100644
---
a/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryTest.java
+++
b/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/DruidSQLRecognizerFactoryTest.java
@@ -16,104 +16,116 @@
*/
package org.apache.seata.sqlparser.druid;
+import com.alibaba.druid.sql.SQLUtils;
+import com.alibaba.druid.sql.ast.SQLStatement;
+import
com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import org.apache.seata.common.exception.NotSupportYetException;
import org.apache.seata.common.loader.EnhancedServiceLoader;
import org.apache.seata.sqlparser.SQLRecognizer;
import org.apache.seata.sqlparser.SQLRecognizerFactory;
import org.apache.seata.sqlparser.SQLType;
import org.apache.seata.sqlparser.SqlParserType;
+import org.apache.seata.sqlparser.druid.oracle.OracleOperateRecognizerHolder;
import org.apache.seata.sqlparser.util.JdbcConstants;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
public class DruidSQLRecognizerFactoryTest {
@Test
public void testSqlRecognizerCreation() {
SQLRecognizerFactory recognizerFactory =
EnhancedServiceLoader.load(SQLRecognizerFactory.class,
SqlParserType.SQL_PARSER_TYPE_DRUID);
- Assertions.assertNotNull(recognizerFactory);
+ assertNotNull(recognizerFactory);
List<SQLRecognizer> recognizers = recognizerFactory.create("delete
from t1", JdbcConstants.MYSQL);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
recognizers = recognizerFactory.create("delete from t1",
JdbcConstants.MARIADB);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
recognizers = recognizerFactory.create("delete from t1",
JdbcConstants.POLARDBX);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
recognizers = recognizerFactory.create("delete from t1",
JdbcConstants.DM);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
recognizers = recognizerFactory.create("delete from t1",
JdbcConstants.KINGBASE);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
recognizers = recognizerFactory.create("delete from t1",
JdbcConstants.OSCAR);
- Assertions.assertNotNull(recognizers);
+ assertNotNull(recognizers);
Assertions.assertEquals(recognizers.size(), 1);
Assertions.assertEquals(SQLType.DELETE,
recognizers.get(0).getSQLType());
// test sql syntax
String sql = "update d.t set d.t.a = ?, d.t.b = ?, d.t.c = ?";
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.MYSQL));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.MARIADB));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.POLARDBX));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.ORACLE));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.POSTGRESQL));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.DM));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.KINGBASE));
- Assertions.assertNotNull(recognizerFactory.create(sql,
JdbcConstants.OSCAR));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.MYSQL));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.MARIADB));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.POLARDBX));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.ORACLE));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.POSTGRESQL));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.DM));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.KINGBASE));
+ assertNotNull(recognizerFactory.create(sql, JdbcConstants.OSCAR));
String sql5 = "insert into a values (1, 2)";
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.MYSQL));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.MARIADB));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.POLARDBX));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.ORACLE));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.POSTGRESQL));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.DM));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.KINGBASE));
- Assertions.assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.OSCAR));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.MYSQL));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.MARIADB));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.POLARDBX));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.ORACLE));
+ assertNotNull(recognizerFactory.create(sql5,
JdbcConstants.POSTGRESQL));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.DM));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.KINGBASE));
+ assertNotNull(recognizerFactory.create(sql5, JdbcConstants.OSCAR));
String sql6 = "insert into a (id, name) values (1, 2), (3, 4)";
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.MYSQL));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.MARIADB));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.POLARDBX));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.ORACLE));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.POSTGRESQL));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.DM));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.KINGBASE));
- Assertions.assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.OSCAR));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.MYSQL));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.MARIADB));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.POLARDBX));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.ORACLE));
+ assertNotNull(recognizerFactory.create(sql6,
JdbcConstants.POSTGRESQL));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.DM));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.KINGBASE));
+ assertNotNull(recognizerFactory.create(sql6, JdbcConstants.OSCAR));
String sql8 = "delete from t where id = ?";
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.MYSQL));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.MARIADB));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.POLARDBX));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.ORACLE));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.POSTGRESQL));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.DM));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.KINGBASE));
- Assertions.assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.OSCAR));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.MYSQL));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.MARIADB));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.POLARDBX));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.ORACLE));
+ assertNotNull(recognizerFactory.create(sql8,
JdbcConstants.POSTGRESQL));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.DM));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.KINGBASE));
+ assertNotNull(recognizerFactory.create(sql8, JdbcConstants.OSCAR));
String sql10 = "select * from t for update";
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.MYSQL));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.MARIADB));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.POLARDBX));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.ORACLE));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.POSTGRESQL));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.KINGBASE));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.DM));
- Assertions.assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.OSCAR));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.MYSQL));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.MARIADB));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.POLARDBX));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.ORACLE));
+ assertNotNull(recognizerFactory.create(sql10,
JdbcConstants.POSTGRESQL));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.KINGBASE));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.DM));
+ assertNotNull(recognizerFactory.create(sql10, JdbcConstants.OSCAR));
}
@Test
@@ -208,4 +220,44 @@ public class DruidSQLRecognizerFactoryTest {
Assertions.assertThrows(
NotSupportYetException.class, () ->
recognizerFactory.create(sql9, JdbcConstants.KINGBASE));
}
+
+ @Test
+ public void testInsertFirstNotSupported() {
+ SQLRecognizerFactory recognizerFactory =
+ EnhancedServiceLoader.load(SQLRecognizerFactory.class,
SqlParserType.SQL_PARSER_TYPE_DRUID);
+ // Test that INSERT FIRST syntax should be rejected at the Factory
level
+ String sql = "INSERT FIRST "
+ + "WHEN salary > 1000 THEN INTO high_earners (id, name,
salary) VALUES (1, 'John', 2000) "
+ + "WHEN salary <= 1000 THEN INTO low_earners (id, name,
salary) VALUES (1, 'John', 800) "
+ + "SELECT 1 FROM DUAL";
+
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () ->
recognizerFactory.create(sql, JdbcConstants.ORACLE));
+
+ assertTrue(exception.getMessage().contains("INSERT FIRST not supported
yet"));
+ }
+
+ @Test
+ void testGetMultiInsertRecognizerDelegation() {
+ // 1.sql
+ String sql = "INSERT ALL INTO a(id) VALUES(1) INTO a(id) VALUES(2)
SELECT 1 FROM dual";
+
+ SQLStatement stmt = SQLUtils.parseSingleStatement(sql, "oracle");
+ assertTrue(stmt instanceof OracleMultiInsertStatement);
+
+ // 2. mock recognizerHolder and recognizer
+ OracleOperateRecognizerHolder recognizerHolder =
mock(OracleOperateRecognizerHolder.class);
+ SQLRecognizer mockRecognizer = mock(SQLRecognizer.class);
+
+ // 3. stub getMultiInsertRecognizer
+ when(recognizerHolder.getMultiInsertRecognizer(sql,
stmt)).thenReturn(mockRecognizer);
+
+ SQLRecognizer recognizer =
+ ((OracleOperateRecognizerHolder)
recognizerHolder).getMultiInsertRecognizer(sql, stmt);
+
+ assertNotNull(recognizer);
+ assertSame(mockRecognizer, recognizer);
+
+ verify(recognizerHolder, times(1)).getMultiInsertRecognizer(sql, stmt);
+ }
}
diff --git
a/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizerTest.java
b/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizerTest.java
new file mode 100644
index 0000000000..6b6e1ee15b
--- /dev/null
+++
b/sqlparser/seata-sqlparser-druid/src/test/java/org/apache/seata/sqlparser/druid/oracle/OracleMultiInsertRecognizerTest.java
@@ -0,0 +1,404 @@
+/*
+ * 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.seata.sqlparser.druid.oracle;
+
+import com.alibaba.druid.sql.SQLUtils;
+import com.alibaba.druid.sql.ast.SQLStatement;
+import org.apache.seata.common.exception.NotSupportYetException;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.Null;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Oracle Multi Insert Recognizer Test
+ *
+ */
+public class OracleMultiInsertRecognizerTest {
+
+ private static final String DB_TYPE = "oracle";
+
+ @Test
+ public void testGetSqlType() {
+ String sql = "INSERT ALL INTO a (id) VALUES (1) INTO a (id) VALUES (2)
SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ Assertions.assertEquals(SQLType.INSERT, recognizer.getSQLType());
+ }
+
+ @Test
+ public void testGetTableName() {
+ String sql = "INSERT ALL INTO users (id) VALUES (1) INTO users (id)
VALUES (2) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ Assertions.assertEquals("users", recognizer.getTableName());
+ }
+
+ @Test
+ public void testGetTableAlias() {
+ // Test the case without alias
+ String sql = "INSERT ALL INTO users (id) VALUES (1) INTO users (id)
VALUES (2) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ Assertions.assertNull(recognizer.getTableAlias());
+ }
+
+ @Test
+ public void testGetInsertColumns() {
+ String sql =
+ "INSERT ALL INTO users (id, name, age) VALUES (1, 'Tom', 20)
INTO users (id, name, age) VALUES (2, 'Jerry', 25) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<String> columns = recognizer.getInsertColumns();
+
+ Assertions.assertNotNull(columns);
+ Assertions.assertEquals(3, columns.size());
+ Assertions.assertEquals("id", columns.get(0));
+ Assertions.assertEquals("name", columns.get(1));
+ Assertions.assertEquals("age", columns.get(2));
+ }
+
+ @Test
+ public void testInsertColumnsIsEmpty() {
+ // Test the case with columns
+ String sqlWithColumns =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sqlWithColumns,
DB_TYPE);
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sqlWithColumns, asts.get(0));
+ Assertions.assertFalse(recognizer.insertColumnsIsEmpty());
+ }
+
+ @Test
+ public void testGetInsertRows() {
+ String sql =
+ "INSERT ALL INTO users (id, name, age) VALUES (1, 'Tom', 20)
INTO users (id, name, age) VALUES (2, 'Jerry', 25) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<List<Object>> rows =
recognizer.getInsertRows(Collections.emptySet());
+
+ Assertions.assertNotNull(rows);
+ Assertions.assertEquals(2, rows.size());
+
+ // Verify the first row of data
+ List<Object> firstRow = rows.get(0);
+ Assertions.assertEquals(3, firstRow.size());
+ Assertions.assertEquals(1, firstRow.get(0));
+ Assertions.assertEquals("Tom", firstRow.get(1));
+ Assertions.assertEquals(20, firstRow.get(2));
+
+ // Verify the second row of data
+ List<Object> secondRow = rows.get(1);
+ Assertions.assertEquals(3, secondRow.size());
+ Assertions.assertEquals(2, secondRow.get(0));
+ Assertions.assertEquals("Jerry", secondRow.get(1));
+ Assertions.assertEquals(25, secondRow.get(2));
+ }
+
+ @Test
+ public void testGetInsertRowsWithNullValues() {
+ String sql =
+ "INSERT ALL INTO users (id, name, age) VALUES (1, 'Tom', NULL)
INTO users (id, name, age) VALUES (2, NULL, 25) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<List<Object>> rows =
recognizer.getInsertRows(Collections.emptySet());
+
+ Assertions.assertNotNull(rows);
+ Assertions.assertEquals(2, rows.size());
+
+ // Verify the first row of data (age is NULL)
+ List<Object> firstRow = rows.get(0);
+ Assertions.assertEquals(3, firstRow.size());
+ Assertions.assertEquals(1, firstRow.get(0));
+ Assertions.assertEquals("Tom", firstRow.get(1));
+ Assertions.assertEquals(Null.get(), firstRow.get(2));
+
+ // Verify the second row of data (name is NULL)
+ List<Object> secondRow = rows.get(1);
+ Assertions.assertEquals(3, secondRow.size());
+ Assertions.assertEquals(2, secondRow.get(0));
+ Assertions.assertEquals(Null.get(), secondRow.get(1));
+ Assertions.assertEquals(25, secondRow.get(2));
+ }
+
+ @Test
+ public void testGetInsertRowsWithParameters() {
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (?, ?) INTO users
(id, name) VALUES (?, ?) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<List<Object>> rows =
recognizer.getInsertRows(Collections.emptySet());
+
+ Assertions.assertNotNull(rows);
+ Assertions.assertEquals(2, rows.size());
+
+ // Validation parameter placeholders
+ for (List<Object> row : rows) {
+ Assertions.assertEquals(2, row.size());
+ for (Object value : row) {
+ Assertions.assertTrue(value instanceof String);
+ Assertions.assertEquals("?", value);
+ }
+ }
+ }
+
+ @Test
+ public void testGetInsertParamsValue() {
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<String> paramsValue = recognizer.getInsertParamsValue();
+
+ Assertions.assertNull(paramsValue);
+ }
+
+ @Test
+ public void testGetDuplicateKeyUpdate() {
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ List<String> duplicateKeyUpdate = recognizer.getDuplicateKeyUpdate();
+
+ Assertions.assertNull(duplicateKeyUpdate);
+ }
+
+ @Test
+ public void testIsSqlSyntaxSupports() {
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+ Assertions.assertTrue(recognizer.isSqlSyntaxSupports());
+ }
+
+ @Test
+ public void testMultipleTablesInsert() {
+ // Now this should throw an exception since different tables are
involved
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO
orders (id, user_id) VALUES (101, 1) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // should throw NotSupportYetException
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () -> new
OracleMultiInsertRecognizer(sql, asts.get(0)));
+
+ Assertions.assertTrue(
+ exception.getMessage().contains("Oracle Multi Insert with
different tables is not supported yet"));
+ Assertions.assertTrue(exception.getMessage().contains("users"));
+ Assertions.assertTrue(exception.getMessage().contains("orders"));
+ }
+
+ @Test
+ public void testMultipleTablesWithDifferentColumns() {
+ // Testing the same table but different column definitions
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name, age) VALUES (2, 'Jerry', 25) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // should throw NotSupportYetException
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () -> new
OracleMultiInsertRecognizer(sql, asts.get(0)));
+
+ Assertions.assertTrue(exception
+ .getMessage()
+ .contains("Oracle Multi Insert with different column
definitions is not supported yet"));
+ Assertions.assertTrue(exception.getMessage().contains("users"));
+ }
+
+ @Test
+ public void testSameTableMultipleInserts() {
+ // Test the normal situation of the same table and column
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // success
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals("users", recognizer.getTableName());
+ Assertions.assertEquals(2, recognizer.getInsertColumns().size());
+ Assertions.assertEquals("id", recognizer.getInsertColumns().get(0));
+ Assertions.assertEquals("name", recognizer.getInsertColumns().get(1));
+
+ List<List<Object>> rows =
recognizer.getInsertRows(Collections.emptySet());
+ Assertions.assertEquals(2, rows.size());
+ }
+
+ @Test
+ public void testEmptyColumnsConsistency() {
+ // Tests the consistency of empty column definitions
+ String sql = "INSERT ALL INTO users VALUES (1, 'Tom') INTO users
VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // This should succeed since neither entry specifies a column.
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals("users", recognizer.getTableName());
+ Assertions.assertTrue(recognizer.insertColumnsIsEmpty());
+ }
+
+ @Test
+ public void testMixedColumnDefinitions() {
+ // Test one case with column definition and one case without column
definition
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO users
VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // An exception should be thrown because the column definitions are
inconsistent
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () -> new
OracleMultiInsertRecognizer(sql, asts.get(0)));
+
+ Assertions.assertTrue(exception
+ .getMessage()
+ .contains("Oracle Multi Insert with different column
definitions is not supported yet"));
+ }
+
+ @Test
+ public void testCaseInsensitiveTableNames() {
+ // Test table name case sensitivity
+ String sql =
+ "INSERT ALL INTO users (id, name) VALUES (1, 'Tom') INTO USERS
(id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // This should succeed because equalsIgnoreCase is used
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals("users", recognizer.getTableName());
+ Assertions.assertEquals(2, recognizer.getInsertColumns().size());
+ }
+
+ @Test
+ public void testTableWithSchema() {
+ // Test table name with Schema
+ String sql =
+ "INSERT ALL INTO schema1.users (id, name) VALUES (1, 'Tom')
INTO schema1.users (id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // success
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals("schema1.users", recognizer.getTableName());
+ Assertions.assertEquals(2, recognizer.getInsertColumns().size());
+ }
+
+ @Test
+ public void testDifferentSchemas() {
+ // Testing tables with the same name in different schemas
+ String sql =
+ "INSERT ALL INTO schema1.users (id, name) VALUES (1, 'Tom')
INTO schema2.users (id, name) VALUES (2, 'Jerry') SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // An exception should be thrown because they are different tables
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () -> new
OracleMultiInsertRecognizer(sql, asts.get(0)));
+
+ Assertions.assertTrue(
+ exception.getMessage().contains("Oracle Multi Insert with
different tables is not supported yet"));
+
Assertions.assertTrue(exception.getMessage().contains("schema1.users"));
+
Assertions.assertTrue(exception.getMessage().contains("schema2.users"));
+ }
+
+ @Test
+ public void testComplexInsertAllStatement() {
+ // Testing a more complex INSERT ALL statement
+ String sql = "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, weekly_start_date+1, sales_mon) "
+ + "INTO sales (prod_id, cust_id, time_id, amount) VALUES
(product_id, customer_id, weekly_start_date+2, sales_tue) "
+ + "SELECT product_id, customer_id, weekly_start_date,
sales_sun, sales_mon, sales_tue FROM sales_input_table";
+
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals("sales", recognizer.getTableName());
+ Assertions.assertEquals(SQLType.INSERT, recognizer.getSQLType());
+
+ List<String> columns = recognizer.getInsertColumns();
+ Assertions.assertNotNull(columns);
+ Assertions.assertEquals(4, columns.size());
+ Assertions.assertEquals("prod_id", columns.get(0));
+ Assertions.assertEquals("cust_id", columns.get(1));
+ Assertions.assertEquals("time_id", columns.get(2));
+ Assertions.assertEquals("amount", columns.get(3));
+ }
+
+ @Test
+ public void testEmptyEntriesHandling() {
+ // This test is mainly to ensure that no exceptions occur in boundary
conditions
+
+ String sql = "INSERT ALL INTO users (id, name) VALUES (1, 'Tom')
SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ Assertions.assertEquals(SQLType.INSERT, recognizer.getSQLType());
+ Assertions.assertEquals("users", recognizer.getTableName());
+ Assertions.assertTrue(recognizer.isSqlSyntaxSupports());
+ }
+
+ @Test
+ public void testGetInsertRowsWithPrimaryKeyIndex() {
+ String sql =
+ "INSERT ALL INTO users (id, name, age) VALUES (1, 'Tom', 20)
INTO users (id, name, age) VALUES (2, 'Jerry', 25) SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ OracleMultiInsertRecognizer recognizer = new
OracleMultiInsertRecognizer(sql, asts.get(0));
+
+ // Test behavior when primary key index is specified
+ List<List<Object>> rows = recognizer.getInsertRows(Arrays.asList(0));
+
+ Assertions.assertNotNull(rows);
+ Assertions.assertEquals(2, rows.size());
+
+ // Validate the value of the primary key field
+ for (List<Object> row : rows) {
+ Assertions.assertEquals(3, row.size());
+ Assertions.assertTrue(row.get(0) instanceof Integer);
+ }
+ }
+
+ @Test
+ public void testConditionalInsertNotSupported() {
+ // Testing conditional insert statements
+ String sql = "INSERT ALL " + "INTO users (id, name) VALUES (1, 'Tom') "
+ + "WHEN salary > 1000 THEN INTO employees (id, name) VALUES
(2, 'Jerry') "
+ + "SELECT 1 FROM DUAL";
+ List<SQLStatement> asts = SQLUtils.parseStatements(sql, DB_TYPE);
+
+ // Should throw NotSupportYetException
+ NotSupportYetException exception = Assertions.assertThrows(
+ NotSupportYetException.class, () -> new
OracleMultiInsertRecognizer(sql, asts.get(0)));
+
+ Assertions.assertTrue(exception.getMessage().contains("conditional
clauses"));
+ Assertions.assertTrue(exception.getMessage().contains("WHEN...THEN"));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]