From 697f0adec249154377b7efda9231e368f69d09e9 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 22 Apr 2020 09:57:16 +0200 Subject: [PATCH] [jOOQ/jOOQ#10100] Emulate JSON_TABLE in SQL Server with OPENJSON --- .../JSONTableColumnForOrdinalityStep.java | 1 + .../org/jooq/JSONTableColumnPathStep.java | 1 + .../org/jooq/JSONTableColumnsFirstStep.java | 1 + .../main/java/org/jooq/impl/JSONTable.java | 88 +++++++++++++++---- jOOQ/src/main/java/org/jooq/impl/Names.java | 1 + 5 files changed, 75 insertions(+), 17 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/JSONTableColumnForOrdinalityStep.java b/jOOQ/src/main/java/org/jooq/JSONTableColumnForOrdinalityStep.java index 326b7b624b..d9564d8440 100644 --- a/jOOQ/src/main/java/org/jooq/JSONTableColumnForOrdinalityStep.java +++ b/jOOQ/src/main/java/org/jooq/JSONTableColumnForOrdinalityStep.java @@ -41,6 +41,7 @@ package org.jooq; import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; +// ... /** * A step in the construction of an JSON_TABLE expression. diff --git a/jOOQ/src/main/java/org/jooq/JSONTableColumnPathStep.java b/jOOQ/src/main/java/org/jooq/JSONTableColumnPathStep.java index 7c8631f39d..d28af64b8f 100644 --- a/jOOQ/src/main/java/org/jooq/JSONTableColumnPathStep.java +++ b/jOOQ/src/main/java/org/jooq/JSONTableColumnPathStep.java @@ -41,6 +41,7 @@ package org.jooq; import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; +// ... /** * A step in the construction of an JSON_TABLE expression. diff --git a/jOOQ/src/main/java/org/jooq/JSONTableColumnsFirstStep.java b/jOOQ/src/main/java/org/jooq/JSONTableColumnsFirstStep.java index 28cccff58d..4e66dbec7f 100644 --- a/jOOQ/src/main/java/org/jooq/JSONTableColumnsFirstStep.java +++ b/jOOQ/src/main/java/org/jooq/JSONTableColumnsFirstStep.java @@ -41,6 +41,7 @@ package org.jooq; import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; +// ... /** * A step in the construction of an JSON_TABLE expression. diff --git a/jOOQ/src/main/java/org/jooq/impl/JSONTable.java b/jOOQ/src/main/java/org/jooq/impl/JSONTable.java index f6fb30223a..3a71acd363 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JSONTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JSONTable.java @@ -40,9 +40,11 @@ package org.jooq.impl; // ... import static org.jooq.SQLDialect.MYSQL; // ... +// ... import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.keyword; +import static org.jooq.impl.DSL.rowNumber; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.val; import static org.jooq.impl.Keywords.K_COLUMNS; @@ -52,7 +54,9 @@ import static org.jooq.impl.Keywords.K_JSON_TABLE; import static org.jooq.impl.Keywords.K_ON; import static org.jooq.impl.Keywords.K_ORDINALITY; import static org.jooq.impl.Keywords.K_PATH; +import static org.jooq.impl.Keywords.K_WITH; import static org.jooq.impl.Names.N_JSON_TABLE; +import static org.jooq.impl.Names.N_OPENJSON; import static org.jooq.impl.SQLDataType.JSONB; import java.util.ArrayList; @@ -93,22 +97,25 @@ implements private final Field path; private final Field json; private final QueryPartList columns; + private final boolean hasOrdinality; private transient Fields fields; JSONTable(Field json, Field path) { - this(json, path, null); + this(json, path, null, false); } private JSONTable( Field json, Field path, - QueryPartList columns + QueryPartList columns, + boolean hasOrdinality ) { super(TableOptions.expression(), N_JSON_TABLE); this.json = json; this.path = path; this.columns = columns == null ? new QueryPartList<>() : columns; + this.hasOrdinality = hasOrdinality; } // ------------------------------------------------------------------------- @@ -144,7 +151,7 @@ implements public final JSONTable column(Field name, DataType type) { QueryPartList c = new QueryPartList<>(columns); c.add(new JSONTableColumn(name, type, false, null)); - return new JSONTable(json, path, c); + return new JSONTable(json, path, c, hasOrdinality); } @Override @@ -162,7 +169,7 @@ implements int i = c.size() - 1; JSONTableColumn last = c.get(i); c.set(i, new JSONTableColumn(last.field, last.type, forOrdinality, p)); - return new JSONTable(json, path, c); + return new JSONTable(json, path, c, hasOrdinality || forOrdinality); } // ------------------------------------------------------------------------- @@ -199,6 +206,12 @@ implements acceptPostgres(ctx); break; + + + + + + default: acceptStandard(ctx); break; @@ -208,28 +221,23 @@ implements private final void acceptPostgres(Context ctx) { List> cols = new ArrayList<>(); - boolean requireOrdinality = false; - for (JSONTableColumn col : columns) { - if (col.forOrdinality) { - requireOrdinality = true; + for (JSONTableColumn col : columns) + if (col.forOrdinality) cols.add(DSL.field("o").as(col.field)); - } - else { + else cols.add( DSL.field("(jsonb_path_query_first(j, {0}::jsonpath)->>0)::{1}", col.path != null ? val(col.path) : inline("$." + col.field.getName()), keyword(col.type.getCastTypeName(ctx.configuration())) ).as(col.field) ); - } - } ctx.sql('(') .formatIndentStart() .formatNewLine() .subquery(true) .visit( - select(cols).from(requireOrdinality + select(cols).from(hasOrdinality ? "jsonb_path_query({0}, {1}::jsonpath) {with} {ordinality} {as} t(j, o)" : "jsonb_path_query({0}, {1}::jsonpath) {as} t(j)", json.getType() == JSONB.class ? json : json.cast(JSONB), @@ -242,6 +250,42 @@ implements .sql(')'); } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private final void acceptStandard(Context ctx) { ctx.visit(K_JSON_TABLE).sql('(') .formatIndentStart() @@ -315,10 +359,20 @@ implements else Tools.toSQLDDLTypeDeclaration(ctx, type); - if (path != null) - ctx.sql(' ').visit(K_PATH).sql(' ').visit(inline(path)); - else if (!forOrdinality && REQUIRES_COLUMN_PATH.contains(ctx.dialect())) - ctx.sql(' ').visit(K_PATH).sql(' ').visit(inline("$." + field.getName())); + String p = + path != null + ? path + : !forOrdinality && REQUIRES_COLUMN_PATH.contains(ctx.dialect()) + ? "$." + field.getName() + : null; + + if (p != null) + + + + + + ctx.sql(' ').visit(K_PATH).sql(' ').visit(inline(p)); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Names.java b/jOOQ/src/main/java/org/jooq/impl/Names.java index eee57afc54..2c4650736b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Names.java +++ b/jOOQ/src/main/java/org/jooq/impl/Names.java @@ -156,6 +156,7 @@ final class Names { static final Name N_NUMTODSINTERVAL = unquotedName("numtodsinterval"); static final Name N_NVL = unquotedName("nvl"); static final Name N_NVL2 = unquotedName("nvl2"); + static final Name N_OPENJSON = unquotedName("openjson"); static final Name N_OVERLAY = unquotedName("overlay"); static final Name N_PI = unquotedName("pi"); static final Name N_PIVOT = unquotedName("pivot");