diff --git a/changes/en-us/2.0.0.md b/changes/en-us/2.0.0.md index 13c0be7f6b7..0f82d6b2c0d 100644 --- a/changes/en-us/2.0.0.md +++ b/changes/en-us/2.0.0.md @@ -93,6 +93,8 @@ The version is updated as follows: - [[#5419](https://github.com/seata/seata/pull/5419)] optimize images based on java 8/17 and support maven-3.9.0 - [[#5549](https://github.com/seata/seata/pull/5549)] update expire gpg key and publish workflow - [[#5576](https://github.com/seata/seata/pull/5576)] The common fence clean task is only initiated when useTCCFence is set to true +- [[#5623](https://github.com/seata/seata/pull/5623)] optimize possible conflict between asyncCommitting thread and retryCommitting thread +- [[#5553](https://github.com/seata/seata/pull/5553)] support case-sensitive attributes for table and column metadata ### test: - [[#5308](https://github.com/seata/seata/pull/5308)] add unit test [FileLoader, ObjectHolder, StringUtils] @@ -103,10 +105,7 @@ The version is updated as follows: - [[#5391](https://github.com/seata/seata/pull/5391)] add unit test for config module - [[#5428](https://github.com/seata/seata/pull/5428)] fix FileTransactionStoreManagerTest failed - [[#5622](https://github.com/seata/seata/pull/5622)] add unit test [ExporterType, RegistryType] -<<<<<<< HEAD - [[#5637](https://github.com/seata/seata/pull/5637)] add unit test [BatchResultMessage, HeartbeatMessage, RegisterRMResponse, ResultCode, RegisterTMResponse, MergeResultMessage, MergedWarpMessage, Version] -======= ->>>>>>> 4de002f1 (test: add unit test case for ExporterType/RegistryType (#5622)) ### Contributors: @@ -132,6 +131,7 @@ Thanks to these contributors for their code commits. Please report an unintended - [dmego](https://github.com/dmego) - [zsp419](https://github.com/zsp419) - [tuwenlin](https://github.com/tuwenlin) +- [sixlei](https://github.com/sixlei) Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. diff --git a/changes/zh-cn/2.0.0.md b/changes/zh-cn/2.0.0.md index 5bf2fc0dc20..eb3600e517d 100644 --- a/changes/zh-cn/2.0.0.md +++ b/changes/zh-cn/2.0.0.md @@ -93,6 +93,8 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单 - [[#5419](https://github.com/seata/seata/pull/5419)] 优化镜像发布流水线支持jdk8/17和支持maven 3.9.0 - [[#5549](https://github.com/seata/seata/pull/5549)] 优化 gpg key 和 发布流水线 - [[#5576](https://github.com/seata/seata/pull/5576)] 仅当 useTCCFence 设置为 true 时,才开启 Fence 表清理任务 +- [[#5623](https://github.com/seata/seata/pull/5623)] 优化异步提交线程和重试线程之间可能存在的冲突 +- [[#5553](https://github.com/seata/seata/pull/5553)] 支持表和列元数据大小写敏感设置 ### security: - [[#5172](https://github.com/seata/seata/pull/5172)] 修复一些安全漏洞的版本 @@ -106,10 +108,7 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单 - [[#5391](https://github.com/seata/seata/pull/5391)] 添加 config 模块的单元测试用例 - [[#5428](https://github.com/seata/seata/pull/5428)] 修复 FileTransactionStoreManagerTest 单测失败问题 - [[#5622](https://github.com/seata/seata/pull/5622)] 添加单元测试用例 [ExporterType, RegistryType] -<<<<<<< HEAD - [[#5637](https://github.com/seata/seata/pull/5637)] 添加单元测试用例 [BatchResultMessage, HeartbeatMessage, RegisterRMResponse, ResultCode, RegisterTMResponse, MergeResultMessage, MergedWarpMessage, Version] -======= ->>>>>>> 4de002f1 (test: add unit test case for ExporterType/RegistryType (#5622)) ### Contributors: @@ -135,6 +134,7 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单 - [dmego](https://github.com/dmego) - [zsp419](https://github.com/zsp419) - [tuwenlin](https://github.com/tuwenlin) +- [sixlei](https://github.com/sixlei) 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 diff --git a/common/src/main/java/io/seata/common/util/StringUtils.java b/common/src/main/java/io/seata/common/util/StringUtils.java index 9f48319ce1b..139541dded1 100644 --- a/common/src/main/java/io/seata/common/util/StringUtils.java +++ b/common/src/main/java/io/seata/common/util/StringUtils.java @@ -349,4 +349,28 @@ public static String hump2Line(String str) { return sb.toString(); } + public static boolean hasLowerCase(String str) { + if (null == str) { + return false; + } + for (int i = 0; i < str.length(); i++) { + if (Character.isLowerCase(str.charAt(i))) { + return true; + } + } + return false; + } + + public static boolean hasUpperCase(String str) { + if (null == str) { + return false; + } + for (int i = 0; i < str.length(); i++) { + if (Character.isUpperCase(str.charAt(i))) { + return true; + } + } + return false; + } + } diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/ColumnMeta.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/ColumnMeta.java index 6cc8eaa189a..6b0694043b5 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/ColumnMeta.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/ColumnMeta.java @@ -43,6 +43,8 @@ public class ColumnMeta { private String isAutoincrement; private boolean isOnUpdate; + private boolean isCaseSensitive; + /** * Instantiates a new Column meta. */ @@ -51,27 +53,29 @@ public ColumnMeta() { @Override public String toString() { - return "ColumnMeta{" + - "tableCat='" + tableCat + '\'' + - ", tableSchemaName='" + tableSchemaName + '\'' + - ", tableName='" + tableName + '\'' + - ", columnName='" + columnName + '\'' + - ", dataType=" + dataType + - ", dataTypeName='" + dataTypeName + '\'' + - ", columnSize=" + columnSize + - ", decimalDigits=" + decimalDigits + - ", numPrecRadix=" + numPrecRadix + - ", nullAble=" + nullAble + - ", remarks='" + remarks + '\'' + - ", columnDef='" + columnDef + '\'' + - ", sqlDataType=" + sqlDataType + - ", sqlDatetimeSub=" + sqlDatetimeSub + - ", charOctetLength=" + charOctetLength + - ", ordinalPosition=" + ordinalPosition + - ", isNullAble='" + isNullAble + '\'' + - ", isAutoincrement='" + isAutoincrement + '\'' + - ", isOnUpdate=" + isOnUpdate + - '}'; + final StringBuilder sb = new StringBuilder("ColumnMeta{"); + sb.append("tableCat='").append(tableCat).append('\''); + sb.append(", tableSchemaName='").append(tableSchemaName).append('\''); + sb.append(", tableName='").append(tableName).append('\''); + sb.append(", columnName='").append(columnName).append('\''); + sb.append(", dataType=").append(dataType); + sb.append(", dataTypeName='").append(dataTypeName).append('\''); + sb.append(", columnSize=").append(columnSize); + sb.append(", decimalDigits=").append(decimalDigits); + sb.append(", numPrecRadix=").append(numPrecRadix); + sb.append(", nullAble=").append(nullAble); + sb.append(", remarks='").append(remarks).append('\''); + sb.append(", columnDef='").append(columnDef).append('\''); + sb.append(", sqlDataType=").append(sqlDataType); + sb.append(", sqlDatetimeSub=").append(sqlDatetimeSub); + sb.append(", charOctetLength=").append(charOctetLength); + sb.append(", ordinalPosition=").append(ordinalPosition); + sb.append(", isNullAble='").append(isNullAble).append('\''); + sb.append(", isAutoincrement='").append(isAutoincrement).append('\''); + sb.append(", isOnUpdate=").append(isOnUpdate); + sb.append(", isCaseSensitive=").append(isCaseSensitive); + sb.append('}'); + return sb.toString(); } /** @@ -415,6 +419,14 @@ public void setOnUpdate(boolean onUpdate) { isOnUpdate = onUpdate; } + public boolean isCaseSensitive() { + return isCaseSensitive; + } + + public void setCaseSensitive(boolean caseSensitive) { + isCaseSensitive = caseSensitive; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -481,6 +493,9 @@ public boolean equals(Object o) { if (!Objects.equals(columnMeta.isOnUpdate, this.isOnUpdate)) { return false; } + if (!Objects.equals(columnMeta.isCaseSensitive, this.isCaseSensitive)) { + return false; + } return true; } @@ -505,6 +520,7 @@ public int hashCode() { hash += Objects.hashCode(isNullAble); hash += Objects.hashCode(isAutoincrement); hash += Objects.hashCode(isOnUpdate); + hash += Objects.hashCode(isCaseSensitive); return hash; } } diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableMeta.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableMeta.java index c58869103f8..b3a46d85470 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableMeta.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableMeta.java @@ -36,6 +36,8 @@ public class TableMeta { private String tableName; + private boolean isCaseSensitive; + /** * key: column name */ @@ -92,6 +94,24 @@ public Map getAllIndexes() { return allIndexes; } + /** + * Is case sensitive boolean. + * + * @return the boolean + */ + public boolean isCaseSensitive() { + return isCaseSensitive; + } + + /** + * Sets case sensitive. + * + * @param caseSensitive the case sensitive + */ + public void setCaseSensitive(boolean caseSensitive) { + isCaseSensitive = caseSensitive; + } + /** * Gets auto increase column. * @@ -214,6 +234,7 @@ public int hashCode() { int hash = Objects.hashCode(tableName); hash += Objects.hashCode(allColumns); hash += Objects.hashCode(allIndexes); + hash += Objects.hashCode(isCaseSensitive); return hash; } } diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java index 3d6b65679fb..8dba04d4375 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java @@ -106,6 +106,9 @@ private TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaData TableMeta tm = new TableMeta(); tm.setTableName(tableName); + //always true and nothing to do with escape characters for mysql. + // May be not consistent with lower_case_table_names + tm.setCaseSensitive(true); /* * here has two different type to get the data @@ -137,6 +140,7 @@ private TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaData col.setOrdinalPosition(rsColumns.getInt("ORDINAL_POSITION")); col.setIsNullAble(rsColumns.getString("IS_NULLABLE")); col.setIsAutoincrement(rsColumns.getString("IS_AUTOINCREMENT")); + col.setCaseSensitive(rsmd.isCaseSensitive(col.getOrdinalPosition())); if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); 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 d11329dedad..009aedfeb52 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 @@ -91,6 +91,7 @@ private TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) } else { tableName = tableName.toUpperCase(); } + tm.setCaseSensitive(StringUtils.hasLowerCase(tableName)); try (ResultSet rsColumns = dbmd.getColumns("", schemaName, tableName, "%"); ResultSet rsIndex = dbmd.getIndexInfo(null, schemaName, tableName, false, true); @@ -114,6 +115,7 @@ private TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) col.setCharOctetLength(rsColumns.getInt("CHAR_OCTET_LENGTH")); col.setOrdinalPosition(rsColumns.getInt("ORDINAL_POSITION")); col.setIsNullAble(rsColumns.getString("IS_NULLABLE")); + col.setCaseSensitive(StringUtils.hasLowerCase(col.getColumnName())); if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java index 074d63b1d7e..c3312c6367c 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java @@ -108,6 +108,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName) } else { tableName = tableName.toLowerCase(); } + tm.setCaseSensitive(StringUtils.hasUpperCase(tableName)); try (ResultSet rsColumns = dbmd.getColumns(null, schemaName, tableName, "%"); ResultSet rsIndex = dbmd.getIndexInfo(null, schemaName, tableName, false, true); @@ -132,6 +133,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName) col.setOrdinalPosition(rsColumns.getInt("ORDINAL_POSITION")); col.setIsNullAble(rsColumns.getString("IS_NULLABLE")); col.setIsAutoincrement(rsColumns.getString("IS_AUTOINCREMENT")); + col.setCaseSensitive(StringUtils.hasUpperCase(col.getColumnName())); if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); diff --git a/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java index e39e4e82c8b..6c4694d270c 100644 --- a/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java @@ -411,7 +411,9 @@ protected void handleRetryCommitting() { SessionHelper.forEach(committingSessions, committingSession -> { try { // prevent repeated commit - if (GlobalStatus.Committing.equals(committingSession.getStatus()) && !committingSession.isDeadSession()) { + if ((GlobalStatus.Committing.equals(committingSession.getStatus()) + || GlobalStatus.Committed.equals(committingSession.getStatus())) + && !committingSession.isDeadSession()) { // The function of this 'return' is 'continue'. return; }