From c8292cf9c4dd050415129e2fd3d7c6a483c32967 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 20 Apr 2020 17:01:41 +0200 Subject: [PATCH] [jOOQ/jOOQ#10089] Emulate JSON_OBJECTAGG where unavailable --- .../java/org/jooq/JSONObjectAggNullStep.java | 7 ++- .../java/org/jooq/impl/JSONNullClause.java | 3 + .../java/org/jooq/impl/JSONObjectAgg.java | 57 +++++++++++++++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/JSONObjectAggNullStep.java b/jOOQ/src/main/java/org/jooq/JSONObjectAggNullStep.java index b10fd00774..57f8a6f1e6 100644 --- a/jOOQ/src/main/java/org/jooq/JSONObjectAggNullStep.java +++ b/jOOQ/src/main/java/org/jooq/JSONObjectAggNullStep.java @@ -37,9 +37,12 @@ */ package org.jooq; +// ... // ... // ... import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.MARIADB; +import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; @@ -57,12 +60,12 @@ public interface JSONObjectAggNullStep extends AggregateFilterStep { /** * Include NULL values in output JSON. */ - @Support({ H2, POSTGRES }) + @Support({ H2, MARIADB, MYSQL, POSTGRES }) AggregateFilterStep nullOnNull(); /** * Exclude NULL values in output JSON. */ - @Support({ H2, POSTGRES }) + @Support({ H2, MARIADB, MYSQL, POSTGRES }) AggregateFilterStep absentOnNull(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/JSONNullClause.java b/jOOQ/src/main/java/org/jooq/impl/JSONNullClause.java index e14e0b2df0..8587e05241 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JSONNullClause.java +++ b/jOOQ/src/main/java/org/jooq/impl/JSONNullClause.java @@ -37,6 +37,9 @@ */ package org.jooq.impl; +/** + * @author Lukas Eder + */ enum JSONNullClause { NULL_ON_NULL, ABSENT_ON_NULL } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/JSONObjectAgg.java b/jOOQ/src/main/java/org/jooq/impl/JSONObjectAgg.java index 6a797285c0..67eaa7d22d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JSONObjectAgg.java +++ b/jOOQ/src/main/java/org/jooq/impl/JSONObjectAgg.java @@ -37,6 +37,11 @@ */ package org.jooq.impl; +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.when; import static org.jooq.impl.JSONNullClause.ABSENT_ON_NULL; import static org.jooq.impl.JSONNullClause.NULL_ON_NULL; import static org.jooq.impl.JSONObject.acceptJSONNullClause; @@ -47,6 +52,7 @@ import static org.jooq.impl.SQLDataType.JSON; import org.jooq.Context; import org.jooq.DataType; +import org.jooq.Field; import org.jooq.JSONEntry; import org.jooq.JSONObjectAggNullStep; @@ -82,8 +88,21 @@ implements JSONObjectAggNullStep { - case POSTGRES: + + + + // [#10089] These dialects support non-standard JSON_OBJECTAGG without ABSENT ON NULL support + case MARIADB: + case MYSQL: + if (nullClause == ABSENT_ON_NULL) + acceptGroupConcat(ctx); + else + acceptStandard(ctx); + + break; + + case POSTGRES: ctx.visit(getDataType() == JSON ? N_JSON_OBJECT_AGG : N_JSONB_OBJECT_AGG).sql('('); ctx.visit(entry); ctx.sql(')'); @@ -95,14 +114,42 @@ implements JSONObjectAggNullStep { break; default: - ctx.visit(N_JSON_OBJECTAGG).sql('('); - ctx.visit(entry); - acceptJSONNullClause(ctx, nullClause); - ctx.sql(')'); + acceptStandard(ctx); break; } } + @SuppressWarnings({ "unchecked", "rawtypes" }) + private final void acceptGroupConcat(Context ctx) { + Field value; + + if (entry.value().getDataType().isJSON()) { + value = entry.value(); + } + else { + value = jsonValue(jsonObject(inline("x"), entry.value()), inline("$.x")); + + if (nullClause == ABSENT_ON_NULL) + value = when(entry.value().isNull(), inline((String) null)).else_((Field) value); + } + + Field listagg = groupConcat(DSL.concat( + inline('"'), + DSL.replace(entry.key(), inline('"'), inline("\\\"")), + inline("\":"), + nullClause == ABSENT_ON_NULL ? value : DSL.coalesce(value, inline("null")) + )); + + ctx.sql('(').visit(DSL.concat(inline('{'), listagg, inline('}'))).sql(')'); + } + + private final void acceptStandard(Context ctx) { + ctx.visit(N_JSON_OBJECTAGG).sql('('); + ctx.visit(entry); + acceptJSONNullClause(ctx, nullClause); + ctx.sql(')'); + } + @Override public final JSONObjectAgg nullOnNull() { nullClause = NULL_ON_NULL;