Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix: fix the sql exception when pk is str #4653

Merged
merged 10 commits into from Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -18,11 +18,14 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Collections;
import java.util.StringJoiner;


import com.google.common.base.Joiner;
import io.seata.common.exception.NotSupportYetException;
import io.seata.common.exception.ShouldNeverHappenException;
Expand Down Expand Up @@ -122,8 +125,7 @@ protected void prepareUndoLogAll(TableRecords beforeImage, TableRecords afterIma
return;
}
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
TableRecords lockKeyRecords = afterImage;
String lockKeys = buildLockKey(lockKeyRecords);
String lockKeys = buildLockKey(afterImage);
connectionProxy.appendLockKey(lockKeys);
buildUndoItemAll(connectionProxy, beforeImage, afterImage);
}
Expand Down Expand Up @@ -201,7 +203,8 @@ protected SQLUndoLog buildUndoItem(SQLType sqlType, TableRecords beforeImage, Ta

@Override
protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
TableMeta tmeta = getTableMeta();
TableMeta tableMeta = getTableMeta();

List<Row> rows = beforeImage.getRows();
Map<String, ArrayList<Object>> primaryValueMap = new HashMap<>();
rows.forEach(m -> {
Expand All @@ -212,49 +215,59 @@ protected TableRecords afterImage(TableRecords beforeImage) throws SQLException
});
});

// The origin select sql contains the unique keys sql
StringBuilder afterImageSql = new StringBuilder(selectSQL);
List<Object> primaryValues = new ArrayList<>();

// Appends the pk when the origin select sql not contains
for (int i = 0; i < rows.size(); i++) {
int finalI = i;
List<String> wherePrimaryList = new ArrayList<>();
primaryValueMap.forEach((k, v) -> {
wherePrimaryList.add(k + " = " + primaryValueMap.get(k).get(finalI) + " ");
wherePrimaryList.add(k + " = ? ");
primaryValues.add(v);
});
afterImageSql.append(" OR (").append(Joiner.on(" and ").join(wherePrimaryList)).append(") ");
}
return buildTableRecords2(tmeta, afterImageSql.toString(), paramAppenderList);

return buildTableRecords2(tableMeta, afterImageSql.toString(), paramAppenderList, primaryValues);
}

@Override
public TableRecords beforeImage() throws SQLException {
TableMeta tmeta = getTableMeta();
//after image sql the same of before image
TableMeta tableMeta = getTableMeta();
// After image sql the same of before image
if (StringUtils.isBlank(selectSQL)) {
paramAppenderList = new ArrayList<>();
selectSQL = buildImageSQL(tmeta);
selectSQL = buildImageSQL(tableMeta);
}
return buildTableRecords2(tmeta, selectSQL, paramAppenderList);
return buildTableRecords2(tableMeta, selectSQL, paramAppenderList, Collections.emptyList());
}

