fix: 【数据集 】SQL 参数拼接报错

This commit is contained in:
tjlygdx
2026-06-08 12:53:09 +08:00
parent da7fb16393
commit 758aab8f55
2 changed files with 323 additions and 75 deletions

View File

@@ -77,46 +77,34 @@ public class DeSqlparserUtils {
StringBuilder sqlItemBuilder = new StringBuilder();
int sqlItemLastIndex = 0;
while (m.find()) {
if (m.start() < sqlItemLastIndex) {
continue;
}
String sqlVariable = m.group();
sqlItemBuilder.append(sqlItem, sqlItemLastIndex, m.start());
boolean replaceParamItem = false;
String variableName = sqlVariable.substring(2, sqlVariable.length() - 1);
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
if (filterParameter != null) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragment(filterParameter);
boolean quoted = isQuotedVariable(sqlItem, m.start(), m.end());
if (quoted) {
sqlItemBuilder.setLength(sqlItemBuilder.length() - 1);
QuotedLiteralContext quotedLiteralContext = findQuotedLiteralContext(sqlItem, m.start(), m.end());
int appendEnd = quotedLiteralContext == null ? m.start() : quotedLiteralContext.start();
if (appendEnd < sqlItemLastIndex) {
continue;
}
sqlItemBuilder.append(sqlItem, sqlItemLastIndex, appendEnd);
PreparedSqlFragment preparedSqlFragment;
if (quotedLiteralContext != null) {
preparedSqlFragment = buildPreparedSqlFragmentForQuotedLiteral(quotedLiteralContext, parameters, isEdit, isFromDataSet);
if (preparedSqlFragment != null) {
sqlItemBuilder.append(preparedSqlFragment.replacement());
sqlItemLastIndex = quotedLiteralContext.end() + 1;
sqlItemFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaceParamItem = true;
}
sqlItemBuilder.append(preparedSqlFragment.replacement());
sqlItemLastIndex = quoted ? m.end() + 1 : m.end();
sqlItemFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaceParamItem = true;
} else {
if (defaultsSqlVariableDetail != null && StringUtils.isNotEmpty(defaultsSqlVariableDetail.getDefaultValue())) {
if (!isEdit && isFromDataSet && defaultsSqlVariableDetail.getDefaultValueScope().equals(SqlVariableDetails.DefaultValueScope.ALLSCOPE)) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
boolean quoted = isQuotedVariable(sqlItem, m.start(), m.end());
if (quoted) {
sqlItemBuilder.setLength(sqlItemBuilder.length() - 1);
}
sqlItemBuilder.append(preparedSqlFragment.replacement());
sqlItemLastIndex = quoted ? m.end() + 1 : m.end();
sqlItemFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaceParamItem = true;
}
if (isEdit) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
boolean quoted = isQuotedVariable(sqlItem, m.start(), m.end());
if (quoted) {
sqlItemBuilder.setLength(sqlItemBuilder.length() - 1);
}
sqlItemBuilder.append(preparedSqlFragment.replacement());
sqlItemLastIndex = quoted ? m.end() + 1 : m.end();
sqlItemFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaceParamItem = true;
}
preparedSqlFragment = resolvePreparedSqlFragment(variableName, parameters, isEdit, isFromDataSet);
if (preparedSqlFragment != null) {
sqlItemBuilder.append(preparedSqlFragment.replacement());
sqlItemLastIndex = m.end();
sqlItemFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaceParamItem = true;
}
}
if (!replaceParamItem) {
@@ -248,6 +236,25 @@ public class DeSqlparserUtils {
return null;
}
private PreparedSqlFragment resolvePreparedSqlFragment(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
if (filterParameter != null) {
return buildPreparedSqlFragment(filterParameter);
}
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
if (shouldUseDefaultValue(defaultsSqlVariableDetail, isEdit, isFromDataSet)) {
return buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
}
return null;
}
private boolean shouldUseDefaultValue(SqlVariableDetails sqlVariableDetails, boolean isEdit, boolean isFromDataSet) {
if (sqlVariableDetails == null || StringUtils.isEmpty(sqlVariableDetails.getDefaultValue())) {
return false;
}
return isEdit || isFromDataSet && sqlVariableDetails.getDefaultValueScope() == SqlVariableDetails.DefaultValueScope.ALLSCOPE;
}
private boolean isQuotedVariable(String sqlItem, int start, int end) {
return start > 0
&& end < sqlItem.length()
@@ -255,6 +262,118 @@ public class DeSqlparserUtils {
&& sqlItem.charAt(end) == '\'';
}
private QuotedLiteralContext findQuotedLiteralContext(String sqlItem, int start, int end) {
int literalStart = -1;
for (int i = 0; i < sqlItem.length(); i++) {
if (sqlItem.charAt(i) != '\'') {
continue;
}
if (literalStart < 0) {
literalStart = i;
continue;
}
if (i + 1 < sqlItem.length() && sqlItem.charAt(i + 1) == '\'') {
i++;
continue;
}
if (start > literalStart && end <= i) {
return new QuotedLiteralContext(literalStart, i, sqlItem.substring(literalStart + 1, i));
}
literalStart = -1;
}
return null;
}
private PreparedSqlFragment buildPreparedSqlFragmentForQuotedLiteral(QuotedLiteralContext quotedLiteralContext, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
List<LiteralSegment> literalSegments = parseLiteralSegments(quotedLiteralContext.content());
if (literalSegments.size() == 1 && literalSegments.get(0).variable()) {
return resolvePreparedSqlFragment(literalSegments.get(0).content(), parameters, isEdit, isFromDataSet);
}
StringBuilder preparedValueBuilder = new StringBuilder();
boolean hasVariable = false;
for (LiteralSegment literalSegment : literalSegments) {
if (!literalSegment.variable()) {
preparedValueBuilder.append(unescapeQuotedLiteralText(literalSegment.content()));
continue;
}
hasVariable = true;
String preparedValue = resolveQuotedLiteralVariableValue(literalSegment.content(), parameters, isEdit, isFromDataSet);
if (preparedValue == null) {
return null;
}
preparedValueBuilder.append(preparedValue);
}
if (!hasVariable) {
return null;
}
TableFieldWithValue tableFieldWithValue = new TableFieldWithValue();
tableFieldWithValue.setFiledName(firstVariableName(literalSegments));
tableFieldWithValue.setType(Types.VARCHAR);
tableFieldWithValue.setColumnTypeName("VARCHAR");
tableFieldWithValue.setValue(preparedValueBuilder.toString());
return new PreparedSqlFragment("?", Collections.singletonList(tableFieldWithValue));
}
private String resolveQuotedLiteralVariableValue(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
List<String> preparedValues = resolvePreparedValuesForQuotedLiteral(variableName, parameters, isEdit, isFromDataSet);
if (CollectionUtils.isEmpty(preparedValues)) {
return null;
}
if (preparedValues.size() != 1) {
DEException.throwException("SQL模板字符串仅支持单值参数");
}
return preparedValues.get(0);
}
private List<String> resolvePreparedValuesForQuotedLiteral(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
if (filterParameter != null) {
return resolvePreparedValues(filterParameter);
}
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
if (!shouldUseDefaultValue(defaultsSqlVariableDetail, isEdit, isFromDataSet)) {
return null;
}
SqlVariableDetails defaultValueDetail = new SqlVariableDetails();
defaultValueDetail.setVariableName(defaultsSqlVariableDetail.getVariableName());
defaultValueDetail.setType(defaultsSqlVariableDetail.getType());
defaultValueDetail.setDeType(defaultsSqlVariableDetail.getDeType());
defaultValueDetail.setId(defaultsSqlVariableDetail.getId());
defaultValueDetail.setOperator(defaultsSqlVariableDetail.getOperator());
defaultValueDetail.setValue(Collections.singletonList(defaultsSqlVariableDetail.getDefaultValue()));
return resolvePreparedValues(defaultValueDetail);
}
private List<LiteralSegment> parseLiteralSegments(String literalContent) {
List<LiteralSegment> literalSegments = new ArrayList<>();
Matcher matcher = Pattern.compile(sqlParamsRegex).matcher(literalContent);
int lastIndex = 0;
while (matcher.find()) {
if (matcher.start() > lastIndex) {
literalSegments.add(new LiteralSegment(false, literalContent.substring(lastIndex, matcher.start())));
}
literalSegments.add(new LiteralSegment(true, matcher.group().substring(2, matcher.group().length() - 1)));
lastIndex = matcher.end();
}
if (lastIndex < literalContent.length()) {
literalSegments.add(new LiteralSegment(false, literalContent.substring(lastIndex)));
}
return literalSegments;
}
private String unescapeQuotedLiteralText(String text) {
return StringUtils.replace(text, "''", "'");
}
private String firstVariableName(List<LiteralSegment> literalSegments) {
for (LiteralSegment literalSegment : literalSegments) {
if (literalSegment.variable()) {
return literalSegment.content();
}
}
return null;
}
private PreparedSqlFragment buildPreparedSqlFragment(SqlVariableDetails sqlVariableDetails) {
List<TableFieldWithValue> values = new ArrayList<>();
List<String> replacements = new ArrayList<>();
@@ -342,6 +461,12 @@ public class DeSqlparserUtils {
private record PreparedSqlFragment(String replacement, List<TableFieldWithValue> tableFieldWithValues) {
}
private record QuotedLiteralContext(int start, int end, String content) {
}
private record LiteralSegment(boolean variable, String content) {
}
private String handleSubstitutedSql(String sysVariableId) {
if (userEntity != null) {
if (sysVariableId.equalsIgnoreCase("sysParams.userId")) {

View File

@@ -52,6 +52,7 @@ public class SqlparserUtils {
private UserFormVO userEntity;
private final List<Map<String, String>> sysParams = new ArrayList<>();
private static final String deVariablePattern = "\\$DE_PARAM\\{(.*?)\\}";
private List<SqlVariableDetails> defaultsSqlVariableDetails = new ArrayList<>();
public String handleVariableDefaultValue(String sql, String sqlVariableDetails, boolean isEdit, boolean isFromDataSet, List<SqlVariableDetails> parameters, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap, PluginManageApi pluginManage, UserFormVO userEntity) {
return handleVariableDefaultValueWithPreparedParams(sql, sqlVariableDetails, isEdit, isFromDataSet, parameters, isCross, dsMap, pluginManage, userEntity).getSql();
@@ -75,58 +76,43 @@ public class SqlparserUtils {
sql = sql.substring(0, sql.length() - 1);
}
List<TableFieldWithValue> tableFieldWithValues = new ArrayList<>();
defaultsSqlVariableDetails = new ArrayList<>();
if (StringUtils.isNotEmpty(sqlVariableDetails)) {
TypeReference<List<SqlVariableDetails>> listTypeReference = new TypeReference<List<SqlVariableDetails>>() {
};
List<SqlVariableDetails> defaultsSqlVariableDetails = JsonUtil.parseList(sqlVariableDetails, listTypeReference);
defaultsSqlVariableDetails = JsonUtil.parseList(sqlVariableDetails, listTypeReference);
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(sql);
StringBuilder sqlBuilder = new StringBuilder();
int lastIndex = 0;
while (matcher.find()) {
sqlBuilder.append(sql, lastIndex, matcher.start());
if (matcher.start() < lastIndex) {
continue;
}
String variableName = matcher.group().substring(2, matcher.group().length() - 1);
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
QuotedLiteralContext quotedLiteralContext = findQuotedLiteralContext(sql, matcher.start(), matcher.end());
int appendEnd = quotedLiteralContext == null ? matcher.start() : quotedLiteralContext.start();
if (appendEnd < lastIndex) {
continue;
}
sqlBuilder.append(sql, lastIndex, appendEnd);
boolean replaced = false;
if (filterParameter != null) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragment(filterParameter);
boolean quoted = isQuotedVariable(sql, matcher.start(), matcher.end());
if (quoted) {
sqlBuilder.setLength(sqlBuilder.length() - 1);
PreparedSqlFragment preparedSqlFragment;
if (quotedLiteralContext != null) {
preparedSqlFragment = buildPreparedSqlFragmentForQuotedLiteral(quotedLiteralContext, parameters, isEdit, isFromDataSet);
if (preparedSqlFragment != null) {
sqlBuilder.append(preparedSqlFragment.replacement());
lastIndex = quotedLiteralContext.end() + 1;
tableFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaced = true;
}
sqlBuilder.append(preparedSqlFragment.replacement());
if (quoted) {
lastIndex = matcher.end() + 1;
} else {
lastIndex = matcher.end();
}
tableFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaced = true;
} else {
if (defaultsSqlVariableDetail != null && StringUtils.isNotEmpty(defaultsSqlVariableDetail.getDefaultValue())) {
if (!isEdit && isFromDataSet && defaultsSqlVariableDetail.getDefaultValueScope().equals(SqlVariableDetails.DefaultValueScope.ALLSCOPE)) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
boolean quoted = isQuotedVariable(sql, matcher.start(), matcher.end());
if (quoted) {
sqlBuilder.setLength(sqlBuilder.length() - 1);
}
sqlBuilder.append(preparedSqlFragment.replacement());
lastIndex = quoted ? matcher.end() + 1 : matcher.end();
tableFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaced = true;
}
if (isEdit) {
PreparedSqlFragment preparedSqlFragment = buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
boolean quoted = isQuotedVariable(sql, matcher.start(), matcher.end());
if (quoted) {
sqlBuilder.setLength(sqlBuilder.length() - 1);
}
sqlBuilder.append(preparedSqlFragment.replacement());
lastIndex = quoted ? matcher.end() + 1 : matcher.end();
tableFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaced = true;
}
preparedSqlFragment = resolvePreparedSqlFragment(variableName, parameters, isEdit, isFromDataSet);
if (preparedSqlFragment != null) {
sqlBuilder.append(preparedSqlFragment.replacement());
lastIndex = matcher.end();
tableFieldWithValues.addAll(preparedSqlFragment.tableFieldWithValues());
replaced = true;
}
}
if (!replaced) {
@@ -735,6 +721,25 @@ public class SqlparserUtils {
return null;
}
private PreparedSqlFragment resolvePreparedSqlFragment(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
if (filterParameter != null) {
return buildPreparedSqlFragment(filterParameter);
}
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
if (shouldUseDefaultValue(defaultsSqlVariableDetail, isEdit, isFromDataSet)) {
return buildPreparedSqlFragmentForDefaultValue(defaultsSqlVariableDetail);
}
return null;
}
private boolean shouldUseDefaultValue(SqlVariableDetails sqlVariableDetails, boolean isEdit, boolean isFromDataSet) {
if (sqlVariableDetails == null || StringUtils.isEmpty(sqlVariableDetails.getDefaultValue())) {
return false;
}
return isEdit || isFromDataSet && sqlVariableDetails.getDefaultValueScope() == SqlVariableDetails.DefaultValueScope.ALLSCOPE;
}
private boolean isQuotedVariable(String sql, int start, int end) {
return start > 0
&& end < sql.length()
@@ -742,6 +747,118 @@ public class SqlparserUtils {
&& sql.charAt(end) == '\'';
}
private QuotedLiteralContext findQuotedLiteralContext(String sql, int start, int end) {
int literalStart = -1;
for (int i = 0; i < sql.length(); i++) {
if (sql.charAt(i) != '\'') {
continue;
}
if (literalStart < 0) {
literalStart = i;
continue;
}
if (i + 1 < sql.length() && sql.charAt(i + 1) == '\'') {
i++;
continue;
}
if (start > literalStart && end <= i) {
return new QuotedLiteralContext(literalStart, i, sql.substring(literalStart + 1, i));
}
literalStart = -1;
}
return null;
}
private PreparedSqlFragment buildPreparedSqlFragmentForQuotedLiteral(QuotedLiteralContext quotedLiteralContext, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
List<LiteralSegment> literalSegments = parseLiteralSegments(quotedLiteralContext.content());
if (literalSegments.size() == 1 && literalSegments.get(0).variable()) {
return resolvePreparedSqlFragment(literalSegments.get(0).content(), parameters, isEdit, isFromDataSet);
}
StringBuilder preparedValueBuilder = new StringBuilder();
boolean hasVariable = false;
for (LiteralSegment literalSegment : literalSegments) {
if (!literalSegment.variable()) {
preparedValueBuilder.append(unescapeQuotedLiteralText(literalSegment.content()));
continue;
}
hasVariable = true;
String preparedValue = resolveQuotedLiteralVariableValue(literalSegment.content(), parameters, isEdit, isFromDataSet);
if (preparedValue == null) {
return null;
}
preparedValueBuilder.append(preparedValue);
}
if (!hasVariable) {
return null;
}
TableFieldWithValue tableFieldWithValue = new TableFieldWithValue();
tableFieldWithValue.setFiledName(firstVariableName(literalSegments));
tableFieldWithValue.setType(Types.VARCHAR);
tableFieldWithValue.setColumnTypeName("VARCHAR");
tableFieldWithValue.setValue(preparedValueBuilder.toString());
return new PreparedSqlFragment("?", Collections.singletonList(tableFieldWithValue));
}
private String resolveQuotedLiteralVariableValue(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
List<String> preparedValues = resolvePreparedValuesForQuotedLiteral(variableName, parameters, isEdit, isFromDataSet);
if (CollectionUtils.isEmpty(preparedValues)) {
return null;
}
if (preparedValues.size() != 1) {
DEException.throwException("SQL模板字符串仅支持单值参数");
}
return preparedValues.get(0);
}
private List<String> resolvePreparedValuesForQuotedLiteral(String variableName, List<SqlVariableDetails> parameters, boolean isEdit, boolean isFromDataSet) {
SqlVariableDetails filterParameter = findSqlVariableDetail(parameters, variableName);
if (filterParameter != null) {
return resolvePreparedValues(filterParameter);
}
SqlVariableDetails defaultsSqlVariableDetail = findSqlVariableDetail(defaultsSqlVariableDetails, variableName);
if (!shouldUseDefaultValue(defaultsSqlVariableDetail, isEdit, isFromDataSet)) {
return null;
}
SqlVariableDetails defaultValueDetail = new SqlVariableDetails();
defaultValueDetail.setVariableName(defaultsSqlVariableDetail.getVariableName());
defaultValueDetail.setType(defaultsSqlVariableDetail.getType());
defaultValueDetail.setDeType(defaultsSqlVariableDetail.getDeType());
defaultValueDetail.setId(defaultsSqlVariableDetail.getId());
defaultValueDetail.setOperator(defaultsSqlVariableDetail.getOperator());
defaultValueDetail.setValue(Collections.singletonList(defaultsSqlVariableDetail.getDefaultValue()));
return resolvePreparedValues(defaultValueDetail);
}
private List<LiteralSegment> parseLiteralSegments(String literalContent) {
List<LiteralSegment> literalSegments = new ArrayList<>();
Matcher matcher = Pattern.compile(regex).matcher(literalContent);
int lastIndex = 0;
while (matcher.find()) {
if (matcher.start() > lastIndex) {
literalSegments.add(new LiteralSegment(false, literalContent.substring(lastIndex, matcher.start())));
}
literalSegments.add(new LiteralSegment(true, matcher.group().substring(2, matcher.group().length() - 1)));
lastIndex = matcher.end();
}
if (lastIndex < literalContent.length()) {
literalSegments.add(new LiteralSegment(false, literalContent.substring(lastIndex)));
}
return literalSegments;
}
private String unescapeQuotedLiteralText(String text) {
return StringUtils.replace(text, "''", "'");
}
private String firstVariableName(List<LiteralSegment> literalSegments) {
for (LiteralSegment literalSegment : literalSegments) {
if (literalSegment.variable()) {
return literalSegment.content();
}
}
return null;
}
private PreparedSqlFragment buildPreparedSqlFragment(SqlVariableDetails sqlVariableDetails) {
List<TableFieldWithValue> values = new ArrayList<>();
List<String> replacements = new ArrayList<>();
@@ -829,6 +946,12 @@ public class SqlparserUtils {
private record PreparedSqlFragment(String replacement, List<TableFieldWithValue> tableFieldWithValues) {
}
private record QuotedLiteralContext(int start, int end, String content) {
}
private record LiteralSegment(boolean variable, String content) {
}
private String handleSubstitutedSql(String sql) {
if (sql.contains(SysParamsSubstitutedParams) && userEntity != null) {
sql = sql.replace(SysParamsSubstitutedParams + "sysParams.userId", userEntity.getAccount());