[jOOQ/jOOQ#11886] Avoid parentheses when rendering associative concatenation chains

This includes:

- [jOOQ/jOOQ#16745] Wrong SQL rendered for MS Access CONCAT function with more than 2 arguments
This commit is contained in:
Lukas Eder 2024-05-31 17:00:49 +02:00
parent 5a5ac940be
commit 55d044f50f
2 changed files with 64 additions and 15 deletions

View File

@ -39,13 +39,16 @@ package org.jooq.impl;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.systemName;
import static org.jooq.impl.ExpressionOperator.ADD;
import static org.jooq.impl.ExpressionOperator.CONCAT;
import static org.jooq.impl.Names.N_CONCAT;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.castAllIfNeeded;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.Function1;
@ -64,6 +67,7 @@ final class Concat extends AbstractField<String> implements QOM.Concat {
this.arguments = arguments;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public final void accept(Context<?> ctx) {
if (arguments.length == 0) {
@ -75,24 +79,15 @@ final class Concat extends AbstractField<String> implements QOM.Concat {
return;
}
// [#461] Type cast the concat expression, if this isn't a VARCHAR field
Field<String>[] cast = castAllIfNeeded(arguments, String.class);
if (Boolean.TRUE.equals(ctx.settings().isRenderCoalesceToEmptyStringInConcat()) && ctx.configuration().commercial(() -> "Auto-coalescing of CONCAT arguments is available in the jOOQ 3.15 Professional Edition and jOOQ Enterprise Edition, see https://github.com/jOOQ/jOOQ/issues/11757")) {
}
Field<String>[] cast = (Field<String>[]) cast(ctx).toArray(EMPTY_FIELD);
ExpressionOperator op = CONCAT;
switch (ctx.family()) {
case MARIADB:
case MYSQL:
ctx.visit(function(systemName("concat"), SQLDataType.VARCHAR, cast));
ctx.visit(function(N_CONCAT, SQLDataType.VARCHAR, cast));
return;
@ -110,11 +105,55 @@ final class Concat extends AbstractField<String> implements QOM.Concat {
}
Field<?> expression = new Expression<>(op, false, cast[0], cast[1]);
ctx.sql('(');
Expression.acceptAssociative(
ctx,
(QOM.UOperator2) toExpression(op, cast),
op.toQueryPart(),
c -> c.sql(' '),
Expression.Associativity.BOTH
);
ctx.sql(')');
}
private final List<Field<String>> cast(Context<?> ctx) {
List<Field<String>> result = cast(ctx, new ArrayList<>());
if (Boolean.TRUE.equals(ctx.settings().isRenderCoalesceToEmptyStringInConcat()) && ctx.configuration().commercial(() -> "Auto-coalescing of CONCAT arguments is available in the jOOQ 3.15 Professional Edition and jOOQ Enterprise Edition, see https://github.com/jOOQ/jOOQ/issues/11757")) {
}
return result;
}
private final List<Field<String>> cast(Context<?> ctx, List<Field<String>> result) {
// [#461] Type cast the concat expression, if this isn't a VARCHAR field
for (Field<String> f : castAllIfNeeded(arguments, String.class)) {
if (f instanceof Concat c)
c.cast(ctx, result);
else
result.add(f);
}
return result;
}
private final Expression<?> toExpression(ExpressionOperator op, Field<String>[] cast) {
Expression<?> expression = new Expression<>(op, false, cast[0], cast[1]);
for (int i = 2; i < cast.length; i++)
expression = new Expression<>(op, false, expression, cast[i]);
ctx.visit(expression);
return expression;
}
// -------------------------------------------------------------------------

View File

@ -37,7 +37,11 @@
*/
package org.jooq.impl;
import static org.jooq.impl.DSL.raw;
import org.jooq.Name;
import org.jooq.QueryPart;
import org.jooq.SQL;
/**
* The operator used in <code>Expression</code>
@ -74,6 +78,7 @@ enum ExpressionOperator {
;
private final String sql;
private final SQL queryPart;
private final Name name;
private final boolean associative;
private final boolean commutative;
@ -84,6 +89,7 @@ enum ExpressionOperator {
private ExpressionOperator(String sql, boolean associative, boolean commutative) {
this.sql = sql;
this.queryPart = raw(sql);
this.name = DSL.name(name().toLowerCase());
this.associative = associative;
this.commutative = commutative;
@ -93,6 +99,10 @@ enum ExpressionOperator {
return sql;
}
final QueryPart toQueryPart() {
return queryPart;
}
final Name toName() {
return name;
}