diff --git a/all/pom.xml b/all/pom.xml index 8a8bf0295a4..ea4861581c8 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -586,6 +586,11 @@ postgresql provided + + com.dameng + DmJdbcDriver18 + provided + com.fasterxml.jackson.core jackson-databind diff --git a/common/src/main/java/io/seata/common/util/PageUtil.java b/common/src/main/java/io/seata/common/util/PageUtil.java index 831e2ee15b8..2583f91070f 100644 --- a/common/src/main/java/io/seata/common/util/PageUtil.java +++ b/common/src/main/java/io/seata/common/util/PageUtil.java @@ -105,6 +105,7 @@ public static String pageSql(String sourceSql, String dbType, int pageNum, int p case "h2": case "postgresql": case "oceanbase": + case "dm": 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)); @@ -130,6 +131,7 @@ public static String countSql(String sourceSql, String dbType) { case "h2": case "oceanbase": case "oracle": + case "dm": return sourceSql.replaceAll("(?i)(?<=select)(.*)(?=from)", " count(1) "); case "postgresql": int lastIndexOfOrderBy = sourceSql.toLowerCase().lastIndexOf("order by"); diff --git a/common/src/test/java/io/seata/common/util/PageUtilTest.java b/common/src/test/java/io/seata/common/util/PageUtilTest.java index 9d544e43185..713f95d2596 100644 --- a/common/src/test/java/io/seata/common/util/PageUtilTest.java +++ b/common/src/test/java/io/seata/common/util/PageUtilTest.java @@ -42,6 +42,7 @@ public void testPageSql() { assertEquals(PageUtil.pageSql(sourceSql, "h2", 1, 5), mysqlTargetSql); 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, "oracle", 1, 5), oracleTargetSql); assertThrows(NotSupportYetException.class, () -> PageUtil.pageSql(sourceSql, "xxx", 1, 5)); @@ -57,6 +58,7 @@ void testCountSql() { assertEquals(PageUtil.countSql(sourceSql, "h2"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "postgresql"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "oceanbase"), targetSql); + assertEquals(PageUtil.countSql(sourceSql, "dm"), targetSql); assertEquals(PageUtil.countSql(sourceSql, "oracle"), targetSql); assertThrows(NotSupportYetException.class, () -> PageUtil.countSql(sourceSql, "xxx")); diff --git a/core/src/main/java/io/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java b/core/src/main/java/io/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java index c0849f0b276..97913c3679e 100644 --- a/core/src/main/java/io/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java +++ b/core/src/main/java/io/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactory.java @@ -24,7 +24,7 @@ public class DistributedLockSqlFactory { /** * get the lock store sql * - * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase, it's useless now, but maybe useful later + * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm, it's useless now, but maybe useful later * @return lock store sql */ public static DistributedLockSql getDistributedLogStoreSql(String dbType) { diff --git a/core/src/main/java/io/seata/core/store/db/sql/lock/DmLockStoreSql.java b/core/src/main/java/io/seata/core/store/db/sql/lock/DmLockStoreSql.java new file mode 100644 index 00000000000..9fba8d0e1bd --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/sql/lock/DmLockStoreSql.java @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.core.store.db.sql.lock; + +import io.seata.common.loader.LoadLevel; + +/** + * the database lock store DaMeng sql + * + * @author wang.liang + * @since 1.8.0 + */ +@LoadLevel(name = "dm") +public class DmLockStoreSql extends MysqlLockStoreSql { +} diff --git a/core/src/main/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactory.java b/core/src/main/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactory.java index 4cc711b3c00..b13bac3a093 100644 --- a/core/src/main/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactory.java +++ b/core/src/main/java/io/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 + * @param dbType the dbType, support mysql/oracle/h2/postgre/oceanbase/dm * @return lock store sql */ public static LockStoreSql getLogStoreSql(String dbType) { diff --git a/core/src/main/java/io/seata/core/store/db/sql/log/DmLogStoreSqls.java b/core/src/main/java/io/seata/core/store/db/sql/log/DmLogStoreSqls.java new file mode 100644 index 00000000000..f824851844b --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/sql/log/DmLogStoreSqls.java @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.core.store.db.sql.log; + +import io.seata.common.loader.LoadLevel; + +/** + * Database log store DaMeng sql + * + * @author wang.liang + * @since 1.8.0 + */ +@LoadLevel(name = "dm") +public class DmLogStoreSqls extends MysqlLogStoreSqls { +} diff --git a/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.lock.LockStoreSql b/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.lock.LockStoreSql index 5eaa1b103dc..13c73b1d369 100644 --- a/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.lock.LockStoreSql +++ b/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.lock.LockStoreSql @@ -3,4 +3,5 @@ io.seata.core.store.db.sql.lock.MariadbLockStoreSql io.seata.core.store.db.sql.lock.OracleLockStoreSql io.seata.core.store.db.sql.lock.OceanbaseLockStoreSql io.seata.core.store.db.sql.lock.PostgresqlLockStoreSql -io.seata.core.store.db.sql.lock.H2LockStoreSql \ No newline at end of file +io.seata.core.store.db.sql.lock.H2LockStoreSql +io.seata.core.store.db.sql.lock.DmLockStoreSql \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.log.LogStoreSqls b/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.log.LogStoreSqls index b60c0b876d0..e61aeca2604 100644 --- a/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.log.LogStoreSqls +++ b/core/src/main/resources/META-INF/services/io.seata.core.store.db.sql.log.LogStoreSqls @@ -3,4 +3,5 @@ io.seata.core.store.db.sql.log.MariadbLogStoreSqls io.seata.core.store.db.sql.log.OracleLogStoreSqls io.seata.core.store.db.sql.log.PostgresqlLogStoreSqls io.seata.core.store.db.sql.log.OceanbaseLogStoreSqls -io.seata.core.store.db.sql.log.H2LogStoreSqls \ No newline at end of file +io.seata.core.store.db.sql.log.H2LogStoreSqls +io.seata.core.store.db.sql.log.DmLogStoreSqls \ No newline at end of file diff --git a/core/src/test/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java b/core/src/test/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java index 3a02b57952b..36d4cbf6ce6 100644 --- a/core/src/test/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java +++ b/core/src/test/java/io/seata/core/store/db/sql/lock/LockStoreSqlFactoryTest.java @@ -38,6 +38,8 @@ public class LockStoreSqlFactoryTest { private static LockStoreSql OCEANBASE_LOCK_STORE = LockStoreSqlFactory.getLogStoreSql("oceanbase"); + private static LockStoreSql DM_LOCK_STORE = LockStoreSqlFactory.getLogStoreSql("dm"); + private static String GLOBAL_TABLE = "global_table"; private static String BRANCH_TABLE = "branch_table"; @@ -331,4 +333,50 @@ public void oceanbaseLockTest() { sql = OCEANBASE_LOCK_STORE.getCheckLockableSql(BRANCH_TABLE, 3); Assertions.assertEquals(EXPECT_CHECK_BRANCH_LOCKABLE_SQL,sql); } + + @Test + public void dmLockTest() { + String sql; + // Get insert lock sql string. + sql = DM_LOCK_STORE.getInsertLockSQL(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = DM_LOCK_STORE.getInsertLockSQL(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get delete lock sql string. + sql = DM_LOCK_STORE.getDeleteLockSql(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = DM_LOCK_STORE.getDeleteLockSql(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get batch delete lock sql string. + sql = DM_LOCK_STORE.getBatchDeleteLockSql(GLOBAL_TABLE, 3); + Assertions.assertEquals(EXPECT_BATCH_GLOBAL_DELETE_LOCK_SQL,sql); + sql = DM_LOCK_STORE.getBatchDeleteLockSql(BRANCH_TABLE, 3); + Assertions.assertEquals(EXPECT_BATCH_BRANCH_DELETE_LOCK_SQL,sql); + + // Get batch delete lock sql string. + sql = DM_LOCK_STORE.getBatchDeleteLockSqlByBranchId(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = DM_LOCK_STORE.getBatchDeleteLockSqlByBranchId(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get batch delete lock sql string. + sql = DM_LOCK_STORE.getBatchDeleteLockSqlByXid(GLOBAL_TABLE); + Assertions.assertEquals(EXPECT_BATCH_GLOBAL_DELETE_LOCK_BY_BRANCHS_SQL,sql); + sql = DM_LOCK_STORE.getBatchDeleteLockSqlByXid(BRANCH_TABLE); + Assertions.assertEquals(EXPECT_BATCH_BRANCH_DELETE_LOCK_BY_BRANCHS_SQL,sql); + + // Get query lock sql string. + sql = DM_LOCK_STORE.getQueryLockSql(GLOBAL_TABLE); + Assertions.assertNotNull(sql); + sql = DM_LOCK_STORE.getQueryLockSql(BRANCH_TABLE); + Assertions.assertNotNull(sql); + + // Get check lock sql string. + sql = DM_LOCK_STORE.getCheckLockableSql(GLOBAL_TABLE, 3); + Assertions.assertEquals(EXPECT_CHECK_GLOBAL_LOCKABLE_SQL,sql); + sql = DM_LOCK_STORE.getCheckLockableSql(BRANCH_TABLE, 3); + Assertions.assertEquals(EXPECT_CHECK_BRANCH_LOCKABLE_SQL,sql); + } } diff --git a/core/src/test/java/io/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java b/core/src/test/java/io/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java index 83e3942eb29..b895f2e59dd 100644 --- a/core/src/test/java/io/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java +++ b/core/src/test/java/io/seata/core/store/db/sql/log/LogStoreSqlsFactoryTest.java @@ -33,6 +33,8 @@ public class LogStoreSqlsFactoryTest { private static LogStoreSqls oceanbase = LogStoreSqlsFactory.getLogStoreSqls("oceanbase"); + private static LogStoreSqls dmLog = LogStoreSqlsFactory.getLogStoreSqls("dm"); + private static String globalTable = "global_table"; private static String branchTable = "branch_table"; @@ -211,4 +213,38 @@ public void oceanbaseLogTest() { sql = oceanbase.getQueryBranchMax(branchTable); Assertions.assertNotNull(sql); } + + @Test + public void dmLogTest() { + String sql = dmLog.getInsertGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getUpdateGlobalTransactionStatusSQL(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getDeleteGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryGlobalTransactionSQL(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryGlobalTransactionSQLByTransactionId(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryGlobalTransactionSQLByStatus(globalTable, "1"); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryGlobalTransactionForRecoverySQL(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getInsertBranchTransactionSQL(branchTable); + Assertions.assertNotNull(sql); + sql = dmLog.getUpdateBranchTransactionStatusSQL(branchTable); + Assertions.assertNotNull(sql); + sql = dmLog.getDeleteBranchTransactionByBranchIdSQL(branchTable); + Assertions.assertNotNull(sql); + sql = dmLog.getDeleteBranchTransactionByXId(branchTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryBranchTransaction(branchTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryBranchTransaction(branchTable, "1"); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryGlobalMax(globalTable); + Assertions.assertNotNull(sql); + sql = dmLog.getQueryBranchMax(branchTable); + Assertions.assertNotNull(sql); + } } diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 80b3977b43d..425c92a3b79 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -98,6 +98,7 @@ 42.3.3 1.4.181 2.7.2 + 8.1.2.192 1.2.7 2.9.0 @@ -215,6 +216,11 @@ mariadb-java-client ${mariadb.version} + + com.dameng + DmJdbcDriver18 + ${dm.version} + com.alipay.sofa sofa-rpc-all diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/DataSourceProxy.java b/rm-datasource/src/main/java/io/seata/rm/datasource/DataSourceProxy.java index 3d513cd692e..555ee692679 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/DataSourceProxy.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/DataSourceProxy.java @@ -159,6 +159,8 @@ private void initResourceId() { initOracleResourceId(); } else if (JdbcConstants.MYSQL.equals(dbType)) { initMysqlResourceId(); + } else if (JdbcConstants.DM.equals(dbType)) { + initDMResourceId(); } else { initDefaultResourceId(); } @@ -244,6 +246,36 @@ private void initPGResourceId() { } } + private void initDMResourceId() { + LOGGER.warn("support for the dameng database is currently an experimental feature "); + if (jdbcUrl.contains("?")) { + StringBuilder jdbcUrlBuilder = new StringBuilder(); + jdbcUrlBuilder.append(jdbcUrl, 0, jdbcUrl.indexOf('?')); + + StringBuilder paramsBuilder = new StringBuilder(); + String paramUrl = jdbcUrl.substring(jdbcUrl.indexOf('?') + 1); + String[] urlParams = paramUrl.split("&"); + for (String urlParam : urlParams) { + if (urlParam.contains("schema")) { + // remove the '"' + if (urlParam.contains("\"")) { + urlParam = urlParam.replaceAll("\"", ""); + } + paramsBuilder.append(urlParam); + break; + } + } + + if (paramsBuilder.length() > 0) { + jdbcUrlBuilder.append("?"); + jdbcUrlBuilder.append(paramsBuilder); + } + resourceId = jdbcUrlBuilder.toString(); + } else { + resourceId = jdbcUrl; + } + } + @Override public BranchType getBranchType() { return BranchType.AT; diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/dm/DmInsertExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/dm/DmInsertExecutor.java new file mode 100644 index 00000000000..7e3d1cac5d3 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/dm/DmInsertExecutor.java @@ -0,0 +1,99 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.exec.dm; + +import io.seata.common.exception.NotSupportYetException; +import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; +import io.seata.rm.datasource.StatementProxy; +import io.seata.rm.datasource.exec.BaseInsertExecutor; +import io.seata.rm.datasource.exec.StatementCallback; +import io.seata.sqlparser.SQLRecognizer; +import io.seata.sqlparser.struct.Null; +import io.seata.sqlparser.struct.Sequenceable; +import io.seata.sqlparser.struct.SqlMethodExpr; +import io.seata.sqlparser.struct.SqlSequenceExpr; +import io.seata.sqlparser.util.JdbcConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * The type DM insert executor. + * + * @author chengxiaoxiao + */ +@LoadLevel(name = JdbcConstants.DM, scope = Scope.PROTOTYPE) +public class DmInsertExecutor extends BaseInsertExecutor implements Sequenceable { + + private static final Logger LOGGER = LoggerFactory.getLogger(DmInsertExecutor.class); + + /** + * Instantiates a new Abstract dml base executor. + * + * @param statementProxy the statement proxy + * @param statementCallback the statement callback + * @param sqlRecognizer the sql recognizer + */ + public DmInsertExecutor(StatementProxy statementProxy, StatementCallback statementCallback, + SQLRecognizer sqlRecognizer) { + super(statementProxy, statementCallback, sqlRecognizer); + } + + @Override + public Map> getPkValues() throws SQLException { + Map> pkValuesMap = null; + Boolean isContainsPk = containsPK(); + //when there is only one pk in the table + if (isContainsPk) { + pkValuesMap = getPkValuesByColumn(); + } + else if (containsColumns()) { + String columnName = getTableMeta().getPrimaryKeyOnlyName().get(0); + pkValuesMap = Collections.singletonMap(columnName, getGeneratedKeys()); + } + else { + pkValuesMap = getPkValuesByColumn(); + } + return pkValuesMap; + } + + @Override + public Map> getPkValuesByColumn() throws SQLException { + Map> pkValuesMap = parsePkValuesFromStatement(); + String pkKey = pkValuesMap.keySet().iterator().next(); + List pkValues = pkValuesMap.get(pkKey); + + if (!pkValues.isEmpty() && pkValues.get(0) instanceof SqlSequenceExpr) { + pkValuesMap.put(pkKey,getPkValuesBySequence((SqlSequenceExpr) pkValues.get(0))); + } else if (pkValues.size() == 1 && pkValues.get(0) instanceof SqlMethodExpr) { + pkValuesMap.put(pkKey,getGeneratedKeys()); + } else if (pkValues.size() == 1 && pkValues.get(0) instanceof Null) { + throw new NotSupportYetException("dm not support null"); + } + + return pkValuesMap; + } + + @Override + public String getSequenceSql(SqlSequenceExpr expr) { + return "SELECT " + expr.getSequence() + ".currval"; + } +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/handler/dm/DmEscapeHandler.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/handler/dm/DmEscapeHandler.java new file mode 100644 index 00000000000..74a9a62577d --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/handler/dm/DmEscapeHandler.java @@ -0,0 +1,828 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.handler.dm; + +import io.seata.common.loader.LoadLevel; +import io.seata.sqlparser.EscapeHandler; +import io.seata.sqlparser.struct.TableMeta; +import io.seata.sqlparser.util.JdbcConstants; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * The type dm sql keyword checker. + * + * @author chengxiaoxiao + */ +@LoadLevel(name = JdbcConstants.DM) +public class DmEscapeHandler implements EscapeHandler { + + private Set keywordSet = Arrays.stream(DmKeyword.values()).map(DmKeyword::name).collect(Collectors.toSet()); + + /** + * dm keyword + */ + private enum DmKeyword { + + /** + * ABORT + */ + ABORT("ABORT"), + ABSOLUTE("ABSOLUTE"), + ABSTRACT("ABSTRACT"), + ACCESSED("ACCESSED"), + ACCOUNT("ACCOUNT"), + ACROSS("ACROSS"), + ACTION("ACTION"), + ADD("ADD"), + + ADMIN("ADMIN"), + AFTER("AFTER"), + AGGREGATE("AGGREGATE"), + ALL("ALL"), + ALLOW_DATETIME("ALLOW_DATETIME"), + ALLOW_IP("ALLOW_IP"), + ALTER("ALTER"), + ANALYZE("ANALYZE"), + + AND("AND"), + ANY("ANY"), + ARCHIVE("ARCHIVE"), + ARCHIVEDIR("ARCHIVEDIR"), + ARCHIVELOG("ARCHIVELOG"), + ARCHIVESTYLE("ARCHIVESTYLE"), + ARRAY("ARRAY"), + + ARRAYLEN("ARRAYLEN"), + AS("AS"), + ASC("ASC"), + ASENSITIVE("ASENSITIVE"), + ASSIGN("ASSIGN"), + ASYNCHRONOUS("ASYNCHRONOUS"), + AT("AT"), + ATTACH("ATTACH"), + + AUDIT("AUDIT"), + AUTHID("AUTHID"), + AUTHORIZATION("AUTHORIZATION"), + AUTO("AUTO"), + AUTOEXTEND("AUTOEXTEND"), + + AUTONOMOUS_TRANSACTION("AUTONOMOUS_TRANSACTION"), + AVG("AVG"), + + BACKED("BACKED"), + BACKUP("BACKUP"), + BACKUPDIR("BACKUPDIR"), + BACKUPINFO("BACKUPINFO"), + BACKSET("BACKSET"), + BADFILE("BADFILE"), + BAKFILE("BAKFILE"), + + BASE("BASE"), + BEFORE("BEFORE"), + BEGIN("BEGIN"), + BETWEEN("BETWEEN"), + BIGDATEDIFF("BIGDATEDIFF"), + BIGINT("BIGINT"), + BINARY("BINARY"), + BIT("BIT"), + BITMAP("BITMAP"), + + BLOB("BLOB"), + BLOCK("BLOCK"), + BOOL("BOOL"), + BOOLEAN("BOOLEAN"), + BOTH("BOTH"), + BRANCH("BRANCH"), + BREAK("BREAK"), + BSTRING("BSTRING"), + BTREE("BTREE"), + + BUFFER("BUFFER"), + BUILD("BUILD"), + BULK("BULK"), + BY("BY"), + BYTE("BYTE"), + C("C"), + CACHE("CACHE"), + CALCULATE("CALCULATE"), + CALL("CALL"), + CASCADE("CASCADE"), + CASCADED("CASCADED"), + CASE("CASE"), + CAST("CAST"), + CATALOG("CATALOG"), + + CATCH("CATCH"), + CHAIN("CHAIN"), + CHAR("CHAR"), + CHARACTER("CHARACTER"), + CHARACTERISTICS("CHARACTERISTICS"), + CHECK("CHECK"), + CIPHER("CIPHER"), + CLASS("CLASS"), + + CLOB("CLOB"), + CLOSE("CLOSE"), + CLUSTER("CLUSTER"), + CLUSTERBTR("CLUSTERBTR"), + COLLATE("COLLATE"), + COLLATION("COLLATION"), + COLLECT("COLLECT"), + COLUMN("COLUMN"), + + COLUMNS("COLUMNS"), + COMMENT("COMMENT"), + COMMIT("COMMIT"), + COMMITTED("COMMITTED"), + COMMITWORK("COMMITWORK"), + COMPILE("COMPILE"), + COMPLETE("COMPLETE"), + + COMPRESS("COMPRESS"), + COMPRESSED("COMPRESSED"), + CONNECT("CONNECT"), + CONNECT_BY_IS_CYCLE("CONNECT_BY_IS_CYCLE"), + + CONNECT_BY_ISLEAF("CONNECT_BY_ISLEAF"), + CONNECT_BY_ROOT("CONNECT_BY_ROOT"), + CONNECT_IDLE_TIME("CONNECT_IDLE_TIME"), + CONNECT_TIME("CONNECT_TIME"), + + CONST("CONST"), + CONSTANT("CONSTANT"), + CONSTRAINT("CONSTRAINT"), + CONSTRAINTS("CONSTRAINTS"), + CONSTRUCTOR("CONSTRUCTOR"), + CONTAINS("CONTAINS"), + + CONTEXT("CONTEXT"), + CONTINUE("CONTINUE"), + CONVERT("CONVERT"), + COPY("COPY"), + CORRESPONDING("CORRESPONDING"), + COUNT("COUNT"), + COUNTER("COUNTER"), + + CPU_PER_CALL("CPU_PER_CALL"), + CPU_PER_SESSION("CPU_PER_SESSION"), + CREATE("CREATE"), + CROSS("CROSS"), + CRYPTO("CRYPTO"), + CTLFILE("CTLFILE"), + CUBE("CUBE"), + + CUMULATIVE("CUMULATIVE"), + CURRENT("CURRENT"), + CURRENT_SCHEMA("CURRENT_SCHEMA"), + CURRENT_USER("CURRENT_USER"), + CURSOR("CURSOR"), + CYCLE("CYCLE"), + D("D"), + DANGLING("DANGLING"), + DATA("DATA"), + DATABASE("DATABASE"), + DATAFILE("DATAFILE"), + DATE("DATE"), + DATEADD("DATEADD"), + DATEDIFF("DATEDIFF"), + + DATEPART("DATEPART"), + DATETIME("DATETIME"), + DAY("DAY"), + DBFILE("DBFILE"), + DDL("DDL"), + DDL_CLONE("DDL_CLONE"), + DEBUG("DEBUG"), + DEC("DEC"), + DECIMAL("DECIMAL"), + + DECLARE("DECLARE"), + DECODE("DECODE"), + DEFAULT("DEFAULT"), + DEFERRABLE("DEFERRABLE"), + DEFERRED("DEFERRED"), + DEFINER("DEFINER"), + DELETE("DELETE"), + + DELETING("DELETING"), + DELIMITED("DELIMITED"), + DELTA("DELTA"), + DEMAND("DEMAND"), + DENSE_RANK("DENSE_RANK"), + DEREF("DEREF"), + DESC("DESC"), + DETACH("DETACH"), + + DETERMINISTIC("DETERMINISTIC"), + DEVICE("DEVICE"), + DIAGNOSTICS("DIAGNOSTICS"), + DICTIONARY("DICTIONARY"), + DISABLE("DISABLE"), + DISCONNECT("DISCONNECT"), + + DISKSPACE("DISKSPACE"), + DISTINCT("DISTINCT"), + DISTRIBUTED("DISTRIBUTED"), + DO("DO"), + DOMAIN("DOMAIN"), + DOUBLE("DOUBLE"), + DOWN("DOWN"), + DROP("DROP"), + + DUMP("DUMP"), + E("E"), + EACH("EACH"), + ELSE("ELSE"), + ELSEIF("ELSEIF"), + ELSIF("ELSIF"), + ENABLE("ENABLE"), + ENCRYPT("ENCRYPT"), + ENCRYPTION("ENCRYPTION"), + END("END"), + + EQU("EQU"), + ERROR("ERROR"), + ERRORS("ERRORS"), + ESCAPE("ESCAPE"), + EVENTINFO("EVENTINFO"), + EVENTS("EVENTS"), + EXCEPT("EXCEPT"), + EXCEPTION("EXCEPTION"), + + EXCEPTIONS("EXCEPTIONS"), + EXCEPTION_INIT("EXCEPTION_INIT"), + EXCHANGE("EXCHANGE"), + EXCLUDE("EXCLUDE"), + EXCLUDING("EXCLUDING"), + EXCLUSIVE("EXCLUSIVE"), + + EXEC("EXEC"), + EXECUTE("EXECUTE"), + EXISTS("EXISTS"), + EXIT("EXIT"), + EXPLAIN("EXPLAIN"), + EXTENDS("EXTENDS"), + EXTERN("EXTERN"), + EXTERNAL("EXTERNAL"), + + EXTERNALLY("EXTERNALLY"), + EXTRACT("EXTRACT"), + F("F"), + FAILED_LOGIN_ATTEMPS("FAILED_LOGIN_ATTEMPS"), + FAST("FAST"), + FETCH("FETCH"), + FIELDS("FIELDS"), + FILE("FILE"), + FILEGROUP("FILEGROUP"), + + FILESIZE("FILESIZE"), + FILLFACTOR("FILLFACTOR"), + FINAL("FINAL"), + FINALLY("FINALLY"), + FIRST("FIRST"), + FLOAT("FLOAT"), + FOLLOWING("FOLLOWING"), + FOR("FOR"), + + FORALL("FORALL"), + FORCE("FORCE"), + FOREIGN("FOREIGN"), + FREQUENCE("FREQUENCE"), + FROM("FROM"), + FULL("FULL"), + FULLY("FULLY"), + FUNCTION("FUNCTION"), + + GET("GET"), + GLOBAL("GLOBAL"), + GLOBALLY("GLOBALLY"), + GOTO("GOTO"), + GRANT("GRANT"), + GROUP("GROUP"), + GROUPING("GROUPING"), + + HASH("HASH"), + HAVING("HAVING"), + HEXTORAW("HEXTORAW"), + HOLD("HOLD"), + HOUR("HOUR"), + HUGE("HUGE"), + + IDENTIFIED("IDENTIFIED"), + IDENTITY("IDENTITY"), + IDENTITY_INSERT("IDENTITY_INSERT"), + IF("IF"), + IMAGE("IMAGE"), + IMMEDIATE("IMMEDIATE"), + + IN("IN"), + INCLUDE("INCLUDE"), + INCLUDING("INCLUDING"), + INCREASE("INCREASE"), + INCREMENT("INCREMENT"), + INDEX("INDEX"), + INDEXES("INDEXES"), + INDICES("INDICES"), + + INITIAL("INITIAL"), + INITIALIZED("INITIALIZED"), + INITIALLY("INITIALLY"), + INLINE("INLINE"), + INNER("INNER"), + INNERID("INNERID"), + INPUT("INPUT"), + + INSENSITIVE("INSENSITIVE"), + INSERT("INSERT"), + INSERTING("INSERTING"), + INSTANTIABLE("INSTANTIABLE"), + INSTEAD("INSTEAD"), + INT("INT"), + INTEGER("INTEGER"), + + INTENT("INTENT"), + INTERNAL("INTERNAL"), + INTERSECT("INTERSECT"), + INTERVAL("INTERVAL"), + INTO("INTO"), + INVISIBLE("INVISIBLE"), + IS("IS"), + ISOLATION("ISOLATION"), + + JAVA("JAVA"), + JOB("JOB"), + JOIN("JOIN"), + + KEEP("KEEP"), + KEY("KEY"), + + LABEL("LABEL"), + LARGE("LARGE"), + LAST("LAST"), + LEADING("LEADING"), + LEFT("LEFT"), + LESS("LESS"), + LEVEL("LEVEL"), + LEXER("LEXER"), + LIKE("LIKE"), + LIMIT("LIMIT"), + + LINK("LINK"), + LIST("LIST"), + LNNVL("LNNVL"), + LOB("LOB"), + LOCAL("LOCAL"), + LOCALLY("LOCALLY"), + LOCK("LOCK"), + LOCKED("LOCKED"), + LOG("LOG"), + LOGFILE("LOGFILE"), + + LOGGING("LOGGING"), + LOGIN("LOGIN"), + LOGOFF("LOGOFF"), + LOGON("LOGON"), + LOGOUT("LOGOUT"), + LONG("LONG"), + LONGVARBINARY("LONGVARBINARY"), + LONGVARCHAR("LONGVARCHAR"), + + LOOP("LOOP"), + LSN("LSN"), + + MANUAL("MANUAL"), + MAP("MAP"), + MAPPED("MAPPED"), + MATCH("MATCH"), + MATCHED("MATCHED"), + MATERIALIZED("MATERIALIZED"), + MAX("MAX"), + MAXPIECESIZE("MAXPIECESIZE"), + + MAXSIZE("MAXSIZE"), + MAXVALUE("MAXVALUE"), + MEMBER("MEMBER"), + MEMORY("MEMORY"), + MEM_SPACE("MEM_SPACE"), + MERGE("MERGE"), + MIN("MIN"), + MINEXTENTS("MINEXTENTS"), + + MINUS("MINUS"), + MINUTE("MINUTE"), + MINVALUE("MINVALUE"), + MIRROR("MIRROR"), + MOD("MOD"), + MODE("MODE"), + MODIFY("MODIFY"), + MONEY("MONEY"), + MONITORING("MONITORING"), + + MONTH("MONTH"), + MOUNT("MOUNT"), + MOVEMENT("MOVEMENT"), + + NATIONAL("NATIONAL"), + NATURAL("NATURAL"), + NCHAR("NCHAR"), + NCHARACTER("NCHARACTER"), + NEVER("NEVER"), + NEW("NEW"), + NEXT("NEXT"), + NO("NO"), + + NOARCHIVELOG("NOARCHIVELOG"), + NOAUDIT("NOAUDIT"), + NOBRANCH("NOBRANCH"), + NOCACHE("NOCACHE"), + NOCOPY("NOCOPY"), + NOCYCLE("NOCYCLE"), + NOLOGGING("NOLOGGING"), + + NOMAXVALUE("NOMAXVALUE"), + NOMINVALUE("NOMINVALUE"), + NOMONITORING("NOMONITORING"), + NONE("NONE"), + NOORDER("NOORDER"), + NORMAL("NORMAL"), + NOSORT("NOSORT"), + + NOT("NOT"), + NOT_ALLOW_DATETIME("NOT_ALLOW_DATETIME"), + NOT_ALLOW_IP("NOT_ALLOW_IP"), + NOWAIT("NOWAIT"), + NULL("NULL"), + NULLS("NULLS"), + NUMBER("NUMBER"), + + NUMERIC("NUMERIC"), + + OBJECT("OBJECT"), + OF("OF"), + OFF("OFF"), + OFFLINE("OFFLINE"), + OFFSET("OFFSET"), + OLD("OLD"), + ON("ON"), + ONCE("ONCE"), + ONLINE("ONLINE"), + ONLY("ONLY"), + + OPEN("OPEN"), + OPTIMIZE("OPTIMIZE"), + OPTION("OPTION"), + OR("OR"), + ORDER("ORDER"), + OUT("OUT"), + OUTER("OUTER"), + OVER("OVER"), + OVERLAPS("OVERLAPS"), + + OVERLAY("OVERLAY"), + OVERRIDE("OVERRIDE"), + OVERRIDING("OVERRIDING"), + + PACKAGE("PACKAGE"), + PAD("PAD"), + PAGE("PAGE"), + PARALLEL("PARALLEL"), + PARALLEL_ENABLE("PARALLEL_ENABLE"), + PARMS("PARMS"), + PARTIAL("PARTIAL"), + + PARTITION("PARTITION"), + PARTITIONS("PARTITIONS"), + PASSWORD_GRACE_TIME("PASSWORD_GRACE_TIME"), + PASSWORD_LIFE_TIME("PASSWORD_LIFE_TIME"), + + + PASSWORD_LOCK_TIME("PASSWORD_LOCK_TIME"), + PASSWORD_POLICY("PASSWORD_POLICY"), + PASSWORD_REUSE_MAX("PASSWORD_REUSE_MAX"), + + PASSWORD_REUSE_TIME("PASSWORD_REUSE_TIME"), + PATH("PATH"), + PENDANT("PENDANT"), + PERCENT("PERCENT"), + PIPE("PIPE"), + PIPELINED("PIPELINED"), + PIVOT("PIVOT"), + + PLACING("PLACING"), + PLS_INTEGER("PLS_INTEGER"), + PRAGMA("PRAGMA"), + PRECEDING("PRECEDING"), + PRECISION("PRECISION"), + PRESERVE("PRESERVE"), + PRIMARY("PRIMARY"), + + PRINT("PRINT"), + PRIOR("PRIOR"), + PRIVATE("PRIVATE"), + PRIVILEGE("PRIVILEGE"), + PRIVILEGES("PRIVILEGES"), + PROCEDURE("PROCEDURE"), + PROTECTED("PROTECTED"), + + PUBLIC("PUBLIC"), + PURGE("PURGE"), + + QUERY_REWRITE_INTEGRITY("QUERY_REWRITE_INTEGRITY"), + + RAISE("RAISE"), + RANDOMLY("RANDOMLY"), + RANGE("RANGE"), + RAWTOHEX("RAWTOHEX"), + READ("READ"), + READONLY("READONLY"), + READ_PER_CALL("READ_PER_CALL"), + + READ_PER_SESSION("READ_PER_SESSION"), + REAL("REAL"), + REBUILD("REBUILD"), + RECORD("RECORD"), + RECORDS("RECORDS"), + REF("REF"), + REFERENCE("REFERENCE"), + + REFERENCES("REFERENCES"), + REFERENCING("REFERENCING"), + REFRESH("REFRESH"), + RELATED("RELATED"), + RELATIVE("RELATIVE"), + RENAME("RENAME"), + REPEAT("REPEAT"), + + REPEATABLE("REPEATABLE"), + REPLACE("REPLACE"), + REPLAY("REPLAY"), + REPLICATE("REPLICATE"), + RESIZE("RESIZE"), + RESTORE("RESTORE"), + RESTRICT("RESTRICT"), + + RESULT("RESULT"), + RESULT_CACHE("RESULT_CACHE"), + RETURN("RETURN"), + RETURNING("RETURNING"), + REVERSE("REVERSE"), + REVOKE("REVOKE"), + RIGHT("RIGHT"), + + ROLE("ROLE"), + ROLLBACK("ROLLBACK"), + ROLLFILE("ROLLFILE"), + ROLLUP("ROLLUP"), + ROOT("ROOT"), + ROW("ROW"), + ROWCOUNT("ROWCOUNT"), + ROWID("ROWID"), + ROWNUM("ROWNUM"), + + ROWS("ROWS"), + RULE("RULE"), + + SALT("SALT"), + SAMPLE("SAMPLE"), + SAVE("SAVE"), + SAVEPOINT("SAVEPOINT"), + SBYTE("SBYTE"), + SCHEMA("SCHEMA"), + SCOPE("SCOPE"), + SCROLL("SCROLL"), + SEALED("SEALED"), + + SECOND("SECOND"), + SECTION("SECTION"), + SEED("SEED"), + SELECT("SELECT"), + SELF("SELF"), + SENSITIVE("SENSITIVE"), + SEQUENCE("SEQUENCE"), + SERERR("SERERR"), + + SERIALIZABLE("SERIALIZABLE"), + SERVER("SERVER"), + SESSION("SESSION"), + SESSION_PER_USER("SESSION_PER_USER"), + SET("SET"), + SETS("SETS"), + SHARE("SHARE"), + + SHORT("SHORT"), + SHUTDOWN("SHUTDOWN"), + SIBLINGS("SIBLINGS"), + SIMPLE("SIMPLE"), + SINCE("SINCE"), + SIZE("SIZE"), + SIZEOF("SIZEOF"), + SKIP("SKIP"), + SMALLINT("SMALLINT"), + + SNAPSHOT("SNAPSHOT"), + SOME("SOME"), + SOUND("SOUND"), + SPACE("SPACE"), + SPATIAL("SPATIAL"), + SPFILE("SPFILE"), + SPLIT("SPLIT"), + SQL("SQL"), + STANDBY("STANDBY"), + + STARTUP("STARTUP"), + STAT("STAT"), + STATEMENT("STATEMENT"), + STATIC("STATIC"), + STDDEV("STDDEV"), + STORAGE("STORAGE"), + STORE("STORE"), + STRING("STRING"), + + STRUCT("STRUCT"), + STYLE("STYLE"), + SUBPARTITION("SUBPARTITION"), + SUBPARTITIONS("SUBPARTITIONS"), + SUBSTRING("SUBSTRING"), + SUBTYPE("SUBTYPE"), + + SUCCESSFUL("SUCCESSFUL"), + SUM("SUM"), + SUSPEND("SUSPEND"), + SWITCH("SWITCH"), + SYNC("SYNC"), + SYNCHRONOUS("SYNCHRONOUS"), + SYNONYM("SYNONYM"), + SYSTEM("SYSTEM"), + + SYS_CONNECT_BY_PATH("SYS_CONNECT_BY_PATH"), + + TABLE("TABLE"), + TABLESPACE("TABLESPACE"), + TASK("TASK"), + TEMPLATE("TEMPLATE"), + TEMPORARY("TEMPORARY"), + TEXT("TEXT"), + THAN("THAN"), + THEN("THEN"), + + THREAD("THREAD"), + THROW("THROW"), + TIES("TIES"), + TIME("TIME"), + TIMER("TIMER"), + TIMES("TIMES"), + TIMESTAMP("TIMESTAMP"), + TIMESTAMPADD("TIMESTAMPADD"), + + TIMESTAMPDIFF("TIMESTAMPDIFF"), + TIME_ZONE("TIME_ZONE"), + TINYINT("TINYINT"), + TO("TO"), + TOP("TOP"), + TRACE("TRACE"), + TRAILING("TRAILING"), + + TRANSACTION("TRANSACTION"), + TRANSACTIONAL("TRANSACTIONAL"), + TRIGGER("TRIGGER"), + TRIGGERS("TRIGGERS"), + TRIM("TRIM"), + TRUNCATE("TRUNCATE"), + + TRUNCSIZE("TRUNCSIZE"), + TRXID("TRXID"), + TRY("TRY"), + TYPE("TYPE"), + TYPEDEF("TYPEDEF"), + TYPEOF("TYPEOF"), + + UINT("UINT"), + ULONG("ULONG"), + UNBOUNDED("UNBOUNDED"), + UNCOMMITTED("UNCOMMITTED"), + UNDER("UNDER"), + UNION("UNION"), + UNIQUE("UNIQUE"), + + UNLIMITED("UNLIMITED"), + UNLOCK("UNLOCK"), + UNPIVOT("UNPIVOT"), + UNTIL("UNTIL"), + UNUSABLE("UNUSABLE"), + UP("UP"), + UPDATE("UPDATE"), + UPDATING("UPDATING"), + + USAGE("USAGE"), + USER("USER"), + USE_HASH("USE_HASH"), + USE_MERGE("USE_MERGE"), + USE_NL("USE_NL"), + USE_NL_WITH_INDEX("USE_NL_WITH_INDEX"), + + USHORT("USHORT"), + USING("USING"), + + VALUE("VALUE"), + VALUES("VALUES"), + VARBINARY("VARBINARY"), + VARCHAR("VARCHAR"), + VARCHAR2("VARCHAR2"), + VARIANCE("VARIANCE"), + VARRAY("VARRAY"), + + VARYING("VARYING"), + VERIFY("VERIFY"), + VERSIONS("VERSIONS"), + VERSIONS_STARTTIME("VERSIONS_STARTTIME"), + VERSIONS_ENDTIME("VERSIONS_ENDTIME"), + + VERSIONS_STARTTRXID("VERSIONS_STARTTRXID"), + VERSIONS_ENDTRXID("VERSIONS_ENDTRXID"), + VERSIONS_OPERATION("VERSIONS_OPERATION"), + VERTICAL("VERTICAL"), + + VIEW("VIEW"), + VIRTUAL("VIRTUAL"), + VISIBLE("VISIBLE"), + VOID("VOID"), + VOLATILE("VOLATILE"), + VSIZE("VSIZE"), + + WAIT("WAIT"), + WEEK("WEEK"), + WHEN("WHEN"), + WHENEVER("WHENEVER"), + WHERE("WHERE"), + WHILE("WHILE"), + WITH("WITH"), + WITHIN("WITHIN"), + WITHOUT("WITHOUT"), + + WORK("WORK"), + WRAPPED("WRAPPED"), + WRITE("WRITE"), + XML("XML"), + YEAR("YEAR"), + ZONE("ZONE"); + /** + * The Name. + */ + public final String name; + + DmKeyword(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 fieldOrTableName, TableMeta tableMeta) { + boolean check = checkIfKeyWords(fieldOrTableName); + // dm + // we are recommend table name and column name must uppercase. + // if exists full uppercase, the table name or column name does't bundle escape symbol. + return check || !isUppercase(fieldOrTableName); + } + + 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/io/seata/rm/datasource/sql/struct/cache/AbstractTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/AbstractTableMetaCache.java index cbae2e9ad10..033aa2e4f09 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/AbstractTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/AbstractTableMetaCache.java @@ -63,9 +63,8 @@ public TableMeta getTableMeta(final Connection connection, final String tableNam throw new IllegalArgumentException("TableMeta cannot be fetched without tableName"); } - TableMeta tmeta; final String key = getCacheKey(connection, tableName, resourceId); - tmeta = TABLE_META_CACHE.get(key, mappingFunction -> { + TableMeta tmeta = TABLE_META_CACHE.get(key, mappingFunction -> { try { return fetchSchema(connection, tableName); } catch (SQLException e) { @@ -75,7 +74,7 @@ public TableMeta getTableMeta(final Connection connection, final String tableNam }); if (tmeta == null) { - throw new ShouldNeverHappenException(String.format("[xid:%s]get table meta failed," + + throw new ShouldNeverHappenException(String.format("[xid:%s] Get table meta failed," + " please check whether the table `%s` exists.", RootContext.getXID(), tableName)); } return tmeta; diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java new file mode 100644 index 00000000000..e685d0684af --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java @@ -0,0 +1,176 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.struct.cache; + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.StringUtils; +import io.seata.sqlparser.struct.ColumnMeta; +import io.seata.sqlparser.struct.IndexMeta; +import io.seata.sqlparser.struct.IndexType; +import io.seata.sqlparser.struct.TableMeta; +import io.seata.sqlparser.util.JdbcConstants; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * The type Table meta cache. + * + * @author chengxiaoxiao + */ +@LoadLevel(name = JdbcConstants.DM) +public class DmTableMetaCache extends OracleTableMetaCache { + public static class TableNameMeta { + private final String schema; + private final String tableName; + + public TableNameMeta(String schema, String tableName) { + this.schema = schema; + this.tableName = tableName; + } + + public String getSchema() { + return schema; + } + + public String getTableName() { + return tableName; + } + } + + @Override + protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException { + TableMeta result = new TableMeta(); + result.setTableName(tableName); + + TableNameMeta tableNameMeta = toTableNameMeta(tableName, dbmd.getConnection().getSchema()); + try (ResultSet rsColumns = dbmd.getColumns("", tableNameMeta.getSchema(), tableNameMeta.getTableName(), "%"); + ResultSet rsIndex = dbmd.getIndexInfo(null, tableNameMeta.getSchema(), tableNameMeta.getTableName(), false, true); + ResultSet rsPrimary = dbmd.getPrimaryKeys(null, tableNameMeta.getSchema(), tableNameMeta.getTableName())) { + processColumns(result, rsColumns); + + processIndexes(result, rsIndex); + + processPrimaries(result, rsPrimary); + + if (result.getAllIndexes().isEmpty()) { + throw new ShouldNeverHappenException(String.format("Could not found any index in the table: %s", tableName)); + } + } + + return result; + } + + protected TableNameMeta toTableNameMeta(String tableName, String schemaFromConnection) { + String[] schemaTable = tableName.split("\\."); + + String schema = schemaTable.length > 1 ? schemaTable[0] : schemaFromConnection; + if (schema != null) { + schema = schema.contains("\"") ? schema.replace("\"", "") : schema.toUpperCase(); + } + + tableName = schemaTable.length > 1 ? schemaTable[1] : tableName; + tableName = tableName.contains("\"") ? tableName.replace("\"", "") : tableName.toUpperCase(); + + return new TableNameMeta(schema, tableName); + } + + protected void processColumns(TableMeta tableMeta, ResultSet rs) throws SQLException { + while (rs.next()) { + ColumnMeta col = toColumnMeta(rs); + tableMeta.getAllColumns().put(col.getColumnName(), col); + } + } + + protected void processIndexes(TableMeta tableMeta, ResultSet rs) throws SQLException { + while (rs.next()) { + String indexName = rs.getString("INDEX_NAME"); + if (StringUtils.isNullOrEmpty(indexName)) { + continue; + } + + String colName = rs.getString("COLUMN_NAME"); + ColumnMeta col = tableMeta.getAllColumns().get(colName); + if (tableMeta.getAllIndexes().containsKey(indexName)) { + IndexMeta index = tableMeta.getAllIndexes().get(indexName); + index.getValues().add(col); + continue; + } + + tableMeta.getAllIndexes().put(indexName, toIndexMeta(rs, indexName, col)); + } + } + + protected void processPrimaries(TableMeta tableMeta, ResultSet rs) throws SQLException { + while (rs.next()) { + String pkColName; + try { + pkColName = rs.getString("COLUMN_NAME"); + } catch (Exception e) { + pkColName = rs.getString("PK_NAME"); + } + + String finalPkColName = pkColName; + for (IndexMeta i : tableMeta.getAllIndexes().values()) { + i.getValues().stream() + .filter(c -> finalPkColName.equals(c.getColumnName())) + .forEach(c -> i.setIndextype(IndexType.PRIMARY)); + } + } + } + + protected ColumnMeta toColumnMeta(ResultSet rs) throws SQLException { + ColumnMeta result = new ColumnMeta(); + result.setTableCat(rs.getString("TABLE_CAT")); + result.setTableSchemaName(rs.getString("TABLE_SCHEM")); + result.setTableName(rs.getString("TABLE_NAME")); + result.setColumnName(rs.getString("COLUMN_NAME")); + result.setDataType(rs.getInt("DATA_TYPE")); + result.setDataTypeName(rs.getString("TYPE_NAME")); + result.setColumnSize(rs.getInt("COLUMN_SIZE")); + result.setDecimalDigits(rs.getInt("DECIMAL_DIGITS")); + result.setNumPrecRadix(rs.getInt("NUM_PREC_RADIX")); + result.setNullAble(rs.getInt("NULLABLE")); + result.setRemarks(rs.getString("REMARKS")); + result.setColumnDef(rs.getString("COLUMN_DEF")); + result.setSqlDataType(rs.getInt("SQL_DATA_TYPE")); + result.setSqlDatetimeSub(rs.getInt("SQL_DATETIME_SUB")); + result.setCharOctetLength(rs.getInt("CHAR_OCTET_LENGTH")); + result.setOrdinalPosition(rs.getInt("ORDINAL_POSITION")); + result.setIsNullAble(rs.getString("IS_NULLABLE")); + return result; + } + + protected IndexMeta toIndexMeta(ResultSet rs, String indexName, ColumnMeta columnMeta) throws SQLException { + IndexMeta result = new IndexMeta(); + result.setIndexName(indexName); + result.setNonUnique(rs.getBoolean("NON_UNIQUE")); + result.setIndexQualifier(rs.getString("INDEX_QUALIFIER")); + result.setType(rs.getShort("TYPE")); + result.setOrdinalPosition(rs.getShort("ORDINAL_POSITION")); + result.setAscOrDesc(rs.getString("ASC_OR_DESC")); + result.setCardinality(rs.getInt("CARDINALITY")); + result.getValues().add(columnMeta); + if (!result.isNonUnique()) { + result.setIndextype(IndexType.UNIQUE); + } else { + result.setIndextype(IndexType.NORMAL); + } + return result; + } +} \ No newline at end of file diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java index bea9afe6f59..3477add9a6a 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java @@ -73,7 +73,7 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws } } - private TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException { + protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException { TableMeta tm = new TableMeta(); tm.setTableName(tableName); String[] schemaTable = tableName.split("\\."); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java new file mode 100644 index 00000000000..c03865bd45f --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.undo.dm; + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.util.CollectionUtils; +import io.seata.rm.datasource.sql.struct.Field; +import io.seata.rm.datasource.sql.struct.Row; +import io.seata.rm.datasource.sql.struct.TableRecords; +import io.seata.rm.datasource.undo.AbstractUndoExecutor; +import io.seata.rm.datasource.undo.SQLUndoLog; +import io.seata.sqlparser.util.ColumnUtils; +import io.seata.sqlparser.util.JdbcConstants; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The type dm undo delete executor. + * + * @author chengxiaoxiao + */ +public class DmUndoDeleteExecutor 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 dm undo delete executor. + * + * @param sqlUndoLog the sql undo log + */ + public DmUndoDeleteExecutor(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.DM)); + + // 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.DM)) + .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/io/seata/rm/datasource/undo/dm/DmUndoExecutorHolder.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoExecutorHolder.java new file mode 100644 index 00000000000..1d015c08f71 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoExecutorHolder.java @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.undo.dm; + +import io.seata.common.loader.LoadLevel; +import io.seata.rm.datasource.undo.AbstractUndoExecutor; +import io.seata.rm.datasource.undo.SQLUndoLog; +import io.seata.rm.datasource.undo.UndoExecutorHolder; +import io.seata.sqlparser.util.JdbcConstants; + +/** + * The Type DmUndoExecutorHolder + * + * @author: chengxiaoxiao + */ +@LoadLevel(name = JdbcConstants.DM) +public class DmUndoExecutorHolder implements UndoExecutorHolder { + + @Override + public AbstractUndoExecutor getInsertExecutor(SQLUndoLog sqlUndoLog) { + return new DmUndoInsertExecutor(sqlUndoLog); + } + + @Override + public AbstractUndoExecutor getUpdateExecutor(SQLUndoLog sqlUndoLog) { + return new DmUndoUpdateExecutor(sqlUndoLog); + } + + @Override + public AbstractUndoExecutor getDeleteExecutor(SQLUndoLog sqlUndoLog) { + return new DmUndoDeleteExecutor(sqlUndoLog); + } +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoInsertExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoInsertExecutor.java new file mode 100644 index 00000000000..9f60369ff69 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoInsertExecutor.java @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.undo.dm; + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.util.CollectionUtils; +import io.seata.rm.datasource.SqlGenerateUtils; +import io.seata.rm.datasource.sql.struct.Field; +import io.seata.rm.datasource.sql.struct.Row; +import io.seata.rm.datasource.sql.struct.TableRecords; +import io.seata.rm.datasource.undo.AbstractUndoExecutor; +import io.seata.rm.datasource.undo.SQLUndoLog; +import io.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 dm undo insert executor. + * + * @author chengxiaoxiao + */ +public class DmUndoInsertExecutor 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.DM).stream().map( + e -> e.getName()).collect(Collectors.toList()); + String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, JdbcConstants.DM); + return String.format(DELETE_SQL_TEMPLATE, sqlUndoLog.getTableName(), whereSql); + } + + /** + * Instantiates a new My sql undo insert executor. + * + * @param sqlUndoLog the sql undo log + */ + public DmUndoInsertExecutor(SQLUndoLog sqlUndoLog) { + super(sqlUndoLog); + } + + @Override + protected TableRecords getUndoRows() { + return sqlUndoLog.getAfterImage(); + } +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoLogManager.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoLogManager.java new file mode 100644 index 00000000000..39053e8b173 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoLogManager.java @@ -0,0 +1,109 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.undo.dm; + + +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.BlobUtils; +import io.seata.core.compressor.CompressorType; +import io.seata.core.constants.ClientTableColumnsName; +import io.seata.rm.datasource.undo.AbstractUndoLogManager; +import io.seata.rm.datasource.undo.UndoLogParser; +import io.seata.sqlparser.util.JdbcConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Blob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; + +/** + * @author chengxiaoxiao + */ +@LoadLevel(name = JdbcConstants.DM) +public class DmUndoLogManager extends AbstractUndoLogManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(DmUndoLogManager.class); + + + private static final String INSERT_UNDO_LOG_SQL = "INSERT INTO " + UNDO_LOG_TABLE_NAME + + " (" + ClientTableColumnsName.UNDO_LOG_BRANCH_XID + ", " + + ClientTableColumnsName.UNDO_LOG_XID + ", \"" + ClientTableColumnsName.UNDO_LOG_CONTEXT.toUpperCase() + "\", " + + ClientTableColumnsName.UNDO_LOG_ROLLBACK_INFO + ", " + ClientTableColumnsName.UNDO_LOG_LOG_STATUS + ", " + + ClientTableColumnsName.UNDO_LOG_LOG_CREATED + ", " + ClientTableColumnsName.UNDO_LOG_LOG_MODIFIED + ")" + + "VALUES (?, ?, ?, ?, ?, sysdate, sysdate)"; + + private static final String DELETE_UNDO_LOG_BY_CREATE_SQL = "DELETE FROM " + UNDO_LOG_TABLE_NAME + + " WHERE " + ClientTableColumnsName.UNDO_LOG_LOG_CREATED + " <= ? 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)) { + deletePST.setDate(1, new java.sql.Date(logCreated.getTime())); + 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); + } + + @Override + protected byte[] getRollbackInfo(ResultSet rs) throws SQLException { + Blob b = rs.getBlob(ClientTableColumnsName.UNDO_LOG_ROLLBACK_INFO); + byte[] rollbackInfo = BlobUtils.blob2Bytes(b); + return rollbackInfo; + } + + 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; + } + } + +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoUpdateExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoUpdateExecutor.java new file mode 100644 index 00000000000..08d38c9ae11 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/dm/DmUndoUpdateExecutor.java @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.undo.dm; + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.util.CollectionUtils; +import io.seata.rm.datasource.SqlGenerateUtils; +import io.seata.rm.datasource.sql.struct.Field; +import io.seata.rm.datasource.sql.struct.Row; +import io.seata.rm.datasource.sql.struct.TableRecords; +import io.seata.rm.datasource.undo.AbstractUndoExecutor; +import io.seata.rm.datasource.undo.SQLUndoLog; +import io.seata.sqlparser.util.ColumnUtils; +import io.seata.sqlparser.util.JdbcConstants; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * The type dm undo update executor. + * + * @author chengxiaoxiao + */ +public class DmUndoUpdateExecutor 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.DM) + " = ?").collect( + Collectors.joining(", ")); + + List pkNameList = getOrderedPkList(beforeImage, row, JdbcConstants.DM).stream().map( + e -> e.getName()).collect(Collectors.toList()); + String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, JdbcConstants.DM); + + return String.format(UPDATE_SQL_TEMPLATE, sqlUndoLog.getTableName(), updateColumns, whereSql); + } + + /** + * Instantiates a new My sql undo update executor. + * + * @param sqlUndoLog the sql undo log + */ + public DmUndoUpdateExecutor(SQLUndoLog sqlUndoLog) { + super(sqlUndoLog); + } + + @Override + protected TableRecords getUndoRows() { + return sqlUndoLog.getBeforeImage(); + } +} diff --git a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.exec.InsertExecutor b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.exec.InsertExecutor index 4790ddabe30..24e1943099a 100644 --- a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.exec.InsertExecutor +++ b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.exec.InsertExecutor @@ -1,4 +1,5 @@ io.seata.rm.datasource.exec.mysql.MySQLInsertExecutor io.seata.rm.datasource.exec.mariadb.MariadbInsertExecutor io.seata.rm.datasource.exec.oracle.OracleInsertExecutor -io.seata.rm.datasource.exec.postgresql.PostgresqlInsertExecutor \ No newline at end of file +io.seata.rm.datasource.exec.postgresql.PostgresqlInsertExecutor +io.seata.rm.datasource.exec.dm.DmInsertExecutor \ No newline at end of file diff --git a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoExecutorHolder b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoExecutorHolder index da1c3923aa8..0b15e5a644f 100644 --- a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoExecutorHolder +++ b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoExecutorHolder @@ -1,4 +1,5 @@ io.seata.rm.datasource.undo.mysql.MySQLUndoExecutorHolder io.seata.rm.datasource.undo.mariadb.MariadbUndoExecutorHolder io.seata.rm.datasource.undo.oracle.OracleUndoExecutorHolder -io.seata.rm.datasource.undo.postgresql.PostgresqlUndoExecutorHolder \ No newline at end of file +io.seata.rm.datasource.undo.postgresql.PostgresqlUndoExecutorHolder +io.seata.rm.datasource.undo.dm.DmUndoExecutorHolder \ No newline at end of file diff --git a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoLogManager b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoLogManager index 2877f6e72ba..a81dfa12976 100644 --- a/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoLogManager +++ b/rm-datasource/src/main/resources/META-INF/services/io.seata.rm.datasource.undo.UndoLogManager @@ -1,4 +1,5 @@ io.seata.rm.datasource.undo.mysql.MySQLUndoLogManager io.seata.rm.datasource.undo.mariadb.MariadbUndoLogManager io.seata.rm.datasource.undo.oracle.OracleUndoLogManager -io.seata.rm.datasource.undo.postgresql.PostgresqlUndoLogManager \ No newline at end of file +io.seata.rm.datasource.undo.postgresql.PostgresqlUndoLogManager +io.seata.rm.datasource.undo.dm.DmUndoLogManager \ No newline at end of file diff --git a/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.EscapeHandler b/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.EscapeHandler index 137aeea649e..4d5c579fd18 100644 --- a/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.EscapeHandler +++ b/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.EscapeHandler @@ -1,4 +1,5 @@ io.seata.rm.datasource.sql.handler.oracle.OracleEscapeHandler io.seata.rm.datasource.sql.handler.mysql.MySQLEscapeHandler io.seata.rm.datasource.sql.handler.postgresql.PostgresqlEscapeHandler -io.seata.rm.datasource.sql.handler.mariadb.MariadbEscapeHandler \ No newline at end of file +io.seata.rm.datasource.sql.handler.mariadb.MariadbEscapeHandler +io.seata.rm.datasource.sql.handler.dm.DmEscapeHandler \ No newline at end of file diff --git a/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.struct.TableMetaCache b/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.struct.TableMetaCache index 323d96661d7..87390401af8 100644 --- a/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.struct.TableMetaCache +++ b/rm-datasource/src/main/resources/META-INF/services/io.seata.sqlparser.struct.TableMetaCache @@ -1,4 +1,5 @@ io.seata.rm.datasource.sql.struct.cache.MysqlTableMetaCache io.seata.rm.datasource.sql.struct.cache.MariadbTableMetaCache io.seata.rm.datasource.sql.struct.cache.OracleTableMetaCache -io.seata.rm.datasource.sql.struct.cache.PostgresqlTableMetaCache \ No newline at end of file +io.seata.rm.datasource.sql.struct.cache.PostgresqlTableMetaCache +io.seata.rm.datasource.sql.struct.cache.DmTableMetaCache \ No newline at end of file diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/DmInsertExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/DmInsertExecutorTest.java new file mode 100644 index 00000000000..ff4e250cbbb --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/DmInsertExecutorTest.java @@ -0,0 +1,216 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.exec; + +import io.seata.common.exception.NotSupportYetException; +import io.seata.rm.datasource.ConnectionProxy; +import io.seata.rm.datasource.PreparedStatementProxy; +import io.seata.rm.datasource.StatementProxy; +import io.seata.rm.datasource.exec.dm.DmInsertExecutor; +import io.seata.sqlparser.SQLInsertRecognizer; +import io.seata.sqlparser.struct.ColumnMeta; +import io.seata.sqlparser.struct.Null; +import io.seata.sqlparser.struct.SqlSequenceExpr; +import io.seata.sqlparser.struct.TableMeta; +import io.seata.sqlparser.util.JdbcConstants; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Jefferlau + */ +public class DmInsertExecutorTest { + + 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 = 100; + + private ConnectionProxy connectionProxy; + + private StatementProxy statementProxy; + + private SQLInsertRecognizer sqlInsertRecognizer; + + private StatementCallback statementCallback; + + private TableMeta tableMeta; + + private DmInsertExecutor insertExecutor; + + private final int pkIndex = 0; + private HashMap pkIndexMap; + + @BeforeEach + public void init() { + connectionProxy = mock(ConnectionProxy.class); + when(connectionProxy.getDbType()).thenReturn(JdbcConstants.DM); + + 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 DmInsertExecutor(statementProxy, statementCallback, sqlInsertRecognizer)); + + pkIndexMap = new HashMap() {{ + put(ID_COLUMN, pkIndex); + }}; + } + + @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); + + doReturn(pkValuesSeq).when(insertExecutor).getPkValuesBySequence(expr); + doReturn(pkIndexMap).when(insertExecutor).getPkIndex(); + + Map> pkValuesByColumn = insertExecutor.getPkValuesByColumn(); + verify(insertExecutor).getPkValuesBySequence(expr); + Assertions.assertEquals(pkValuesByColumn.get(ID_COLUMN), pkValuesSeq); + } + + @Test + public void testPkValue_auto() throws Exception { + mockInsertColumns(); + mockParametersPkWithAuto(); + doReturn(tableMeta).when(insertExecutor).getTableMeta(); + when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[] {ID_COLUMN})); + ; + doReturn(Arrays.asList(new Object[] {PK_VALUE})).when(insertExecutor).getGeneratedKeys(); + Map> pkValuesByAuto = insertExecutor.getPkValues(); + + verify(insertExecutor).getGeneratedKeys(); + Assertions.assertEquals(pkValuesByAuto.get(ID_COLUMN), Arrays.asList(new Object[] {PK_VALUE})); + } + + @Test + public void testStatement_pkValueByAuto_NotSupportYetException() throws Exception { + mockInsertColumns(); + mockStatementInsertRows(); + + statementProxy = mock(StatementProxy.class); + when(statementProxy.getConnectionProxy()).thenReturn(connectionProxy); + when(connectionProxy.getDbType()).thenReturn(JdbcConstants.DM); + + insertExecutor = Mockito.spy(new DmInsertExecutor(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(); + }); + + doReturn(pkIndexMap).when(insertExecutor).getPkIndex(); + + Assertions.assertThrows(NotSupportYetException.class, () -> { + insertExecutor.getPkValuesByColumn(); + }); + + } + + 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 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 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/io/seata/rm/datasource/mock/MockDatabaseMetaData.java b/rm-datasource/src/test/java/io/seata/rm/datasource/mock/MockDatabaseMetaData.java index 888b0d0b593..a3e2540a235 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/mock/MockDatabaseMetaData.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/mock/MockDatabaseMetaData.java @@ -863,7 +863,7 @@ public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePa @Override public Connection getConnection() throws SQLException { - return null; + return connection; } @Override diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmDeleteRecognizerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmDeleteRecognizerTest.java new file mode 100644 index 00000000000..4895fcfabd1 --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmDeleteRecognizerTest.java @@ -0,0 +1,196 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.druid.dm; + +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 io.seata.sqlparser.ParametersHolder; +import io.seata.sqlparser.SQLType; +import io.seata.sqlparser.druid.dm.DmDeleteRecognizer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Jefferlau + */ +public class DmDeleteRecognizerTest { + + private static final String DB_TYPE = "dm"; + + @Test + public void testGetSqlType() { + String sql = "delete from t where id = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmDeleteRecognizer recognizer = new DmDeleteRecognizer(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); + + DmDeleteRecognizer recognizer = new DmDeleteRecognizer(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); + + DmDeleteRecognizer recognizer = new DmDeleteRecognizer(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); + + DmDeleteRecognizer recognizer = new DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(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); + + DmDeleteRecognizer recognizer = new DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(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 DmDeleteRecognizer(s, deleteAst).getWhereCondition(); + }); + } +} diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmInsertRecognizerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmInsertRecognizerTest.java new file mode 100644 index 00000000000..1efacb31eaf --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmInsertRecognizerTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.druid.dm; + +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 io.seata.sqlparser.SQLParsingException; +import io.seata.sqlparser.SQLType; +import io.seata.sqlparser.druid.dm.DmInsertRecognizer; +import io.seata.sqlparser.struct.NotPlaceholderExpr; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Jefferlau + */ +public class DmInsertRecognizerTest { + + private static final String DB_TYPE = "dm"; + + @Test + public void testGetSqlType() { + String sql = "insert into t(id) values (?)"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmInsertRecognizer recognizer = new DmInsertRecognizer(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); + + DmInsertRecognizer recognizer = new DmInsertRecognizer(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); + + DmInsertRecognizer recognizer = new DmInsertRecognizer(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); + + DmInsertRecognizer recognizer = new DmInsertRecognizer(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 DmInsertRecognizer(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()); + + DmInsertRecognizer dmInsertRecognizer = new DmInsertRecognizer(s, sqlInsertStatement); + dmInsertRecognizer.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); + + DmInsertRecognizer recognizer = new DmInsertRecognizer(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()); + + DmInsertRecognizer dmInsertRecognizer = new DmInsertRecognizer(s, sqlInsertStatement); + dmInsertRecognizer.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); + ; + + DmInsertRecognizer dm = new DmInsertRecognizer(sql, sqlStatements.get(0)); + List> insertRows = dm.getInsertRows(Collections.singletonList(-1)); + Assertions.assertTrue(insertRows.get(0).get(0) instanceof NotPlaceholderExpr); + } +} diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmSelectForUpdateRecognizerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmSelectForUpdateRecognizerTest.java new file mode 100644 index 00000000000..ba87879df29 --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmSelectForUpdateRecognizerTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.druid.dm; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import io.seata.sqlparser.ParametersHolder; +import io.seata.sqlparser.SQLParsingException; +import io.seata.sqlparser.SQLType; +import io.seata.sqlparser.druid.dm.DmSelectForUpdateRecognizer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Jefferlau + */ +public class DmSelectForUpdateRecognizerTest { + + private static final String DB_TYPE = "dm"; + + @Test + public void testGetSqlType() { + String sql = "select * from t where id = ? for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmSelectForUpdateRecognizer recognizer = new DmSelectForUpdateRecognizer(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); + + DmSelectForUpdateRecognizer recognizer = new DmSelectForUpdateRecognizer(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); + + DmSelectForUpdateRecognizer recognizer = new DmSelectForUpdateRecognizer(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 DmSelectForUpdateRecognizer(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 DmSelectForUpdateRecognizer(s, selectAst).getWhereCondition(); + }); + } + + @Test + public void testGetTableAlias() { + String sql = "select * from t where id = ? for update"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmSelectForUpdateRecognizer recognizer = new DmSelectForUpdateRecognizer(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); + + DmSelectForUpdateRecognizer recognizer = new DmSelectForUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } +} diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmUpdateRecognizerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmUpdateRecognizerTest.java new file mode 100644 index 00000000000..dbd4cdd7b57 --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/druid/dm/DmUpdateRecognizerTest.java @@ -0,0 +1,155 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.druid.dm; + +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 io.seata.sqlparser.ParametersHolder; +import io.seata.sqlparser.SQLParsingException; +import io.seata.sqlparser.SQLType; +import io.seata.sqlparser.druid.dm.DmUpdateRecognizer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Jefferlau + */ +public class DmUpdateRecognizerTest { + + private static final String DB_TYPE = "dm"; + + @Test + public void testGetSqlType() { + String sql = "update t set n = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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); + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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 DmUpdateRecognizer(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()); + } + DmUpdateRecognizer dmUpdateRecognizer = new DmUpdateRecognizer(s, sqlUpdateStatement); + dmUpdateRecognizer.getUpdateColumns(); + }); + } + + @Test + public void testGetUpdateValues() { + // test with normal + String sql = "update t set a = ?, b = ?, c = ?"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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 DmUpdateRecognizer(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()); + } + DmUpdateRecognizer dmUpdateRecognizer = new DmUpdateRecognizer(s, sqlUpdateStatement); + dmUpdateRecognizer.getUpdateValues(); + }); + } + + @Test + public void testGetWhereCondition_0() { + + String sql = "update t set a = 1"; + List asts = SQLUtils.parseStatements(sql, DB_TYPE); + + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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); + + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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); + + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(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); + + DmUpdateRecognizer recognizer = new DmUpdateRecognizer(sql, asts.get(0)); + Assertions.assertEquals(recognizer.getTableName(), "t"); + } +} diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCacheTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCacheTest.java new file mode 100644 index 00000000000..e4319ea5b3f --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCacheTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed 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 io.seata.rm.datasource.sql.struct.cache; + +import com.alibaba.druid.pool.DruidDataSource; +import io.seata.rm.datasource.DataSourceProxy; +import io.seata.rm.datasource.mock.MockDriver; +import io.seata.rm.datasource.sql.struct.TableMetaCacheFactory; +import io.seata.sqlparser.struct.TableMeta; +import io.seata.sqlparser.struct.TableMetaCache; +import io.seata.sqlparser.util.JdbcConstants; +import java.sql.SQLException; +import java.sql.Types; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Jefferlau + */ +public class DmTableMetaCacheTest { + + private static Object[][] columnMetas = + new Object[][] { + new Object[] {"", "", "dt1", "id", Types.INTEGER, "INTEGER", 64, 0, 10, 1, "", "", 0, 0, 64, 1, "NO", "YES"}, + new Object[] { + "", "", "dt1", "name1", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES", + "NO"}, + new Object[] { + "", "", "dt1", "name2", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 3, "YES", + "NO"}, + new Object[] { + "", "", "dt1", "name3", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 4, "YES", + "NO"} + }; + + private static Object[][] indexMetas = + new Object[][] { + new Object[] {"id", "id", false, "", 3, 0, "A", 34}, + new Object[] {"name1", "name1", false, "", 3, 1, "A", 34}, + new Object[] {"name2", "name2", true, "", 3, 2, "A", 34}, + }; + + private static Object[][] pkMetas = + new Object[][] { + new Object[] {"id"} + }; + + @Test + public void getTableMetaTest() throws SQLException { + MockDriver mockDriver = new MockDriver(columnMetas, indexMetas, pkMetas); + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setDriver(mockDriver); + + DataSourceProxy proxy = new DataSourceProxy(dataSource); + + TableMetaCache tableMetaCache = TableMetaCacheFactory.getTableMetaCache(JdbcConstants.DM); + + TableMeta tableMeta = tableMetaCache.getTableMeta(proxy.getPlainConnection(), "dt1", proxy.getResourceId()); + + Assertions.assertNotNull(tableMeta); + + tableMeta = tableMetaCache.getTableMeta(proxy.getPlainConnection(), "dt1", proxy.getResourceId()); + + Assertions.assertNotNull(tableMeta); + } +} diff --git a/script/client/at/db/dm.sql b/script/client/at/db/dm.sql new file mode 100644 index 00000000000..534e94e0a54 --- /dev/null +++ b/script/client/at/db/dm.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS "UNDO_LOG" +( + "ID" BIGINT IDENTITY(1, 1) NOT NULL, + "BRANCH_ID" BIGINT NOT NULL, + "XID" VARCHAR(128) NOT NULL, + "CONTEXT" VARCHAR(128) NOT NULL, + "ROLLBACK_INFO" BLOB NOT NULL, + "LOG_STATUS" INT NOT NULL, + "LOG_CREATED" TIMESTAMP(0) NOT NULL, + "LOG_MODIFIED" TIMESTAMP(0) NOT NULL, + "EXT" VARCHAR(100), + NOT CLUSTER PRIMARY KEY("ID"), + CONSTRAINT "UX_UNDO_LOG" UNIQUE("XID", "BRANCH_ID") +) STORAGE (ON "MAIN", CLUSTERBTR); + +CREATE UNIQUE INDEX "PRIMARY" ON "UNDO_LOG"("ID" ASC) STORAGE (ON "MAIN", CLUSTERBTR); + +COMMENT ON TABLE "UNDO_LOG" IS 'AT transaction mode undo table'; diff --git a/script/server/db/dm.sql b/script/server/db/dm.sql new file mode 100644 index 00000000000..34e00e565dc --- /dev/null +++ b/script/server/db/dm.sql @@ -0,0 +1,76 @@ +-- -------------------------------- The script used when storeMode is 'db' -------------------------------- + +-- the table to store GlobalSession data +CREATE TABLE "SEATA"."GLOBAL_TABLE" +( + "XID" VARCHAR2(128) NOT NULL, + "TRANSACTION_ID" BIGINT, + "STATUS" TINYINT NOT NULL, + "APPLICATION_ID" VARCHAR2(32), + "TRANSACTION_SERVICE_GROUP" VARCHAR2(32), + "TRANSACTION_NAME" VARCHAR2(128), + "TIMEOUT" INT, + "BEGIN_TIME" BIGINT, + "APPLICATION_DATA" VARCHAR2(2000), + "GMT_CREATE" TIMESTAMP(0), + "GMT_MODIFIED" TIMESTAMP(0), + PRIMARY KEY ("XID") +); + +CREATE INDEX "IDX_GMT_MODIFIED_STATUS" ON "SEATA"."GLOBAL_TABLE"("GMT_MODIFIED" ASC,"STATUS" ASC); +CREATE INDEX "IDX_TRANSACTION_ID" ON "SEATA"."GLOBAL_TABLE"("TRANSACTION_ID" ASC); + + +-- the table to store BranchSession data +CREATE TABLE "SEATA"."BRANCH_TABLE" +( + "BRANCH_ID" BIGINT NOT NULL, + "XID" VARCHAR2(128) NOT NULL, + "TRANSACTION_ID" BIGINT, + "RESOURCE_GROUP_ID" VARCHAR2(32), + "RESOURCE_ID" VARCHAR2(256), + "BRANCH_TYPE" VARCHAR2(8), + "STATUS" TINYINT, + "CLIENT_ID" VARCHAR2(64), + "APPLICATION_DATA" VARCHAR2(2000), + "GMT_CREATE" TIMESTAMP(0), + "GMT_MODIFIED" TIMESTAMP(0), + PRIMARY KEY ("BRANCH_ID") +); + +CREATE INDEX "IDX_XID" ON "SEATA"."BRANCH_TABLE"("XID" ASC); + + +-- the table to store lock data +CREATE TABLE "SEATA"."LOCK_TABLE" +( + "ROW_KEY" VARCHAR2(128) NOT NULL, + "XID" VARCHAR2(128), + "TRANSACTION_ID" BIGINT, + "BRANCH_ID" BIGINT NOT NULL, + "RESOURCE_ID" VARCHAR2(256), + "TABLE_NAME" VARCHAR2(32), + "PK" VARCHAR2(36), + "STATUS" TINYINT NOT NULL DEFAULT 0, + "GMT_CREATE" TIMESTAMP(0), + "GMT_MODIFIED" TIMESTAMP(0), + PRIMARY KEY ("ROW_KEY") +); + +COMMENT ON COLUMN "SEATA"."LOCK_TABLE"."STATUS" IS '0:locked ,1:rollbacking'; + +CREATE INDEX "IDX_BRANCH_ID" ON "SEATA"."LOCK_TABLE" ("BRANCH_ID" ASC); +CREATE INDEX "IDX_STATUS" ON "SEATA"."LOCK_TABLE" ("STATUS" ASC); + +CREATE TABLE "SEATA"."DISTRIBUTED_LOCK" +( + "LOCK_KEY" VARCHAR2(20) NOT NULL, + "LOCK_VALUE" VARCHAR2(20) NOT NULL, + "EXPIRE" BIGINT NOT NULL, + PRIMARY KEY ("LOCK_KEY") +); + +INSERT INTO "SEATA"."DISTRIBUTED_LOCK" ("LOCK_KEY", "LOCK_VALUE", "EXPIRE") VALUES ('AsyncCommitting', ' ', 0); +INSERT INTO "SEATA"."DISTRIBUTED_LOCK" ("LOCK_KEY", "LOCK_VALUE", "EXPIRE") VALUES ('RetryCommitting', ' ', 0); +INSERT INTO "SEATA"."DISTRIBUTED_LOCK" ("LOCK_KEY", "LOCK_VALUE", "EXPIRE") VALUES ('RetryRollbacking', ' ', 0); +INSERT INTO "SEATA"."DISTRIBUTED_LOCK" ("LOCK_KEY", "LOCK_VALUE", "EXPIRE") VALUES ('TxTimeoutCheck', ' ', 0); diff --git a/server/pom.xml b/server/pom.xml index 66911a0d08a..aefaadcc0bc 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -175,6 +175,10 @@ org.postgresql postgresql + + com.dameng + DmJdbcDriver18 +