/**
* build TableRecords
*
* @param tableMeta
* @param selectSQL
* @param paramAppenderList
* @param tableMeta the meta info of table
* @param selectSQL the sql to select images
* @param paramAppenderList the param list
* @param primaryKeys the primary keys
* @return the table records
* @throws SQLException
* @throws SQLException then execute fail
*/
public TableRecords buildTableRecords2(TableMeta tableMeta, String selectSQL, ArrayList<List<Object>> paramAppenderList) throws SQLException {
public TableRecords buildTableRecords2(TableMeta tableMeta, String selectSQL, ArrayList<List<Object>> paramAppenderList, List<Object> primaryKeys) throws SQLException {
ResultSet rs = null;
try (PreparedStatement ps = statementProxy.getConnection().prepareStatement(selectSQL + " FOR UPDATE")) {
if (CollectionUtils.isNotEmpty(paramAppenderList)) {
for (int i = 0, ts = paramAppenderList.size(); i < ts; i++) {
List<Object> paramAppender = paramAppenderList.get(i);
for (int j = 0, ds = paramAppender.size(); j < ds; j++) {
ps.setObject(i * ds + j + 1, "NULL".equals(paramAppender.get(j).toString()) ? null : paramAppender.get(j));
}
int ts = CollectionUtils.isEmpty(paramAppenderList) ? 0 : paramAppenderList.size();
int ds = ts == 0 ? 0 : paramAppenderList.get(0).size();
for (int i = 0; i < ts; i++) {
List<Object> 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));
}
}
for (int i = 0; i < primaryKeys.size(); i++) {
ps.setObject(ts * ds + i + 1, primaryKeys.get(i));
}

rs = ps.executeQuery();
return TableRecords.buildRecords(tableMeta, rs);
} finally {
Expand All @@ -265,7 +278,7 @@ public TableRecords buildTableRecords2(TableMeta tableMeta, String selectSQL, Ar
/**
* build image sql
*
* @param tableMeta
* @param tableMeta the meta info of table
* @return image sql
*/
public String buildImageSQL(TableMeta tableMeta) {
Expand All @@ -274,8 +287,8 @@ public String buildImageSQL(TableMeta tableMeta) {
}
SQLInsertRecognizer recognizer = (SQLInsertRecognizer) sqlRecognizer;
int insertNum = recognizer.getInsertParamsValue().size();
Map<String, ArrayList<Object>> imageParamperterMap = buildImageParamperters(recognizer);
StringBuilder prefix = new StringBuilder("SELECT * ");
Map<String, ArrayList<Object>> imageParameterMap = buildImageParameters(recognizer);
String prefix = "SELECT * ";
StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL());
boolean[] isContainWhere = {false};
for (int i = 0; i < insertNum; i++) {
Expand All @@ -287,12 +300,12 @@ public String buildImageSQL(TableMeta tableMeta) {
List<String> uniqueList = new ArrayList<>();
for (ColumnMeta m : v.getValues()) {
String columnName = m.getColumnName();
if (imageParamperterMap.get(columnName) == null && m.getColumnDef() != null) {
if (imageParameterMap.get(columnName) == null && m.getColumnDef() != null) {
uniqueList.add(columnName + " = DEFAULT(" + columnName + ") ");
columnIsNull = false;
continue;
}
if ((imageParamperterMap.get(columnName) == null && m.getColumnDef() == null) || imageParamperterMap.get(columnName).get(finalI) == null || imageParamperterMap.get(columnName).get(finalI) instanceof Null) {
if ((imageParameterMap.get(columnName) == null && m.getColumnDef() == null) || imageParameterMap.get(columnName).get(finalI) == null || imageParameterMap.get(columnName).get(finalI) instanceof Null) {
if (!"PRIMARY".equalsIgnoreCase(k)) {
columnIsNull = false;
uniqueList.add(columnName + " is ? ");
Expand All @@ -303,7 +316,7 @@ public String buildImageSQL(TableMeta tableMeta) {
}
columnIsNull = false;
uniqueList.add(columnName + " = ? ");
paramAppenderTempList.add(imageParamperterMap.get(columnName).get(finalI));
paramAppenderTempList.add(imageParameterMap.get(columnName).get(finalI));
}
if (!columnIsNull) {
if (isContainWhere[0]) {
Expand All @@ -317,31 +330,31 @@ public String buildImageSQL(TableMeta tableMeta) {
});
paramAppenderList.add(paramAppenderTempList);
}
StringJoiner selectSQLJoin = new StringJoiner(", ", prefix.toString(), suffix.toString());
StringJoiner selectSQLJoin = new StringJoiner(", ", prefix, suffix.toString());
return selectSQLJoin.toString();
}

/**
* build sql params
*
* @param recognizer
* @param recognizer the sql recognizer
* @return map, key is column, value is paramperter
*/
@SuppressWarnings("lgtm[java/dereferenced-value-may-be-null]")
public Map<String, ArrayList<Object>> buildImageParamperters(SQLInsertRecognizer recognizer) {
List<String> duplicateKeyUpdateCloms = recognizer.getDuplicateKeyUpdate();
if (CollectionUtils.isNotEmpty(duplicateKeyUpdateCloms)) {
public Map<String, ArrayList<Object>> buildImageParameters(SQLInsertRecognizer recognizer) {
List<String> duplicateKeyUpdateColumns = recognizer.getDuplicateKeyUpdate();
if (CollectionUtils.isNotEmpty(duplicateKeyUpdateColumns)) {
getTableMeta().getAllIndexes().forEach((k, v) -> {
if ("PRIMARY".equalsIgnoreCase(k)) {
for (ColumnMeta m : v.getValues()) {
if (duplicateKeyUpdateCloms.contains(m.getColumnName())) {
if (duplicateKeyUpdateColumns.contains(m.getColumnName())) {
throw new ShouldNeverHappenException("update pk value is not supported!");
}
}
}
});
}
Map<String, ArrayList<Object>> imageParamperterMap = new HashMap<>();
Map<String, ArrayList<Object>> imageParameterMap = new HashMap<>();
Map<Integer, ArrayList<Object>> parameters = ((PreparedStatementProxy) statementProxy).getParameters();
// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
List<String> insertParamsList = recognizer.getInsertParamsValue();
Expand All @@ -352,13 +365,13 @@ public Map<String, ArrayList<Object>> buildImageParamperters(SQLInsertRecognizer
for (int i = 0; i < insertColumns.size(); i++) {
String m = insertColumns.get(i);
String params = insertParamsArray[i];
ArrayList<Object> imageListTemp = imageParamperterMap.computeIfAbsent(m, k -> new ArrayList<>());
ArrayList<Object> imageListTemp = imageParameterMap.computeIfAbsent(m, k -> new ArrayList<>());
if ("?".equals(params.trim())) {
ArrayList<Object> objects = parameters.get(paramsindex);
imageListTemp.addAll(objects);
paramsindex++;
} else if (params instanceof String) {
// params is characterstring constant
// 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);
Expand All @@ -367,10 +380,10 @@ public Map<String, ArrayList<Object>> buildImageParamperters(SQLInsertRecognizer
} else {
imageListTemp.add(params);
}
imageParamperterMap.put(m, imageListTemp);
imageParameterMap.put(m, imageListTemp);
}
}
return imageParamperterMap;
return imageParameterMap;
}

}
Expand Up @@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collections;

import com.google.common.collect.Lists;
import io.seata.rm.datasource.ConnectionProxy;
Expand Down Expand Up @@ -87,34 +88,34 @@ public void init() {
}

@Test
public void TestBuildImageParamperters(){
public void TestBuildImageParameters(){
mockParameters();
List<String> insertParamsList = new ArrayList<>();
insertParamsList.add("?,?,?,?");
insertParamsList.add("?,?,?,?");
when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList);
mockInsertColumns();
Map<String, ArrayList<Object>> imageParamperterMap = insertOrUpdateExecutor.buildImageParamperters(sqlInsertRecognizer);
Assertions.assertEquals(imageParamperterMap.toString(),mockImageParamperterMap().toString());
Map<String, ArrayList<Object>> imageParameterMap = insertOrUpdateExecutor.buildImageParameters(sqlInsertRecognizer);
Assertions.assertEquals(imageParameterMap.toString(),mockImageParameterMap().toString());
}

@Test
public void TestBuildImageParamperters_contain_constant(){
mockImageParamperterMap_contain_constant();
public void TestBuildImageParameters_contain_constant(){
mockImageParameterMap_contain_constant();
List<String> insertParamsList = new ArrayList<>();
insertParamsList.add("?,?,?,userStatus1");
insertParamsList.add("?,?,?,userStatus2");
when(sqlInsertRecognizer.getInsertParamsValue()).thenReturn(insertParamsList);
mockInsertColumns();
Map<String, ArrayList<Object>> imageParamperterMap = insertOrUpdateExecutor.buildImageParamperters(sqlInsertRecognizer);
Assertions.assertEquals(imageParamperterMap.toString(),mockImageParamperterMap().toString());
Map<String, ArrayList<Object>> imageParameterMap = insertOrUpdateExecutor.buildImageParameters(sqlInsertRecognizer);
Assertions.assertEquals(imageParameterMap.toString(),mockImageParameterMap().toString());
}

@Test
public void testBuildImageSQL(){
String selectSQLStr = "SELECT * FROM null WHERE (user_id = ? ) OR (id = ? ) OR (user_id = ? ) OR (id = ? ) ";
String paramAppenderListStr = "[[userId1, 100], [userId2, 101]]";
mockImageParamperterMap_contain_constant();
mockImageParameterMap_contain_constant();
List<String> insertParamsList = new ArrayList<>();
insertParamsList.add("?,?,?,userStatus1");
insertParamsList.add("?,?,?,userStatus2");
Expand All @@ -128,7 +129,7 @@ public void testBuildImageSQL(){

@Test
public void testBeforeImage(){
mockImageParamperterMap_contain_constant();
mockImageParameterMap_contain_constant();
List<String> insertParamsList = new ArrayList<>();
insertParamsList.add("?,?,?,userStatus1");
insertParamsList.add("?,?,?,userStatus2");
Expand All @@ -140,7 +141,7 @@ public void testBeforeImage(){
TableRecords tableRecords = new TableRecords();
String selectSQL = insertOrUpdateExecutor.buildImageSQL(tableMeta);
ArrayList<List<Object>> paramAppenderList = insertOrUpdateExecutor.getParamAppenderList();
doReturn(tableRecords).when(insertOrUpdateExecutor).buildTableRecords2(tableMeta,selectSQL,paramAppenderList);
doReturn(tableRecords).when(insertOrUpdateExecutor).buildTableRecords2(tableMeta,selectSQL,paramAppenderList, Collections.emptyList());
TableRecords tableRecordsResult = insertOrUpdateExecutor.beforeImage();
Assertions.assertEquals(tableRecords,tableRecordsResult);
} catch (SQLException throwables) {
Expand Down Expand Up @@ -216,7 +217,7 @@ private void mockParameters() {
* exist insert parms is constant
* {1=[100], 2=[userId1], 3=[userName1], 4=[101], 5=[userId2], 6=[userName2]}
*/
private void mockImageParamperterMap_contain_constant() {
private void mockImageParameterMap_contain_constant() {
Map<Integer,ArrayList<Object>> paramters = new HashMap<>(4);
ArrayList arrayList10 = new ArrayList<>();
arrayList10.add(PK_VALUE);
Expand All @@ -240,25 +241,25 @@ private void mockImageParamperterMap_contain_constant() {
when(psp.getParameters()).thenReturn(paramters);
}

private Map<String, ArrayList<Object>> mockImageParamperterMap(){
Map<String, ArrayList<Object>> imageParamperterMap = new HashMap<>();
private Map<String, ArrayList<Object>> mockImageParameterMap(){
Map<String, ArrayList<Object>> imageParameterMap = new HashMap<>();
ArrayList<Object> idList = new ArrayList<>();
idList.add("100");
idList.add("101");
imageParamperterMap.put("id",idList);
imageParameterMap.put("id",idList);
ArrayList<Object> user_idList = new ArrayList<>();
user_idList.add("userId1");
user_idList.add("userId2");
imageParamperterMap.put("user_id",user_idList);
imageParameterMap.put("user_id",user_idList);
ArrayList<Object> user_nameList = new ArrayList<>();
user_nameList.add("userName1");
user_nameList.add("userName2");
imageParamperterMap.put("user_name",user_nameList);
imageParameterMap.put("user_name",user_nameList);
ArrayList<Object> user_statusList = new ArrayList<>();
user_statusList.add("userStatus1");
user_statusList.add("userStatus2");
imageParamperterMap.put("user_status",user_statusList);
return imageParamperterMap;
imageParameterMap.put("user_status",user_statusList);
return imageParameterMap;
}

private void mockParametersOfOnePk() {
Expand Down