diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index e93e075b700..a4a567fa78d 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -6,6 +6,7 @@ Add changes here for all PR submitted to the 2.x branch. - [[#6876](https://github.com/apache/incubator-seata/pull/6876)]support kingbase - [[#6881](https://github.com/apache/incubator-seata/pull/6881)]support grpc +- [[#6864](https://github.com/apache/incubator-seata/pull/6864)]support shentong database ### bugfix: - [[#6899](https://github.com/apache/incubator-seata/pull/6899)] fix file.conf read failed after package @@ -44,6 +45,7 @@ Thanks to these contributors for their code commits. Please report an unintended - [arrrnold17](https://github.com/arrrnold17) - [xjlgod](https://github.com/xjlgod) - [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) +- [dsomehan](https://github.com/dsomehan) diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 771a64ad0d7..efb5f32ae50 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -3,8 +3,10 @@ ### feature: -[[#6876](https://github.com/apache/incubator-seata/pull/6876)]支持人大金仓数据库(kingbase) -[[#6881](https://github.com/apache/incubator-seata/pull/6881)]全链路支持grpc +- [[#6876](https://github.com/apache/incubator-seata/pull/6876)]支持人大金仓数据库(kingbase) +- [[#6881](https://github.com/apache/incubator-seata/pull/6881)]全链路支持grpc +- [[#6864](https://github.com/apache/incubator-seata/pull/6864)]支持神通数据库(oscar) + ### bugfix: - [[#6899](https://github.com/apache/incubator-seata/pull/6899)] 修复file.conf打包后的读取 @@ -46,6 +48,7 @@ - [arrrnold17](https://github.com/arrrnold17) - [xjlgod](https://github.com/xjlgod) - [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) +- [dsomehan](https://github.com/dsomehan) 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 diff --git a/common/src/main/java/org/apache/seata/common/util/PageUtil.java b/common/src/main/java/org/apache/seata/common/util/PageUtil.java index ea658a64af1..b49b10eddc4 100644 --- a/common/src/main/java/org/apache/seata/common/util/PageUtil.java +++ b/common/src/main/java/org/apache/seata/common/util/PageUtil.java @@ -112,6 +112,7 @@ public static String pageSql(String sourceSql, String dbType, int pageNum, int p case "kingbase": case "oceanbase": case "dm": + case "oscar": return LIMIT_TEMPLATE.replace(SOURCE_SQL_PLACE_HOLD, sourceSql) .replace(LIMIT_PLACE_HOLD, String.valueOf(pageSize)) .replace(OFFSET_PLACE_HOLD, String.valueOf((pageNum - 1) * pageSize)); @@ -142,6 +143,7 @@ public static String countSql(String sourceSql, String dbType) { case "oceanbase": case "oracle": case "dm": + case "oscar": return sourceSql.replaceAll("(?i)(?<=select)(.*)(?=from)", " count(1) "); case "postgresql": case "kingbase": @@ -185,6 +187,7 @@ public static String getTimeStartSql(String dbType, String timeColumnName) { case "postgresql": case "sqlserver": case "dm": + case "oscar": return " and FLOOR(" + timeColumnName + "/1000) >= ? "; default: throw new IllegalArgumentException("The DB type :" + dbType + " is not supported yet"); @@ -204,6 +207,7 @@ public static String getTimeEndSql(String dbType, String timeColumnName) { case "postgresql": case "sqlserver": case "dm": + case "oscar": return " and FLOOR(" + timeColumnName + "/1000) <= ? "; default: throw new IllegalArgumentException("The DB type :" + dbType + " is not supported yet"); diff --git a/common/src/test/java/org/apache/seata/common/util/PageUtilTest.java b/common/src/test/java/org/apache/seata/common/util/PageUtilTest.java index 5fcdcfd5116..640fa037015 100644 --- a/common/src/test/java/org/apache/seata/common/util/PageUtilTest.java +++ b/common/src/test/java/org/apache/seata/common/util/PageUtilTest.java @@ -44,6 +44,7 @@ public void testPageSql() { assertEquals(PageUtil.pageSql(sourceSql, "postgresql", 1, 5), mysqlTargetSql); assertEquals(PageUtil.pageSql(sourceSql, "oceanbase", 1, 5), mysqlTargetSql); assertEquals(PageUtil.pageSql(sourceSql, "dm", 1, 5), mysqlTargetSql); + assertEquals(PageUtil.pageSql(sourceSql, "oscar", 1, 5), mysqlTargetSql); assertEquals(PageUtil.pageSql(sourceSql, "oracle", 1, 5), oracleTargetSql); assertEquals(PageUtil.pageSql(sourceSql, "sqlserver", 1, 5), sqlserverTargetSql); @@ -61,6 +62,7 @@ void testCountSql() { assertEquals(PageUtil.countSql(sourceSql, "postgresql"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "oceanbase"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "dm"), targetSql); + assertEquals(PageUtil.countSql(sourceSql, "oscar"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "oracle"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "sqlserver"), targetSql); diff --git a/core/src/main/java/org/apache/seata/core/constants/DBType.java b/core/src/main/java/org/apache/seata/core/constants/DBType.java index af0e82d312a..3521e3e4545 100644 --- a/core/src/main/java/org/apache/seata/core/constants/DBType.java +++ b/core/src/main/java/org/apache/seata/core/constants/DBType.java @@ -192,7 +192,12 @@ public enum DBType { /** * PolarDB db type. */ - POLARDB; + POLARDB, + + /** + * oscar db type. + */ + OSCAR; /** * Valueof db type. diff --git a/core/src/main/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java b/core/src/main/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java index 2072d4631c5..8f589780caa 100644 --- a/core/src/main/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java +++ b/core/src/main/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java @@ -34,7 +34,7 @@ public class DistributedLockSqlFactory { /** * get the lock store sql * - * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm/sqlserver ... + * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm/sqlserver/oscar ... * @return lock store sql */ public static DistributedLockSql getDistributedLogStoreSql(String dbType) { diff --git a/core/src/main/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactory.java b/core/src/main/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactory.java index dcf5781edc5..6fb7a61e335 100644 --- a/core/src/main/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactory.java +++ b/core/src/main/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactory.java @@ -34,7 +34,7 @@ public class LockStoreSqlFactory { /** * get the lock store sql * - * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm + * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm/oscar * @return lock store sql */ public static LockStoreSql getLogStoreSql(String dbType) { diff --git a/core/src/main/java/org/apache/seata/core/store/db/sql/lock/OscarLockStoreSql.java b/core/src/main/java/org/apache/seata/core/store/db/sql/lock/OscarLockStoreSql.java new file mode 100644 index 00000000000..bd72749ea1f --- /dev/null +++ b/core/src/main/java/org/apache/seata/core/store/db/sql/lock/OscarLockStoreSql.java @@ -0,0 +1,28 @@ +/* + * 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.core.store.db.sql.lock; + + +import org.apache.seata.common.loader.LoadLevel; + +/** + * the database lock store shentong sql + * + */ +@LoadLevel(name = "oscar") +public class OscarLockStoreSql extends OracleLockStoreSql { +} diff --git a/core/src/main/java/org/apache/seata/core/store/db/sql/log/OscarLogStoreSqls.java b/core/src/main/java/org/apache/seata/core/store/db/sql/log/OscarLogStoreSqls.java new file mode 100644 index 00000000000..f310e03ff2e --- /dev/null +++ b/core/src/main/java/org/apache/seata/core/store/db/sql/log/OscarLogStoreSqls.java @@ -0,0 +1,27 @@ +/* + * 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.core.store.db.sql.log; + + +import org.apache.seata.common.loader.LoadLevel; + +/** + * Database log store oscar sql + */ +@LoadLevel(name = "oscar") +public class OscarLogStoreSqls extends OracleLogStoreSqls { +} diff --git a/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.lock.LockStoreSql b/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.lock.LockStoreSql index 375298bc638..838ee9d70f3 100644 --- a/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.lock.LockStoreSql +++ b/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.lock.LockStoreSql @@ -23,4 +23,5 @@ org.apache.seata.core.store.db.sql.lock.SqlServerLockStoreSql org.apache.seata.core.store.db.sql.lock.MariadbLockStoreSql org.apache.seata.core.store.db.sql.lock.PolarDBXLockStoreSql org.apache.seata.core.store.db.sql.lock.DmLockStoreSql -org.apache.seata.core.store.db.sql.lock.KingbaseLockStoreSql \ No newline at end of file +org.apache.seata.core.store.db.sql.lock.OscarLockStoreSql +org.apache.seata.core.store.db.sql.lock.KingbaseLockStoreSql diff --git a/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.log.LogStoreSqls b/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.log.LogStoreSqls index 5eb45d22e0c..90f94d6a3e5 100644 --- a/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.log.LogStoreSqls +++ b/core/src/main/resources/META-INF/services/org.apache.seata.core.store.db.sql.log.LogStoreSqls @@ -23,4 +23,5 @@ org.apache.seata.core.store.db.sql.log.SqlServerLogStoreSqls org.apache.seata.core.store.db.sql.log.MariadbLogStoreSqls org.apache.seata.core.store.db.sql.log.PolarDBXLogStoreSqls org.apache.seata.core.store.db.sql.log.DmLogStoreSqls -org.apache.seata.core.store.db.sql.log.KingbaseLogStoreSqls \ No newline at end of file +org.apache.seata.core.store.db.sql.log.OscarLogStoreSqls +org.apache.seata.core.store.db.sql.log.KingbaseLogStoreSqls diff --git a/core/src/test/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java b/core/src/test/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java index 4a598bbe52a..a38f75644cb 100644 --- a/core/src/test/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java +++ b/core/src/test/java/org/apache/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java @@ -40,6 +40,8 @@ public class LockStoreSqlFactoryTest { private static LockStoreSql DM_LOCK_STORE = LockStoreSqlFactory.getLogStoreSql("dm"); + private static LockStoreSql OSCAR_LOCK_STORE = LockStoreSqlFactory.getLogStoreSql("oscar"); + private static String GLOBAL_TABLE = "global_table"; private static String BRANCH_TABLE = "branch_table"; @@ -379,4 +381,51 @@ public void dmLockTest() { sql = DM_LOCK_STORE.getCheckLockableSql(BRANCH_TABLE, 3); Assertions.assertEquals(EXPECT_CHECK_BRANCH_LOCKABLE_SQL,sql); } + + + @Test + public void oscarLockTest() { + String sql; + // Get insert lock sql string. + sql = OSCAR_LOCK_STORE.getInsertLockSQL(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = OSCAR_LOCK_STORE.getInsertLockSQL(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get delete lock sql string. + sql = OSCAR_LOCK_STORE.getDeleteLockSql(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = OSCAR_LOCK_STORE.getDeleteLockSql(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get batch delete lock sql string. + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSql(GLOBAL_TABLE, 3); + Assertions.assertEquals(EXPECT_BATCH_GLOBAL_DELETE_LOCK_SQL,sql); + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSql(BRANCH_TABLE, 3); + Assertions.assertEquals(EXPECT_BATCH_BRANCH_DELETE_LOCK_SQL,sql); + + // Get batch delete lock sql string. + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSqlByBranchId(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSqlByBranchId(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get batch delete lock sql string. + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSqlByXid(GLOBAL_TABLE); + Assertions.assertEquals(EXPECT_BATCH_GLOBAL_DELETE_LOCK_BY_BRANCHS_SQL,sql); + sql = OSCAR_LOCK_STORE.getBatchDeleteLockSqlByXid(BRANCH_TABLE); + Assertions.assertEquals(EXPECT_BATCH_BRANCH_DELETE_LOCK_BY_BRANCHS_SQL,sql); + + // Get query lock sql string. + sql = OSCAR_LOCK_STORE.getQueryLockSql(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = OSCAR_LOCK_STORE.getQueryLockSql(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get check lock sql string. + sql = OSCAR_LOCK_STORE.getCheckLockableSql(GLOBAL_TABLE, 3); + Assertions.assertEquals(EXPECT_CHECK_GLOBAL_LOCKABLE_SQL,sql); + sql = OSCAR_LOCK_STORE.getCheckLockableSql(BRANCH_TABLE, 3); + Assertions.assertEquals(EXPECT_CHECK_BRANCH_LOCKABLE_SQL,sql); + } } diff --git a/core/src/test/java/org/apache/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java b/core/src/test/java/org/apache/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java index f3b9421862e..45c1aa3d101 100644 --- a/core/src/test/java/org/apache/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java +++ b/core/src/test/java/org/apache/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java @@ -34,6 +34,8 @@ public class LogStoreSqlsFactoryTest { private static LogStoreSqls dmLog = LogStoreSqlsFactory.getLogStoreSqls("dm"); + private static LogStoreSqls oscarLog = LogStoreSqlsFactory.getLogStoreSqls("oscar"); + private static String globalTable = "global_table"; private static String branchTable = "branch_table"; @@ -246,4 +248,38 @@ public void dmLogTest() { sql = dmLog.getQueryBranchMax(branchTable); Assertions.assertNotNull(sql); } + + @Test + public void oscarLogTest() { + String sql = oscarLog.getInsertGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getUpdateGlobalTransactionStatusSQL(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getDeleteGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryGlobalTransactionSQLByTransactionId(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryGlobalTransactionSQLByStatus(globalTable, "1"); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryGlobalTransactionForRecoverySQL(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getInsertBranchTransactionSQL(branchTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getUpdateBranchTransactionStatusSQL(branchTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getDeleteBranchTransactionByBranchIdSQL(branchTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getDeleteBranchTransactionByXId(branchTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryBranchTransaction(branchTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryBranchTransaction(branchTable, "1"); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryGlobalMax(globalTable); + Assertions.assertNotNull(sql); + sql = oscarLog.getQueryBranchMax(branchTable); + Assertions.assertNotNull(sql); + } } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataSourceProxy.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataSourceProxy.java index dfab706294e..8868a0c964b 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataSourceProxy.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/DataSourceProxy.java @@ -243,6 +243,8 @@ private void initResourceId() { initSqlServerResourceId(); } else if (JdbcConstants.DM.equals(dbType)) { initDMResourceId(); + } else if (JdbcConstants.OSCAR.equals(dbType)) { + initOscarResourceId(); } else { initDefaultResourceId(); } @@ -321,6 +323,18 @@ private void initDMResourceId() { } } + /** + * init the oscar resource id + * jdbc:oscar://192.168.x.xx:2003/OSRDB + */ + private void initOscarResourceId() { + if (jdbcUrl.contains("?")) { + resourceId = jdbcUrl.substring(0, jdbcUrl.indexOf('?')) + "/" + userName; + } else { + resourceId = jdbcUrl + "/" + userName; + } + } + /** * prevent pg sql url like * jdbc:postgresql://127.0.0.1:5432/seata?currentSchema=public diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/oscar/OscarInsertExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/oscar/OscarInsertExecutor.java new file mode 100644 index 00000000000..1a68e7f30d2 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/oscar/OscarInsertExecutor.java @@ -0,0 +1,139 @@ +/* + * 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.rm.datasource.exec.oscar; + +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.loader.Scope; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.rm.datasource.StatementProxy; +import org.apache.seata.rm.datasource.exec.BaseInsertExecutor; +import org.apache.seata.rm.datasource.exec.StatementCallback; +import org.apache.seata.sqlparser.SQLInsertRecognizer; +import org.apache.seata.sqlparser.SQLRecognizer; +import org.apache.seata.sqlparser.struct.Null; +import org.apache.seata.sqlparser.struct.Sequenceable; +import org.apache.seata.sqlparser.struct.SqlMethodExpr; +import org.apache.seata.sqlparser.struct.SqlSequenceExpr; +import org.apache.seata.sqlparser.util.ColumnUtils; +import org.apache.seata.sqlparser.util.JdbcConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The type Oscar insert executor. + * + */ +@LoadLevel(name = JdbcConstants.OSCAR, scope = Scope.PROTOTYPE) +public class OscarInsertExecutor extends BaseInsertExecutor implements Sequenceable { + + private static final Logger LOGGER = LoggerFactory.getLogger(OscarInsertExecutor.class); + + /** + * Instantiates a new Abstract dml base executor. + * + * @param statementProxy the statement proxy + * @param statementCallback the statement callback + * @param sqlRecognizer the sql recognizer + */ + public OscarInsertExecutor(StatementProxy statementProxy, StatementCallback statementCallback, + SQLRecognizer sqlRecognizer) { + super(statementProxy, statementCallback, sqlRecognizer); + } + + /** + * 1. If the insert columns are not empty and do not contain any pk columns, + * it means that there is no pk value in the insert rows, then all the pk values should come from auto-increment. + *

+ * 2. The pk value exists in insert rows. The possible situations are: + *

+ * + * @return {@link Map}<{@link String}, {@link List}<{@link Object}>> + * @throws SQLException the sql exception + */ + @Override + public Map> getPkValues() throws SQLException { + List pkColumnNameList = getTableMeta().getPrimaryKeyOnlyName(); + Map> pkValuesMap = new HashMap<>(pkColumnNameList.size()); + + // first obtain the existing pk value from the insert rows (if exists) + if (!containsColumns() || containsAnyPk()) { + pkValuesMap.putAll(getPkValuesByColumn()); + } + // other from auto-increment + for (String columnName : pkColumnNameList) { + if (!pkValuesMap.containsKey(columnName)) { + pkValuesMap.put(columnName, getGeneratedKeys(columnName)); + } + } + return pkValuesMap; + } + + /** + * Whether the insert columns contain any pk columns + * + * @return true: contain at least one pk column. false: do not contain any pk columns + */ + public boolean containsAnyPk() { + SQLInsertRecognizer recognizer = (SQLInsertRecognizer)sqlRecognizer; + List insertColumns = recognizer.getInsertColumns(); + if (CollectionUtils.isEmpty(insertColumns)) { + return false; + } + List pkColumnNameList = getTableMeta().getPrimaryKeyOnlyName(); + if (CollectionUtils.isEmpty(pkColumnNameList)) { + return false; + } + List newColumns = ColumnUtils.delEscape(insertColumns, getDbType()); + return pkColumnNameList.stream().anyMatch(pkColumn -> newColumns.contains(pkColumn) + || CollectionUtils.toUpperList(newColumns).contains(pkColumn.toUpperCase())); + } + + @Override + public Map> getPkValuesByColumn() throws SQLException { + Map> pkValuesMap = parsePkValuesFromStatement(); + Set keySet = pkValuesMap.keySet(); + for (String pkKey : keySet) { + List pkValues = pkValuesMap.get(pkKey); + for (int i = 0; i < pkValues.size(); i++) { + if (!pkKey.isEmpty() && pkValues.get(i) instanceof SqlSequenceExpr) { + pkValues.set(i, getPkValuesBySequence((SqlSequenceExpr) pkValues.get(i), pkKey).get(0)); + } else if (!pkKey.isEmpty() && pkValues.get(i) instanceof SqlMethodExpr) { + pkValues.set(i, getGeneratedKeys(pkKey).get(0)); + } else if (!pkKey.isEmpty() && pkValues.get(i) instanceof Null) { + pkValues.set(i, getGeneratedKeys(pkKey).get(0)); + } + } + pkValuesMap.put(pkKey, pkValues); + } + return pkValuesMap; + } + + @Override + public String getSequenceSql(SqlSequenceExpr expr) { + return "SELECT " + expr.getSequence() + ".currval FROM DUAL"; + } + +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/handler/oscar/OscarEscapeHandler.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/handler/oscar/OscarEscapeHandler.java new file mode 100644 index 00000000000..22c67143ef1 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/handler/oscar/OscarEscapeHandler.java @@ -0,0 +1,2661 @@ +/* + * 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.rm.datasource.sql.handler.oscar; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.sqlparser.EscapeHandler; +import org.apache.seata.sqlparser.struct.ColumnMeta; +import org.apache.seata.sqlparser.struct.TableMeta; +import org.apache.seata.sqlparser.util.JdbcConstants; + +/** + * The type OSCAR keyword checker. + * + */ +@LoadLevel(name = JdbcConstants.OSCAR) +public class OscarEscapeHandler implements EscapeHandler { + + protected Set keywordSet = Arrays.stream(OscarKeyword.values()).map(OscarKeyword::name).collect(Collectors.toSet()); + + /** + * OSCAR keyword + */ + private enum OscarKeyword { + /** + * ABORT is oscar keyword + */ + ABORT("ABORT"), + /** + * ABSOLUTE is oscar keyword + */ + ABSOLUTE("ABSOLUTE"), + /** + * ACCESS is oscar keyword + */ + ACCESS("ACCESS"), + /** + * ACCESSED is oscar keyword + */ + ACCESSED("ACCESSED"), + /** + * ACTION is oscar keyword + */ + ACTION("ACTION"), + /** + * ADD is oscar keyword + */ + ADD("ADD"), + /** + * ADMIN is oscar keyword + */ + ADMIN("ADMIN"), + /** + * ADVISOR is oscar keyword + */ + ADVISOR("ADVISOR"), + /** + * AFTER is oscar keyword + */ + AFTER("AFTER"), + /** + * AGGREGATE is oscar keyword + */ + AGGREGATE("AGGREGATE"), + /** + * ALTER is oscar keyword + */ + ALTER("ALTER"), + /** + * ALWAYS is oscar keyword + */ + ALWAYS("ALWAYS"), + /** + * ANALYSE is oscar keyword + */ + ANALYSE("ANALYSE"), + /** + * ANALYZE is oscar keyword + */ + ANALYZE("ANALYZE"), + /** + * ANALYZER is oscar keyword + */ + ANALYZER("ANALYZER"), + /** + * APP is oscar keyword + */ + APP("APP"), + /** + * ARCHIVE is oscar keyword + */ + ARCHIVE("ARCHIVE"), + /** + * ARCHIVELOG is oscar keyword + */ + ARCHIVELOG("ARCHIVELOG"), + /** + * ARE is oscar keyword + */ + ARE("ARE"), + /** + * ARRAY is oscar keyword + */ + ARRAY("ARRAY"), + /** + * ASC is oscar keyword + */ + ASC("ASC"), + /** + * ASSERTION is oscar keyword + */ + ASSERTION("ASSERTION"), + /** + * ASSIGNMENT is oscar keyword + */ + ASSIGNMENT("ASSIGNMENT"), + /** + * AST is oscar keyword + */ + AST("AST"), + /** + * ASYNC is oscar keyword + */ + ASYNC("ASYNC"), + /** + * ATTRIBUTES is oscar keyword + */ + ATTRIBUTES("ATTRIBUTES"), + /** + * AUDIT is oscar keyword + */ + AUDIT("AUDIT"), + /** + * AUDITFILE is oscar keyword + */ + AUDITFILE("AUDITFILE"), + /** + * AUTHID is oscar keyword + */ + AUTHID("AUTHID"), + /** + * AUTHORIZATION is oscar keyword + */ + AUTHORIZATION("AUTHORIZATION"), + /** + * AUTO is oscar keyword + */ + AUTO("AUTO"), + /** + * AUTO_INCREMENT is oscar keyword + */ + AUTO_INCREMENT("AUTO_INCREMENT"), + /** + * AUTOEXTEND is oscar keyword + */ + AUTOEXTEND("AUTOEXTEND"), + /** + * BACKUP is oscar keyword + */ + BACKUP("BACKUP"), + /** + * BACKWARD is oscar keyword + */ + BACKWARD("BACKWARD"), + /** + * BASICANALYZER is oscar keyword + */ + BASICANALYZER("BASICANALYZER"), + /** + * BATCHSIZE is oscar keyword + */ + BATCHSIZE("BATCHSIZE"), + /** + * BEFORE is oscar keyword + */ + BEFORE("BEFORE"), + /** + * BEGIN is oscar keyword + */ + BEGIN("BEGIN"), + /** + * BETWEEN is oscar keyword + */ + BETWEEN("BETWEEN"), + /** + * BIGINT is oscar keyword + */ + BIGINT("BIGINT"), + /** + * BINARY is oscar keyword + */ + BINARY("BINARY"), + /** + * BINLOG is oscar keyword + */ + BINLOG("BINLOG"), + /** + * BIT is oscar keyword + */ + BIT("BIT"), + /** + * BITMAP is oscar keyword + */ + BITMAP("BITMAP"), + /** + * BLOCK is oscar keyword + */ + BLOCK("BLOCK"), + /** + * BODY is oscar keyword + */ + BODY("BODY"), + /** + * BOOLEAN is oscar keyword + */ + BOOLEAN("BOOLEAN"), + /** + * BOTH is oscar keyword + */ + BOTH("BOTH"), + /** + * BPCHAR is oscar keyword + */ + BPCHAR("BPCHAR"), + /** + * BUFFER is oscar keyword + */ + BUFFER("BUFFER"), + /** + * BUFFER_CACHE is oscar keyword + */ + BUFFER_CACHE("BUFFER_CACHE"), + /** + * BUFFER_POOL is oscar keyword + */ + BUFFER_POOL("BUFFER_POOL"), + /** + * BUILD is oscar keyword + */ + BUILD("BUILD"), + /** + * BULK is oscar keyword + */ + BULK("BULK"), + /** + * BY is oscar keyword + */ + BY("BY"), + /** + * BYTE is oscar keyword + */ + BYTE("BYTE"), + /** + * CACHE is oscar keyword + */ + CACHE("CACHE"), + /** + * CALL is oscar keyword + */ + CALL("CALL"), + /** + * CALLED is oscar keyword + */ + CALLED("CALLED"), + /** + * CANCEL is oscar keyword + */ + CANCEL("CANCEL"), + /** + * CASCADED is oscar keyword + */ + CASCADED("CASCADED"), + /** + * CDC is oscar keyword + */ + CDC("CDC"), + /** + * CHAIN is oscar keyword + */ + CHAIN("CHAIN"), + /** + * CHANGE is oscar keyword + */ + CHANGE("CHANGE"), + /** + * CHARACTERISTICS is oscar keyword + */ + CHARACTERISTICS("CHARACTERISTICS"), + /** + * CHARACTERSET is oscar keyword + */ + CHARACTERSET("CHARACTERSET"), + /** + * CHEAT is oscar keyword + */ + CHEAT("CHEAT"), + /** + * CHECKPOINT is oscar keyword + */ + CHECKPOINT("CHECKPOINT"), + /** + * CHINESEANALYZER is oscar keyword + */ + CHINESEANALYZER("CHINESEANALYZER"), + /** + * CHUNK is oscar keyword + */ + CHUNK("CHUNK"), + /** + * CJKANALYZER is oscar keyword + */ + CJKANALYZER("CJKANALYZER"), + /** + * CLASS is oscar keyword + */ + CLASS("CLASS"), + /** + * CLEAN is oscar keyword + */ + CLEAN("CLEAN"), + /** + * CLOSE is oscar keyword + */ + CLOSE("CLOSE"), + /** + * CLUSTER is oscar keyword + */ + CLUSTER("CLUSTER"), + /** + * COLUMNS is oscar keyword + */ + COLUMNS("COLUMNS"), + /** + * COMMENT is oscar keyword + */ + COMMENT("COMMENT"), + /** + * COMMENTS is oscar keyword + */ + COMMENTS("COMMENTS"), + /** + * COMMIT is oscar keyword + */ + COMMIT("COMMIT"), + /** + * COMMITTED is oscar keyword + */ + COMMITTED("COMMITTED"), + /** + * COMPILE is oscar keyword + */ + COMPILE("COMPILE"), + /** + * COMPLETE is oscar keyword + */ + COMPLETE("COMPLETE"), + /** + * COMPRESS is oscar keyword + */ + COMPRESS("COMPRESS"), + /** + * CONCAT is oscar keyword + */ + CONCAT("CONCAT"), + /** + * CONFIGURATION is oscar keyword + */ + CONFIGURATION("CONFIGURATION"), + /** + * CONNECT is oscar keyword + */ + CONNECT("CONNECT"), + /** + * CONNECT_BY_ISCYCLE is oscar keyword + */ + CONNECT_BY_ISCYCLE("CONNECT_BY_ISCYCLE"), + /** + * CONNECT_BY_ISLEAF is oscar keyword + */ + CONNECT_BY_ISLEAF("CONNECT_BY_ISLEAF"), + /** + * CONNECT_BY_ROOT is oscar keyword + */ + CONNECT_BY_ROOT("CONNECT_BY_ROOT"), + /** + * CONSTRAINTS is oscar keyword + */ + CONSTRAINTS("CONSTRAINTS"), + /** + * CONTENT is oscar keyword + */ + CONTENT("CONTENT"), + /** + * CONTEXT is oscar keyword + */ + CONTEXT("CONTEXT"), + /** + * CONTINUE is oscar keyword + */ + CONTINUE("CONTINUE"), + /** + * CONTROLFILE is oscar keyword + */ + CONTROLFILE("CONTROLFILE"), + /** + * CONVERSION is oscar keyword + */ + CONVERSION("CONVERSION"), + /** + * COPY is oscar keyword + */ + COPY("COPY"), + /** + * CROSS is oscar keyword + */ + CROSS("CROSS"), + /** + * CSV is oscar keyword + */ + CSV("CSV"), + /** + * CUBE is oscar keyword + */ + CUBE("CUBE"), + /** + * CURRENT is oscar keyword + */ + CURRENT("CURRENT"), + /** + * CURRENT_USER is oscar keyword + */ + CURRENT_USER("CURRENT_USER"), + /** + * CURSOR is oscar keyword + */ + CURSOR("CURSOR"), + /** + * CYCLE is oscar keyword + */ + CYCLE("CYCLE"), + /** + * DATA is oscar keyword + */ + DATA("DATA"), + /** + * DATABASE is oscar keyword + */ + DATABASE("DATABASE"), + /** + * DATABASELINK is oscar keyword + */ + DATABASELINK("DATABASELINK"), + /** + * DATAFILE is oscar keyword + */ + DATAFILE("DATAFILE"), + /** + * DATAFILETYPE is oscar keyword + */ + DATAFILETYPE("DATAFILETYPE"), + /** + * DATE is oscar keyword + */ + DATE("DATE"), + /** + * DATE_ADD is oscar keyword + */ + DATE_ADD("DATE_ADD"), + /** + * DATE_SUB is oscar keyword + */ + DATE_SUB("DATE_SUB"), + /** + * DATEFORMAT is oscar keyword + */ + DATEFORMAT("DATEFORMAT"), + /** + * DATETIME is oscar keyword + */ + DATETIME("DATETIME"), + /** + * DAY is oscar keyword + */ + DAY("DAY"), + /** + * DBA is oscar keyword + */ + DBA("DBA"), + /** + * DEALLOCATE is oscar keyword + */ + DEALLOCATE("DEALLOCATE"), + /** + * DEBUG is oscar keyword + */ + DEBUG("DEBUG"), + /** + * DEC is oscar keyword + */ + DEC("DEC"), + /** + * DECLARE is oscar keyword + */ + DECLARE("DECLARE"), + /** + * DECODE is oscar keyword + */ + DECODE("DECODE"), + /** + * DECRYPT is oscar keyword + */ + DECRYPT("DECRYPT"), + /** + * DEFERRABLE is oscar keyword + */ + DEFERRABLE("DEFERRABLE"), + /** + * DEFERRED is oscar keyword + */ + DEFERRED("DEFERRED"), + /** + * DEFINER is oscar keyword + */ + DEFINER("DEFINER"), + /** + * DELETE is oscar keyword + */ + DELETE("DELETE"), + /** + * DELIMITED is oscar keyword + */ + DELIMITED("DELIMITED"), + /** + * DELIMITER is oscar keyword + */ + DELIMITER("DELIMITER"), + /** + * DELIMITERS is oscar keyword + */ + DELIMITERS("DELIMITERS"), + /** + * DEMAND is oscar keyword + */ + DEMAND("DEMAND"), + /** + * DENSE_RANK is oscar keyword + */ + DENSE_RANK("DENSE_RANK"), + /** + * DESC is oscar keyword + */ + DESC("DESC"), + /** + * DESCRIPTION is oscar keyword + */ + DESCRIPTION("DESCRIPTION"), + /** + * DETERMINISTIC is oscar keyword + */ + DETERMINISTIC("DETERMINISTIC"), + /** + * DIRECTORY is oscar keyword + */ + DIRECTORY("DIRECTORY"), + /** + * DISABLE is oscar keyword + */ + DISABLE("DISABLE"), + /** + * DOCUMENT is oscar keyword + */ + DOCUMENT("DOCUMENT"), + /** + * DOMAIN is oscar keyword + */ + DOMAIN("DOMAIN"), + /** + * DOUBLE is oscar keyword + */ + DOUBLE("DOUBLE"), + /** + * DUMP is oscar keyword + */ + DUMP("DUMP"), + /** + * EACH is oscar keyword + */ + EACH("EACH"), + /** + * ELOG is oscar keyword + */ + ELOG("ELOG"), + /** + * ELT is oscar keyword + */ + ELT("ELT"), + /** + * EMPTY is oscar keyword + */ + EMPTY("EMPTY"), + /** + * ENABLE is oscar keyword + */ + ENABLE("ENABLE"), + /** + * ENCODING is oscar keyword + */ + ENCODING("ENCODING"), + /** + * ENCRYPT is oscar keyword + */ + ENCRYPT("ENCRYPT"), + /** + * ENCRYPTED is oscar keyword + */ + ENCRYPTED("ENCRYPTED"), + /** + * ENCRYPTION is oscar keyword + */ + ENCRYPTION("ENCRYPTION"), + /** + * END is oscar keyword + */ + END("END"), + /** + * ERROR is oscar keyword + */ + ERROR("ERROR"), + /** + * ERRORS is oscar keyword + */ + ERRORS("ERRORS"), + /** + * ESCALATION is oscar keyword + */ + ESCALATION("ESCALATION"), + /** + * ESCAPE is oscar keyword + */ + ESCAPE("ESCAPE"), + /** + * EVENTS is oscar keyword + */ + EVENTS("EVENTS"), + /** + * EXCHANGE is oscar keyword + */ + EXCHANGE("EXCHANGE"), + /** + * EXCLUDING is oscar keyword + */ + EXCLUDING("EXCLUDING"), + /** + * EXCLUSIVE is oscar keyword + */ + EXCLUSIVE("EXCLUSIVE"), + /** + * EXEC is oscar keyword + */ + EXEC("EXEC"), + /** + * EXECUTE is oscar keyword + */ + EXECUTE("EXECUTE"), + /** + * EXPLAIN is oscar keyword + */ + EXPLAIN("EXPLAIN"), + /** + * EXPORT is oscar keyword + */ + EXPORT("EXPORT"), + /** + * EXTEND is oscar keyword + */ + EXTEND("EXTEND"), + /** + * EXTERNALLY is oscar keyword + */ + EXTERNALLY("EXTERNALLY"), + /** + * FAILOVER is oscar keyword + */ + FAILOVER("FAILOVER"), + /** + * FALSE is oscar keyword + */ + FALSE("FALSE"), + /** + * FAR is oscar keyword + */ + FAR("FAR"), + /** + * FAST is oscar keyword + */ + FAST("FAST"), + /** + * FAULT is oscar keyword + */ + FAULT("FAULT"), + /** + * FETCH is oscar keyword + */ + FETCH("FETCH"), + /** + * FIELD is oscar keyword + */ + FIELD("FIELD"), + /** + * FIELDS is oscar keyword + */ + FIELDS("FIELDS"), + /** + * FIELDTERMINATOR is oscar keyword + */ + FIELDTERMINATOR("FIELDTERMINATOR"), + /** + * FILE is oscar keyword + */ + FILE("FILE"), + /** + * FILESIZE is oscar keyword + */ + FILESIZE("FILESIZE"), + /** + * FILL is oscar keyword + */ + FILL("FILL"), + /** + * FILTER is oscar keyword + */ + FILTER("FILTER"), + /** + * FIRE_TRIGGERS is oscar keyword + */ + FIRE_TRIGGERS("FIRE_TRIGGERS"), + /** + * FIRST is oscar keyword + */ + FIRST("FIRST"), + /** + * FIRSTROW is oscar keyword + */ + FIRSTROW("FIRSTROW"), + /** + * FLUSH is oscar keyword + */ + FLUSH("FLUSH"), + /** + * FOLLOWING is oscar keyword + */ + FOLLOWING("FOLLOWING"), + /** + * FORCE is oscar keyword + */ + FORCE("FORCE"), + /** + * FOREIGNKEY_CONSTRAINTS is oscar keyword + */ + FOREIGNKEY_CONSTRAINTS("FOREIGNKEY_CONSTRAINTS"), + /** + * FOREVER is oscar keyword + */ + FOREVER("FOREVER"), + /** + * FORMATFILE is oscar keyword + */ + FORMATFILE("FORMATFILE"), + /** + * FORWARD is oscar keyword + */ + FORWARD("FORWARD"), + /** + * FREELISTS is oscar keyword + */ + FREELISTS("FREELISTS"), + /** + * FREEPOOLS is oscar keyword + */ + FREEPOOLS("FREEPOOLS"), + /** + * FULL is oscar keyword + */ + FULL("FULL"), + /** + * FULLTEXT is oscar keyword + */ + FULLTEXT("FULLTEXT"), + /** + * FUNCTION is oscar keyword + */ + FUNCTION("FUNCTION"), + /** + * G is oscar keyword + */ + G("G"), + /** + * GB is oscar keyword + */ + GB("GB"), + /** + * GBK is oscar keyword + */ + GBK("GBK"), + /** + * GCOV is oscar keyword + */ + GCOV("GCOV"), + /** + * GENERATED is oscar keyword + */ + GENERATED("GENERATED"), + /** + * GEOGRAPHY is oscar keyword + */ + GEOGRAPHY("GEOGRAPHY"), + /** + * GEOMETRY is oscar keyword + */ + GEOMETRY("GEOMETRY"), + /** + * GET is oscar keyword + */ + GET("GET"), + /** + * GETCLOBVAL is oscar keyword + */ + GETCLOBVAL("GETCLOBVAL"), + /** + * GETSTRINGVAL is oscar keyword + */ + GETSTRINGVAL("GETSTRINGVAL"), + /** + * GLOBAL is oscar keyword + */ + GLOBAL("GLOBAL"), + /** + * GLOBAL_NAME is oscar keyword + */ + GLOBAL_NAME("GLOBAL_NAME"), + /** + * GLOBALLY is oscar keyword + */ + GLOBALLY("GLOBALLY"), + /** + * GREATEST is oscar keyword + */ + GREATEST("GREATEST"), + /** + * GROUPING is oscar keyword + */ + GROUPING("GROUPING"), + /** + * GROUPING_ID is oscar keyword + */ + GROUPING_ID("GROUPING_ID"), + /** + * GUARANTEE is oscar keyword + */ + GUARANTEE("GUARANTEE"), + /** + * HANDLER is oscar keyword + */ + HANDLER("HANDLER"), + /** + * HASH is oscar keyword + */ + HASH("HASH"), + /** + * HEADER is oscar keyword + */ + HEADER("HEADER"), + /** + * HEAP is oscar keyword + */ + HEAP("HEAP"), + /** + * HOLD is oscar keyword + */ + HOLD("HOLD"), + /** + * HOUR is oscar keyword + */ + HOUR("HOUR"), + /** + * IDENTIFIED is oscar keyword + */ + IDENTIFIED("IDENTIFIED"), + /** + * IDENTITY is oscar keyword + */ + IDENTITY("IDENTITY"), + /** + * IF is oscar keyword + */ + IF("IF"), + /** + * IGNORE is oscar keyword + */ + IGNORE("IGNORE"), + /** + * ILIKE is oscar keyword + */ + ILIKE("ILIKE"), + /** + * IMMEDIATE is oscar keyword + */ + IMMEDIATE("IMMEDIATE"), + /** + * IMMUTABLE is oscar keyword + */ + IMMUTABLE("IMMUTABLE"), + /** + * IMPLICIT is oscar keyword + */ + IMPLICIT("IMPLICIT"), + /** + * IMPORT is oscar keyword + */ + IMPORT("IMPORT"), + /** + * IMPORT_POLCOL is oscar keyword + */ + IMPORT_POLCOL("IMPORT_POLCOL"), + /** + * INCREMENT is oscar keyword + */ + INCREMENT("INCREMENT"), + /** + * INDEX is oscar keyword + */ + INDEX("INDEX"), + /** + * INDEXES is oscar keyword + */ + INDEXES("INDEXES"), + /** + * INHERITS is oscar keyword + */ + INHERITS("INHERITS"), + /** + * INIT is oscar keyword + */ + INIT("INIT"), + /** + * INITIAL is oscar keyword + */ + INITIAL("INITIAL"), + /** + * INITIALIZED is oscar keyword + */ + INITIALIZED("INITIALIZED"), + /** + * INITIALLY is oscar keyword + */ + INITIALLY("INITIALLY"), + /** + * INITRANS is oscar keyword + */ + INITRANS("INITRANS"), + /** + * INNER is oscar keyword + */ + INNER("INNER"), + /** + * INOUT is oscar keyword + */ + INOUT("INOUT"), + /** + * INPUT is oscar keyword + */ + INPUT("INPUT"), + /** + * INSENSITIVE is oscar keyword + */ + INSENSITIVE("INSENSITIVE"), + /** + * INSERT is oscar keyword + */ + INSERT("INSERT"), + /** + * INSTEAD is oscar keyword + */ + INSTEAD("INSTEAD"), + /** + * INTERVAL is oscar keyword + */ + INTERVAL("INTERVAL"), + /** + * INVALIDATE is oscar keyword + */ + INVALIDATE("INVALIDATE"), + /** + * INVISIBLE is oscar keyword + */ + INVISIBLE("INVISIBLE"), + /** + * INVOKER is oscar keyword + */ + INVOKER("INVOKER"), + /** + * IP is oscar keyword + */ + IP("IP"), + /** + * IS is oscar keyword + */ + IS("IS"), + /** + * ISNULL is oscar keyword + */ + ISNULL("ISNULL"), + /** + * ISOLATION is oscar keyword + */ + ISOLATION("ISOLATION"), + /** + * JOIN is oscar keyword + */ + JOIN("JOIN"), + /** + * JSON is oscar keyword + */ + JSON("JSON"), + /** + * JSON_TABLE is oscar keyword + */ + JSON_TABLE("JSON_TABLE"), + /** + * JSON_VALUE is oscar keyword + */ + JSON_VALUE("JSON_VALUE"), + /** + * K is oscar keyword + */ + K("K"), + /** + * KB is oscar keyword + */ + KB("KB"), + /** + * KEEP is oscar keyword + */ + KEEP("KEEP"), + /** + * KEEPIDENTITY is oscar keyword + */ + KEEPIDENTITY("KEEPIDENTITY"), + /** + * KEEPNULLS is oscar keyword + */ + KEEPNULLS("KEEPNULLS"), + /** + * KEY is oscar keyword + */ + KEY("KEY"), + /** + * KEYSTORE is oscar keyword + */ + KEYSTORE("KEYSTORE"), + /** + * KILL is oscar keyword + */ + KILL("KILL"), + /** + * KILOBYTES_PER_BATCH is oscar keyword + */ + KILOBYTES_PER_BATCH("KILOBYTES_PER_BATCH"), + /** + * KSTORE is oscar keyword + */ + KSTORE("KSTORE"), + /** + * LABEL is oscar keyword + */ + LABEL("LABEL"), + /** + * LANCOMPILER is oscar keyword + */ + LANCOMPILER("LANCOMPILER"), + /** + * LANGUAGE is oscar keyword + */ + LANGUAGE("LANGUAGE"), + /** + * LAST is oscar keyword + */ + LAST("LAST"), + /** + * LASTROW is oscar keyword + */ + LASTROW("LASTROW"), + /** + * LC_COLLATE is oscar keyword + */ + LC_COLLATE("LC_COLLATE"), + /** + * LC_CTYPE is oscar keyword + */ + LC_CTYPE("LC_CTYPE"), + /** + * LDRTRIM is oscar keyword + */ + LDRTRIM("LDRTRIM"), + /** + * LEADING is oscar keyword + */ + LEADING("LEADING"), + /** + * LEAK is oscar keyword + */ + LEAK("LEAK"), + /** + * LEAST is oscar keyword + */ + LEAST("LEAST"), + /** + * LEFT is oscar keyword + */ + LEFT("LEFT"), + /** + * LESS is oscar keyword + */ + LESS("LESS"), + /** + * LIFETIME is oscar keyword + */ + LIFETIME("LIFETIME"), + /** + * LIKE is oscar keyword + */ + LIKE("LIKE"), + /** + * LIMIT is oscar keyword + */ + LIMIT("LIMIT"), + /** + * LIST is oscar keyword + */ + LIST("LIST"), + /** + * LISTEN is oscar keyword + */ + LISTEN("LISTEN"), + /** + * LOAD is oscar keyword + */ + LOAD("LOAD"), + /** + * LOB is oscar keyword + */ + LOB("LOB"), + /** + * LOCAL is oscar keyword + */ + LOCAL("LOCAL"), + /** + * LOCATION is oscar keyword + */ + LOCATION("LOCATION"), + /** + * LOCK is oscar keyword + */ + LOCK("LOCK"), + /** + * LOCKED is oscar keyword + */ + LOCKED("LOCKED"), + /** + * LOG is oscar keyword + */ + LOG("LOG"), + /** + * LOGFILE is oscar keyword + */ + LOGFILE("LOGFILE"), + /** + * LOGGING is oscar keyword + */ + LOGGING("LOGGING"), + /** + * LOGICAL is oscar keyword + */ + LOGICAL("LOGICAL"), + /** + * LONG is oscar keyword + */ + LONG("LONG"), + /** + * LOOP is oscar keyword + */ + LOOP("LOOP"), + /** + * LRTRIM is oscar keyword + */ + LRTRIM("LRTRIM"), + /** + * LSN is oscar keyword + */ + LSN("LSN"), + /** + * LTRIM is oscar keyword + */ + LTRIM("LTRIM"), + /** + * M is oscar keyword + */ + M("M"), + /** + * MAINTAIN_INDEX is oscar keyword + */ + MAINTAIN_INDEX("MAINTAIN_INDEX"), + /** + * MAINTENANCE is oscar keyword + */ + MAINTENANCE("MAINTENANCE"), + /** + * MANUAL is oscar keyword + */ + MANUAL("MANUAL"), + /** + * MASKING is oscar keyword + */ + MASKING("MASKING"), + /** + * MATCH is oscar keyword + */ + MATCH("MATCH"), + /** + * MATCHED is oscar keyword + */ + MATCHED("MATCHED"), + /** + * MATERIALIZED is oscar keyword + */ + MATERIALIZED("MATERIALIZED"), + /** + * MAX is oscar keyword + */ + MAX("MAX"), + /** + * MAXERRORS is oscar keyword + */ + MAXERRORS("MAXERRORS"), + /** + * MAXEXTENDS is oscar keyword + */ + MAXEXTENDS("MAXEXTENDS"), + /** + * MAXEXTENTS is oscar keyword + */ + MAXEXTENTS("MAXEXTENTS"), + /** + * MAXSIZE is oscar keyword + */ + MAXSIZE("MAXSIZE"), + /** + * MAXTRANS is oscar keyword + */ + MAXTRANS("MAXTRANS"), + /** + * MAXVALUE is oscar keyword + */ + MAXVALUE("MAXVALUE"), + /** + * MB is oscar keyword + */ + MB("MB"), + /** + * MEMBER is oscar keyword + */ + MEMBER("MEMBER"), + /** + * MEMORY is oscar keyword + */ + MEMORY("MEMORY"), + /** + * MERGE is oscar keyword + */ + MERGE("MERGE"), + /** + * MIN is oscar keyword + */ + MIN("MIN"), + /** + * MINEXTENDS is oscar keyword + */ + MINEXTENDS("MINEXTENDS"), + /** + * MINEXTENTS is oscar keyword + */ + MINEXTENTS("MINEXTENTS"), + /** + * MINSIZE is oscar keyword + */ + MINSIZE("MINSIZE"), + /** + * MINUS is oscar keyword + */ + MINUS("MINUS"), + /** + * MINUTE is oscar keyword + */ + MINUTE("MINUTE"), + /** + * MINVALUE is oscar keyword + */ + MINVALUE("MINVALUE"), + /** + * MISSING is oscar keyword + */ + MISSING("MISSING"), + /** + * MOD is oscar keyword + */ + MOD("MOD"), + /** + * MODE is oscar keyword + */ + MODE("MODE"), + /** + * MODIFY is oscar keyword + */ + MODIFY("MODIFY"), + /** + * MONEY is oscar keyword + */ + MONEY("MONEY"), + /** + * MONTH is oscar keyword + */ + MONTH("MONTH"), + /** + * MOUNT is oscar keyword + */ + MOUNT("MOUNT"), + /** + * MOVE is oscar keyword + */ + MOVE("MOVE"), + /** + * MOVEMENT is oscar keyword + */ + MOVEMENT("MOVEMENT"), + /** + * MULTICOLUMN is oscar keyword + */ + MULTICOLUMN("MULTICOLUMN"), + /** + * MULTIPLE is oscar keyword + */ + MULTIPLE("MULTIPLE"), + /** + * NAME is oscar keyword + */ + NAME("NAME"), + /** + * NAMES is oscar keyword + */ + NAMES("NAMES"), + /** + * NATURAL is oscar keyword + */ + NATURAL("NATURAL"), + /** + * NCHAR is oscar keyword + */ + NCHAR("NCHAR"), + /** + * NEVER is oscar keyword + */ + NEVER("NEVER"), + /** + * NEWLINE is oscar keyword + */ + NEWLINE("NEWLINE"), + /** + * NEXT is oscar keyword + */ + NEXT("NEXT"), + /** + * NEXTVAL is oscar keyword + */ + NEXTVAL("NEXTVAL"), + /** + * NO is oscar keyword + */ + NO("NO"), + /** + * NOARCHIVELOG is oscar keyword + */ + NOARCHIVELOG("NOARCHIVELOG"), + /** + * NOAUDIT is oscar keyword + */ + NOAUDIT("NOAUDIT"), + /** + * NOCACHE is oscar keyword + */ + NOCACHE("NOCACHE"), + /** + * NOCOMPRESS is oscar keyword + */ + NOCOMPRESS("NOCOMPRESS"), + /** + * NOCOPY is oscar keyword + */ + NOCOPY("NOCOPY"), + /** + * NOCYCLE is oscar keyword + */ + NOCYCLE("NOCYCLE"), + /** + * NODE is oscar keyword + */ + NODE("NODE"), + /** + * NOGUARANTEE is oscar keyword + */ + NOGUARANTEE("NOGUARANTEE"), + /** + * NOLOGGING is oscar keyword + */ + NOLOGGING("NOLOGGING"), + /** + * NOMAXVALUE is oscar keyword + */ + NOMAXVALUE("NOMAXVALUE"), + /** + * NOMINVALUE is oscar keyword + */ + NOMINVALUE("NOMINVALUE"), + /** + * NOMOUNT is oscar keyword + */ + NOMOUNT("NOMOUNT"), + /** + * NORMAL is oscar keyword + */ + NORMAL("NORMAL"), + /** + * NOTHING is oscar keyword + */ + NOTHING("NOTHING"), + /** + * NOTIFY is oscar keyword + */ + NOTIFY("NOTIFY"), + /** + * NOTNULL is oscar keyword + */ + NOTNULL("NOTNULL"), + /** + * NOTRIM is oscar keyword + */ + NOTRIM("NOTRIM"), + /** + * NOVALIDATE is oscar keyword + */ + NOVALIDATE("NOVALIDATE"), + /** + * NOWAIT is oscar keyword + */ + NOWAIT("NOWAIT"), + /** + * NVARCHAR2 is oscar keyword + */ + NVARCHAR2("NVARCHAR2"), + /** + * NVL is oscar keyword + */ + NVL("NVL"), + /** + * NVL2 is oscar keyword + */ + NVL2("NVL2"), + /** + * OBJECT is oscar keyword + */ + OBJECT("OBJECT"), + /** + * OF is oscar keyword + */ + OF("OF"), + /** + * OFF is oscar keyword + */ + OFF("OFF"), + /** + * OFFLINE is oscar keyword + */ + OFFLINE("OFFLINE"), + /** + * OFFSET is oscar keyword + */ + OFFSET("OFFSET"), + /** + * OIDS is oscar keyword + */ + OIDS("OIDS"), + /** + * ONLINE is oscar keyword + */ + ONLINE("ONLINE"), + /** + * OPEN is oscar keyword + */ + OPEN("OPEN"), + /** + * OPERATOR is oscar keyword + */ + OPERATOR("OPERATOR"), + /** + * OPTIMIZE is oscar keyword + */ + OPTIMIZE("OPTIMIZE"), + /** + * OPTIMIZE_KSCACHE is oscar keyword + */ + OPTIMIZE_KSCACHE("OPTIMIZE_KSCACHE"), + /** + * OPTION is oscar keyword + */ + OPTION("OPTION"), + /** + * ORACLE is oscar keyword + */ + ORACLE("ORACLE"), + /** + * ORDINALITY is oscar keyword + */ + ORDINALITY("ORDINALITY"), + /** + * ORGANIZATION is oscar keyword + */ + ORGANIZATION("ORGANIZATION"), + /** + * OSCAR is oscar keyword + */ + OSCAR("OSCAR"), + /** + * OUT is oscar keyword + */ + OUT("OUT"), + /** + * OUTER is oscar keyword + */ + OUTER("OUTER"), + /** + * OUTLINE is oscar keyword + */ + OUTLINE("OUTLINE"), + /** + * OVER is oscar keyword + */ + OVER("OVER"), + /** + * OVERFLOW is oscar keyword + */ + OVERFLOW("OVERFLOW"), + /** + * OVERLAPS is oscar keyword + */ + OVERLAPS("OVERLAPS"), + /** + * OVERLAY is oscar keyword + */ + OVERLAY("OVERLAY"), + /** + * OWNER is oscar keyword + */ + OWNER("OWNER"), + /** + * PACKAGE is oscar keyword + */ + PACKAGE("PACKAGE"), + /** + * PAGESIZE is oscar keyword + */ + PAGESIZE("PAGESIZE"), + /** + * PARALLEL is oscar keyword + */ + PARALLEL("PARALLEL"), + /** + * PARAMETER is oscar keyword + */ + PARAMETER("PARAMETER"), + /** + * PARAMINFO is oscar keyword + */ + PARAMINFO("PARAMINFO"), + /** + * PARTIAL is oscar keyword + */ + PARTIAL("PARTIAL"), + /** + * PARTITION is oscar keyword + */ + PARTITION("PARTITION"), + /** + * PARTITIONS is oscar keyword + */ + PARTITIONS("PARTITIONS"), + /** + * PASSING is oscar keyword + */ + PASSING("PASSING"), + /** + * PASSWORD is oscar keyword + */ + PASSWORD("PASSWORD"), + /** + * PATH is oscar keyword + */ + PATH("PATH"), + /** + * PCTFREE is oscar keyword + */ + PCTFREE("PCTFREE"), + /** + * PCTINCREASE is oscar keyword + */ + PCTINCREASE("PCTINCREASE"), + /** + * PCTTHRESHOLD is oscar keyword + */ + PCTTHRESHOLD("PCTTHRESHOLD"), + /** + * PCTUSED is oscar keyword + */ + PCTUSED("PCTUSED"), + /** + * PCTVERSION is oscar keyword + */ + PCTVERSION("PCTVERSION"), + /** + * PENDANT is oscar keyword + */ + PENDANT("PENDANT"), + /** + * PETENTION is oscar keyword + */ + PETENTION("PETENTION"), + /** + * PFILE is oscar keyword + */ + PFILE("PFILE"), + /** + * PIPELINED is oscar keyword + */ + PIPELINED("PIPELINED"), + /** + * PIVOT is oscar keyword + */ + PIVOT("PIVOT"), + /** + * PLACING is oscar keyword + */ + PLACING("PLACING"), + /** + * PLS_INTEGER is oscar keyword + */ + PLS_INTEGER("PLS_INTEGER"), + /** + * POLICY is oscar keyword + */ + POLICY("POLICY"), + /** + * PORT is oscar keyword + */ + PORT("PORT"), + /** + * POSITION is oscar keyword + */ + POSITION("POSITION"), + /** + * PRECEDING is oscar keyword + */ + PRECEDING("PRECEDING"), + /** + * PRECISION is oscar keyword + */ + PRECISION("PRECISION"), + /** + * PREPARE is oscar keyword + */ + PREPARE("PREPARE"), + /** + * PRESERVE is oscar keyword + */ + PRESERVE("PRESERVE"), + /** + * PREVAL is oscar keyword + */ + PREVAL("PREVAL"), + /** + * PRIMARY is oscar keyword + */ + PRIMARY("PRIMARY"), + /** + * PRIOR is oscar keyword + */ + PRIOR("PRIOR"), + /** + * PRIORITY is oscar keyword + */ + PRIORITY("PRIORITY"), + /** + * PRIVILEGES is oscar keyword + */ + PRIVILEGES("PRIVILEGES"), + /** + * PROCEDURAL is oscar keyword + */ + PROCEDURAL("PROCEDURAL"), + /** + * PROCEDURE is oscar keyword + */ + PROCEDURE("PROCEDURE"), + /** + * PUBLIC is oscar keyword + */ + PUBLIC("PUBLIC"), + /** + * PURGE is oscar keyword + */ + PURGE("PURGE"), + /** + * QU is oscar keyword + */ + QU("QU"), + /** + * QUERY is oscar keyword + */ + QUERY("QUERY"), + /** + * QUICK is oscar keyword + */ + QUICK("QUICK"), + /** + * QUOTE is oscar keyword + */ + QUOTE("QUOTE"), + /** + * RAC is oscar keyword + */ + RAC("RAC"), + /** + * RANGE is oscar keyword + */ + RANGE("RANGE"), + /** + * RATIO_TO_REPORT is oscar keyword + */ + RATIO_TO_REPORT("RATIO_TO_REPORT"), + /** + * RAW is oscar keyword + */ + RAW("RAW"), + /** + * READ is oscar keyword + */ + READ("READ"), + /** + * READABLE is oscar keyword + */ + READABLE("READABLE"), + /** + * READS is oscar keyword + */ + READS("READS"), + /** + * READSIZE is oscar keyword + */ + READSIZE("READSIZE"), + /** + * REBUILD is oscar keyword + */ + REBUILD("REBUILD"), + /** + * RECHECK is oscar keyword + */ + RECHECK("RECHECK"), + /** + * RECORDS is oscar keyword + */ + RECORDS("RECORDS"), + /** + * RECOVERY is oscar keyword + */ + RECOVERY("RECOVERY"), + /** + * RECREATE is oscar keyword + */ + RECREATE("RECREATE"), + /** + * RECURSIVE is oscar keyword + */ + RECURSIVE("RECURSIVE"), + /** + * RECYCLE is oscar keyword + */ + RECYCLE("RECYCLE"), + /** + * REFRESH is oscar keyword + */ + REFRESH("REFRESH"), + /** + * REGEXP is oscar keyword + */ + REGEXP("REGEXP"), + /** + * REGION is oscar keyword + */ + REGION("REGION"), + /** + * REJECT is oscar keyword + */ + REJECT("REJECT"), + /** + * RELATIVE is oscar keyword + */ + RELATIVE("RELATIVE"), + /** + * REMOVE is oscar keyword + */ + REMOVE("REMOVE"), + /** + * RENAME is oscar keyword + */ + RENAME("RENAME"), + /** + * REPEATABLE is oscar keyword + */ + REPEATABLE("REPEATABLE"), + /** + * REPLACE is oscar keyword + */ + REPLACE("REPLACE"), + /** + * RESET is oscar keyword + */ + RESET("RESET"), + /** + * RESIZE is oscar keyword + */ + RESIZE("RESIZE"), + /** + * RESOURCE is oscar keyword + */ + RESOURCE("RESOURCE"), + /** + * RESTART is oscar keyword + */ + RESTART("RESTART"), + /** + * RESTORE is oscar keyword + */ + RESTORE("RESTORE"), + /** + * RESTRICT is oscar keyword + */ + RESTRICT("RESTRICT"), + /** + * RESULT is oscar keyword + */ + RESULT("RESULT"), + /** + * RESUME is oscar keyword + */ + RESUME("RESUME"), + /** + * RETENTION is oscar keyword + */ + RETENTION("RETENTION"), + /** + * RETURN is oscar keyword + */ + RETURN("RETURN"), + /** + * RETURN_GENERATED_KEYS is oscar keyword + */ + RETURN_GENERATED_KEYS("RETURN_GENERATED_KEYS"), + /** + * RETURNING is oscar keyword + */ + RETURNING("RETURNING"), + /** + * RETURNS is oscar keyword + */ + RETURNS("RETURNS"), + /** + * REUSE is oscar keyword + */ + REUSE("REUSE"), + /** + * REVERSE is oscar keyword + */ + REVERSE("REVERSE"), + /** + * REVOKE is oscar keyword + */ + REVOKE("REVOKE"), + /** + * REWRITE is oscar keyword + */ + REWRITE("REWRITE"), + /** + * RIGHT is oscar keyword + */ + RIGHT("RIGHT"), + /** + * ROLE is oscar keyword + */ + ROLE("ROLE"), + /** + * ROLLBACK is oscar keyword + */ + ROLLBACK("ROLLBACK"), + /** + * ROLLUP is oscar keyword + */ + ROLLUP("ROLLUP"), + /** + * ROW is oscar keyword + */ + ROW("ROW"), + /** + * ROWDESCRIPTION is oscar keyword + */ + ROWDESCRIPTION("ROWDESCRIPTION"), + /** + * ROWID is oscar keyword + */ + ROWID("ROWID"), + /** + * ROWS is oscar keyword + */ + ROWS("ROWS"), + /** + * ROWS_PER_BATCH is oscar keyword + */ + ROWS_PER_BATCH("ROWS_PER_BATCH"), + /** + * ROWTERMINATOR is oscar keyword + */ + ROWTERMINATOR("ROWTERMINATOR"), + /** + * ROWTYPE is oscar keyword + */ + ROWTYPE("ROWTYPE"), + /** + * RTRIM is oscar keyword + */ + RTRIM("RTRIM"), + /** + * RULE is oscar keyword + */ + RULE("RULE"), + /** + * SAMPLE is oscar keyword + */ + SAMPLE("SAMPLE"), + /** + * SAVEPOINT is oscar keyword + */ + SAVEPOINT("SAVEPOINT"), + /** + * SCAN is oscar keyword + */ + SCAN("SCAN"), + /** + * SCHEMA is oscar keyword + */ + SCHEMA("SCHEMA"), + /** + * SCN is oscar keyword + */ + SCN("SCN"), + /** + * SCROLL is oscar keyword + */ + SCROLL("SCROLL"), + /** + * SECOND is oscar keyword + */ + SECOND("SECOND"), + /** + * SECURITY is oscar keyword + */ + SECURITY("SECURITY"), + /** + * SEGMENT is oscar keyword + */ + SEGMENT("SEGMENT"), + /** + * SEPARATOR is oscar keyword + */ + SEPARATOR("SEPARATOR"), + /** + * SEQUENCE is oscar keyword + */ + SEQUENCE("SEQUENCE"), + /** + * SERIALIZABLE is oscar keyword + */ + SERIALIZABLE("SERIALIZABLE"), + /** + * SESSION is oscar keyword + */ + SESSION("SESSION"), + /** + * SETS is oscar keyword + */ + SETS("SETS"), + /** + * SHARE is oscar keyword + */ + SHARE("SHARE"), + /** + * SHOW is oscar keyword + */ + SHOW("SHOW"), + /** + * SHRINK is oscar keyword + */ + SHRINK("SHRINK"), + /** + * SHRINKLOG is oscar keyword + */ + SHRINKLOG("SHRINKLOG"), + /** + * SHUTDOWN is oscar keyword + */ + SHUTDOWN("SHUTDOWN"), + /** + * SIBLINGS is oscar keyword + */ + SIBLINGS("SIBLINGS"), + /** + * SIGNED is oscar keyword + */ + SIGNED("SIGNED"), + /** + * SILENTLY is oscar keyword + */ + SILENTLY("SILENTLY"), + /** + * SIMILAR is oscar keyword + */ + SIMILAR("SIMILAR"), + /** + * SIMPLE is oscar keyword + */ + SIMPLE("SIMPLE"), + /** + * SINGLE is oscar keyword + */ + SINGLE("SINGLE"), + /** + * SINGLEROW is oscar keyword + */ + SINGLEROW("SINGLEROW"), + /** + * SIZE is oscar keyword + */ + SIZE("SIZE"), + /** + * SKIP is oscar keyword + */ + SKIP("SKIP"), + /** + * SMALLINT is oscar keyword + */ + SMALLINT("SMALLINT"), + /** + * SPACE is oscar keyword + */ + SPACE("SPACE"), + /** + * SPLIT is oscar keyword + */ + SPLIT("SPLIT"), + /** + * STABLE is oscar keyword + */ + STABLE("STABLE"), + /** + * STANDALONE is oscar keyword + */ + STANDALONE("STANDALONE"), + /** + * STANDARDANALYZER is oscar keyword + */ + STANDARDANALYZER("STANDARDANALYZER"), + /** + * START is oscar keyword + */ + START("START"), + /** + * STARTFILE is oscar keyword + */ + STARTFILE("STARTFILE"), + /** + * STARTPOS is oscar keyword + */ + STARTPOS("STARTPOS"), + /** + * STARTTIME is oscar keyword + */ + STARTTIME("STARTTIME"), + /** + * STARTUP is oscar keyword + */ + STARTUP("STARTUP"), + /** + * STATEMENT is oscar keyword + */ + STATEMENT("STATEMENT"), + /** + * STATIC is oscar keyword + */ + STATIC("STATIC"), + /** + * STATISTICS is oscar keyword + */ + STATISTICS("STATISTICS"), + /** + * STDIN is oscar keyword + */ + STDIN("STDIN"), + /** + * STDOUT is oscar keyword + */ + STDOUT("STDOUT"), + /** + * STOP is oscar keyword + */ + STOP("STOP"), + /** + * STOPFILE is oscar keyword + */ + STOPFILE("STOPFILE"), + /** + * STOPPOS is oscar keyword + */ + STOPPOS("STOPPOS"), + /** + * STOPTIME is oscar keyword + */ + STOPTIME("STOPTIME"), + /** + * STOPWORDS is oscar keyword + */ + STOPWORDS("STOPWORDS"), + /** + * STORAGE is oscar keyword + */ + STORAGE("STORAGE"), + /** + * STORE is oscar keyword + */ + STORE("STORE"), + /** + * STORED is oscar keyword + */ + STORED("STORED"), + /** + * STRICT is oscar keyword + */ + STRICT("STRICT"), + /** + * SUBPARTITION is oscar keyword + */ + SUBPARTITION("SUBPARTITION"), + /** + * SUBPARTITIONS is oscar keyword + */ + SUBPARTITIONS("SUBPARTITIONS"), + /** + * SUBSTRING is oscar keyword + */ + SUBSTRING("SUBSTRING"), + /** + * SUCCESSFUL is oscar keyword + */ + SUCCESSFUL("SUCCESSFUL"), + /** + * SUSPEND is oscar keyword + */ + SUSPEND("SUSPEND"), + /** + * SWITCHOVER is oscar keyword + */ + SWITCHOVER("SWITCHOVER"), + /** + * SYNC is oscar keyword + */ + SYNC("SYNC"), + /** + * SYSAUX is oscar keyword + */ + SYSAUX("SYSAUX"), + /** + * SYSID is oscar keyword + */ + SYSID("SYSID"), + /** + * SYSTEM is oscar keyword + */ + SYSTEM("SYSTEM"), + /** + * T is oscar keyword + */ + T("T"), + /** + * TABLESPACE is oscar keyword + */ + TABLESPACE("TABLESPACE"), + /** + * TB is oscar keyword + */ + TB("TB"), + /** + * TEMP is oscar keyword + */ + TEMP("TEMP"), + /** + * TEMPFILE is oscar keyword + */ + TEMPFILE("TEMPFILE"), + /** + * TEMPLATE is oscar keyword + */ + TEMPLATE("TEMPLATE"), + /** + * TEMPORARY is oscar keyword + */ + TEMPORARY("TEMPORARY"), + /** + * TERMINATED is oscar keyword + */ + TERMINATED("TERMINATED"), + /** + * THAN is oscar keyword + */ + THAN("THAN"), + /** + * TIMES is oscar keyword + */ + TIMES("TIMES"), + /** + * TIMEZONE is oscar keyword + */ + TIMEZONE("TIMEZONE"), + /** + * TINYINT is oscar keyword + */ + TINYINT("TINYINT"), + /** + * TOAST is oscar keyword + */ + TOAST("TOAST"), + /** + * TRACE is oscar keyword + */ + TRACE("TRACE"), + /** + * TRACKING is oscar keyword + */ + TRACKING("TRACKING"), + /** + * TRAIL is oscar keyword + */ + TRAIL("TRAIL"), + /** + * TRAILING is oscar keyword + */ + TRAILING("TRAILING"), + /** + * TRANSACTION is oscar keyword + */ + TRANSACTION("TRANSACTION"), + /** + * TRANSACTIONAL is oscar keyword + */ + TRANSACTIONAL("TRANSACTIONAL"), + /** + * TRANSFORMS is oscar keyword + */ + TRANSFORMS("TRANSFORMS"), + /** + * TREAT is oscar keyword + */ + TREAT("TREAT"), + /** + * TRIAL is oscar keyword + */ + TRIAL("TRIAL"), + /** + * TRIGGER is oscar keyword + */ + TRIGGER("TRIGGER"), + /** + * TRIGGERS is oscar keyword + */ + TRIGGERS("TRIGGERS"), + /** + * TRIM is oscar keyword + */ + TRIM("TRIM"), + /** + * TRUE is oscar keyword + */ + TRUE("TRUE"), + /** + * TRUNCATE is oscar keyword + */ + TRUNCATE("TRUNCATE"), + /** + * TRUSTED is oscar keyword + */ + TRUSTED("TRUSTED"), + /** + * TUPLE is oscar keyword + */ + TUPLE("TUPLE"), + /** + * TYPE is oscar keyword + */ + TYPE("TYPE"), + /** + * UNBOUNDED is oscar keyword + */ + UNBOUNDED("UNBOUNDED"), + /** + * UNCOMMITTED is oscar keyword + */ + UNCOMMITTED("UNCOMMITTED"), + /** + * UNDO is oscar keyword + */ + UNDO("UNDO"), + /** + * UNENCRYPTED is oscar keyword + */ + UNENCRYPTED("UNENCRYPTED"), + /** + * UNKNOWN is oscar keyword + */ + UNKNOWN("UNKNOWN"), + /** + * UNLIMITED is oscar keyword + */ + UNLIMITED("UNLIMITED"), + /** + * UNLISTEN is oscar keyword + */ + UNLISTEN("UNLISTEN"), + /** + * UNLOCK is oscar keyword + */ + UNLOCK("UNLOCK"), + /** + * UNMAINTENANCE is oscar keyword + */ + UNMAINTENANCE("UNMAINTENANCE"), + /** + * UNPIVOT is oscar keyword + */ + UNPIVOT("UNPIVOT"), + /** + * UNSIGNED is oscar keyword + */ + UNSIGNED("UNSIGNED"), + /** + * UNTIL is oscar keyword + */ + UNTIL("UNTIL"), + /** + * UNUSABLE is oscar keyword + */ + UNUSABLE("UNUSABLE"), + /** + * UP is oscar keyword + */ + UP("UP"), + /** + * UPDATE is oscar keyword + */ + UPDATE("UPDATE"), + /** + * UPDATELABEL is oscar keyword + */ + UPDATELABEL("UPDATELABEL"), + /** + * UPDATEXML is oscar keyword + */ + UPDATEXML("UPDATEXML"), + /** + * USAGE is oscar keyword + */ + USAGE("USAGE"), + /** + * USE is oscar keyword + */ + USE("USE"), + /** + * USER is oscar keyword + */ + USER("USER"), + /** + * UTF8 is oscar keyword + */ + UTF8("UTF8"), + /** + * UTF8MB4 is oscar keyword + */ + UTF8MB4("UTF8MB4"), + /** + * VACUUM is oscar keyword + */ + VACUUM("VACUUM"), + /** + * VALID is oscar keyword + */ + VALID("VALID"), + /** + * VALIDATE is oscar keyword + */ + VALIDATE("VALIDATE"), + /** + * VALIDATION is oscar keyword + */ + VALIDATION("VALIDATION"), + /** + * VALIDATOR is oscar keyword + */ + VALIDATOR("VALIDATOR"), + /** + * VALUE is oscar keyword + */ + VALUE("VALUE"), + /** + * VALUES is oscar keyword + */ + VALUES("VALUES"), + /** + * VARBINARY is oscar keyword + */ + VARBINARY("VARBINARY"), + /** + * VARBIT is oscar keyword + */ + VARBIT("VARBIT"), + /** + * VARCHAR is oscar keyword + */ + VARCHAR("VARCHAR"), + /** + * VARCHAR2 is oscar keyword + */ + VARCHAR2("VARCHAR2"), + /** + * VARYING is oscar keyword + */ + VARYING("VARYING"), + /** + * VERBOSE is oscar keyword + */ + VERBOSE("VERBOSE"), + /** + * VERSION is oscar keyword + */ + VERSION("VERSION"), + /** + * VERSIONS is oscar keyword + */ + VERSIONS("VERSIONS"), + /** + * VIEW is oscar keyword + */ + VIEW("VIEW"), + /** + * VIRTUAL is oscar keyword + */ + VIRTUAL("VIRTUAL"), + /** + * VISIBLE is oscar keyword + */ + VISIBLE("VISIBLE"), + /** + * VOLATILE is oscar keyword + */ + VOLATILE("VOLATILE"), + /** + * VOTEDISK is oscar keyword + */ + VOTEDISK("VOTEDISK"), + /** + * WAIT is oscar keyword + */ + WAIT("WAIT"), + /** + * WALLET is oscar keyword + */ + WALLET("WALLET"), + /** + * WEIGHT is oscar keyword + */ + WEIGHT("WEIGHT"), + /** + * WHEN is oscar keyword + */ + WHEN("WHEN"), + /** + * WHENEVER is oscar keyword + */ + WHENEVER("WHENEVER"), + /** + * WINDOW is oscar keyword + */ + WINDOW("WINDOW"), + /** + * WORK is oscar keyword + */ + WORK("WORK"), + /** + * XML is oscar keyword + */ + XML("XML"), + /** + * XMLATTRIBUTES is oscar keyword + */ + XMLATTRIBUTES("XMLATTRIBUTES"), + /** + * XMLCONCAT is oscar keyword + */ + XMLCONCAT("XMLCONCAT"), + /** + * XMLELEMENT is oscar keyword + */ + XMLELEMENT("XMLELEMENT"), + /** + * XMLFOREST is oscar keyword + */ + XMLFOREST("XMLFOREST"), + /** + * XMLPARSE is oscar keyword + */ + XMLPARSE("XMLPARSE"), + /** + * XMLPI is oscar keyword + */ + XMLPI("XMLPI"), + /** + * XMLROOT is oscar keyword + */ + XMLROOT("XMLROOT"), + /** + * XMLSERIALIZE is oscar keyword + */ + XMLSERIALIZE("XMLSERIALIZE"), + /** + * XMLTABLE is oscar keyword + */ + XMLTABLE("XMLTABLE"), + /** + * YEAR is oscar keyword + */ + YEAR("YEAR"), + /** + * YES is oscar keyword + */ + YES("YES"), + /** + * ZONE is oscar keyword + */ + ZONE("ZONE"); + /** + * The Name. + */ + public final String name; + OscarKeyword(String name) { + this.name = name; + } + } + + + @Override + public boolean checkIfKeyWords(String fieldOrTableName) { + if (keywordSet.contains(fieldOrTableName)) { + return true; + } + if (fieldOrTableName != null) { + fieldOrTableName = fieldOrTableName.toUpperCase(); + } + return keywordSet.contains(fieldOrTableName); + + } + + @Override + public boolean checkIfNeedEscape(String columnName, TableMeta tableMeta) { + if (StringUtils.isBlank(columnName)) { + return false; + } + columnName = columnName.trim(); + if (containsEscape(columnName)) { + return false; + } + boolean isKeyWord = checkIfKeyWords(columnName); + if (isKeyWord) { + return true; + } + // oscar + // we are recommend table name and column name must uppercase. + // if exists full uppercase, the table name or column name doesn't bundle escape symbol. + //create\read table TABLE "table" "TABLE" + if (null != tableMeta) { + ColumnMeta columnMeta = tableMeta.getColumnMeta(columnName); + if (null != columnMeta) { + return columnMeta.isCaseSensitive(); + } + } else if (isUppercase(columnName)) { + return false; + } + return true; + } + + private static boolean isUppercase(String fieldOrTableName) { + if (fieldOrTableName == null) { + return false; + } + char[] chars = fieldOrTableName.toCharArray(); + for (char ch : chars) { + if (ch >= 'a' && ch <= 'z') { + return false; + } + } + return true; + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/OscarTableMetaCache.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/OscarTableMetaCache.java new file mode 100644 index 00000000000..bed10c785f1 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/OscarTableMetaCache.java @@ -0,0 +1,199 @@ +/* + * 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.rm.datasource.sql.struct.cache; + +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.sqlparser.struct.ColumnMeta; +import org.apache.seata.sqlparser.struct.IndexMeta; +import org.apache.seata.sqlparser.struct.IndexType; +import org.apache.seata.sqlparser.struct.TableMeta; +import org.apache.seata.sqlparser.util.JdbcConstants; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * The type Table meta cache. + * + */ +@LoadLevel(name = JdbcConstants.OSCAR) +public class OscarTableMetaCache extends AbstractTableMetaCache { + + @Override + protected String getCacheKey(Connection connection, String tableName, String resourceId) { + StringBuilder cacheKey = new StringBuilder(resourceId); + cacheKey.append("."); + + //separate it to schemaName and tableName + String[] tableNameWithSchema = tableName.split("\\."); + String defaultTableName = tableNameWithSchema.length > 1 ? tableNameWithSchema[1] : tableNameWithSchema[0]; + + //oscar does not implement supportsMixedCaseIdentifiers in DatabaseMetadata + if (defaultTableName.contains("\"")) { + cacheKey.append(defaultTableName.replace("\"", "")); + } else { + // oscar default store in upper case + cacheKey.append(defaultTableName.toUpperCase()); + } + + return cacheKey.toString(); + } + + @Override + protected TableMeta fetchSchema(Connection connection, String tableName) throws SQLException { + try { + return resultSetMetaToSchema(connection.getMetaData(), tableName); + } catch (SQLException sqlEx) { + throw sqlEx; + } catch (Exception e) { + throw new SQLException(String.format("Failed to fetch schema of %s", tableName), e); + } + } + + private TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException { + TableMeta tm = new TableMeta(); + tm.setTableName(tableName); + String[] schemaTable = tableName.split("\\."); + + String schemaName = schemaTable.length > 1 ? schemaTable[0] : dbmd.getConnection().getSchema(); + tableName = schemaTable.length > 1 ? schemaTable[1] : tableName; + if (schemaName.contains("\"")) { + schemaName = schemaName.replace("\"", ""); + } else { + schemaName = schemaName.toUpperCase(); + } + + if (tableName.contains("\"")) { + tableName = tableName.replace("\"", ""); + + } else { + tableName = tableName.toUpperCase(); + } + tm.setCaseSensitive(StringUtils.hasLowerCase(tableName)); + + try (ResultSet rsColumns = dbmd.getColumns("", schemaName, tableName, "%"); + ResultSet rsIndex = dbmd.getIndexInfo(null, schemaName, tableName, false, true); + ResultSet rsPrimary = dbmd.getPrimaryKeys(null, schemaName, tableName)) { + while (rsColumns.next()) { + ColumnMeta col = new ColumnMeta(); + col.setTableCat(rsColumns.getString("TABLE_CAT")); + col.setTableSchemaName(rsColumns.getString("TABLE_SCHEM")); + col.setTableName(rsColumns.getString("TABLE_NAME")); + col.setColumnName(rsColumns.getString("COLUMN_NAME")); + col.setDataType(rsColumns.getInt("DATA_TYPE")); + col.setDataTypeName(rsColumns.getString("TYPE_NAME")); + col.setColumnSize(rsColumns.getInt("COLUMN_SIZE")); + col.setDecimalDigits(rsColumns.getInt("DECIMAL_DIGITS")); + col.setNumPrecRadix(rsColumns.getInt("NUM_PREC_RADIX")); + col.setNullAble(rsColumns.getInt("NULLABLE")); + col.setRemarks(rsColumns.getString("REMARKS")); + col.setColumnDef(rsColumns.getString("COLUMN_DEF")); + col.setSqlDataType(rsColumns.getInt("SQL_DATA_TYPE")); + col.setSqlDatetimeSub(rsColumns.getInt("SQL_DATETIME_SUB")); + col.setCharOctetLength(rsColumns.getInt("CHAR_OCTET_LENGTH")); + col.setOrdinalPosition(rsColumns.getInt("ORDINAL_POSITION")); + col.setIsNullAble(rsColumns.getString("IS_NULLABLE")); + col.setCaseSensitive(StringUtils.hasLowerCase(col.getColumnName())); + + if (tm.getAllColumns().containsKey(col.getColumnName())) { + throw new NotSupportYetException("Not support the table has the same column name with different case yet"); + } + tm.getAllColumns().put(col.getColumnName(), col); + } + + while (rsIndex.next()) { + String indexName = rsIndex.getString("INDEX_NAME"); + if (StringUtils.isNullOrEmpty(indexName)) { + continue; + } + String colName = rsIndex.getString("COLUMN_NAME"); + ColumnMeta col = tm.getAllColumns().get(colName); + if (tm.getAllIndexes().containsKey(indexName)) { + IndexMeta index = tm.getAllIndexes().get(indexName); + index.getValues().add(col); + } else { + IndexMeta index = new IndexMeta(); + index.setIndexName(indexName); + index.setNonUnique(rsIndex.getBoolean("NON_UNIQUE")); + index.setIndexQualifier(rsIndex.getString("INDEX_QUALIFIER")); + index.setIndexName(rsIndex.getString("INDEX_NAME")); + index.setType(rsIndex.getShort("TYPE")); + index.setOrdinalPosition(rsIndex.getShort("ORDINAL_POSITION")); + index.setAscOrDesc(rsIndex.getString("ASC_OR_DESC")); + index.setCardinality(rsIndex.getLong("CARDINALITY")); + index.getValues().add(col); + if (!index.isNonUnique()) { + index.setIndextype(IndexType.UNIQUE); + } else { + index.setIndextype(IndexType.NORMAL); + } + tm.getAllIndexes().put(indexName, index); + + } + } + if (tm.getAllIndexes().isEmpty()) { + throw new ShouldNeverHappenException(String.format("Could not found any index in the table: %s", tableName)); + } + // when we create a primary key constraint oracle will uses and existing unique index. + // if we create a unique index before create a primary constraint in the same column will cause the problem + // that primary key constraint name was different from the unique name. + List pkcol = new ArrayList<>(); + while (rsPrimary.next()) { + String pkConstraintName = rsPrimary.getString("PK_NAME"); + if (tm.getAllIndexes().containsKey(pkConstraintName)) { + IndexMeta index = tm.getAllIndexes().get(pkConstraintName); + index.setIndextype(IndexType.PRIMARY); + } else { + //save the columns that constraint primary key name was different from unique index name + pkcol.add(rsPrimary.getString("COLUMN_NAME")); + } + } + //find the index that belong to the primary key constraint + if (!pkcol.isEmpty()) { + int matchCols = 0; + for (Map.Entry entry : tm.getAllIndexes().entrySet()) { + IndexMeta index = entry.getValue(); + // only the unique index and all the unique index's columes same as primary key columes, + // it belongs to primary key + if (index.getIndextype().value() == IndexType.UNIQUE.value()) { + for (ColumnMeta col : index.getValues()) { + if (pkcol.contains(col.getColumnName())) { + matchCols++; + } + } + if (matchCols == pkcol.size()) { + index.setIndextype(IndexType.PRIMARY); + // each table only has one primary key + break; + } else { + matchCols = 0; + } + } + } + } + } + return tm; + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoDeleteExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoDeleteExecutor.java new file mode 100644 index 00000000000..04105a4b152 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoDeleteExecutor.java @@ -0,0 +1,79 @@ +/* + * 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.rm.datasource.undo.oscar; + +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.rm.datasource.sql.struct.Field; +import org.apache.seata.rm.datasource.sql.struct.Row; +import org.apache.seata.rm.datasource.sql.struct.TableRecords; +import org.apache.seata.rm.datasource.undo.AbstractUndoExecutor; +import org.apache.seata.rm.datasource.undo.SQLUndoLog; +import org.apache.seata.sqlparser.util.ColumnUtils; +import org.apache.seata.sqlparser.util.JdbcConstants; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The type oscar undo delete executor. + * + */ +public class OscarUndoDeleteExecutor extends AbstractUndoExecutor { + + /** + * INSERT INTO a (x, y, z, pk) VALUES (?, ?, ?, ?) + */ + private static final String INSERT_SQL_TEMPLATE = "INSERT INTO %s (%s) VALUES (%s)"; + + /** + * Instantiates a new oscar undo delete executor. + * + * @param sqlUndoLog the sql undo log + */ + public OscarUndoDeleteExecutor(SQLUndoLog sqlUndoLog) { + super(sqlUndoLog); + } + + @Override + protected String buildUndoSQL() { + TableRecords beforeImage = sqlUndoLog.getBeforeImage(); + List beforeImageRows = beforeImage.getRows(); + if (CollectionUtils.isEmpty(beforeImageRows)) { + throw new ShouldNeverHappenException("Invalid UNDO LOG"); + } + Row row = beforeImageRows.get(0); + List fields = new ArrayList<>(row.nonPrimaryKeys()); + fields.addAll(getOrderedPkList(beforeImage,row,JdbcConstants.OSCAR)); + + // delete sql undo log before image all field come from table meta, need add escape. + // see BaseTransactionalExecutor#buildTableRecords + String insertColumns = fields.stream() + .map(field -> ColumnUtils.addEscape(field.getName(), JdbcConstants.OSCAR)) + .collect(Collectors.joining(", ")); + String insertValues = fields.stream().map(field -> "?") + .collect(Collectors.joining(", ")); + + return String.format(INSERT_SQL_TEMPLATE, sqlUndoLog.getTableName(), insertColumns, insertValues); + } + + @Override + protected TableRecords getUndoRows() { + return sqlUndoLog.getBeforeImage(); + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoExecutorHolder.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoExecutorHolder.java new file mode 100644 index 00000000000..aa6c560136e --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoExecutorHolder.java @@ -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.seata.rm.datasource.undo.oscar; + +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.rm.datasource.undo.AbstractUndoExecutor; +import org.apache.seata.rm.datasource.undo.SQLUndoLog; +import org.apache.seata.rm.datasource.undo.UndoExecutorHolder; +import org.apache.seata.sqlparser.util.JdbcConstants; + +/** + * The Type OscarUndoExecutorHolder + * + */ +@LoadLevel(name = JdbcConstants.OSCAR) +public class OscarUndoExecutorHolder implements UndoExecutorHolder { + + @Override + public AbstractUndoExecutor getInsertExecutor(SQLUndoLog sqlUndoLog) { + return new OscarUndoInsertExecutor(sqlUndoLog); + } + + @Override + public AbstractUndoExecutor getUpdateExecutor(SQLUndoLog sqlUndoLog) { + return new OscarUndoUpdateExecutor(sqlUndoLog); + } + + @Override + public AbstractUndoExecutor getDeleteExecutor(SQLUndoLog sqlUndoLog) { + return new OscarUndoDeleteExecutor(sqlUndoLog); + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoInsertExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoInsertExecutor.java new file mode 100644 index 00000000000..3e56b37059f --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoInsertExecutor.java @@ -0,0 +1,86 @@ +/* + * 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.rm.datasource.undo.oscar; + +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.rm.datasource.SqlGenerateUtils; +import org.apache.seata.rm.datasource.sql.struct.Field; +import org.apache.seata.rm.datasource.sql.struct.Row; +import org.apache.seata.rm.datasource.sql.struct.TableRecords; +import org.apache.seata.rm.datasource.undo.AbstractUndoExecutor; +import org.apache.seata.rm.datasource.undo.SQLUndoLog; +import org.apache.seata.sqlparser.util.JdbcConstants; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The type oscar undo insert executor. + * + */ +public class OscarUndoInsertExecutor extends AbstractUndoExecutor { + + /** + * DELETE FROM a WHERE pk = ? + */ + private static final String DELETE_SQL_TEMPLATE = "DELETE FROM %s WHERE %s "; + + @Override + protected String buildUndoSQL() { + TableRecords afterImage = sqlUndoLog.getAfterImage(); + List afterImageRows = afterImage.getRows(); + if (CollectionUtils.isEmpty(afterImageRows)) { + throw new ShouldNeverHappenException("Invalid UNDO LOG"); + } + return generateDeleteSql(afterImageRows,afterImage); + } + + @Override + protected void undoPrepare(PreparedStatement undoPST, ArrayList undoValues, List pkValueList) + throws SQLException { + int undoIndex = 0; + for (Field pkField:pkValueList) { + undoIndex++; + undoPST.setObject(undoIndex, pkField.getValue(), pkField.getType()); + } + } + + private String generateDeleteSql(List rows, TableRecords afterImage) { + List pkNameList = getOrderedPkList(afterImage, rows.get(0), JdbcConstants.OSCAR).stream().map( + e -> e.getName()).collect(Collectors.toList()); + String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, JdbcConstants.OSCAR); + return String.format(DELETE_SQL_TEMPLATE, sqlUndoLog.getTableName(), whereSql); + } + + /** + * Instantiates a new Oscar undo insert executor. + * + * @param sqlUndoLog the sql undo log + */ + public OscarUndoInsertExecutor(SQLUndoLog sqlUndoLog) { + super(sqlUndoLog); + } + + @Override + protected TableRecords getUndoRows() { + return sqlUndoLog.getAfterImage(); + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoLogManager.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoLogManager.java new file mode 100644 index 00000000000..68b405b6237 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoLogManager.java @@ -0,0 +1,106 @@ +/* + * 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.rm.datasource.undo.oscar; + +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.util.DateUtil; +import org.apache.seata.core.compressor.CompressorType; +import org.apache.seata.core.constants.ClientTableColumnsName; +import org.apache.seata.rm.datasource.undo.AbstractUndoLogManager; +import org.apache.seata.rm.datasource.undo.UndoLogParser; +import org.apache.seata.sqlparser.util.JdbcConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Date; + +/** + * The type Oscar undo log manager. + */ +@LoadLevel(name = JdbcConstants.OSCAR) +public class OscarUndoLogManager extends AbstractUndoLogManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(OscarUndoLogManager.class); + + private static final String CHECK_UNDO_LOG_TABLE_EXIST_SQL = "SELECT 1 FROM " + UNDO_LOG_TABLE_NAME + " WHERE ROWNUM = 1"; + + private static final String INSERT_UNDO_LOG_SQL = "INSERT INTO " + UNDO_LOG_TABLE_NAME + + " (" + ClientTableColumnsName.UNDO_LOG_ID + "," + ClientTableColumnsName.UNDO_LOG_BRANCH_XID + ", " + + ClientTableColumnsName.UNDO_LOG_XID + ", " + ClientTableColumnsName.UNDO_LOG_CONTEXT + ", " + + ClientTableColumnsName.UNDO_LOG_ROLLBACK_INFO + ", " + ClientTableColumnsName.UNDO_LOG_LOG_STATUS + ", " + + ClientTableColumnsName.UNDO_LOG_LOG_CREATED + ", " + ClientTableColumnsName.UNDO_LOG_LOG_MODIFIED + ")" + + "VALUES (UNDO_LOG_SEQ.nextval, ?, ?, ?, ?, ?, sysdate, sysdate)"; + + private static final String DELETE_UNDO_LOG_BY_CREATE_SQL = "DELETE FROM " + UNDO_LOG_TABLE_NAME + " WHERE " + ClientTableColumnsName.UNDO_LOG_LOG_CREATED + " <= to_date(?,'yyyy-mm-dd hh24:mi:ss') and ROWNUM <= ?"; + + @Override + public int deleteUndoLogByLogCreated(Date logCreated, int limitRows, Connection conn) throws SQLException { + try (PreparedStatement deletePST = conn.prepareStatement(DELETE_UNDO_LOG_BY_CREATE_SQL)) { + String dateStr = DateUtil.formatDate(logCreated, "yyyy-MM-dd HH:mm:ss"); + deletePST.setString(1, dateStr); + deletePST.setInt(2, limitRows); + int deleteRows = deletePST.executeUpdate(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("batch delete undo log size {}", deleteRows); + } + return deleteRows; + } catch (Exception e) { + if (!(e instanceof SQLException)) { + e = new SQLException(e); + } + throw (SQLException) e; + } + } + + @Override + protected void insertUndoLogWithNormal(String xid, long branchId, String rollbackCtx, byte[] undoLogContent, + Connection conn) throws SQLException { + insertUndoLog(xid, branchId,rollbackCtx, undoLogContent, State.Normal, conn); + } + + @Override + protected void insertUndoLogWithGlobalFinished(String xid, long branchId, UndoLogParser parser, Connection conn) throws SQLException { + insertUndoLog(xid, branchId, buildContext(parser.getName(), CompressorType.NONE), parser.getDefaultContent(), + State.GlobalFinished, conn); + } + + + private void insertUndoLog(String xid, long branchID, String rollbackCtx, byte[] undoLogContent, + State state, Connection conn) throws SQLException { + try (PreparedStatement pst = conn.prepareStatement(INSERT_UNDO_LOG_SQL)) { + pst.setLong(1, branchID); + pst.setString(2, xid); + pst.setString(3, rollbackCtx); + pst.setBytes(4, undoLogContent); + pst.setInt(5, state.getValue()); + pst.executeUpdate(); + } catch (Exception e) { + if (!(e instanceof SQLException)) { + e = new SQLException(e); + } + throw (SQLException) e; + } + } + + @Override + protected String getCheckUndoLogTableExistSql() { + return CHECK_UNDO_LOG_TABLE_EXIST_SQL; + } +} diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoUpdateExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoUpdateExecutor.java new file mode 100644 index 00000000000..d2364d7f482 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/oscar/OscarUndoUpdateExecutor.java @@ -0,0 +1,80 @@ +/* + * 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.rm.datasource.undo.oscar; + +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.rm.datasource.SqlGenerateUtils; +import org.apache.seata.rm.datasource.sql.struct.Field; +import org.apache.seata.rm.datasource.sql.struct.Row; +import org.apache.seata.rm.datasource.sql.struct.TableRecords; +import org.apache.seata.rm.datasource.undo.AbstractUndoExecutor; +import org.apache.seata.rm.datasource.undo.SQLUndoLog; +import org.apache.seata.sqlparser.util.ColumnUtils; +import org.apache.seata.sqlparser.util.JdbcConstants; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * The type oscar undo update executor. + * + */ +public class OscarUndoUpdateExecutor extends AbstractUndoExecutor { + + /** + * UPDATE a SET x = ?, y = ?, z = ? WHERE pk1 = ? and pk2 = ? + */ + private static final String UPDATE_SQL_TEMPLATE = "UPDATE %s SET %s WHERE %s "; + + @Override + protected String buildUndoSQL() { + TableRecords beforeImage = sqlUndoLog.getBeforeImage(); + List beforeImageRows = beforeImage.getRows(); + if (CollectionUtils.isEmpty(beforeImageRows)) { + throw new ShouldNeverHappenException("Invalid UNDO LOG"); + } + Row row = beforeImageRows.get(0); + + List nonPkFields = row.nonPrimaryKeys(); + // update sql undo log before image all field come from table meta. need add escape. + // see BaseTransactionalExecutor#buildTableRecords + String updateColumns = nonPkFields.stream().map( + field -> ColumnUtils.addEscape(field.getName(), JdbcConstants.OSCAR) + " = ?").collect( + Collectors.joining(", ")); + + List pkNameList = getOrderedPkList(beforeImage, row, JdbcConstants.OSCAR).stream().map( + e -> e.getName()).collect(Collectors.toList()); + String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, JdbcConstants.OSCAR); + + return String.format(UPDATE_SQL_TEMPLATE, sqlUndoLog.getTableName(), updateColumns, whereSql); + } + + /** + * Instantiates a new Oscar undo update executor. + * + * @param sqlUndoLog the sql undo log + */ + public OscarUndoUpdateExecutor(SQLUndoLog sqlUndoLog) { + super(sqlUndoLog); + } + + @Override + protected TableRecords getUndoRows() { + return sqlUndoLog.getBeforeImage(); + } +} diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.exec.InsertExecutor b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.exec.InsertExecutor index 49701f788b2..bdd61e97e24 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.exec.InsertExecutor +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.exec.InsertExecutor @@ -21,4 +21,5 @@ org.apache.seata.rm.datasource.exec.sqlserver.SqlServerInsertExecutor org.apache.seata.rm.datasource.exec.mariadb.MariadbInsertExecutor org.apache.seata.rm.datasource.exec.polardbx.PolarDBXInsertExecutor org.apache.seata.rm.datasource.exec.dm.DmInsertExecutor -org.apache.seata.rm.datasource.exec.kingbase.KingbaseInsertExecutor \ No newline at end of file +org.apache.seata.rm.datasource.exec.oscar.OscarInsertExecutor +org.apache.seata.rm.datasource.exec.kingbase.KingbaseInsertExecutor diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoExecutorHolder b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoExecutorHolder index 178aca432f5..e2ae497e2ab 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoExecutorHolder +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoExecutorHolder @@ -21,4 +21,5 @@ org.apache.seata.rm.datasource.undo.sqlserver.SqlServerUndoExecutorHolder org.apache.seata.rm.datasource.undo.mariadb.MariadbUndoExecutorHolder org.apache.seata.rm.datasource.undo.polardbx.PolarDBXUndoExecutorHolder org.apache.seata.rm.datasource.undo.dm.DmUndoExecutorHolder -org.apache.seata.rm.datasource.undo.kingbase.KingbaseUndoExecutorHolder \ No newline at end of file +org.apache.seata.rm.datasource.undo.oscar.OscarUndoExecutorHolder +org.apache.seata.rm.datasource.undo.kingbase.KingbaseUndoExecutorHolder diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogManager b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogManager index 040242e30e7..fbc80b929ff 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogManager +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogManager @@ -21,4 +21,5 @@ org.apache.seata.rm.datasource.undo.sqlserver.SqlServerUndoLogManager org.apache.seata.rm.datasource.undo.mariadb.MariadbUndoLogManager org.apache.seata.rm.datasource.undo.polardbx.PolarDBXUndoLogManager org.apache.seata.rm.datasource.undo.dm.DmUndoLogManager -org.apache.seata.rm.datasource.undo.kingbase.KingbaseUndoLogManager \ No newline at end of file +org.apache.seata.rm.datasource.undo.oscar.OscarUndoLogManager +org.apache.seata.rm.datasource.undo.kingbase.KingbaseUndoLogManager diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.EscapeHandler b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.EscapeHandler index 6c3ef4dc8aa..0b4af20529c 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.EscapeHandler +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.EscapeHandler @@ -21,4 +21,5 @@ org.apache.seata.rm.datasource.sql.handler.mariadb.MariadbEscapeHandler org.apache.seata.rm.datasource.sql.handler.sqlserver.SqlServerEscapeHandler org.apache.seata.rm.datasource.sql.handler.polardbx.PolarDBXEscapeHandler org.apache.seata.rm.datasource.sql.handler.dm.DmEscapeHandler -org.apache.seata.rm.datasource.sql.handler.kingbase.KingbaseEscapeHandler \ No newline at end of file +org.apache.seata.rm.datasource.sql.handler.oscar.OscarEscapeHandler +org.apache.seata.rm.datasource.sql.handler.kingbase.KingbaseEscapeHandler diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.struct.TableMetaCache b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.struct.TableMetaCache index a55afda3015..4ed7384dac1 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.struct.TableMetaCache +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.sqlparser.struct.TableMetaCache @@ -21,4 +21,5 @@ org.apache.seata.rm.datasource.sql.struct.cache.SqlServerTableMetaCache org.apache.seata.rm.datasource.sql.struct.cache.MariadbTableMetaCache org.apache.seata.rm.datasource.sql.struct.cache.PolarDBXTableMetaCache org.apache.seata.rm.datasource.sql.struct.cache.DmTableMetaCache -org.apache.seata.rm.datasource.sql.struct.cache.KingbaseTableMetaCache \ No newline at end of file +org.apache.seata.rm.datasource.sql.struct.cache.OscarTableMetaCache +org.apache.seata.rm.datasource.sql.struct.cache.KingbaseTableMetaCache diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/OscarInsertExecutorTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/OscarInsertExecutorTest.java new file mode 100644 index 00000000000..0e27823212c --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/OscarInsertExecutorTest.java @@ -0,0 +1,446 @@ +/* + * 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.rm.datasource.exec; + +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.rm.datasource.ConnectionProxy; +import org.apache.seata.rm.datasource.PreparedStatementProxy; +import org.apache.seata.rm.datasource.StatementProxy; +import org.apache.seata.rm.datasource.exec.oscar.OscarInsertExecutor; +import org.apache.seata.sqlparser.SQLInsertRecognizer; +import org.apache.seata.sqlparser.struct.ColumnMeta; +import org.apache.seata.sqlparser.struct.Null; +import org.apache.seata.sqlparser.struct.SqlSequenceExpr; +import org.apache.seata.sqlparser.struct.TableMeta; +import org.apache.seata.sqlparser.util.JdbcConstants; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static org.mockito.Mockito.*; + +/** + * The type Oscar insert executor test. + */ +public class OscarInsertExecutorTest { + + private static final String ID_COLUMN = "id"; + private static final String USER_ID_COLUMN = "user_id"; + private static final String USER_NAME_COLUMN = "user_name"; + private static final String USER_STATUS_COLUMN = "user_status"; + private static final Integer PK_VALUE_ID = 100; + private static final Integer PK_VALUE_USER_ID = 200; + + private ConnectionProxy connectionProxy; + + private StatementProxy statementProxy; + + private SQLInsertRecognizer sqlInsertRecognizer; + + private StatementCallback statementCallback; + + private TableMeta tableMeta; + + private OscarInsertExecutor insertExecutor; + + private final int pkIndexId = 0; + + private final int pkIndexUserId = 1; + + private HashMap pkIndexMap; + + private HashMap multiPkIndexMap; + + @BeforeEach + public void init() { + connectionProxy = mock(ConnectionProxy.class); + when(connectionProxy.getDbType()).thenReturn(JdbcConstants.OSCAR); + + statementProxy = mock(PreparedStatementProxy.class); + when(statementProxy.getConnectionProxy()).thenReturn(connectionProxy); + + statementCallback = mock(StatementCallback.class); + sqlInsertRecognizer = mock(SQLInsertRecognizer.class); + tableMeta = mock(TableMeta.class); + insertExecutor = Mockito.spy(new OscarInsertExecutor(statementProxy, statementCallback, sqlInsertRecognizer)); + + pkIndexMap = new HashMap() {{ + put(ID_COLUMN, pkIndexId); + }}; + + multiPkIndexMap = new HashMap() {{ + put(ID_COLUMN, pkIndexId); + put(USER_ID_COLUMN, pkIndexUserId); + }}; + } + + @Test + public void testPkValue_sequence() throws Exception { + mockInsertColumns(); + SqlSequenceExpr expr = mockParametersPkWithSeq(); + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[]{ID_COLUMN})); + List pkValuesSeq = new ArrayList<>(); + pkValuesSeq.add(PK_VALUE_ID); + + doReturn(pkValuesSeq).when(insertExecutor).getPkValuesBySequence(expr, ID_COLUMN); + doReturn(pkIndexMap).when(insertExecutor).getPkIndex(); + + Map> pkValuesByColumn = insertExecutor.getPkValuesByColumn(); + verify(insertExecutor).getPkValuesBySequence(expr, ID_COLUMN); + Assertions.assertEquals(pkValuesByColumn.get(ID_COLUMN), pkValuesSeq); + } + + @Test + public void testMultiPkValue_sequence() throws Exception { + mockInsertColumns(); + SqlSequenceExpr expr = mockParametersMultiPkWithSeq(); + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[]{ID_COLUMN, USER_ID_COLUMN})); + List pkValuesSeqId = new ArrayList<>(); + pkValuesSeqId.add(PK_VALUE_ID); + List pkValuesSeqUserId = new ArrayList<>(); + pkValuesSeqUserId.add(PK_VALUE_USER_ID); + + doReturn(pkValuesSeqId).when(insertExecutor).getPkValuesBySequence(expr, ID_COLUMN); + doReturn(pkValuesSeqUserId).when(insertExecutor).getPkValuesBySequence(expr, USER_ID_COLUMN); + doReturn(multiPkIndexMap).when(insertExecutor).getPkIndex(); + + Map> pkValuesByColumn = insertExecutor.getPkValuesByColumn(); + verify(insertExecutor).getPkValuesBySequence(expr, ID_COLUMN); + verify(insertExecutor).getPkValuesBySequence(expr, USER_ID_COLUMN); + Assertions.assertEquals(pkValuesByColumn.get(ID_COLUMN), pkValuesSeqId); + Assertions.assertEquals(pkValuesByColumn.get(USER_ID_COLUMN), pkValuesSeqUserId); + } + + @Test + public void testPkValue_auto() throws Exception { + mockInsertColumns(); + mockParametersPkWithAuto(); + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + doReturn(pkIndexMap).when(insertExecutor).getPkIndex(); + when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[]{ID_COLUMN})); + doReturn(Arrays.asList(new Object[]{PK_VALUE_ID})).when(insertExecutor).getGeneratedKeys(ID_COLUMN); + Map> pkValuesByAuto = insertExecutor.getPkValues(); + + verify(insertExecutor).getGeneratedKeys(ID_COLUMN); + Assertions.assertEquals(pkValuesByAuto.get(ID_COLUMN), Arrays.asList(new Object[]{PK_VALUE_ID})); + } + + @Test + public void testMultiPkValue_auto() throws Exception { + mockInsertColumns(); + mockParametersMultiPkWithAuto(); + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + doReturn(multiPkIndexMap).when(insertExecutor).getPkIndex(); + when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[]{ID_COLUMN, USER_ID_COLUMN})); + Assertions.assertThrows(NotSupportYetException.class, () -> { + insertExecutor.getPkValues(); + }); + + + } + + @Test + public void testStatement_pkValueByAuto_NotSupportYetException() throws Exception { + mockInsertColumns(); + mockStatementInsertRows(); + + statementProxy = mock(StatementProxy.class); + when(statementProxy.getConnectionProxy()).thenReturn(connectionProxy); + when(connectionProxy.getDbType()).thenReturn(JdbcConstants.ORACLE); + + insertExecutor = Mockito.spy(new OscarInsertExecutor(statementProxy, statementCallback, sqlInsertRecognizer)); + + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + + Map map = new HashMap<>(); + map.put(ID_COLUMN, mock(ColumnMeta.class)); + doReturn(map).when(tableMeta).getPrimaryKeyMap(); + + ResultSet rs = mock(ResultSet.class); + doReturn(rs).when(statementProxy).getGeneratedKeys(); + doReturn(false).when(rs).next(); + + Assertions.assertThrows(NotSupportYetException.class, () -> { + insertExecutor.getGeneratedKeys(ID_COLUMN); + }); + + doReturn(pkIndexMap).when(insertExecutor).getPkIndex(); + + Assertions.assertThrows(NotSupportYetException.class, () -> { + insertExecutor.getPkValuesByColumn(); + }); + + } + + @Test + public void testGetPkValues_SinglePk() throws SQLException { + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + + List pkColumns = new ArrayList<>(); + pkColumns.add(ID_COLUMN); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + + // mock pk values from insert rows + Map> mockPkValuesFromColumn = new HashMap<>(); + mockPkValuesFromColumn.put(ID_COLUMN, Collections.singletonList(PK_VALUE_ID + 1)); + doReturn(mockPkValuesFromColumn).when(insertExecutor).getPkValuesByColumn(); + + // mock pk values from auto increment + List mockPkValuesAutoGenerated = Collections.singletonList(PK_VALUE_ID); + doReturn(mockPkValuesAutoGenerated).when(insertExecutor).getGeneratedKeys(ID_COLUMN); + + // situation1: insert columns are empty + List columns = new ArrayList<>(); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(columns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(true); + Assertions.assertIterableEquals(mockPkValuesFromColumn.entrySet(), insertExecutor.getPkValues().entrySet()); + + // situation2: insert columns contain the pk column + columns = new ArrayList<>(); + columns.add(ID_COLUMN); + columns.add(USER_NAME_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(columns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(false); + Assertions.assertIterableEquals(mockPkValuesFromColumn.entrySet(), insertExecutor.getPkValues().entrySet()); + + // situation3: insert columns are not empty and do not contain the pk column + columns = new ArrayList<>(); + columns.add(USER_NAME_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(columns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(false); + Assertions.assertIterableEquals( + Collections.singletonMap(ID_COLUMN, mockPkValuesAutoGenerated).entrySet(), + insertExecutor.getPkValues().entrySet()); + } + + @Test + public void testGetPkValues_MultiPk() throws SQLException { + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + + List pkColumns = new ArrayList<>(); + pkColumns.add(ID_COLUMN); + pkColumns.add(USER_ID_COLUMN); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + + // mock all pk values from insert rows + Map> mockAllPkValuesFromColumn = new HashMap<>(); + mockAllPkValuesFromColumn.put(ID_COLUMN, Collections.singletonList(PK_VALUE_ID + 1)); + mockAllPkValuesFromColumn.put(USER_ID_COLUMN, Collections.singletonList(PK_VALUE_USER_ID + 1)); + doReturn(mockAllPkValuesFromColumn).when(insertExecutor).getPkValuesByColumn(); + + // mock pk values from auto increment + List mockPkValuesAutoGenerated_ID = Collections.singletonList(PK_VALUE_ID); + doReturn(mockPkValuesAutoGenerated_ID).when(insertExecutor).getGeneratedKeys(ID_COLUMN); + List mockPkValuesAutoGenerated_USER_ID = Collections.singletonList(PK_VALUE_USER_ID); + doReturn(mockPkValuesAutoGenerated_USER_ID).when(insertExecutor).getGeneratedKeys(USER_ID_COLUMN); + + // situation1: insert columns are empty + List insertColumns = new ArrayList<>(); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(insertColumns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(true); + Assertions.assertIterableEquals(mockAllPkValuesFromColumn.entrySet(), insertExecutor.getPkValues().entrySet()); + + // situation2: insert columns contain all pk columns + insertColumns = new ArrayList<>(); + insertColumns.add(ID_COLUMN); + insertColumns.add(USER_ID_COLUMN); + insertColumns.add(USER_NAME_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(insertColumns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(false); + Assertions.assertIterableEquals(mockAllPkValuesFromColumn.entrySet(), insertExecutor.getPkValues().entrySet()); + + // situation3: insert columns contain partial pk columns + insertColumns = new ArrayList<>(); + insertColumns.add(ID_COLUMN); + insertColumns.add(USER_NAME_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(insertColumns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(false); + + Map> mockPkValuesFromColumn_ID = new HashMap<>(); + mockPkValuesFromColumn_ID.put(ID_COLUMN, Collections.singletonList(PK_VALUE_ID + 1)); + doReturn(mockPkValuesFromColumn_ID).when(insertExecutor).getPkValuesByColumn(); + + Map> expectPkValues = new HashMap<>(mockPkValuesFromColumn_ID); + expectPkValues.put(USER_ID_COLUMN, mockPkValuesAutoGenerated_USER_ID); + Assertions.assertIterableEquals(expectPkValues.entrySet(), insertExecutor.getPkValues().entrySet()); + + // situation4: insert columns are not empty and do not contain the pk column + insertColumns = new ArrayList<>(); + insertColumns.add(USER_NAME_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(insertColumns); + when(sqlInsertRecognizer.insertColumnsIsEmpty()).thenReturn(false); + + doReturn(new HashMap<>()).when(insertExecutor).getPkValuesByColumn(); + + expectPkValues = new HashMap<>(); + expectPkValues.put(ID_COLUMN, mockPkValuesAutoGenerated_ID); + expectPkValues.put(USER_ID_COLUMN, mockPkValuesAutoGenerated_USER_ID); + Assertions.assertIterableEquals(expectPkValues.entrySet(), insertExecutor.getPkValues().entrySet()); + } + + @Test + public void testContainsAnyPK() { + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + + Assertions.assertFalse(insertExecutor.containsAnyPk()); + + mockInsertColumns(); + doReturn(null).when(tableMeta).getPrimaryKeyOnlyName(); + Assertions.assertFalse(insertExecutor.containsAnyPk()); + + List pkColumns = new ArrayList<>(); + pkColumns.add(System.currentTimeMillis() + ""); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + Assertions.assertFalse(insertExecutor.containsAnyPk()); + + pkColumns = new ArrayList<>(); + pkColumns.add(ID_COLUMN); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + Assertions.assertTrue(insertExecutor.containsAnyPk()); + + pkColumns = new ArrayList<>(); + pkColumns.add(ID_COLUMN); + pkColumns.add(USER_ID_COLUMN); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + Assertions.assertTrue(insertExecutor.containsAnyPk()); + + pkColumns = new ArrayList<>(); + pkColumns.add(ID_COLUMN); + pkColumns.add(System.currentTimeMillis() + ""); + doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName(); + Assertions.assertTrue(insertExecutor.containsAnyPk()); + } + + private List mockInsertColumns() { + List columns = new ArrayList<>(); + columns.add(ID_COLUMN); + columns.add(USER_ID_COLUMN); + columns.add(USER_NAME_COLUMN); + columns.add(USER_STATUS_COLUMN); + when(sqlInsertRecognizer.getInsertColumns()).thenReturn(columns); + return columns; + } + + private SqlSequenceExpr mockParametersPkWithSeq() { + SqlSequenceExpr expr = new SqlSequenceExpr("seq", "nextval"); + Map> paramters = new HashMap(4); + ArrayList arrayList0 = new ArrayList<>(); + arrayList0.add(expr); + ArrayList arrayList1 = new ArrayList<>(); + arrayList1.add("userId1"); + ArrayList arrayList2 = new ArrayList<>(); + arrayList2.add("userName1"); + ArrayList arrayList3 = new ArrayList<>(); + arrayList3.add("userStatus1"); + paramters.put(1, arrayList0); + paramters.put(2, arrayList1); + paramters.put(3, arrayList2); + paramters.put(4, arrayList3); + PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy; + when(psp.getParameters()).thenReturn(paramters); + + List> rows = new ArrayList<>(); + rows.add(Arrays.asList("?", "?", "?")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows); + + return expr; + } + + private SqlSequenceExpr mockParametersMultiPkWithSeq() { + SqlSequenceExpr expr = new SqlSequenceExpr("seq", "nextval"); + Map> paramters = new HashMap(4); + ArrayList arrayList0 = new ArrayList<>(); + arrayList0.add(expr); + ArrayList arrayList1 = new ArrayList<>(); + arrayList1.add(expr); + ArrayList arrayList2 = new ArrayList<>(); + arrayList2.add("userName1"); + ArrayList arrayList3 = new ArrayList<>(); + arrayList3.add("userStatus1"); + paramters.put(1, arrayList0); + paramters.put(2, arrayList1); + paramters.put(3, arrayList2); + paramters.put(4, arrayList3); + PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy; + when(psp.getParameters()).thenReturn(paramters); + + List> rows = new ArrayList<>(); + rows.add(Arrays.asList("?", "?")); + when(sqlInsertRecognizer.getInsertRows(multiPkIndexMap.values())).thenReturn(rows); + + return expr; + } + + private void mockParametersPkWithAuto() { + Map> paramters = new HashMap<>(4); + ArrayList arrayList0 = new ArrayList<>(); + arrayList0.add(Null.get()); + ArrayList arrayList1 = new ArrayList<>(); + arrayList1.add("userId1"); + ArrayList arrayList2 = new ArrayList<>(); + arrayList2.add("userName1"); + ArrayList arrayList3 = new ArrayList<>(); + arrayList3.add("userStatus1"); + paramters.put(1, arrayList0); + paramters.put(2, arrayList1); + paramters.put(3, arrayList2); + paramters.put(4, arrayList3); + PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy; + when(psp.getParameters()).thenReturn(paramters); + + List> rows = new ArrayList<>(); + rows.add(Arrays.asList("?", "?", "?", "?")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows); + } + + private void mockParametersMultiPkWithAuto() { + Map> paramters = new HashMap<>(4); + ArrayList arrayList0 = new ArrayList<>(); + arrayList0.add(Null.get()); + ArrayList arrayList1 = new ArrayList<>(); + arrayList1.add(Null.get()); + ArrayList arrayList2 = new ArrayList<>(); + arrayList2.add("userName1"); + ArrayList arrayList3 = new ArrayList<>(); + arrayList3.add("userStatus1"); + paramters.put(1, arrayList0); + paramters.put(2, arrayList1); + paramters.put(3, arrayList2); + paramters.put(4, arrayList3); + PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy; + when(psp.getParameters()).thenReturn(paramters); + + List> rows = new ArrayList<>(); + rows.add(Arrays.asList("?", "?", "?", "?")); + when(sqlInsertRecognizer.getInsertRows(multiPkIndexMap.values())).thenReturn(rows); + } + + private void mockStatementInsertRows() { + List> rows = new ArrayList<>(); + rows.add(Arrays.asList(Null.get(), "xx", "xx", "xx")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows); + } + + +} diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarDeleteRecognizerTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarDeleteRecognizerTest.java new file mode 100644 index 00000000000..405198f5b55 --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarDeleteRecognizerTest.java @@ -0,0 +1,198 @@ +/* + * 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.rm.datasource.sql.druid.oscar; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLType; +import org.apache.seata.sqlparser.druid.oscar.OscarDeleteRecognizer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The type Oscar delete recognizer test. + */ +public class OscarDeleteRecognizerTest { + + private static final String DB_TYPE = "oscar"; + + @Test + public void testGetSqlType() { + String sql = "delete from t where id = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarDeleteRecognizer recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getSQLType(), SQLType.DELETE); + } + + @Test + public void testGetTableAlias() { + String sql = "delete from t where id = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarDeleteRecognizer recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + Assertions.assertNull(recognizer.getTableAlias()); + } + + @Test + public void testGetTableName() { + String sql = "delete from t where id = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarDeleteRecognizer recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } + + @Test + public void testGetWhereCondition_0() { + String sql = "delete from t"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarDeleteRecognizer recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + return null; + } + }, new ArrayList<>()); + + //test for no condition + Assertions.assertEquals("", whereCondition); + + sql = "delete from t where id = ?"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + ArrayList idParam = new ArrayList<>(); + idParam.add(1); + Map result = new HashMap(); + result.put(1, idParam); + return result; + } + }, new ArrayList<>()); + + //test for normal sql + Assertions.assertEquals("id = ?", whereCondition); + + sql = "delete from t where id in (?)"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + ArrayList idParam = new ArrayList<>(); + idParam.add(1); + Map result = new HashMap(); + result.put(1, idParam); + return result; + } + }, new ArrayList<>()); + + //test for sql with in + Assertions.assertEquals("id IN (?)", whereCondition); + + sql = "delete from t where id between ? and ?"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + ArrayList idParam = new ArrayList<>(); + idParam.add(1); + ArrayList idParam2 = new ArrayList<>(); + idParam.add(2); + Map result = new HashMap(); + result.put(1, idParam); + result.put(2, idParam2); + return result; + } + }, new ArrayList<>()); + //test for sql with in + Assertions.assertEquals("id BETWEEN ? AND ?", whereCondition); + + //test for exception + Assertions.assertThrows(IllegalArgumentException.class, () -> { + String s = "delete from t where id in (?)"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLDeleteStatement deleteAst = (SQLDeleteStatement) sqlStatements.get(0); + deleteAst.setWhere(new OracleArgumentExpr()); + new OscarDeleteRecognizer(s, deleteAst).getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + return new HashMap<>(); + } + }, new ArrayList<>()); + }); + } + + @Test + public void testGetWhereCondition_1() { + + String sql = "delete from t"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarDeleteRecognizer recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(); + + //test for no condition + Assertions.assertEquals("", whereCondition); + + sql = "delete from t where id = 1"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(); + + //test for normal sql + Assertions.assertEquals("id = 1", whereCondition); + + sql = "delete from t where id in (1)"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(); + + //test for sql with in + Assertions.assertEquals("id IN (1)", whereCondition); + + sql = "delete from t where id between 1 and 2"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarDeleteRecognizer(sql, asts.get(0)); + whereCondition = recognizer.getWhereCondition(); + //test for sql with in + Assertions.assertEquals("id BETWEEN 1 AND 2", whereCondition); + + //test for exception + Assertions.assertThrows(IllegalArgumentException.class, () -> { + String s = "delete from t where id in (1)"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLDeleteStatement deleteAst = (SQLDeleteStatement) sqlStatements.get(0); + deleteAst.setWhere(new OracleArgumentExpr()); + new OscarDeleteRecognizer(s, deleteAst).getWhereCondition(); + }); + } +} diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarInsertRecognizerTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarInsertRecognizerTest.java new file mode 100644 index 00000000000..2a025866816 --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarInsertRecognizerTest.java @@ -0,0 +1,130 @@ +/* + * 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.rm.datasource.sql.druid.oscar; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleBinaryDoubleExpr; +import org.apache.seata.sqlparser.SQLParsingException; +import org.apache.seata.sqlparser.SQLType; +import org.apache.seata.sqlparser.druid.oscar.OscarInsertRecognizer; +import org.apache.seata.sqlparser.struct.NotPlaceholderExpr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +/** + * The type Oscar insert recognizer test. + */ +public class OscarInsertRecognizerTest { + + private static final String DB_TYPE = "oscar"; + + @Test + public void testGetSqlType() { + String sql = "insert into t(id) values (?)"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getSQLType(), SQLType.INSERT); + } + + @Test + public void testGetTableAlias() { + String sql = "insert into t(id) values (?)"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + Assertions.assertNull(recognizer.getTableAlias()); + } + + @Test + public void testGetTableName() { + String sql = "insert into t(id) values (?)"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } + + @Test + public void testGetInsertColumns() { + + //test for no column + String sql = "insert into t values (?)"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + List insertColumns = recognizer.getInsertColumns(); + Assertions.assertNull(insertColumns); + + //test for normal + sql = "insert into t(a) values (?)"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + + recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + insertColumns = recognizer.getInsertColumns(); + Assertions.assertEquals(1, insertColumns.size()); + + //test for exception + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "insert into t(a) values (?)"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLInsertStatement sqlInsertStatement = (SQLInsertStatement)sqlStatements.get(0); + sqlInsertStatement.getColumns().add(new OracleBinaryDoubleExpr()); + + OscarInsertRecognizer oscarInsertRecognizer = new OscarInsertRecognizer(s, sqlInsertStatement); + oscarInsertRecognizer.getInsertColumns(); + }); + } + + @Test + public void testGetInsertRows() { + final int pkIndex = 0; + //test for null value + String sql = "insert into t(id, no, name, age, time) values (id_seq.nextval, null, 'a', ?, now())"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer recognizer = new OscarInsertRecognizer(sql, asts.get(0)); + List> insertRows = recognizer.getInsertRows(Collections.singletonList(pkIndex)); + Assertions.assertEquals(1, insertRows.size()); + + //test for exception + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "insert into t(a) values (?)"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLInsertStatement sqlInsertStatement = (SQLInsertStatement)sqlStatements.get(0); + sqlInsertStatement.getValuesList().get(0).getValues().set(pkIndex, new OracleBinaryDoubleExpr()); + + OscarInsertRecognizer oscarInsertRecognizer = new OscarInsertRecognizer(s, sqlInsertStatement); + oscarInsertRecognizer.getInsertRows(Collections.singletonList(pkIndex)); + }); + } + + @Test + public void testNotPlaceholder_giveValidPkIndex() { + String sql = "insert into test(create_time) values(sysdate)"; + List sqlStatements = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarInsertRecognizer oscar = new OscarInsertRecognizer(sql, sqlStatements.get(0)); + List> insertRows = oscar.getInsertRows(Collections.singletonList(-1)); + Assertions.assertTrue(insertRows.get(0).get(0) instanceof NotPlaceholderExpr); + } +} diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarSelectForUpdateRecognizerTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarSelectForUpdateRecognizerTest.java new file mode 100644 index 00000000000..604dcd99132 --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarSelectForUpdateRecognizerTest.java @@ -0,0 +1,111 @@ +/* + * 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.rm.datasource.sql.druid.oscar; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLParsingException; +import org.apache.seata.sqlparser.SQLType; +import org.apache.seata.sqlparser.druid.oscar.OscarSelectForUpdateRecognizer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * The type Oscar select for update recognizer test. + */ +public class OscarSelectForUpdateRecognizerTest { + + private static final String DB_TYPE = "oscar"; + + @Test + public void testGetSqlType() { + String sql = "select * from t where id = ? for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarSelectForUpdateRecognizer recognizer = new OscarSelectForUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getSQLType(), SQLType.SELECT_FOR_UPDATE); + } + + + @Test + public void testGetWhereCondition_0() { + String sql = "select * from t for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarSelectForUpdateRecognizer recognizer = new OscarSelectForUpdateRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + return null; + } + }, new ArrayList<>()); + Assertions.assertEquals("", whereCondition); + } + + @Test + public void testGetWhereCondition_1() { + String sql = "select * from t for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarSelectForUpdateRecognizer recognizer = new OscarSelectForUpdateRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(); + + Assertions.assertEquals("", whereCondition); + + //test for select was null + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "select * from t for update"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLSelectStatement selectAst = (SQLSelectStatement) sqlStatements.get(0); + selectAst.setSelect(null); + new OscarSelectForUpdateRecognizer(s, selectAst).getWhereCondition(); + }); + + //test for query was null + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "select * from t"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLSelectStatement selectAst = (SQLSelectStatement) sqlStatements.get(0); + selectAst.getSelect().setQuery(null); + new OscarSelectForUpdateRecognizer(s, selectAst).getWhereCondition(); + }); + } + + @Test + public void testGetTableAlias() { + String sql = "select * from t where id = ? for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarSelectForUpdateRecognizer recognizer = new OscarSelectForUpdateRecognizer(sql, asts.get(0)); + Assertions.assertNull(recognizer.getTableAlias()); + } + + @Test + public void testGetTableName() { + String sql = "select * from t where id = ? for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarSelectForUpdateRecognizer recognizer = new OscarSelectForUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } +} diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarUpdateRecognizerTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarUpdateRecognizerTest.java new file mode 100644 index 00000000000..120086affc5 --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/druid/oscar/OscarUpdateRecognizerTest.java @@ -0,0 +1,157 @@ +/* + * 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.rm.datasource.sql.druid.oscar; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleCursorExpr; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLParsingException; +import org.apache.seata.sqlparser.SQLType; +import org.apache.seata.sqlparser.druid.oscar.OscarUpdateRecognizer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * The Oscar Update Recognizer Test. + */ +public class OscarUpdateRecognizerTest { + + private static final String DB_TYPE = "oscar"; + + @Test + public void testGetSqlType() { + String sql = "update t set n = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getSQLType(), SQLType.UPDATE); + } + + @Test + public void testGetUpdateColumns() { + // test with normal + String sql = "update t set a = ?, b = ?, c = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + List updateColumns = recognizer.getUpdateColumns(); + Assertions.assertEquals(updateColumns.size(), 3); + + // test with alias + sql = "update t set a.a = ?, a.b = ?, a.c = ?"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + updateColumns = recognizer.getUpdateColumns(); + Assertions.assertEquals(updateColumns.size(), 3); + + //test with error + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "update t set a = a"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLUpdateStatement sqlUpdateStatement = (SQLUpdateStatement) sqlStatements.get(0); + List updateSetItems = sqlUpdateStatement.getItems(); + for (SQLUpdateSetItem updateSetItem : updateSetItems) { + updateSetItem.setColumn(new OracleCursorExpr()); + } + OscarUpdateRecognizer oscarUpdateRecognizer = new OscarUpdateRecognizer(s, sqlUpdateStatement); + oscarUpdateRecognizer.getUpdateColumns(); + }); + } + + @Test + public void testGetUpdateValues() { + // test with normal + String sql = "update t set a = ?, b = ?, c = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + List updateValues = recognizer.getUpdateValues(); + Assertions.assertEquals(updateValues.size(), 3); + + // test with values + sql = "update t set a = 1, b = 2, c = 3"; + asts = SQLUtils.parseStatements(sql, DB_TYPE); + recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + updateValues = recognizer.getUpdateValues(); + Assertions.assertEquals(updateValues.size(), 3); + + // test with error + Assertions.assertThrows(SQLParsingException.class, () -> { + String s = "update t set a = ?"; + List sqlStatements = SQLUtils.parseStatements(s, DB_TYPE); + SQLUpdateStatement sqlUpdateStatement = (SQLUpdateStatement)sqlStatements.get(0); + List updateSetItems = sqlUpdateStatement.getItems(); + for (SQLUpdateSetItem updateSetItem : updateSetItems) { + updateSetItem.setValue(new OracleCursorExpr()); + } + OscarUpdateRecognizer oscarUpdateRecognizer = new OscarUpdateRecognizer(s, sqlUpdateStatement); + oscarUpdateRecognizer.getUpdateValues(); + }); + } + + @Test + public void testGetWhereCondition_0() { + + String sql = "update t set a = 1"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(new ParametersHolder() { + @Override + public Map> getParameters() { + return null; + } + }, new ArrayList<>()); + + Assertions.assertEquals("", whereCondition); + } + + @Test + public void testGetWhereCondition_1() { + + String sql = "update t set a = 1"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + String whereCondition = recognizer.getWhereCondition(); + + Assertions.assertEquals("", whereCondition); + } + + @Test + public void testGetTableAlias() { + String sql = "update t set a = ?, b = ?, c = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + Assertions.assertNull(recognizer.getTableAlias()); + } + + @Test + public void testGetTableName() { + String sql = "update t set a = ?, b = ?, c = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + OscarUpdateRecognizer recognizer = new OscarUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } +} diff --git a/script/client/at/db/oscar.sql b/script/client/at/db/oscar.sql new file mode 100644 index 00000000000..ff62de40f76 --- /dev/null +++ b/script/client/at/db/oscar.sql @@ -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. +-- +-- for AT mode you must to init this sql for you business database. the seata server not need it. +CREATE TABLE UNDO_LOG +( + ID numeric(19,0) NOT NULL, + BRANCH_ID numeric(19,0) NOT NULL, + XID character varying(128) NOT NULL, + "CONTEXT" character varying(128) NOT NULL, + ROLLBACK_INFO blob NOT NULL, + LOG_STATUS numeric(10,0) NOT NULL, + LOG_CREATED timestamp(0) without time zone NOT NULL, + LOG_MODIFIED timestamp(0) without time zone NOT NULL, + CONSTRAINT UNDO_LOG_PKEY PRIMARY KEY (ID), + CONSTRAINT UX_UNDO_LOG UNIQUE (XID, BRANCH_ID) +); + +CREATE INDEX ix_log_created ON UNDO_LOG(LOG_CREATED); +COMMENT ON TABLE UNDO_LOG IS 'AT transaction mode undo table'; +COMMENT ON COLUMN UNDO_LOG.BRANCH_ID is 'branch transaction id'; +COMMENT ON COLUMN UNDO_LOG.XID is 'global transaction id'; +COMMENT ON COLUMN UNDO_LOG.CONTEXT is 'undo_log context,such as serialization'; +COMMENT ON COLUMN UNDO_LOG.ROLLBACK_INFO is 'rollback info'; +COMMENT ON COLUMN UNDO_LOG.LOG_STATUS is '0:normal status,1:defense status'; +COMMENT ON COLUMN UNDO_LOG.LOG_CREATED is 'create datetime'; +COMMENT ON COLUMN UNDO_LOG.LOG_MODIFIED is 'modify datetime'; + +-- Generate ID using sequence and trigger +CREATE SEQUENCE UNDO_LOG_SEQ START WITH 1 INCREMENT BY 1; \ No newline at end of file diff --git a/script/server/db/oscar.sql b/script/server/db/oscar.sql new file mode 100644 index 00000000000..a65b5e8b0d5 --- /dev/null +++ b/script/server/db/oscar.sql @@ -0,0 +1,94 @@ +-- +-- 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. +-- +-- -------------------------------- The script used when storeMode is 'db' -------------------------------- +-- the table to store GlobalSession data +CREATE TABLE global_table +( + XID VARCHAR2(128) NOT NULL, + TRANSACTION_ID NUMBER(19), + STATUS NUMBER(3) NOT NULL, + APPLICATION_ID VARCHAR2(32), + TRANSACTION_SERVICE_GROUP VARCHAR2(32), + TRANSACTION_NAME VARCHAR2(128), + TIMEOUT NUMBER(10), + BEGIN_TIME NUMBER(19), + APPLICATION_DATA VARCHAR2(2000), + GMT_CREATE TIMESTAMP(0), + GMT_MODIFIED TIMESTAMP(0), + PRIMARY KEY (XID) +); + +CREATE INDEX idx_status_gmt_modified ON global_table (STATUS, GMT_MODIFIED); +CREATE INDEX idx_transaction_id ON global_table (TRANSACTION_ID); + +-- the table to store BranchSession data +CREATE TABLE branch_table +( + BRANCH_ID NUMBER(19) NOT NULL, + XID VARCHAR2(128) NOT NULL, + TRANSACTION_ID NUMBER(19), + RESOURCE_GROUP_ID VARCHAR2(32), + RESOURCE_ID VARCHAR2(256), + BRANCH_TYPE VARCHAR2(8), + STATUS NUMBER(3), + CLIENT_ID VARCHAR2(64), + APPLICATION_DATA VARCHAR2(2000), + GMT_CREATE TIMESTAMP(6), + GMT_MODIFIED TIMESTAMP(6), + PRIMARY KEY (BRANCH_ID) +); + +CREATE INDEX idx_xid ON branch_table (XID); + +-- the table to store lock data +CREATE TABLE lock_table +( + ROW_KEY VARCHAR2(128) NOT NULL, + XID VARCHAR2(128), + TRANSACTION_ID NUMBER(19), + BRANCH_ID NUMBER(19) NOT NULL, + RESOURCE_ID VARCHAR2(256), + TABLE_NAME VARCHAR2(32), + PK VARCHAR2(36), + STATUS NUMBER(3) DEFAULT 0 NOT NULL, + GMT_CREATE TIMESTAMP(0), + GMT_MODIFIED TIMESTAMP(0), + PRIMARY KEY (ROW_KEY) +); + +comment on column lock_table.STATUS is '0:locked ,1:rollbacking'; +CREATE INDEX idx_branch_id ON lock_table (BRANCH_ID); +CREATE INDEX idx_lock_table_xid ON lock_table (XID); +CREATE INDEX idx_status ON lock_table (STATUS); + +CREATE TABLE distributed_lock ( + LOCK_KEY VARCHAR2(20) NOT NULL, + LOCK_VALUE VARCHAR2(20) NOT NULL, + EXPIRE DECIMAL(18) NOT NULL, + PRIMARY KEY (LOCK_KEY) +); + +INSERT INTO distributed_lock (LOCK_KEY, LOCK_VALUE, EXPIRE) VALUES ('AsyncCommitting', ' ', 0); +INSERT INTO distributed_lock (LOCK_KEY, LOCK_VALUE, EXPIRE) VALUES ('RetryCommitting', ' ', 0); +INSERT INTO distributed_lock (LOCK_KEY, LOCK_VALUE, EXPIRE) VALUES ('RetryRollbacking', ' ', 0); +INSERT INTO distributed_lock (LOCK_KEY, LOCK_VALUE, EXPIRE) VALUES ('TxTimeoutCheck', ' ', 0); +CREATE TABLE VGROUP_TABLE +( + VGROUP VARCHAR2(255) PRIMARY KEY, + NAMESPACE VARCHAR2(255), + CLUSTER VARCHAR2(255) +); \ No newline at end of file diff --git a/sqlparser/seata-sqlparser-core/src/main/java/org/apache/seata/sqlparser/util/JdbcConstants.java b/sqlparser/seata-sqlparser-core/src/main/java/org/apache/seata/sqlparser/util/JdbcConstants.java index d923f8918f7..e5d50257318 100644 --- a/sqlparser/seata-sqlparser-core/src/main/java/org/apache/seata/sqlparser/util/JdbcConstants.java +++ b/sqlparser/seata-sqlparser-core/src/main/java/org/apache/seata/sqlparser/util/JdbcConstants.java @@ -84,4 +84,6 @@ public interface JdbcConstants { String POLARDB = "polardb"; String POLARDBX = "polardb-x"; + + String OSCAR = "oscar"; } diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/BaseOscarRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/BaseOscarRecognizer.java new file mode 100644 index 00000000000..a0a3b505354 --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/BaseOscarRecognizer.java @@ -0,0 +1,196 @@ +/* + * 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.oscar; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.SQLMergeStatement; +import com.alibaba.druid.sql.ast.statement.SQLReplaceStatement; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectJoin; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectSubqueryTableSource; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.druid.BaseRecognizer; +import org.apache.seata.sqlparser.struct.Null; +import org.apache.seata.sqlparser.util.JdbcConstants; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The type Base oscar recognizer. + */ +public abstract class BaseOscarRecognizer extends BaseRecognizer { + + /** + * Instantiates a new oscar base recognizer + * + * @param originalSql the original sql + */ + public BaseOscarRecognizer(String originalSql) { + super(originalSql); + } + + public OracleOutputVisitor createOutputVisitor(final ParametersHolder parametersHolder, + final ArrayList> paramAppenderList, + final StringBuilder sb) { + + return new OracleOutputVisitor(sb) { + @Override + public boolean visit(SQLVariantRefExpr x) { + if ("?".equals(x.getName())) { + ArrayList oneParamValues = parametersHolder.getParameters().get(x.getIndex() + 1); + if (paramAppenderList.isEmpty()) { + oneParamValues.forEach(t -> paramAppenderList.add(new ArrayList<>())); + } + for (int i = 0; i < oneParamValues.size(); i++) { + Object o = oneParamValues.get(i); + paramAppenderList.get(i).add(o instanceof Null ? null : o); + } + } + return super.visit(x); + } + }; + } + + public String getWhereCondition(SQLExpr where, final ParametersHolder parametersHolder, final ArrayList> paramAppenderList) { + if (Objects.isNull(where)) { + return StringUtils.EMPTY; + } + + StringBuilder sb = new StringBuilder(); + executeVisit(where, createOutputVisitor(parametersHolder, paramAppenderList, sb)); + return sb.toString(); + } + + public String getWhereCondition(SQLExpr where) { + if (Objects.isNull(where)) { + return StringUtils.EMPTY; + } + + StringBuilder sb = new StringBuilder(); + executeVisit(where, new OracleOutputVisitor(sb)); + return sb.toString(); + } + + protected String getOrderByCondition(SQLOrderBy sqlOrderBy) { + if (Objects.isNull(sqlOrderBy)) { + return StringUtils.EMPTY; + } + + StringBuilder sb = new StringBuilder(); + executeOrderBy(sqlOrderBy, new OracleOutputVisitor(sb)); + + return sb.toString(); + } + + protected String getOrderByCondition(SQLOrderBy sqlOrderBy, final ParametersHolder parametersHolder, + final ArrayList> paramAppenderList) { + if (Objects.isNull(sqlOrderBy)) { + return StringUtils.EMPTY; + } + + StringBuilder sb = new StringBuilder(); + executeOrderBy(sqlOrderBy, createOutputVisitor(parametersHolder, paramAppenderList, sb)); + return sb.toString(); + } + + @Override + public boolean isSqlSyntaxSupports() { + OracleASTVisitor visitor = new OracleASTVisitorAdapter() { + @Override + public boolean visit(OracleSelectJoin x) { + //just like: UPDATE table a INNER JOIN table b ON a.id = b.pid ... + throw new NotSupportYetException("not support the sql syntax with join table:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + + @Override + public boolean visit(OracleUpdateStatement x) { + if (x.getTableSource() instanceof OracleSelectSubqueryTableSource) { + //just like: "update (select a.id,a.name from a inner join b on a.id = b.id) t set t.name = 'xxx'" + throw new NotSupportYetException("not support the sql syntax with join table:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + List updateSetItems = x.getItems(); + for (SQLUpdateSetItem updateSetItem : updateSetItems) { + if (updateSetItem.getValue() instanceof SQLQueryExpr) { + //just like: "update a set a.id = (select id from b where a.pid = b.pid)" + throw new NotSupportYetException("not support the sql syntax with join table:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + } + return true; + } + + @Override + public boolean visit(SQLInSubQueryExpr x) { + //just like: ...where id in (select id from t) + throw new NotSupportYetException("not support the sql syntax with InSubQuery:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + + @Override + public boolean visit(OracleSelectSubqueryTableSource x) { + //just like: select * from (select * from t) for update + throw new NotSupportYetException("not support the sql syntax with SubQuery:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + + @Override + public boolean visit(SQLReplaceStatement x) { + //just like: replace into t (id,dr) values (1,'2'), (2,'3') + throw new NotSupportYetException("not support the sql syntax with ReplaceStatement:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + + @Override + public boolean visit(SQLMergeStatement x) { + //just like: merge into ... WHEN MATCHED THEN ... + throw new NotSupportYetException("not support the sql syntax with MergeStatement:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + + @Override + public boolean visit(SQLInsertStatement x) { + if (null != x.getQuery()) { + //just like: insert into t select * from t1 + throw new NotSupportYetException("not support the sql syntax insert with query:" + x + + "\nplease see the doc about SQL restrictions https://seata.io/zh-cn/docs/user/sqlreference/dml.html"); + } + return true; + } + }; + getAst().accept(visitor); + return true; + } + + public String getDbType() { + return JdbcConstants.OSCAR; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarDeleteRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarDeleteRecognizer.java new file mode 100644 index 00000000000..fd9213a5349 --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarDeleteRecognizer.java @@ -0,0 +1,137 @@ +/* + * 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.oscar; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLDeleteRecognizer; +import org.apache.seata.sqlparser.SQLType; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type oscar delete recognizer. + * + */ +public class OscarDeleteRecognizer extends BaseOscarRecognizer implements SQLDeleteRecognizer { + + private final SQLDeleteStatement ast; + + /** + * Instantiates a new Oscar sql delete recognizer. + * + * @param originalSQL the original sql + * @param ast the ast + */ + public OscarDeleteRecognizer(String originalSQL, SQLStatement ast) { + super(originalSQL); + this.ast = (SQLDeleteStatement)ast; + } + + @Override + public SQLType getSQLType() { + return SQLType.DELETE; + } + + @Override + public String getTableAlias() { + return ast.getTableSource().getAlias(); + } + + @Override + public String getTableName() { + StringBuilder sb = new StringBuilder(); + OracleOutputVisitor visitor = new OracleOutputVisitor(sb) { + + @Override + public boolean visit(SQLExprTableSource x) { + printTableSourceExpr(x.getExpr()); + return false; + } + + @Override + public boolean visit(SQLJoinTableSource x) { + throw new NotSupportYetException("not support the syntax of delete with join table"); + } + }; + SQLTableSource tableSource; + if (ast.getFrom() == null) { + tableSource = ast.getTableSource(); + } else { + tableSource = ast.getFrom(); + } + + if (tableSource instanceof SQLExprTableSource) { + visitor.visit((SQLExprTableSource) tableSource); + } else if (tableSource instanceof SQLJoinTableSource) { + visitor.visit((SQLJoinTableSource) tableSource); + } else { + throw new NotSupportYetException("not support the syntax of delete with unknow"); + } + return sb.toString(); + } + + @Override + public String getWhereCondition(final ParametersHolder parametersHolder, + final ArrayList> paramAppenderList) { + SQLExpr where = ast.getWhere(); + return super.getWhereCondition(where, parametersHolder, paramAppenderList); + } + + @Override + public String getWhereCondition() { + SQLExpr where = ast.getWhere(); + return super.getWhereCondition(where); + } + + @Override + public String getLimitCondition() { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getLimitCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getOrderByCondition() { + //oscar does not support order by yet + return null; + } + + @Override + public String getOrderByCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + //oscar does not support order by yet + return null; + } + + @Override + protected SQLStatement getAst() { + return ast; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarInsertRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarInsertRecognizer.java new file mode 100644 index 00000000000..9e731d95529 --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarInsertRecognizer.java @@ -0,0 +1,164 @@ +/* + * 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.oscar; + +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.visitor.OracleOutputVisitor; +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.List; + +/** + * The type oscar insert recognizer. + * + */ +public class OscarInsertRecognizer extends BaseOscarRecognizer implements SQLInsertRecognizer { + + private final SQLInsertStatement ast; + + /** + * Instantiates a new Oscar sql insert recognizer. + * + * @param originalSQL the original sql + * @param ast the ast + */ + public OscarInsertRecognizer(String originalSQL, SQLStatement ast) { + super(originalSQL); + this.ast = (SQLInsertStatement)ast; + } + + @Override + public SQLType getSQLType() { + return SQLType.INSERT; + } + + @Override + public String getTableAlias() { + return ast.getTableSource().getAlias(); + } + + @Override + public String getTableName() { + StringBuilder sb = new StringBuilder(); + OracleOutputVisitor visitor = new OracleOutputVisitor(sb) { + + @Override + public boolean visit(SQLExprTableSource x) { + printTableSourceExpr(x.getExpr()); + return false; + } + }; + visitor.visit(ast.getTableSource()); + return sb.toString(); + } + + @Override + public boolean insertColumnsIsEmpty() { + return CollectionUtils.isEmpty(ast.getColumns()); + } + + @Override + public List getInsertColumns() { + List columnSQLExprs = ast.getColumns(); + if (columnSQLExprs.isEmpty()) { + // INSERT INTO ta VALUES (...), without fields clarified + return null; + } + List list = new ArrayList<>(columnSQLExprs.size()); + for (SQLExpr expr : columnSQLExprs) { + if (expr instanceof SQLIdentifierExpr) { + list.add(((SQLIdentifierExpr)expr).getName()); + } else { + wrapSQLParsingException(expr); + } + } + return list; + } + + @Override + public List> getInsertRows(Collection primaryKeyIndex) { + List valuesClauses = ast.getValuesList(); + List> rows = new ArrayList<>(valuesClauses.size()); + for (SQLInsertStatement.ValuesClause valuesClause : valuesClauses) { + List exprs = valuesClause.getValues(); + List row = new ArrayList<>(exprs.size()); + rows.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 rows; + } + + @Override + public List getInsertParamsValue() { + return null; + } + + @Override + public List getDuplicateKeyUpdate() { + return null; + } + + @Override + public List getInsertColumnsUnEscape() { + List insertColumns = getInsertColumns(); + return ColumnUtils.delEscape(insertColumns, getDbType()); + } + + @Override + protected SQLStatement getAst() { + return ast; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarOperateRecognizerHolder.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarOperateRecognizerHolder.java new file mode 100644 index 00000000000..4cffed03774 --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarOperateRecognizerHolder.java @@ -0,0 +1,55 @@ +/* + * 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.oscar; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.sqlparser.SQLRecognizer; +import org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder; +import org.apache.seata.sqlparser.util.JdbcConstants; + +/** + * The Type OscarOperateRecognizerHolder + * + */ +@LoadLevel(name = JdbcConstants.OSCAR) +public class OscarOperateRecognizerHolder implements SQLOperateRecognizerHolder { + + @Override + public SQLRecognizer getDeleteRecognizer(String sql, SQLStatement ast) { + return new OscarDeleteRecognizer(sql, ast); + } + + @Override + public SQLRecognizer getInsertRecognizer(String sql, SQLStatement ast) { + return new OscarInsertRecognizer(sql, ast); + } + + @Override + public SQLRecognizer getUpdateRecognizer(String sql, SQLStatement ast) { + return new OscarUpdateRecognizer(sql, ast); + } + + @Override + public SQLRecognizer getSelectForUpdateRecognizer(String sql, SQLStatement ast) { + if (((SQLSelectStatement) ast).getSelect().getFirstQueryBlock().isForUpdate()) { + return new OscarSelectForUpdateRecognizer(sql, ast); + } + return null; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarSelectForUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarSelectForUpdateRecognizer.java new file mode 100644 index 00000000000..6e30f25f9b4 --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarSelectForUpdateRecognizer.java @@ -0,0 +1,139 @@ +/* + * 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.oscar; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLParsingException; +import org.apache.seata.sqlparser.SQLSelectRecognizer; +import org.apache.seata.sqlparser.SQLType; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type oscar select for update recognizer. + * + */ +public class OscarSelectForUpdateRecognizer extends BaseOscarRecognizer implements SQLSelectRecognizer { + + private final SQLSelectStatement ast; + + /** + * Instantiates a new Oscar sql select for update recognizer. + * + * @param originalSQL the original sql + * @param ast the ast + */ + public OscarSelectForUpdateRecognizer(String originalSQL, SQLStatement ast) { + super(originalSQL); + this.ast = (SQLSelectStatement)ast; + } + + @Override + public SQLType getSQLType() { + return SQLType.SELECT_FOR_UPDATE; + } + + @Override + public String getWhereCondition(final ParametersHolder parametersHolder, + final ArrayList> paramAppenderList) { + SQLSelectQueryBlock selectQueryBlock = getSelect(); + SQLExpr where = selectQueryBlock.getWhere(); + return super.getWhereCondition(where, parametersHolder, paramAppenderList); + } + + @Override + public String getWhereCondition() { + SQLSelectQueryBlock selectQueryBlock = getSelect(); + SQLExpr where = selectQueryBlock.getWhere(); + return super.getWhereCondition(where); + } + + @Override + public String getLimitCondition() { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getLimitCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getOrderByCondition() { + SQLOrderBy sqlOrderBy = getSelect().getOrderBy(); + return super.getOrderByCondition(sqlOrderBy); + } + + @Override + public String getOrderByCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + SQLOrderBy sqlOrderBy = getSelect().getOrderBy(); + return super.getOrderByCondition(sqlOrderBy, parametersHolder, paramAppenderList); + } + + private SQLSelectQueryBlock getSelect() { + SQLSelect select = ast.getSelect(); + if (select == null) { + throw new SQLParsingException("should never happen!"); + } + SQLSelectQueryBlock selectQueryBlock = select.getQueryBlock(); + if (selectQueryBlock == null) { + throw new SQLParsingException("should never happen!"); + } + return selectQueryBlock; + } + + @Override + public String getTableAlias() { + SQLSelectQueryBlock selectQueryBlock = getSelect(); + SQLTableSource tableSource = selectQueryBlock.getFrom(); + return tableSource.getAlias(); + } + + @Override + public String getTableName() { + SQLSelectQueryBlock selectQueryBlock = getSelect(); + SQLTableSource tableSource = selectQueryBlock.getFrom(); + StringBuilder sb = new StringBuilder(); + OracleOutputVisitor visitor = new OracleOutputVisitor(sb) { + + @Override + public boolean visit(SQLExprTableSource x) { + printTableSourceExpr(x.getExpr()); + return false; + } + }; + visitor.visit((SQLExprTableSource)tableSource); + return sb.toString(); + } + + @Override + protected SQLStatement getAst() { + return ast; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarUpdateRecognizer.java new file mode 100644 index 00000000000..2f22469238f --- /dev/null +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oscar/OscarUpdateRecognizer.java @@ -0,0 +1,184 @@ +/* + * 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.oscar; + +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.SQLPropertyExpr; +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.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.sqlparser.ParametersHolder; +import org.apache.seata.sqlparser.SQLType; +import org.apache.seata.sqlparser.SQLUpdateRecognizer; +import org.apache.seata.sqlparser.util.ColumnUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type oscar update recognizer. + * + */ +public class OscarUpdateRecognizer extends BaseOscarRecognizer implements SQLUpdateRecognizer { + + private SQLUpdateStatement ast; + + /** + * Instantiates a new Oscar sql update recognizer. + * + * @param originalSQL the original sql + * @param ast the ast + */ + public OscarUpdateRecognizer(String originalSQL, SQLStatement ast) { + super(originalSQL); + this.ast = (SQLUpdateStatement) ast; + } + + @Override + public SQLType getSQLType() { + return SQLType.UPDATE; + } + + @Override + public List getUpdateColumns() { + List updateSetItems = ast.getItems(); + List list = new ArrayList<>(updateSetItems.size()); + for (SQLUpdateSetItem updateSetItem : updateSetItems) { + SQLExpr expr = updateSetItem.getColumn(); + if (expr instanceof SQLIdentifierExpr) { + list.add(((SQLIdentifierExpr)expr).getName()); + } else if (expr instanceof SQLPropertyExpr) { + // This is alias case, like UPDATE xxx_tbl a SET a.name = ? WHERE a.id = ? + SQLExpr owner = ((SQLPropertyExpr)expr).getOwner(); + if (owner instanceof SQLIdentifierExpr) { + list.add(((SQLIdentifierExpr)owner).getName() + "." + ((SQLPropertyExpr)expr).getName()); + //This is table Field Full path, like update xxx_database.xxx_tbl set xxx_database.xxx_tbl.xxx_field... + } else if (((SQLPropertyExpr) expr).getOwnerName().split("\\.").length > 1) { + list.add(((SQLPropertyExpr)expr).getOwnerName() + "." + ((SQLPropertyExpr)expr).getName()); + } + } else { + wrapSQLParsingException(expr); + } + } + return list; + } + + @Override + public List getUpdateValues() { + List updateSetItems = ast.getItems(); + List list = new ArrayList<>(updateSetItems.size()); + for (SQLUpdateSetItem updateSetItem : updateSetItems) { + SQLExpr expr = updateSetItem.getValue(); + if (expr instanceof SQLValuableExpr) { + list.add(((SQLValuableExpr)expr).getValue()); + } else if (expr instanceof SQLVariantRefExpr) { + list.add(new VMarker()); + } else { + wrapSQLParsingException(expr); + } + } + return list; + } + + @Override + public List getUpdateColumnsUnEscape() { + List updateColumns = getUpdateColumns(); + return ColumnUtils.delEscape(updateColumns, getDbType()); + } + + @Override + public String getWhereCondition(final ParametersHolder parametersHolder, + final ArrayList> paramAppenderList) { + SQLExpr where = ast.getWhere(); + return super.getWhereCondition(where, parametersHolder, paramAppenderList); + } + + @Override + public String getWhereCondition() { + SQLExpr where = ast.getWhere(); + return super.getWhereCondition(where); + } + + @Override + public String getLimitCondition() { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getLimitCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + //oscar does not support limit or rownum yet + return null; + } + + @Override + public String getOrderByCondition() { + //oscar does not support order by yet + return null; + } + + @Override + public String getOrderByCondition(ParametersHolder parametersHolder, ArrayList> paramAppenderList) { + //oscar does not support order by yet + return null; + } + + @Override + public String getTableAlias() { + return ast.getTableSource().getAlias(); + } + + @Override + public String getTableName() { + StringBuilder sb = new StringBuilder(); + OracleOutputVisitor visitor = new OracleOutputVisitor(sb) { + + @Override + public boolean visit(SQLExprTableSource x) { + printTableSourceExpr(x.getExpr()); + return false; + } + + @Override + public boolean visit(SQLJoinTableSource x) { + throw new NotSupportYetException("not support the syntax of update with join table"); + } + }; + SQLTableSource tableSource = ast.getTableSource(); + if (tableSource instanceof SQLExprTableSource) { + visitor.visit((SQLExprTableSource) tableSource); + } else if (tableSource instanceof SQLJoinTableSource) { + visitor.visit((SQLJoinTableSource) tableSource); + } else { + throw new NotSupportYetException("not support the syntax of update with unknow"); + } + return sb.toString(); + } + + @Override + protected SQLStatement getAst() { + return ast; + } +} diff --git a/sqlparser/seata-sqlparser-druid/src/main/resources/META-INF/services/org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder b/sqlparser/seata-sqlparser-druid/src/main/resources/META-INF/services/org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder index a30fcf51bf7..4d33c59879f 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/resources/META-INF/services/org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder +++ b/sqlparser/seata-sqlparser-druid/src/main/resources/META-INF/services/org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder @@ -21,4 +21,5 @@ org.apache.seata.sqlparser.druid.postgresql.PostgresqlOperateRecognizerHolder org.apache.seata.sqlparser.druid.sqlserver.SqlServerOperateRecognizerHolder org.apache.seata.sqlparser.druid.polardbx.PolarDBXOperateRecognizerHolder org.apache.seata.sqlparser.druid.dm.DmOperateRecognizerHolder -org.apache.seata.sqlparser.druid.kingbase.KingbaseOperateRecognizerHolder \ No newline at end of file +org.apache.seata.sqlparser.druid.oscar.OscarOperateRecognizerHolder +org.apache.seata.sqlparser.druid.kingbase.KingbaseOperateRecognizerHolder