[jOOQ/jOOQ#10112] Add QueryPartInternal.rendersContent(Context<?>)
This commit is contained in:
parent
2b29982e37
commit
69c055477d
@ -50,6 +50,15 @@ import org.jooq.exception.DataAccessException;
|
||||
@Internal
|
||||
public interface QueryPartInternal extends QueryPart {
|
||||
|
||||
/**
|
||||
* Whether a call to {@link #accept(Context)} would render content.
|
||||
*
|
||||
* @deprecated - Calling {@link #rendersContent(Context)} directly on a
|
||||
* {@link QueryPart} is almost always a mistake.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean rendersContent(Context<?> ctx);
|
||||
|
||||
/**
|
||||
* This {@link QueryPart} can <code>accept</code> a {@link Context} object
|
||||
* in order to render a SQL string or to bind its variables.
|
||||
|
||||
@ -102,6 +102,14 @@ abstract class AbstractQueryPart implements QueryPartInternal {
|
||||
// The QueryPart and QueryPart internal API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Subclasses may override this
|
||||
*/
|
||||
@Override
|
||||
public boolean rendersContent(Context<?> ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this
|
||||
*/
|
||||
|
||||
@ -187,6 +187,11 @@ final class FieldProxy<T> implements TableField<Record, T>, QueryPartInternal {
|
||||
return delegate.generatesCast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean rendersContent(Context<?> ctx) {
|
||||
return delegate.rendersContent(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
delegate.accept(ctx);
|
||||
|
||||
@ -43,12 +43,9 @@ import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.unquotedName;
|
||||
import static org.jooq.impl.DSL.values;
|
||||
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;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_JSON_ARRAY;
|
||||
import static org.jooq.impl.Keywords.K_NULL;
|
||||
import static org.jooq.impl.Keywords.K_ON;
|
||||
import static org.jooq.impl.Names.N_JSON_ARRAY;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -59,6 +56,7 @@ import org.jooq.Field;
|
||||
import org.jooq.JSONArrayNullStep;
|
||||
import org.jooq.Row1;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.impl.JSONNull.JSONNullType;
|
||||
|
||||
|
||||
/**
|
||||
@ -73,17 +71,17 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
|
||||
*/
|
||||
private static final long serialVersionUID = 1772007627336725780L;
|
||||
private final QueryPartList<Field<?>> args;
|
||||
private final JSONNullClause nullClause;
|
||||
private final JSONNullType nullType;
|
||||
|
||||
JSONArray(DataType<J> type, Collection<? extends Field<?>> args) {
|
||||
this(type, args, null);
|
||||
}
|
||||
|
||||
JSONArray(DataType<J> type, Collection<? extends Field<?>> args, JSONNullClause nullClause) {
|
||||
JSONArray(DataType<J> type, Collection<? extends Field<?>> args, JSONNullType nullType) {
|
||||
super(N_JSON_ARRAY, type);
|
||||
|
||||
this.args = new QueryPartList<>(args);
|
||||
this.nullClause = nullClause;
|
||||
this.nullType = nullType;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -114,7 +112,7 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
|
||||
|
||||
|
||||
case POSTGRES:
|
||||
if (nullClause == ABSENT_ON_NULL) {
|
||||
if (nullType == ABSENT_ON_NULL) {
|
||||
Row1[] rows = new Row1[args.size()];
|
||||
for (int i = 0; i < rows.length; i++)
|
||||
rows[i] = row(args.get(i));
|
||||
@ -128,17 +126,18 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ctx.visit(K_JSON_ARRAY).sql('(').visit(args);
|
||||
default: {
|
||||
JSONNull jsonNull;
|
||||
|
||||
// Workaround for https://github.com/h2database/h2database/issues/2496
|
||||
if (ctx.family() == H2 && args.isEmpty())
|
||||
ctx.visit(K_NULL).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
jsonNull = new JSONNull(NULL_ON_NULL);
|
||||
else
|
||||
acceptJSONNullClause(ctx, nullClause);
|
||||
jsonNull = new JSONNull(nullType);
|
||||
|
||||
ctx.sql(')');
|
||||
ctx.visit(K_JSON_ARRAY).sql('(').visit(new QueryPartList<>(args, jsonNull).separator("")).sql(')');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,9 +42,8 @@ import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.impl.DSL.groupConcat;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
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;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
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;
|
||||
@ -61,6 +60,7 @@ import org.jooq.Field;
|
||||
import org.jooq.JSONArrayAggOrderByStep;
|
||||
import org.jooq.OrderField;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.impl.JSONNull.JSONNullType;
|
||||
|
||||
|
||||
/**
|
||||
@ -78,7 +78,7 @@ implements JSONArrayAggOrderByStep<J> {
|
||||
private static final long serialVersionUID = 1772007627336725780L;
|
||||
static final Set<SQLDialect> EMULATE_WITH_GROUP_CONCAT = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
private JSONNullClause nullClause;
|
||||
private JSONNullType nullType;
|
||||
|
||||
JSONArrayAgg(DataType<J> type, Field<?> arg) {
|
||||
super(false, N_JSON_ARRAYAGG, type, arg);
|
||||
@ -114,7 +114,7 @@ implements JSONArrayAggOrderByStep<J> {
|
||||
ctx.sql(')');
|
||||
|
||||
// TODO: What about a user-defined filter clause?
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
acceptFilterClause(ctx, arguments.get(0).isNotNull());
|
||||
|
||||
break;
|
||||
@ -139,19 +139,23 @@ implements JSONArrayAggOrderByStep<J> {
|
||||
ctx.visit(N_JSON_ARRAYAGG).sql('(');
|
||||
ctx.visit(arguments.get(0));
|
||||
acceptOrderBy(ctx);
|
||||
acceptJSONNullClause(ctx, nullClause);
|
||||
|
||||
JSONNull jsonNull = new JSONNull(nullType);
|
||||
if (jsonNull.rendersContent(ctx))
|
||||
ctx.sql(' ').visit(jsonNull);
|
||||
|
||||
ctx.sql(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONArrayAgg<J> nullOnNull() {
|
||||
nullClause = NULL_ON_NULL;
|
||||
nullType = NULL_ON_NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONArrayAgg<J> absentOnNull() {
|
||||
nullClause = ABSENT_ON_NULL;
|
||||
nullType = ABSENT_ON_NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
87
jOOQ/src/main/java/org/jooq/impl/JSONNull.java
Normal file
87
jOOQ/src/main/java/org/jooq/impl/JSONNull.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_ABSENT;
|
||||
import static org.jooq.impl.Keywords.K_NULL;
|
||||
import static org.jooq.impl.Keywords.K_ON;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.SQLDialect;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class JSONNull extends AbstractQueryPart {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 3121287280045911346L;
|
||||
static final Set<SQLDialect> NO_SUPPORT_ABSENT_ON_NULL = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
final JSONNullType type;
|
||||
|
||||
JSONNull(JSONNullType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean rendersContent(Context<?> ctx) {
|
||||
return !NO_SUPPORT_ABSENT_ON_NULL.contains(ctx.dialect()) && type != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
if (!NO_SUPPORT_ABSENT_ON_NULL.contains(ctx.dialect()))
|
||||
if (type == NULL_ON_NULL)
|
||||
ctx.visit(K_NULL).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
else if (type == ABSENT_ON_NULL)
|
||||
ctx.visit(K_ABSENT).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
}
|
||||
|
||||
enum JSONNullType {
|
||||
NULL_ON_NULL, ABSENT_ON_NULL
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
enum JSONNullClause {
|
||||
NULL_ON_NULL, ABSENT_ON_NULL
|
||||
}
|
||||
@ -38,8 +38,6 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.asterisk;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
@ -48,18 +46,14 @@ import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.unquotedName;
|
||||
import static org.jooq.impl.DSL.values;
|
||||
import static org.jooq.impl.JSONNullClause.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNullClause.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_ABSENT;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_JSON_OBJECT;
|
||||
import static org.jooq.impl.Keywords.K_NULL;
|
||||
import static org.jooq.impl.Keywords.K_ON;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE;
|
||||
import static org.jooq.impl.Names.N_JSON_OBJECT;
|
||||
import static org.jooq.impl.Names.N_T;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
@ -68,8 +62,8 @@ import org.jooq.JSONB;
|
||||
import org.jooq.JSONEntry;
|
||||
import org.jooq.JSONObjectNullStep;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.SQLDialect;
|
||||
// ...
|
||||
import org.jooq.impl.JSONNull.JSONNullType;
|
||||
|
||||
|
||||
/**
|
||||
@ -83,20 +77,19 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 1772007627336725780L;
|
||||
static final Set<SQLDialect> NO_SUPPORT_ABSENT_ON_NULL = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
private final QueryPartList<JSONEntry<?>> args;
|
||||
private final JSONNullClause nullClause;
|
||||
private final JSONNullType nullType;
|
||||
|
||||
JSONObject(DataType<J> type, Collection<? extends JSONEntry<?>> args) {
|
||||
this(type, args, null);
|
||||
}
|
||||
|
||||
JSONObject(DataType<J> type, Collection<? extends JSONEntry<?>> args, JSONNullClause nullClause) {
|
||||
JSONObject(DataType<J> type, Collection<? extends JSONEntry<?>> args, JSONNullType nullType) {
|
||||
super(N_JSON_OBJECT, type);
|
||||
|
||||
this.args = new QueryPartList<>(args);
|
||||
this.nullClause = nullClause;
|
||||
this.nullType = nullType;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -126,12 +119,12 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
|
||||
|
||||
|
||||
case POSTGRES:
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
ctx.visit(unquotedName(getDataType().getType() == JSONB.class ? "jsonb_strip_nulls" : "json_strip_nulls")).sql('(');
|
||||
|
||||
ctx.visit(unquotedName(getDataType().getType() == JSONB.class ? "jsonb_build_object" : "json_build_object")).sql('(').visit(args).sql(')');
|
||||
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
ctx.sql(')');
|
||||
|
||||
break;
|
||||
@ -202,27 +195,19 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
|
||||
}
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
ctx.visit(K_JSON_OBJECT).sql('(').visit(args);
|
||||
JSONNull jsonNull;
|
||||
|
||||
// Workaround for https://github.com/h2database/h2database/issues/2496
|
||||
if (args.isEmpty() && ctx.family() == H2)
|
||||
ctx.visit(K_NULL).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
jsonNull = new JSONNull(NULL_ON_NULL);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
acceptJSONNullClause(ctx, nullClause);
|
||||
jsonNull = new JSONNull(nullType);
|
||||
|
||||
ctx.sql(')');
|
||||
}
|
||||
|
||||
static final void acceptJSONNullClause(Context<?> ctx, JSONNullClause nullClause) {
|
||||
if (!NO_SUPPORT_ABSENT_ON_NULL.contains(ctx.dialect()))
|
||||
if (nullClause == NULL_ON_NULL)
|
||||
ctx.sql(' ').visit(K_NULL).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
else if (nullClause == ABSENT_ON_NULL)
|
||||
ctx.sql(' ').visit(K_ABSENT).sql(' ').visit(K_ON).sql(' ').visit(K_NULL);
|
||||
ctx.visit(K_JSON_OBJECT).sql('(').visit(new QueryPartList<>(args, jsonNull).separator("")).sql(')');
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,9 +43,8 @@ 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;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Names.N_JSONB_OBJECT_AGG;
|
||||
import static org.jooq.impl.Names.N_JSON_OBJECTAGG;
|
||||
import static org.jooq.impl.Names.N_JSON_OBJECT_AGG;
|
||||
@ -57,6 +56,7 @@ import org.jooq.Field;
|
||||
import org.jooq.JSON;
|
||||
import org.jooq.JSONEntry;
|
||||
import org.jooq.JSONObjectAggNullStep;
|
||||
import org.jooq.impl.JSONNull.JSONNullType;
|
||||
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ implements JSONObjectAggNullStep<J> {
|
||||
private static final long serialVersionUID = 1772007627336725780L;
|
||||
|
||||
private final JSONEntry<?> entry;
|
||||
private JSONNullClause nullClause;
|
||||
private JSONNullType nullType;
|
||||
|
||||
JSONObjectAgg(DataType<J> type, JSONEntry<?> entry) {
|
||||
super(false, N_JSON_OBJECTAGG, type, entry.key(), entry.value());
|
||||
@ -101,7 +101,7 @@ implements JSONObjectAggNullStep<J> {
|
||||
// [#10089] These dialects support non-standard JSON_OBJECTAGG without ABSENT ON NULL support
|
||||
case MARIADB:
|
||||
case MYSQL:
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
acceptGroupConcat(ctx);
|
||||
|
||||
|
||||
@ -124,7 +124,7 @@ implements JSONObjectAggNullStep<J> {
|
||||
ctx.sql(')');
|
||||
|
||||
// TODO: What about a user-defined filter clause?
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
acceptFilterClause(ctx, entry.value().isNotNull());
|
||||
|
||||
acceptOverClause(ctx);
|
||||
@ -152,7 +152,7 @@ implements JSONObjectAggNullStep<J> {
|
||||
break;
|
||||
}
|
||||
|
||||
if (nullClause == ABSENT_ON_NULL)
|
||||
if (nullType == ABSENT_ON_NULL)
|
||||
value = when(entry.value().isNull(), inline((String) null)).else_((Field) value);
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ implements JSONObjectAggNullStep<J> {
|
||||
inline('"'),
|
||||
DSL.replace(entry.key(), inline('"'), inline("\\\"")),
|
||||
inline("\":"),
|
||||
nullClause == ABSENT_ON_NULL ? value1 : DSL.coalesce(value1, inline("null"))
|
||||
nullType == ABSENT_ON_NULL ? value1 : DSL.coalesce(value1, inline("null"))
|
||||
)));
|
||||
acceptOverClause(c);
|
||||
}
|
||||
@ -174,23 +174,25 @@ implements JSONObjectAggNullStep<J> {
|
||||
}
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
ctx.visit(N_JSON_OBJECTAGG).sql('(');
|
||||
ctx.visit(entry);
|
||||
acceptJSONNullClause(ctx, nullClause);
|
||||
ctx.sql(')');
|
||||
ctx.visit(N_JSON_OBJECTAGG).sql('(').visit(entry);
|
||||
|
||||
JSONNull jsonNull = new JSONNull(nullType);
|
||||
if (jsonNull.rendersContent(ctx))
|
||||
ctx.sql(' ').visit(jsonNull);
|
||||
|
||||
ctx.sql(')');
|
||||
acceptOverClause(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONObjectAgg<J> nullOnNull() {
|
||||
nullClause = NULL_ON_NULL;
|
||||
nullType = NULL_ON_NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final JSONObjectAgg<J> absentOnNull() {
|
||||
nullClause = ABSENT_ON_NULL;
|
||||
nullType = ABSENT_ON_NULL;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,8 +309,8 @@ import static org.jooq.impl.DSL.xmlquery;
|
||||
import static org.jooq.impl.DSL.xmltable;
|
||||
import static org.jooq.impl.DSL.year;
|
||||
import static org.jooq.impl.DSL.zero;
|
||||
import static org.jooq.impl.JSONNullClause.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNullClause.NULL_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_DELETE;
|
||||
import static org.jooq.impl.Keywords.K_INSERT;
|
||||
import static org.jooq.impl.Keywords.K_SELECT;
|
||||
@ -540,6 +540,7 @@ import org.jooq.conf.RenderNameCase;
|
||||
import org.jooq.conf.RenderQuotedNames;
|
||||
import org.jooq.conf.Settings;
|
||||
import org.jooq.conf.SettingsTools;
|
||||
import org.jooq.impl.JSONNull.JSONNullType;
|
||||
import org.jooq.impl.XMLParse.DocumentOrContent;
|
||||
import org.jooq.tools.StringUtils;
|
||||
import org.jooq.tools.reflect.Reflect;
|
||||
@ -7196,19 +7197,19 @@ final class ParserImpl implements Parser {
|
||||
return DSL.jsonArray();
|
||||
|
||||
List<Field<?>> result = null;
|
||||
JSONNullClause nullClause = parseJSONObjectNullClauseIf(ctx);
|
||||
JSONNullType nullType = parseJSONNullTypeIf(ctx);
|
||||
|
||||
if (nullClause == null) {
|
||||
if (nullType == null) {
|
||||
result = parseFields(ctx);
|
||||
nullClause = parseJSONObjectNullClauseIf(ctx);
|
||||
nullType = parseJSONNullTypeIf(ctx);
|
||||
}
|
||||
|
||||
parse(ctx, ')');
|
||||
|
||||
JSONArrayNullStep<JSON> a = result == null ? DSL.jsonArray() : DSL.jsonArray(result);
|
||||
return nullClause == NULL_ON_NULL
|
||||
return nullType == NULL_ON_NULL
|
||||
? a.nullOnNull()
|
||||
: nullClause == ABSENT_ON_NULL
|
||||
: nullType == ABSENT_ON_NULL
|
||||
? a.absentOnNull()
|
||||
: a;
|
||||
}
|
||||
@ -7221,7 +7222,7 @@ final class ParserImpl implements Parser {
|
||||
Field<?> result;
|
||||
JSONArrayAggOrderByStep<JSON> s1;
|
||||
JSONArrayAggNullStep<JSON> s2;
|
||||
JSONNullClause nullClause;
|
||||
JSONNullType nullType;
|
||||
|
||||
parse(ctx, '(');
|
||||
result = s2 = s1 = DSL.jsonArrayAgg(parseField(ctx));
|
||||
@ -7229,8 +7230,8 @@ final class ParserImpl implements Parser {
|
||||
if (parseKeywordIf(ctx, "ORDER BY"))
|
||||
result = s2 = s1.orderBy(parseSortSpecification(ctx));
|
||||
|
||||
if ((nullClause = parseJSONObjectNullClauseIf(ctx)) != null)
|
||||
result = nullClause == ABSENT_ON_NULL ? s2.absentOnNull() : s2.nullOnNull();
|
||||
if ((nullType = parseJSONNullTypeIf(ctx)) != null)
|
||||
result = nullType == ABSENT_ON_NULL ? s2.absentOnNull() : s2.nullOnNull();
|
||||
|
||||
parse(ctx, ')');
|
||||
return result;
|
||||
@ -7246,22 +7247,22 @@ final class ParserImpl implements Parser {
|
||||
return DSL.jsonObject();
|
||||
|
||||
List<JSONEntry<?>> result = new ArrayList<>();
|
||||
JSONNullClause nullClause = parseJSONObjectNullClauseIf(ctx);
|
||||
JSONNullType nullType = parseJSONNullTypeIf(ctx);
|
||||
|
||||
if (nullClause == null) {
|
||||
if (nullType == null) {
|
||||
do {
|
||||
result.add(parseJSONEntry(ctx));
|
||||
}
|
||||
while (parseIf(ctx, ','));
|
||||
|
||||
nullClause = parseJSONObjectNullClauseIf(ctx);
|
||||
nullType = parseJSONNullTypeIf(ctx);
|
||||
}
|
||||
parse(ctx, ')');
|
||||
|
||||
JSONObjectNullStep<JSON> o = DSL.jsonObject(result);
|
||||
return nullClause == NULL_ON_NULL
|
||||
return nullType == NULL_ON_NULL
|
||||
? o.nullOnNull()
|
||||
: nullClause == ABSENT_ON_NULL
|
||||
: nullType == ABSENT_ON_NULL
|
||||
? o.absentOnNull()
|
||||
: o;
|
||||
}
|
||||
@ -7273,13 +7274,13 @@ final class ParserImpl implements Parser {
|
||||
if (parseFunctionNameIf(ctx, "JSON_OBJECTAGG")) {
|
||||
Field<?> result;
|
||||
JSONObjectAggNullStep<JSON> s1;
|
||||
JSONNullClause nullClause;
|
||||
JSONNullType nullType;
|
||||
|
||||
parse(ctx, '(');
|
||||
result = s1 = DSL.jsonObjectAgg(parseJSONEntry(ctx));
|
||||
|
||||
if ((nullClause = parseJSONObjectNullClauseIf(ctx)) != null)
|
||||
result = nullClause == ABSENT_ON_NULL ? s1.absentOnNull() : s1.nullOnNull();
|
||||
if ((nullType = parseJSONNullTypeIf(ctx)) != null)
|
||||
result = nullType == ABSENT_ON_NULL ? s1.absentOnNull() : s1.nullOnNull();
|
||||
|
||||
parse(ctx, ')');
|
||||
return result;
|
||||
@ -7288,7 +7289,7 @@ final class ParserImpl implements Parser {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final JSONNullClause parseJSONObjectNullClauseIf(ParserContext ctx) {
|
||||
private static final JSONNullType parseJSONNullTypeIf(ParserContext ctx) {
|
||||
if (parseKeywordIf(ctx, "NULL ON NULL"))
|
||||
return NULL_ON_NULL;
|
||||
else if (parseKeywordIf(ctx, "ABSENT ON NULL"))
|
||||
|
||||
@ -42,12 +42,14 @@ import static java.lang.Boolean.TRUE;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.QueryPartInternal;
|
||||
import org.jooq.Statement;
|
||||
|
||||
/**
|
||||
@ -62,6 +64,7 @@ class QueryPartCollectionView<T extends QueryPart> extends AbstractQueryPart imp
|
||||
final Collection<T> wrapped;
|
||||
int indentSize;
|
||||
Boolean qualify;
|
||||
String separator;
|
||||
|
||||
static <T extends QueryPart> QueryPartCollectionView<T> wrap(Collection<T> wrapped) {
|
||||
return new QueryPartCollectionView<>(wrapped);
|
||||
@ -70,6 +73,7 @@ class QueryPartCollectionView<T extends QueryPart> extends AbstractQueryPart imp
|
||||
QueryPartCollectionView(Collection<T> wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
this.indentSize = 2;
|
||||
this.separator = ",";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,13 +89,29 @@ class QueryPartCollectionView<T extends QueryPart> extends AbstractQueryPart imp
|
||||
return this;
|
||||
}
|
||||
|
||||
QueryPartCollectionView<T> separator(String newSeparator) {
|
||||
this.separator = newSeparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
Collection<T> wrapped() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rendersContent(Context<?> ctx) {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public /* non-final */ void accept(Context<?> ctx) {
|
||||
int size = size();
|
||||
BitSet rendersContent = new BitSet(size());
|
||||
int i = 0;
|
||||
|
||||
for (T e : this)
|
||||
rendersContent.set(i++, ((QueryPartInternal) e).rendersContent(ctx));
|
||||
|
||||
int size = rendersContent.cardinality();
|
||||
boolean format = size >= indentSize;
|
||||
boolean previousQualify = ctx.qualify();
|
||||
|
||||
@ -110,18 +130,23 @@ class QueryPartCollectionView<T extends QueryPart> extends AbstractQueryPart imp
|
||||
}
|
||||
|
||||
else {
|
||||
boolean indent = format && !TRUE.equals(ctx.data(DATA_LIST_ALREADY_INDENTED));
|
||||
Object previousIndented = ctx.data(DATA_LIST_ALREADY_INDENTED);
|
||||
boolean indent = format && !TRUE.equals(previousIndented);
|
||||
|
||||
if (indent)
|
||||
ctx.formatIndentStart();
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
for (T part : this) {
|
||||
if (i++ > 0) {
|
||||
if (!rendersContent.get(j++))
|
||||
continue;
|
||||
|
||||
if (k++ > 0) {
|
||||
|
||||
// [#3607] Procedures and functions are not separated by comma
|
||||
if (!(part instanceof Statement))
|
||||
ctx.sql(',');
|
||||
ctx.sql(separator);
|
||||
|
||||
if (format)
|
||||
ctx.formatSeparator();
|
||||
@ -131,7 +156,13 @@ class QueryPartCollectionView<T extends QueryPart> extends AbstractQueryPart imp
|
||||
else if (indent)
|
||||
ctx.formatNewLine();
|
||||
|
||||
ctx.visit(part);
|
||||
if (indent) {
|
||||
ctx.data(DATA_LIST_ALREADY_INDENTED, part instanceof QueryPartCollectionView);
|
||||
ctx.visit(part);
|
||||
ctx.data(DATA_LIST_ALREADY_INDENTED, previousIndented);
|
||||
}
|
||||
else
|
||||
ctx.visit(part);
|
||||
}
|
||||
|
||||
if (indent)
|
||||
|
||||
@ -56,7 +56,8 @@ class QueryPartList<T extends QueryPart> extends QueryPartListView<T> {
|
||||
this((Collection<T>) null);
|
||||
}
|
||||
|
||||
QueryPartList(T[] wrappedList) {
|
||||
@SafeVarargs
|
||||
QueryPartList(T... wrappedList) {
|
||||
this(asList(wrappedList));
|
||||
}
|
||||
|
||||
@ -77,4 +78,9 @@ class QueryPartList<T extends QueryPart> extends QueryPartListView<T> {
|
||||
QueryPartList<T> qualify(boolean newQualify) {
|
||||
return (QueryPartList<T>) super.qualify(newQualify);
|
||||
}
|
||||
|
||||
@Override
|
||||
QueryPartList<T> separator(String newSeparator) {
|
||||
return (QueryPartList<T>) super.separator(newSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +83,11 @@ class QueryPartListView<T extends QueryPart> extends QueryPartCollectionView<T>
|
||||
return (QueryPartListView<T>) super.qualify(newQualify);
|
||||
}
|
||||
|
||||
@Override
|
||||
QueryPartListView<T> separator(String newSeparator) {
|
||||
return (QueryPartListView<T>) super.separator(newSeparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<T> wrapped() {
|
||||
return (List<T>) super.wrapped();
|
||||
|
||||
@ -51,6 +51,11 @@ enum ScopeMarkers implements QueryPartInternal {
|
||||
BEFORE_FIRST_TOP_LEVEL_CTE,
|
||||
AFTER_LAST_TOP_LEVEL_CTE;
|
||||
|
||||
@Override
|
||||
public boolean rendersContent(Context<?> ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {}
|
||||
|
||||
|
||||
@ -63,7 +63,12 @@ final class SelectFieldList<F extends SelectFieldOrAsterisk> extends QueryPartLi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toSQLEmptyList(Context<?> ctx) {
|
||||
public final boolean rendersContent(Context<?> ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void toSQLEmptyList(Context<?> ctx) {
|
||||
ctx.visit(AsteriskImpl.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
@ -115,7 +115,7 @@ import static org.jooq.impl.DSL.xmlagg;
|
||||
import static org.jooq.impl.DSL.xmlattributes;
|
||||
import static org.jooq.impl.DSL.xmlelement;
|
||||
import static org.jooq.impl.JSONArrayAgg.EMULATE_WITH_GROUP_CONCAT;
|
||||
import static org.jooq.impl.JSONObject.NO_SUPPORT_ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.JSONNull.NO_SUPPORT_ABSENT_ON_NULL;
|
||||
import static org.jooq.impl.Keywords.K_AND;
|
||||
import static org.jooq.impl.Keywords.K_BY;
|
||||
import static org.jooq.impl.Keywords.K_CONNECT_BY;
|
||||
|
||||
@ -69,6 +69,11 @@ final class TableList extends QueryPartList<Table<?>> {
|
||||
super(wrappedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean rendersContent(Context<?> ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toSQLEmptyList(Context<?> ctx) {
|
||||
ctx.visit(new Dual());
|
||||
|
||||
@ -5125,12 +5125,17 @@ final class Tools {
|
||||
static final QueryPartList<SelectFieldOrAsterisk> qualify(final Table<?> table, SelectFieldList<SelectFieldOrAsterisk> fields) {
|
||||
QueryPartList<SelectFieldOrAsterisk> result = new QueryPartList<SelectFieldOrAsterisk>() {
|
||||
@Override
|
||||
protected void toSQLEmptyList(Context<?> context) {
|
||||
public final boolean rendersContent(Context<?> ctx) {
|
||||
return super.rendersContent(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void toSQLEmptyList(Context<?> context) {
|
||||
table.asterisk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresFields() {
|
||||
public final boolean declaresFields() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user