Skip to content

Commit 1c0a442

Browse files
authored
bugfix: Fixed that the same record has different lowkeys due to mixed case of table names (apache#6678)
1 parent e8ea2cc commit 1c0a442

File tree

10 files changed

+95
-16
lines changed

10 files changed

+95
-16
lines changed

changes/en-us/2.x.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Add changes here for all PR submitted to the 2.x branch.
1414
- [[#6642](https://github.com/apache/incubator-seata/pull/6642)] codecov token not found
1515
- [[#6661](https://github.com/apache/incubator-seata/pull/6661)] fix `tableMeta` cache scheduled refresh issue
1616
- [[#6668](https://github.com/apache/incubator-seata/pull/6668)] thread safety issue when adding and removing instances
17+
- [[#6678](https://github.com/apache/incubator-seata/pull/6678)] fix the same record has different lowkeys due to mixed case of table names yesterday
1718

1819
### optimize:
1920
- [[#6499](https://github.com/apache/incubator-seata/pull/6499)] split the task thread pool for committing and rollbacking statuses
@@ -56,6 +57,7 @@ Thanks to these contributors for their code commits. Please report an unintended
5657
- [xjlgod](https://github.com/xjlgod)
5758
- [xingfudeshi](https://github.com/xingfudeshi)
5859
- [wuwen5](https://github.com/wuwen5)
59-
- [iAmClever(https://github.com/iAmClever)
60+
- [iAmClever](https://github.com/iAmClever)
61+
- [GoodBoyCoder](https://github.com/GoodBoyCoder)
6062

6163
Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.

changes/zh-cn/2.x.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [[#6642](https://github.com/apache/incubator-seata/pull/6642)] 修复codecov token找不到导致无法提交单测覆盖度报告
1616
- [[#6661](https://github.com/apache/incubator-seata/pull/6661)] 修复`tableMeta`缓存定时刷新失效问题
1717
- [[#6668](https://github.com/apache/incubator-seata/pull/6668)] 解决namingserver同一个集群下instance添加和删除时的线程安全问题
18+
- [[#6678](https://github.com/apache/incubator-seata/pull/6678)] 修复由于表名大小写问题导致的相同记录生成不同RowKey的问题
1819

1920

2021
### optimize:
@@ -59,5 +60,6 @@
5960
- [xingfudeshi](https://github.com/xingfudeshi)
6061
- [wuwen5](https://github.com/wuwen5)
6162
- [iAmClever](https://github.com/iAmClever)
63+
- [GoodBoyCoder](https://github.com/GoodBoyCoder)
6264

6365
同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ public String getTableName() {
5656
@Override
5757
protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException {
5858
TableMeta result = new TableMeta();
59-
result.setTableName(tableName);
6059

6160
TableNameMeta tableNameMeta = toTableNameMeta(tableName, dbmd.getConnection().getSchema());
61+
result.setTableName(tableNameMeta.getTableName());
6262
try (ResultSet rsColumns = dbmd.getColumns("", tableNameMeta.getSchema(), tableNameMeta.getTableName(), "%");
6363
ResultSet rsIndex = dbmd.getIndexInfo(null, tableNameMeta.getSchema(), tableNameMeta.getTableName(), false, true);
6464
ResultSet rsPrimary = dbmd.getPrimaryKeys(null, tableNameMeta.getSchema(), tableNameMeta.getTableName())) {

rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws
7575

7676
protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException {
7777
TableMeta tm = new TableMeta();
78-
tm.setTableName(tableName);
7978
String[] schemaTable = tableName.split("\\.");
8079
String schemaName = schemaTable.length > 1 ? schemaTable[0] : dbmd.getUserName();
8180
tableName = schemaTable.length > 1 ? schemaTable[1] : tableName;
@@ -91,6 +90,10 @@ protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableNam
9190
} else {
9291
tableName = tableName.toUpperCase();
9392
}
93+
// https://github.com/apache/incubator-seata/issues/6612
94+
// The parsed table name may contain both uppercase and lowercase letters,
95+
// resulting in inconsistent metadata information for the same table on different clients.
96+
tm.setTableName(tableName);
9497
tm.setCaseSensitive(StringUtils.hasLowerCase(tableName));
9598

9699
try (ResultSet rsColumns = dbmd.getColumns("", schemaName, tableName, "%");

rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName)
112112

113113
try (ResultSet rsColumns = dbmd.getColumns(null, schemaName, tableName, "%");
114114
ResultSet rsIndex = dbmd.getIndexInfo(null, schemaName, tableName, false, true);
115+
ResultSet rsTable = dbmd.getTables(null, schemaName, tableName, new String[]{"TABLE"});
115116
ResultSet rsPrimary = dbmd.getPrimaryKeys(null, schemaName, tableName)) {
116117
while (rsColumns.next()) {
117118
ColumnMeta col = new ColumnMeta();
@@ -182,6 +183,19 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName)
182183
if (tm.getAllIndexes().isEmpty()) {
183184
throw new ShouldNeverHappenException("Could not found any index in the table: " + tableName);
184185
}
186+
187+
while (rsTable.next()) {
188+
String rsTableName = rsTable.getString("TABLE_NAME");
189+
String rsTableSchema = rsTable.getString("TABLE_SCHEM");
190+
//set origin tableName with schema if necessary
191+
if ("public".equalsIgnoreCase(rsTableSchema)) {
192+
//for compatibility reasons, old clients generally do not have the 'public' default schema by default.
193+
tm.setTableName(rsTableName);
194+
} else {
195+
//without schema, different records with the same primary key value and the same table name in different schemas may have the same lock record.
196+
tm.setTableName(rsTableSchema + "." + rsTableName);
197+
}
198+
}
185199
}
186200

187201
return tm;

rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/cache/SqlServerTableMetaCache.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName)
113113
DatabaseMetaData metaData = connection.getMetaData();
114114
try (ResultSet rsColumns = metaData.getColumns(catalogName, schemaName, pureTableName, "%");
115115
ResultSet rsIndex = metaData.getIndexInfo(catalogName, schemaName, pureTableName, false, true);
116+
ResultSet rsTable = metaData.getTables(catalogName, schemaName, pureTableName, new String[]{"TABLE"});
116117
ResultSet rsPrimary = metaData.getPrimaryKeys(catalogName, schemaName, pureTableName)) {
117118
//get column metaData
118119
while (rsColumns.next()) {
@@ -186,6 +187,19 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName)
186187
if (tm.getAllIndexes().isEmpty()) {
187188
throw new ShouldNeverHappenException(String.format("Could not found any index in the table: %s", tableName));
188189
}
190+
191+
while (rsTable.next()) {
192+
String rsTableName = rsTable.getString("TABLE_NAME");
193+
String rsTableSchema = rsTable.getString("TABLE_SCHEM");
194+
//set origin tableName with schema if necessary
195+
if ("dbo".equalsIgnoreCase(rsTableSchema)) {
196+
//for compatibility reasons, old clients generally do not have the 'dbo' default schema by default.
197+
tm.setTableName(rsTableName);
198+
} else {
199+
//without schema, different records with the same primary key value and the same table name in different schemas may have the same lock record.
200+
tm.setTableName(rsTableSchema + "." + rsTableName);
201+
}
202+
}
189203
}
190204
return tm;
191205
}

rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockDatabaseMetaData.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ public class MockDatabaseMetaData implements DatabaseMetaData {
7878
"PSEUDO_COLUMN"
7979
);
8080

81+
private static List<String> tableMetaColumnLabels = Arrays.asList(
82+
"TABLE_NAME",
83+
"TABLE_SCHEM"
84+
);
85+
8186
private Object[][] columnsMetasReturnValue;
8287

8388
private Object[][] indexMetasReturnValue;
@@ -86,6 +91,8 @@ public class MockDatabaseMetaData implements DatabaseMetaData {
8691

8792
private Object[][] mockColumnsMetasReturnValue;
8893

94+
private Object[][] mockTableMetasReturnValue;
95+
8996
/**
9097
* Instantiate a new MockDatabaseMetaData
9198
*/
@@ -95,6 +102,7 @@ public MockDatabaseMetaData(MockConnection connection) {
95102
this.indexMetasReturnValue = connection.getDriver().getMockIndexMetasReturnValue();
96103
this.pkMetasReturnValue = connection.getDriver().getMockPkMetasReturnValue();
97104
this.mockColumnsMetasReturnValue = connection.getDriver().getMockOnUpdateColumnsReturnValue();
105+
this.mockTableMetasReturnValue = connection.getDriver().getMockTableMetasReturnValue();
98106
}
99107

100108
@Override
@@ -702,7 +710,8 @@ public ResultSet getProcedureColumns(String catalog, String schemaPattern, Strin
702710
@Override
703711
public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types)
704712
throws SQLException {
705-
return null;
713+
return new MockResultSet(this.connection.createStatement())
714+
.mockResultSet(tableMetaColumnLabels, mockTableMetasReturnValue);
706715
}
707716

708717
@Override

rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockDriver.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public class MockDriver extends com.alibaba.druid.mock.MockDriver {
5959
*/
6060
private Object[][] mockPkMetasReturnValue;
6161

62+
/**
63+
* the mock value of table meta return value
64+
*/
65+
private Object[][] mockTableMetasReturnValue;
66+
6267
/**
6368
*
6469
*/
@@ -81,25 +86,34 @@ public MockDriver(Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMe
8186
this(Lists.newArrayList(), new Object[][]{}, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, mockPkMetasReturnValue);
8287
}
8388

89+
public MockDriver(Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue, Object[][] mockPkMetasReturnValue, Object[][] mockTableMetasReturnValue) {
90+
this(Lists.newArrayList(), new Object[][]{}, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, mockPkMetasReturnValue, mockTableMetasReturnValue);
91+
}
92+
8493
public MockDriver(List<String> mockReturnValueColumnLabels, Object[][] mockReturnValue, Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue) {
8594
this(mockReturnValueColumnLabels, mockReturnValue, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, new Object[][]{});
8695
}
8796

8897
public MockDriver(List<String> mockReturnValueColumnLabels, Object[][] mockReturnValue, Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue, Object[][] mockPkMetasReturnValue) {
89-
this(mockReturnValueColumnLabels, mockReturnValue, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, mockPkMetasReturnValue, new Object[][]{});
98+
this(mockReturnValueColumnLabels, mockReturnValue, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, mockPkMetasReturnValue, new Object[][]{}, new Object[][]{});
99+
}
100+
101+
public MockDriver(List<String> mockReturnValueColumnLabels, Object[][] mockReturnValue, Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue, Object[][] mockPkMetasReturnValue, Object[][] mockTableMetasReturnValue) {
102+
this(mockReturnValueColumnLabels, mockReturnValue, mockColumnsMetasReturnValue, mockIndexMetasReturnValue, mockPkMetasReturnValue, new Object[][]{}, mockTableMetasReturnValue);
90103
}
91104

92105
/**
93106
* Instantiate a new MockDriver
94107
*/
95-
public MockDriver(List<String> mockReturnValueColumnLabels, Object[][] mockReturnValue, Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue, Object[][] mockPkMetasReturnValue, Object[][] mockOnUpdateColumnsReturnValue) {
108+
public MockDriver(List<String> mockReturnValueColumnLabels, Object[][] mockReturnValue, Object[][] mockColumnsMetasReturnValue, Object[][] mockIndexMetasReturnValue, Object[][] mockPkMetasReturnValue, Object[][] mockOnUpdateColumnsReturnValue, Object[][] mockTableMetasReturnValue) {
96109
this.mockReturnValueColumnLabels = mockReturnValueColumnLabels;
97110
this.mockReturnValue = mockReturnValue;
98111
this.mockColumnsMetasReturnValue = mockColumnsMetasReturnValue;
99112
this.mockIndexMetasReturnValue = mockIndexMetasReturnValue;
100113
this.mockPkMetasReturnValue = mockPkMetasReturnValue;
101114
this.setMockExecuteHandler(new MockExecuteHandlerImpl(mockReturnValueColumnLabels, mockReturnValue, mockColumnsMetasReturnValue));
102115
this.mockOnUpdateColumnsReturnValue = mockOnUpdateColumnsReturnValue;
116+
this.mockTableMetasReturnValue = mockTableMetasReturnValue;
103117
}
104118

105119
/**
@@ -198,4 +212,12 @@ public Object[][] getMockOnUpdateColumnsReturnValue() {
198212
public void setMockOnUpdateColumnsReturnValue(Object[][] mockOnUpdateColumnsReturnValue) {
199213
this.mockOnUpdateColumnsReturnValue = mockOnUpdateColumnsReturnValue;
200214
}
215+
216+
public Object[][] getMockTableMetasReturnValue() {
217+
return mockTableMetasReturnValue;
218+
}
219+
220+
public void setMockTableMetasReturnValue(Object[][] mockTableMetasReturnValue) {
221+
this.mockTableMetasReturnValue = mockTableMetasReturnValue;
222+
}
201223
}

rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCacheTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,14 @@ public class PostgresqlTableMetaCacheTest {
5959
new Object[] {"id"}
6060
};
6161

62+
private static Object[][] tableMetas =
63+
new Object[][]{
64+
new Object[]{"pt1", "public"}
65+
};
66+
6267
@Test
6368
public void getTableMetaTest() throws SQLException {
64-
MockDriver mockDriver = new MockDriver(columnMetas, indexMetas, pkMetas);
69+
MockDriver mockDriver = new MockDriver(columnMetas, indexMetas, pkMetas, tableMetas);
6570
DruidDataSource dataSource = new DruidDataSource();
6671
dataSource.setUrl("jdbc:mock:xxx");
6772
dataSource.setDriver(mockDriver);
@@ -73,6 +78,9 @@ public void getTableMetaTest() throws SQLException {
7378
TableMeta tableMeta = tableMetaCache.getTableMeta(proxy.getPlainConnection(), "pt1", proxy.getResourceId());
7479

7580
Assertions.assertNotNull(tableMeta);
81+
tableMeta = tableMetaCache.getTableMeta(proxy.getPlainConnection(), "Pt1", proxy.getResourceId());
82+
Assertions.assertNotNull(tableMeta);
83+
Assertions.assertEquals("pt1", tableMeta.getTableName());
7684

7785
tableMeta = tableMetaCache.getTableMeta(proxy.getPlainConnection(), "t.pt1", proxy.getResourceId());
7886

rm-datasource/src/test/java/org/apache/seata/rm/datasource/sql/struct/cache/SqlServerTableMetaCacheTest.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141
public class SqlServerTableMetaCacheTest {
4242
private static Object[][] columnMetas =
4343
new Object[][]{
44-
new Object[]{"", "", "mt1", "id", Types.INTEGER, "INTEGER", 64, 0, 10, 1, "", "", 0, 0, 64, 1, "NO", "YES"},
45-
new Object[]{"", "", "mt1", "name1", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES",
44+
new Object[]{"", "", "st1", "id", Types.INTEGER, "INTEGER", 64, 0, 10, 1, "", "", 0, 0, 64, 1, "NO", "YES"},
45+
new Object[]{"", "", "st1", "name1", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES",
4646
"NO"},
47-
new Object[]{"", "", "mt1", "name2", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 3, "YES",
47+
new Object[]{"", "", "st1", "name2", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 3, "YES",
4848
"NO"},
49-
new Object[]{"", "", "mt1", "name3", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 4, "YES",
49+
new Object[]{"", "", "st1", "name3", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 4, "YES",
5050
"NO"}
5151
};
5252
private static Object[][] indexMetas =
@@ -61,6 +61,11 @@ public class SqlServerTableMetaCacheTest {
6161
new Object[]{"id"}
6262
};
6363

64+
private static Object[][] tableMetas =
65+
new Object[][]{
66+
new Object[]{"st1", "m"}
67+
};
68+
6469
private TableMetaCache getTableMetaCache() {
6570
return TableMetaCacheFactory.getTableMetaCache(JdbcConstants.SQLSERVER);
6671
}
@@ -79,16 +84,16 @@ public void testTableMeta() {
7984
@Test
8085
public void getTableMetaTest_0() throws SQLException {
8186

82-
MockDriver mockDriver = new MockDriver(columnMetas, indexMetas, pkMetas);
87+
MockDriver mockDriver = new MockDriver(columnMetas, indexMetas, pkMetas, tableMetas);
8388
DruidDataSource dataSource = new DruidDataSource();
8489
dataSource.setUrl("jdbc:mock:xxx");
8590
dataSource.setDriver(mockDriver);
8691

8792
DataSourceProxy proxy = DataSourceProxyTest.getDataSourceProxy(dataSource);
8893

89-
TableMeta tableMeta = getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "m.mt1", proxy.getResourceId());
94+
TableMeta tableMeta = getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "m.st1", proxy.getResourceId());
9095

91-
Assertions.assertEquals("m.mt1", tableMeta.getTableName());
96+
Assertions.assertEquals("m.st1", tableMeta.getTableName());
9297
Assertions.assertEquals("id", tableMeta.getPrimaryKeyOnlyName().get(0));
9398

9499
Assertions.assertEquals("id", tableMeta.getColumnMeta("id").getColumnName());
@@ -115,12 +120,12 @@ public void getTableMetaTest_0() throws SQLException {
115120
};
116121
mockDriver.setMockIndexMetasReturnValue(indexMetas);
117122
Assertions.assertThrows(ShouldNeverHappenException.class, () -> {
118-
getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "mt2", proxy.getResourceId());
123+
getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "st2", proxy.getResourceId());
119124
});
120125

121126
mockDriver.setMockColumnsMetasReturnValue(null);
122127
Assertions.assertThrows(ShouldNeverHappenException.class, () -> {
123-
getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "mt2", proxy.getResourceId());
128+
getTableMetaCache().getTableMeta(proxy.getPlainConnection(), "st2", proxy.getResourceId());
124129
});
125130

126131
//can not cover the way to get from connection because the mockConnection not support

0 commit comments

Comments
 (0)