[jOOQ/jOOQ#8950] Emulate JSON_TABLE in PostgreSQL
This commit is contained in:
parent
a1584c5a5d
commit
8c8b5f8de6
@ -39,6 +39,7 @@ package org.jooq;
|
||||
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
|
||||
/**
|
||||
* A step in the construction of an <code>JSON_TABLE</code> expression.
|
||||
@ -51,7 +52,7 @@ public interface JSONTableColumnForOrdinalityStep {
|
||||
* Specify the <code>FOR ORDINALITY</code> clause on a column in the
|
||||
* <code>COLUMNS</code> clause of the <code>JSON_TABLE</code> predicate.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnsStep forOrdinality();
|
||||
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ package org.jooq;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
|
||||
/**
|
||||
* A step in the construction of an <code>JSON_TABLE</code> expression.
|
||||
@ -52,7 +53,7 @@ public interface JSONTableColumnPathStep extends JSONTableColumnForOrdinalitySte
|
||||
* Specify the <code>PATH</code> of a column in the <code>COLUMNS</code>
|
||||
* clause of the <code>JSON_TABLE</code> predicate.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnsStep path(String path);
|
||||
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ package org.jooq;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
|
||||
/**
|
||||
* A step in the construction of an <code>JSON_TABLE</code> expression.
|
||||
@ -52,42 +53,42 @@ public interface JSONTableColumnsFirstStep {
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnForOrdinalityStep column(String name);
|
||||
|
||||
/**
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnForOrdinalityStep column(Name name);
|
||||
|
||||
/**
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnPathStep column(Field<?> name);
|
||||
|
||||
/**
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnPathStep column(String name, DataType<?> type);
|
||||
|
||||
/**
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnPathStep column(Name name, DataType<?> type);
|
||||
|
||||
/**
|
||||
* Add a column to the <code>COLUMNS</code> clause of the
|
||||
* <code>JSON_TABLE</code> expression.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
JSONTableColumnPathStep column(Field<?> name, DataType<?> type);
|
||||
|
||||
}
|
||||
|
||||
@ -18769,7 +18769,7 @@ public class DSL {
|
||||
/**
|
||||
* The JSON table function.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
public static JSONTableColumnsFirstStep jsonTable(JSON json, String path) {
|
||||
return jsonTable(Tools.field(json), Tools.field(path));
|
||||
}
|
||||
@ -18777,7 +18777,7 @@ public class DSL {
|
||||
/**
|
||||
* The JSON table function.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
public static JSONTableColumnsFirstStep jsonTable(Field<JSON> json, Field<String> path) {
|
||||
return new JSONTable(nullSafe(json), nullSafe(path));
|
||||
}
|
||||
@ -18785,7 +18785,7 @@ public class DSL {
|
||||
/**
|
||||
* The JSON table function.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
public static JSONTableColumnsFirstStep jsonbTable(JSONB json, String path) {
|
||||
return jsonbTable(Tools.field(json), Tools.field(path));
|
||||
}
|
||||
@ -18793,7 +18793,7 @@ public class DSL {
|
||||
/**
|
||||
* The JSON table function.
|
||||
*/
|
||||
@Support({ MYSQL })
|
||||
@Support({ MYSQL, POSTGRES })
|
||||
public static JSONTableColumnsFirstStep jsonbTable(Field<JSONB> json, Field<String> path) {
|
||||
return new JSONTable(nullSafe(json), nullSafe(path));
|
||||
}
|
||||
|
||||
@ -42,6 +42,10 @@ 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;
|
||||
import static org.jooq.impl.Keywords.K_ERROR;
|
||||
import static org.jooq.impl.Keywords.K_FOR;
|
||||
@ -50,6 +54,7 @@ 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.Names.N_JSON_TABLE;
|
||||
import static org.jooq.impl.SQLDataType.JSONB;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -58,11 +63,13 @@ import java.util.Set;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.JSONB;
|
||||
import org.jooq.JSONTableColumnPathStep;
|
||||
import org.jooq.Name;
|
||||
// ...
|
||||
import org.jooq.Record;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.SelectField;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableOptions;
|
||||
import org.jooq.conf.ParamType;
|
||||
@ -80,141 +87,177 @@ implements
|
||||
*/
|
||||
private static final long serialVersionUID = -4881363881968319258L;
|
||||
private static final Set<SQLDialect> REQUIRES_COLUMN_PATH = SQLDialect.supportedBy(MYSQL);
|
||||
private static final Set<SQLDialect> REQUIRES_ALIASING = SQLDialect.supportedBy(MYSQL);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.formatIndentEnd()
|
||||
.formatNewLine()
|
||||
.sql(')');
|
||||
private final Field<String> path;
|
||||
private final Field<?> json;
|
||||
private final QueryPartList<JSONTableColumn> columns;
|
||||
private transient Fields<Record> fields;
|
||||
|
||||
JSONTable(Field<?> json, Field<String> path) {
|
||||
this(json, path, null);
|
||||
}
|
||||
|
||||
private JSONTable(
|
||||
Field<?> json,
|
||||
Field<String> path,
|
||||
QueryPartList<JSONTableColumn> columns
|
||||
) {
|
||||
super(TableOptions.expression(), N_JSON_TABLE);
|
||||
|
||||
this.json = json;
|
||||
this.path = path;
|
||||
this.columns = columns == null ? new QueryPartList<>() : columns;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: DSL API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final JSONTable column(String name) {
|
||||
return column(DSL.name(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable column(Name name) {
|
||||
return column(DSL.field(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable column(Field<?> name) {
|
||||
return column(name, name.getDataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable column(String name, DataType<?> type) {
|
||||
return column(DSL.name(name), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable column(Name name, DataType<?> type) {
|
||||
return column(DSL.field(name), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable column(Field<?> name, DataType<?> type) {
|
||||
QueryPartList<JSONTableColumn> c = new QueryPartList<>(columns);
|
||||
c.add(new JSONTableColumn(name, type, false, null));
|
||||
return new JSONTable(json, path, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable forOrdinality() {
|
||||
return path0(true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONTable path(String p) {
|
||||
return path0(false, p);
|
||||
}
|
||||
|
||||
private final JSONTable path0(boolean forOrdinality, String p) {
|
||||
QueryPartList<JSONTableColumn> c = new QueryPartList<>(columns);
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: Table API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final Class<? extends Record> getRecordType() {
|
||||
return RecordImplN.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
final Fields<Record> fields0() {
|
||||
if (fields == null) {
|
||||
List<Field<?>> f = new ArrayList<>();
|
||||
|
||||
for (JSONTableColumn c : columns)
|
||||
f.add(c.field.getDataType() == c.type ? c.field : field(c.field.getQualifiedName(), c.type));
|
||||
|
||||
fields = new Fields<>(f);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: QueryPart API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
switch (ctx.family()) {
|
||||
case POSTGRES:
|
||||
acceptPostgres(ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
acceptStandard(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void acceptJSONPath(Context<?> ctx) {
|
||||
private final void acceptPostgres(Context<?> ctx) {
|
||||
List<SelectField<?>> cols = new ArrayList<>();
|
||||
|
||||
for (JSONTableColumn col : columns)
|
||||
if (col.forOrdinality)
|
||||
cols.add(rowNumber().over().as(col.field));
|
||||
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("jsonb_path_query({0}, {1}::jsonpath) as t(j)",
|
||||
json.getType() == JSONB.class ? json : json.cast(JSONB),
|
||||
path
|
||||
)
|
||||
)
|
||||
.subquery(false)
|
||||
.formatIndentEnd()
|
||||
.formatNewLine()
|
||||
.sql(')');
|
||||
}
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
ctx.visit(K_JSON_TABLE).sql('(')
|
||||
.formatIndentStart()
|
||||
.formatNewLine();
|
||||
|
||||
ctx.visit(json).sql(',').formatSeparator();
|
||||
acceptJSONPath(ctx);
|
||||
|
||||
ctx.formatSeparator().visit(K_COLUMNS).sql(" (").visit(columns).sql(')');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.formatIndentEnd()
|
||||
.formatNewLine()
|
||||
.sql(')');
|
||||
}
|
||||
|
||||
private final void acceptJSONPath(Context<?> ctx) {
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user