[jOOQ/jOOQ#10113] Emulate JSON NULL ON NULL and ABSENT ON NULL clauses

This commit is contained in:
Lukas Eder 2024-06-03 17:51:40 +02:00
parent b65ce6a7a6
commit 317d27fa1d
4 changed files with 81 additions and 27 deletions

View File

@ -84,7 +84,7 @@ public interface JSONArrayNullStep<T> extends JSONArrayReturningStep<T> {
* <p>
* Exclude <code>NULL</code> values in output JSON.
*/
@Support({ H2, POSTGRES, SQLITE, TRINO, YUGABYTEDB })
@Support({ H2, MARIADB, POSTGRES, SQLITE, TRINO, YUGABYTEDB })
@NotNull @CheckReturnValue
JSONArrayReturningStep<T> absentOnNull();
}

View File

@ -84,7 +84,7 @@ public interface JSONObjectNullStep<T> extends JSONObjectReturningStep<T> {
* <p>
* Exclude <code>NULL</code> values in output JSON.
*/
@Support({ H2, MYSQL, POSTGRES, SQLITE, TRINO, YUGABYTEDB })
@Support({ H2, MARIADB, MYSQL, POSTGRES, SQLITE, TRINO, YUGABYTEDB })
@NotNull @CheckReturnValue
JSONObjectReturningStep<T> absentOnNull();
}

View File

@ -37,34 +37,57 @@
*/
package org.jooq.impl;
import static org.jooq.impl.DSL.*;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.Internal.*;
// ...
import static org.jooq.SQLDialect.H2;
import static org.jooq.impl.DSL.array;
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.jsonTable;
import static org.jooq.impl.DSL.jsonbArrayAgg;
import static org.jooq.impl.DSL.rand;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.values;
import static org.jooq.impl.JSONEntryImpl.jsonCastMapper;
import static org.jooq.impl.Keywords.*;
import static org.jooq.impl.Names.*;
import static org.jooq.impl.SQLDataType.*;
import static org.jooq.impl.Tools.*;
import static org.jooq.impl.Tools.BooleanDataKey.*;
import static org.jooq.impl.Tools.ExtendedDataKey.*;
import static org.jooq.impl.Tools.SimpleDataKey.*;
import static org.jooq.SQLDialect.*;
import static org.jooq.impl.Names.N_ARRAY_CONSTRUCT;
import static org.jooq.impl.Names.N_ARRAY_CONSTRUCT_COMPACT;
import static org.jooq.impl.Names.N_JSONB_BUILD_ARRAY;
import static org.jooq.impl.Names.N_JSON_ARRAY;
import static org.jooq.impl.Names.N_JSON_BUILD_ARRAY;
import static org.jooq.impl.Names.N_JSON_EXTRACT;
import static org.jooq.impl.Names.N_JSON_MODIFY;
import static org.jooq.impl.Names.N_JSON_QUERY;
import static org.jooq.impl.Names.N_JSON_TREE;
import static org.jooq.impl.Names.N_KEY;
import static org.jooq.impl.Names.N_T;
import static org.jooq.impl.Names.N_TUPLE;
import static org.jooq.impl.Names.N_VALUE;
import static org.jooq.impl.Names.N_toJSONString;
import static org.jooq.impl.SQLDataType.JSON;
import static org.jooq.impl.SQLDataType.JSONB;
import static org.jooq.impl.SQLDataType.OTHER;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.map;
import org.jooq.*;
import org.jooq.Function1;
import org.jooq.Record;
import org.jooq.conf.ParamType;
import java.util.Collection;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Function4;
import org.jooq.JSON;
import org.jooq.JSONArrayNullStep;
import org.jooq.JSONArrayReturningStep;
import org.jooq.Param;
// ...
import org.jooq.Row1;
import org.jooq.Table;
import org.jooq.impl.QOM.JSONOnNull;
import org.jooq.tools.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
@ -238,6 +261,30 @@ implements
break;
}
case MARIADB: {
if (onNull == JSONOnNull.ABSENT_ON_NULL) {
Field<T> value = DSL.field(N_VALUE, getDataType());
ctx.visit(
DSL.coalesce(
DSL.field(
select(jsonArrayAgg(function(N_JSON_EXTRACT, getDataType(), value, inline("$"))))
.from(jsonTable((Field<JSON>) $onNull(JSONOnNull.NULL_ON_NULL), inline("$[*]"))
.column(value, SQLDataType.JSON).path("$").as(N_T))
.where(value.ne((Field) inline("null")))
// [#10113] Workaround for https://jira.mariadb.org/projects/MDEV/issues/MDEV-34284
.and(rand().isNotNull())
),
jsonArray()
));
}
else
acceptStandard(ctx, mapped);
break;
}
case SQLITE: {
if (onNull == JSONOnNull.ABSENT_ON_NULL) {
Field<String> key = DSL.field(N_KEY, VARCHAR);

View File

@ -39,6 +39,7 @@ package org.jooq.impl;
import static org.jooq.impl.DSL.*;
import static org.jooq.impl.Internal.*;
import static org.jooq.impl.JSONEntryImpl.jsonMerge;
import static org.jooq.impl.Keywords.*;
import static org.jooq.impl.Names.*;
import static org.jooq.impl.SQLDataType.*;
@ -211,11 +212,17 @@ implements
// Workaround for https://jira.mariadb.org/browse/MDEV-13701
if (entries.size() > 1) {
ctx.visit(JSONEntryImpl.jsonMerge(ctx, "{}", Tools.map(entries, e -> jsonObject(e), Field[]::new)));
ctx.visit(jsonMerge(ctx, "{}",
map(entries, onNull == JSONOnNull.ABSENT_ON_NULL
? e -> DSL.nvl2(e.value(), jsonObject(e), jsonObject())
: e -> jsonObject(e)
, Field[]::new
)
));
}
else if (!entries.isEmpty() && isJSONArray((first = entries.iterator().next()).value())) {
ctx.visit(jsonObject(
key(first.key()).value(JSONEntryImpl.jsonMerge(ctx, "[]", first.value()))
key(first.key()).value(jsonMerge(ctx, "[]", first.value()))
));
}
else