From 0e03695e9531cf1104ac9e1a331f785062dfb6b9 Mon Sep 17 00:00:00 2001 From: renliangyu857 <2918490262@qq.com> Date: Sat, 5 Nov 2022 16:04:57 +0800 Subject: [PATCH] bugfix: insert value is all parsed as string in insert on duplicate (#5028) --- changes/en-us/develop.md | 2 + changes/zh-cn/develop.md | 1 + .../MySQLInsertOnDuplicateUpdateExecutor.java | 43 +++++++++---------- ...QLInsertOnDuplicateUpdateExecutorTest.java | 35 ++++++++------- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/changes/en-us/develop.md b/changes/en-us/develop.md index 37a29a297fa..557cc1feb1d 100644 --- a/changes/en-us/develop.md +++ b/changes/en-us/develop.md @@ -29,10 +29,12 @@ Add changes here for all PR submitted to the develop branch. - [[#5018](https://github.com/seata/seata/pull/5018)] fix loader path in startup scripts - [[#5004](https://github.com/seata/seata/pull/5004)] fix duplicate image row for update join - [[#5033](https://github.com/seata/seata/pull/5033)] fix null exception when sql columns is empty for insert on duplicate +- [[#5033](https://github.com/seata/seata/pull/5023)] fix mysql InsertOnDuplicateUpdate insert value type recognize error - [[#5038](https://github.com/seata/seata/pull/5038)] remove @EnableConfigurationProperties({SagaAsyncThreadPoolProperties.class}) - [[#5050](https://github.com/seata/seata/pull/5050)] fix global session is not change to Committed in saga mode + ### optimize: - [[#4774](https://github.com/seata/seata/pull/4774)] optimize mysql8 dependencies for seataio/seata-server image - [[#4790](https://github.com/seata/seata/pull/4790)] Add a github action to publish Seata to OSSRH diff --git a/changes/zh-cn/develop.md b/changes/zh-cn/develop.md index 41efe22c743..f7e8c2c7ca3 100644 --- a/changes/zh-cn/develop.md +++ b/changes/zh-cn/develop.md @@ -31,6 +31,7 @@ - [[#5018](https://github.com/seata/seata/pull/5018)] 修复启动脚本中 loader path 使用相对路径导致 server 启动失败问题 - [[#5004](https://github.com/seata/seata/pull/5004)] 修复mysql update join行数据重复的问题 - [[#5033](https://github.com/seata/seata/pull/5033)] 修复InsertOnDuplicateUpdate的SQL语句中无插入列字段导致的空指针问题 +- [[#5033](https://github.com/seata/seata/pull/5023)] 修复InsertOnDuplicateUpdate中插入值解析为String类型导致的类型识别错误 - [[#5038](https://github.com/seata/seata/pull/5038)] 修复SagaAsyncThreadPoolProperties冲突问题 - [[#5050](https://github.com/seata/seata/pull/5050)] 修复Saga模式下全局状态未正确更改成Committed diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/mysql/MySQLInsertOnDuplicateUpdateExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/mysql/MySQLInsertOnDuplicateUpdateExecutor.java index 31f23161790..95da178e9b8 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/mysql/MySQLInsertOnDuplicateUpdateExecutor.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/mysql/MySQLInsertOnDuplicateUpdateExecutor.java @@ -52,6 +52,7 @@ import io.seata.sqlparser.SQLType; import io.seata.sqlparser.struct.Defaultable; import io.seata.sqlparser.struct.Null; +import io.seata.sqlparser.util.ColumnUtils; import io.seata.sqlparser.util.JdbcConstants; /** @@ -272,7 +273,8 @@ public TableRecords buildTableRecords2(TableMeta tableMeta, String selectSQL, Ar for (int i = 0; i < ts; i++) { List paramAppender = paramAppenderList.get(i); for (int j = 0; j < ds; j++) { - ps.setObject(i * ds + j + 1, "NULL".equals(paramAppender.get(j).toString()) ? null : paramAppender.get(j)); + Object param = paramAppender.get(j); + ps.setObject(i * ds + j + 1, (param instanceof Null) ? null : param); } } for (int i = 0; i < primaryKeys.size(); i++) { @@ -297,7 +299,7 @@ public String buildImageSQL(TableMeta tableMeta) { paramAppenderList = new ArrayList<>(); } SQLInsertRecognizer recognizer = (SQLInsertRecognizer) sqlRecognizer; - int insertNum = recognizer.getInsertParamsValue().size(); + int insertNum = recognizer.getInsertRows(getPkIndex().values()).size(); Map> imageParameterMap = buildImageParameters(recognizer); String prefix = "SELECT * "; StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL()); @@ -376,30 +378,27 @@ public Map> buildImageParameters(SQLInsertRecognizer r } Map> imageParameterMap = new LowerCaseLinkHashMap<>(); Map> parameters = ((PreparedStatementProxy) statementProxy).getParameters(); - // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - List insertParamsList = recognizer.getInsertParamsValue(); List sqlRecognizerColumns = recognizer.getInsertColumns(); List insertColumns = CollectionUtils.isEmpty(sqlRecognizerColumns) ? new ArrayList<>(getTableMeta().getAllColumns().keySet()) : sqlRecognizerColumns; - int paramsindex = 1; - for (String insertParams : insertParamsList) { - String[] insertParamsArray = insertParams.split(","); - for (int i = 0; i < insertColumns.size(); i++) { - String m = insertColumns.get(i); - String params = insertParamsArray[i]; - ArrayList imageListTemp = imageParameterMap.computeIfAbsent(m, k -> new ArrayList<>()); - if ("?".equals(params.trim())) { - ArrayList objects = parameters.get(paramsindex); - imageListTemp.addAll(objects); - paramsindex++; + final Map pkIndexMap = getPkIndex(); + List> insertRows = recognizer.getInsertRows(pkIndexMap.values()); + int placeHolderIndex = 1; + for (List row : insertRows) { + if (row.size() != insertColumns.size()) { + throw new IllegalArgumentException("insert row's size is not equal to column size"); + } + for (int i = 0;i < insertColumns.size();i++) { + String column = ColumnUtils.delEscape(insertColumns.get(i),getDbType()); + Object value = row.get(i); + ArrayList columnImages = imageParameterMap.computeIfAbsent(column, k -> new ArrayList<>()); + if (PLACEHOLDER.equals(value)) { + ArrayList objects = parameters.get(placeHolderIndex); + columnImages.addAll(objects); + placeHolderIndex++; } else { - // params is character string constant - if ((params.trim().startsWith("'") && params.trim().endsWith("'")) || params.trim().startsWith("\"") && params.trim().endsWith("\"")) { - params = params.trim(); - params = params.substring(1, params.length() - 1); - } - imageListTemp.add(params); + columnImages.add(value); } - imageParameterMap.put(m, imageListTemp); + imageParameterMap.put(column,columnImages); } } return imageParameterMap; diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/MySQLInsertOnDuplicateUpdateExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/MySQLInsertOnDuplicateUpdateExecutorTest.java index e54bb79af2a..38da2d6e435 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/MySQLInsertOnDuplicateUpdateExecutorTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/MySQLInsertOnDuplicateUpdateExecutorTest.java @@ -91,11 +91,12 @@ public void init() { @Test public void TestBuildImageParameters(){ mockParameters(); - List insertParamsList = new ArrayList<>(); - insertParamsList.add("?,?,?,?"); - insertParamsList.add("?,?,?,?"); - when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList); + List> rows = new ArrayList<>(); + rows.add(Arrays.asList("?","?","?","?")); + rows.add(Arrays.asList("?","?","?","?")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows); mockInsertColumns(); + doReturn(pkIndexMap).when(insertOrUpdateExecutor).getPkIndex(); Map> imageParameterMap = insertOrUpdateExecutor.buildImageParameters(sqlInsertRecognizer); Assertions.assertEquals(imageParameterMap.toString(),mockImageParameterMap().toString()); } @@ -103,11 +104,12 @@ public void TestBuildImageParameters(){ @Test public void TestBuildImageParameters_contain_constant(){ mockImageParameterMap_contain_constant(); - List insertParamsList = new ArrayList<>(); - insertParamsList.add("?,?,?,userStatus1"); - insertParamsList.add("?,?,?,userStatus2"); - when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList); + List> insertRows = new ArrayList<>(); + insertRows.add(Arrays.asList("?","?","?","userStatus1")); + insertRows.add(Arrays.asList("?","?","?","userStatus2")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(insertRows); mockInsertColumns(); + doReturn(pkIndexMap).when(insertOrUpdateExecutor).getPkIndex(); Map> imageParameterMap = insertOrUpdateExecutor.buildImageParameters(sqlInsertRecognizer); Assertions.assertEquals(imageParameterMap.toString(),mockImageParameterMap().toString()); } @@ -117,12 +119,13 @@ public void testBuildImageSQL(){ String selectSQLStr = "SELECT * FROM null WHERE (user_id = ? ) OR (id = ? ) OR (user_id = ? ) OR (id = ? ) "; String paramAppenderListStr = "[[userId1, 100], [userId2, 101]]"; mockImageParameterMap_contain_constant(); - List insertParamsList = new ArrayList<>(); - insertParamsList.add("?,?,?,userStatus1"); - insertParamsList.add("?,?,?,userStatus2"); - when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList); + List> insertRows = new ArrayList<>(); + insertRows.add(Arrays.asList("?","?","?","userStatus1")); + insertRows.add(Arrays.asList("?","?","?","userStatus2")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(insertRows); mockInsertColumns(); mockAllIndexes(); + doReturn(pkIndexMap).when(insertOrUpdateExecutor).getPkIndex(); String selectSQL = insertOrUpdateExecutor.buildImageSQL(tableMeta); Assertions.assertEquals(selectSQLStr,selectSQL); Assertions.assertEquals(paramAppenderListStr,insertOrUpdateExecutor.getParamAppenderList().toString()); @@ -131,10 +134,10 @@ public void testBuildImageSQL(){ @Test public void testBeforeImage(){ mockImageParameterMap_contain_constant(); - List insertParamsList = new ArrayList<>(); - insertParamsList.add("?,?,?,userStatus1"); - insertParamsList.add("?,?,?,userStatus2"); - when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList); + List> insertRows = new ArrayList<>(); + insertRows.add(Arrays.asList("?,?,?,userStatus1")); + insertRows.add(Arrays.asList("?,?,?,userStatus2")); + when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(insertRows); mockInsertColumns(); mockAllIndexes(); doReturn(tableMeta).when(insertOrUpdateExecutor).getTableMeta();