[jOOQ/jOOQ#12168] Deeply nested MULTISET produce quoted JSON in MariaDB
This commit is contained in:
parent
f22489e93e
commit
4bae0a5129
@ -23843,7 +23843,7 @@ public class DSL {
|
||||
* {@link SelectForStep#forXML()}. jOOQ produces an XML encoding that is
|
||||
* compatible with {@link DSLContext#fetchFromXML(String)}. Future jOOQ
|
||||
* versions will make this format configurable according to
|
||||
* {@link XMLFormat.RecordFormat}.</li>
|
||||
* {@link XMLFormat.RecordFormau}.</li>
|
||||
* <li>{@link NestedCollectionEmulation#NATIVE}: A few dialects have native
|
||||
* support for MULTISET.</li>
|
||||
* <li>{@link NestedCollectionEmulation#DEFAULT}: By default, jOOQ chooses
|
||||
|
||||
@ -39,15 +39,14 @@ package org.jooq.impl;
|
||||
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.function;
|
||||
import static org.jooq.impl.DSL.groupConcat;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.noCondition;
|
||||
import static org.jooq.impl.JSONEntryImpl.jsonCastMapper;
|
||||
import static org.jooq.impl.JSONEntryImpl.jsonMerge;
|
||||
import static org.jooq.impl.JSONOnNull.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONOnNull.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Names.N_GROUP_CONCAT;
|
||||
@ -55,8 +54,6 @@ import static org.jooq.impl.Names.N_JSONB_AGG;
|
||||
import static org.jooq.impl.Names.N_JSON_AGG;
|
||||
import static org.jooq.impl.Names.N_JSON_ARRAYAGG;
|
||||
import static org.jooq.impl.Names.N_JSON_GROUP_ARRAY;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE_PRESERVE;
|
||||
import static org.jooq.impl.Names.N_JSON_QUOTE;
|
||||
import static org.jooq.impl.SQLDataType.JSON;
|
||||
import static org.jooq.impl.SQLDataType.VARCHAR;
|
||||
@ -88,7 +85,6 @@ extends AbstractAggregateFunction<J>
|
||||
implements JSONArrayAggOrderByStep<J> {
|
||||
|
||||
static final Set<SQLDialect> EMULATE_WITH_GROUP_CONCAT = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
static final Set<SQLDialect> SUPPORT_JSON_MERGE_PRESERVE = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
|
||||
|
||||
@ -102,13 +98,13 @@ implements JSONArrayAggOrderByStep<J> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Context<?> ctx) {
|
||||
public final void accept(Context<?> ctx) {
|
||||
switch (ctx.family()) {
|
||||
case MARIADB:
|
||||
case MYSQL: {
|
||||
// Workaround for https://jira.mariadb.org/browse/MDEV-21912,
|
||||
// https://jira.mariadb.org/browse/MDEV-21914, and other issues
|
||||
ctx.visit(SUPPORT_JSON_MERGE_PRESERVE.contains(ctx.dialect()) ? N_JSON_MERGE_PRESERVE : N_JSON_MERGE).sql('(').visit(inline("[]")).sql(", ").visit(groupConcatEmulation(ctx)).sql(')');
|
||||
ctx.visit(jsonMerge(ctx, "[]", groupConcatEmulation(ctx)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,10 @@ package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.NULL;
|
||||
import static org.jooq.impl.DSL.coalesce;
|
||||
import static org.jooq.impl.DSL.condition;
|
||||
@ -53,11 +57,15 @@ import static org.jooq.impl.Keywords.K_JSON;
|
||||
import static org.jooq.impl.Keywords.K_KEY;
|
||||
import static org.jooq.impl.Keywords.K_VALUE;
|
||||
import static org.jooq.impl.Names.N_JSON;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE_PRESERVE;
|
||||
import static org.jooq.impl.Names.N_JSON_QUERY;
|
||||
import static org.jooq.impl.SQLDataType.VARCHAR;
|
||||
import static org.jooq.impl.Tools.combine;
|
||||
import static org.jooq.impl.Tools.emulateMultiset;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -69,6 +77,7 @@ import org.jooq.JSONEntryValueStep;
|
||||
import org.jooq.Param;
|
||||
// ...
|
||||
import org.jooq.Record1;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Scope;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.conf.NestedCollectionEmulation;
|
||||
@ -80,8 +89,12 @@ import org.jooq.conf.NestedCollectionEmulation;
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>, JSONEntryValueStep {
|
||||
private final Field<String> key;
|
||||
private final Field<T> value;
|
||||
|
||||
static final Set<SQLDialect> REQUIRE_JSON_MERGE = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
static final Set<SQLDialect> SUPPORT_JSON_MERGE_PRESERVE = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
private final Field<String> key;
|
||||
private final Field<T> value;
|
||||
|
||||
JSONEntryImpl(Field<String> key) {
|
||||
this(key, null);
|
||||
@ -248,4 +261,12 @@ final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>,
|
||||
|
||||
|
||||
|
||||
|
||||
static final Field<?> jsonMerge(Scope scope, String empty, Field<?>... fields) {
|
||||
return function(
|
||||
SUPPORT_JSON_MERGE_PRESERVE.contains(scope.dialect()) ? N_JSON_MERGE_PRESERVE : N_JSON_MERGE,
|
||||
fields[0].getDataType(),
|
||||
combine(inline(empty), fields)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,23 +182,14 @@ implements
|
||||
|
||||
case MARIADB: {
|
||||
JSONEntry<?> first;
|
||||
Name name = JSONArrayAgg.SUPPORT_JSON_MERGE_PRESERVE.contains(ctx.dialect()) ? N_JSON_MERGE_PRESERVE : N_JSON_MERGE;
|
||||
|
||||
// Workaround for https://jira.mariadb.org/browse/MDEV-13701
|
||||
if (entries.size() > 1) {
|
||||
ctx.visit(name).sql('(').visit(inline("{}"))
|
||||
.formatIndentStart();
|
||||
|
||||
for (JSONEntry<?> entry : entries)
|
||||
ctx.sql(',').formatSeparator().visit(jsonObject(entry));
|
||||
|
||||
ctx.formatIndentEnd()
|
||||
.formatNewLine()
|
||||
.sql(')');
|
||||
ctx.visit(jsonMerge(ctx, "{}", Tools.map(entries, e -> jsonObject(e), Field[]::new)));
|
||||
}
|
||||
else if (!entries.isEmpty() && isJSONArray((first = entries.iterator().next()).value())) {
|
||||
ctx.visit(jsonObject(
|
||||
key(first.key()).value(DSL.field("{0}({1}, {2})", getDataType(), name, inline("[]"), first.value()))
|
||||
key(first.key()).value(jsonMerge(ctx, "[]", first.value()))
|
||||
));
|
||||
}
|
||||
else
|
||||
|
||||
@ -41,7 +41,6 @@ import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.impl.DSL.groupConcat;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.jsonObject;
|
||||
import static org.jooq.impl.DSL.jsonValue;
|
||||
import static org.jooq.impl.DSL.noCondition;
|
||||
import static org.jooq.impl.DSL.when;
|
||||
import static org.jooq.impl.JSONOnNull.ABSENT_ON_NULL;
|
||||
@ -60,11 +59,8 @@ import org.jooq.Field;
|
||||
import org.jooq.JSON;
|
||||
import org.jooq.JSONEntry;
|
||||
import org.jooq.JSONObjectAggNullStep;
|
||||
import org.jooq.JSONObjectNullStep;
|
||||
// ...
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* The JSON object constructor.
|
||||
|
||||
@ -42,6 +42,8 @@ import static java.lang.Boolean.TRUE;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
import static org.jooq.impl.DSL.function;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.jsonArray;
|
||||
import static org.jooq.impl.DSL.jsonArrayAgg;
|
||||
import static org.jooq.impl.DSL.jsonEntry;
|
||||
@ -55,7 +57,13 @@ import static org.jooq.impl.DSL.xmlagg;
|
||||
import static org.jooq.impl.DSL.xmlelement;
|
||||
import static org.jooq.impl.DSL.xmlserializeContent;
|
||||
import static org.jooq.impl.JSONArrayAgg.patchOracleArrayAggBug;
|
||||
import static org.jooq.impl.JSONEntryImpl.REQUIRE_JSON_MERGE;
|
||||
import static org.jooq.impl.JSONEntryImpl.SUPPORT_JSON_MERGE_PRESERVE;
|
||||
import static org.jooq.impl.JSONEntryImpl.isJSON;
|
||||
import static org.jooq.impl.JSONEntryImpl.jsonMerge;
|
||||
import static org.jooq.impl.Keywords.K_MULTISET;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE_PRESERVE;
|
||||
import static org.jooq.impl.Names.N_MULTISET;
|
||||
import static org.jooq.impl.Names.N_RECORD;
|
||||
import static org.jooq.impl.Names.N_RESULT;
|
||||
@ -335,7 +343,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
|
||||
default:
|
||||
return jsonArrayAgg(
|
||||
returningClob(ctx, jsonArray(
|
||||
map(fields.fields(), (f, i) -> agg ? f : DSL.field(fieldName(i), f.getDataType()))
|
||||
map(fields.fields(), (f, i) -> jsonMerge(ctx, "[]", agg ? f : DSL.field(fieldName(i), f.getDataType())))
|
||||
).nullOnNull())
|
||||
);
|
||||
}
|
||||
@ -359,12 +367,24 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
|
||||
default:
|
||||
return jsonbArrayAgg(
|
||||
returningClob(ctx, jsonbArray(
|
||||
map(fields.fields(), (f, i) -> agg ? f : DSL.field(fieldName(i), f.getDataType()))
|
||||
map(fields.fields(), (f, i) -> jsonMerge(ctx, "[]", agg ? f : DSL.field(fieldName(i), f.getDataType())))
|
||||
).nullOnNull())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Field<?> jsonMerge(Scope scope, String empty, Field<?> field) {
|
||||
|
||||
// [#12168] Yet another MariaDB JSON un-escaping workaround https://jira.mariadb.org/browse/MDEV-26134
|
||||
return REQUIRE_JSON_MERGE.contains(scope.dialect()) && isJSON(scope, field.getDataType())
|
||||
? function(
|
||||
SUPPORT_JSON_MERGE_PRESERVE.contains(scope.dialect()) ? N_JSON_MERGE_PRESERVE : N_JSON_MERGE,
|
||||
field.getDataType(),
|
||||
inline(empty),
|
||||
field
|
||||
)
|
||||
: field;
|
||||
}
|
||||
static final XMLAggOrderByStep<XML> xmlaggEmulation(Fields fields, boolean multisetCondition, boolean agg) {
|
||||
return xmlagg(
|
||||
xmlelement(N_RECORD,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user