diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java index 9e810ca82..b04706ef3 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java @@ -10,6 +10,9 @@ package net.sf.jsqlparser.util.validation.validator; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.update.UpdateSet; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.parser.feature.Feature; import net.sf.jsqlparser.statement.select.FromItem; import net.sf.jsqlparser.statement.select.OrderByElement; @@ -137,6 +140,25 @@ protected void validateOptionalExpression(Expression expression, ExpressionValid validateOptional(expression, e -> e.accept(v, null)); } + protected void validateOptionalUpdateSets(List updateSets) { + if (updateSets != null) { + for (UpdateSet updateSet : updateSets) { + validateOptionalExpressions(updateSet.getColumns()); + if (updateSet.getValues() != null) { + for (Expression value : updateSet.getValues()) { + if (value instanceof Select) { + ((Select) value).accept( + (SelectVisitor) getValidator(SelectValidator.class), + null); + } else { + validateOptionalExpression(value); + } + } + } + } + } + } + protected void validateOptionalExpressions(List expressions) { validateOptionalList(expressions, () -> getValidator(ExpressionValidator.class), (o, v) -> o.accept(v, null)); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java index 9de0d2c46..ab5dfa9b1 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java @@ -12,7 +12,6 @@ import net.sf.jsqlparser.parser.feature.Feature; import net.sf.jsqlparser.statement.merge.*; import net.sf.jsqlparser.statement.select.OptionHint; -import net.sf.jsqlparser.statement.update.UpdateSet; import net.sf.jsqlparser.util.validation.ValidationCapability; /** @@ -59,10 +58,7 @@ public void visit(MergeDelete mergeDelete) { @Override public Void visit(MergeUpdate mergeUpdate, S context) { validateOptionalExpression(mergeUpdate.getAndPredicate()); - for (UpdateSet updateSet : mergeUpdate.getUpdateSets()) { - validateOptionalExpressions(updateSet.getColumns()); - validateOptionalExpressions(updateSet.getValues()); - } + validateOptionalUpdateSets(mergeUpdate.getUpdateSets()); validateOptionalExpression(mergeUpdate.getDeleteWhereCondition()); validateOptionalExpression(mergeUpdate.getWhereCondition()); return null; diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java index 7d27e94b3..19e8b7085 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java @@ -10,7 +10,6 @@ package net.sf.jsqlparser.util.validation.validator; import net.sf.jsqlparser.parser.feature.Feature; -import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.OptionHint; import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.util.validation.ValidationCapability; @@ -27,7 +26,6 @@ public void validate(Update update) { validateFeature(c, Feature.update); validateOptionalFeature(c, update.getFromItem(), Feature.updateFrom); validateOptionalFeature(c, update.getStartJoins(), Feature.updateJoins); - validateFeature(c, update.isUseSelect(), Feature.updateUseSelect); validateOptionalFeature(c, update.getOrderByElements(), Feature.updateOrderBy); validateOptionalFeature(c, update.getLimit(), Feature.updateLimit); validateOptionalFeature(c, update.getReturningClause(), @@ -41,14 +39,7 @@ public void validate(Update update) { validateOptional(update.getStartJoins(), j -> getValidator(SelectValidator.class).validateOptionalJoins(j)); - if (update.isUseSelect()) { - validateOptionalExpressions(update.getColumns()); - validateOptional(update.getSelect(), - e -> e.accept((SelectVisitor) getValidator(SelectValidator.class), null)); - } else { - validateOptionalExpressions(update.getColumns()); - validateOptionalExpressions(update.getExpressions()); - } + validateOptionalUpdateSets(update.getUpdateSets()); if (update.getFromItem() != null) { validateOptionalFromItem(update.getFromItem()); diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/UpdateSetValidationTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/UpdateSetValidationTest.java new file mode 100644 index 000000000..5a9186c48 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/UpdateSetValidationTest.java @@ -0,0 +1,44 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.util.validation.ValidationTestAsserts; +import net.sf.jsqlparser.util.validation.feature.FeaturesAllowed; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class UpdateSetValidationTest extends ValidationTestAsserts { + @ParameterizedTest + @ValueSource(strings = {"UPDATE t SET a = ?, b = 1", "UPDATE t SET a = 1, b = ?", + "UPDATE t SET a = 1, b = ?, c = 3", "UPDATE t SET a = 1, b = 2, c = ?", + "UPDATE t SET (b, c) = (2, ?), a = 1", + "UPDATE t SET a = 1, b = (SELECT x FROM s WHERE x = ?)", + "MERGE INTO t USING s ON t.id = s.id WHEN MATCHED THEN UPDATE SET a = 1, b = ?"}) + void validatesValuesInEveryAssignment(String sql) { + validateNotAllowed(sql, 1, 1, FeaturesAllowed.DML.copy().remove(Feature.jdbcParameter), + Feature.jdbcParameter); + validateNoErrors(sql, 1, FeaturesAllowed.DML.copy().add(FeaturesAllowed.JDBC)); + } + + @Test + void reportsIndependentFeaturesAcrossDifferentAssignments() { + validateNotAllowed("UPDATE t SET a = 1, b = COALESCE(c, 0), d = ?", 1, 1, + FeaturesAllowed.UPDATE.copy().remove(Feature.function, Feature.jdbcParameter), + Feature.function, Feature.jdbcParameter); + } + + @Test + void keepsTupleAndSubqueryAssignmentsValid() { + validateNoErrors("UPDATE t SET (a, b) = (SELECT c, d FROM s), e = 1", 1, + FeaturesAllowed.UPDATE); + } +}