[jOOQ/jOOQ#11318] Add native support for the standard JSON function

RETURNING clause

Added API and parser support for RETURNING on:

- JSON_ARRAY
- JSON_ARRAYAGG
- JSON_OBJECT
- JSON_OBJECTAGG
- JSON_VALUE
This commit is contained in:
Lukas Eder 2021-02-10 14:08:23 +01:00
parent 753242b86c
commit 8da28a94c3
23 changed files with 1006 additions and 352 deletions

View File

@ -55,19 +55,19 @@ import org.jooq.impl.DSL;
*
* @author Lukas Eder
*/
public interface JSONArrayAggNullStep<T> extends AggregateFilterStep<T> {
public interface JSONArrayAggNullStep<T> extends JSONArrayAggReturningStep<T> {
/**
* Include <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, POSTGRES })
AggregateFilterStep<T> nullOnNull();
JSONArrayAggReturningStep<T> nullOnNull();
/**
* Exclude <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, POSTGRES })
AggregateFilterStep<T> absentOnNull();
JSONArrayAggReturningStep<T> absentOnNull();
}

View File

@ -0,0 +1,67 @@
/*
* 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;
import org.jetbrains.annotations.*;
// ...
// ...
import static org.jooq.SQLDialect.H2;
// ...
import static org.jooq.SQLDialect.POSTGRES;
import org.jooq.impl.DSL;
/**
* A step in the construction of {@link DSL#jsonArrayAgg(Field)} or
* {@link DSL#jsonbArrayAgg(Field)} functions where the <code>RETURNING</code>
* clause can be defined.
*
* @author Lukas Eder
*/
public interface JSONArrayAggReturningStep<T> extends AggregateFilterStep<T> {
/**
* Add a <code>RETURNING</code> clause to the <code>JSON_ARRAYAGG</code>
* function.
*/
@NotNull
@Support({ H2, POSTGRES })
AggregateFilterStep<T> returning(DataType<?> returning);
}

View File

@ -37,40 +37,51 @@
*/
package org.jooq;
import static org.jooq.SQLDialect.*;
import java.util.*;
import org.jetbrains.annotations.*;
// ...
// ...
// ...
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.MARIADB;
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
import org.jooq.impl.DSL;
/**
* A step in the construction of {@link DSL#jsonArray(Field...)} or
* {@link DSL#jsonbArray(Field...)} functions where the <code>NULL</code> clause
* can be defined.
*
* @author Lukas Eder
* A step in the construction of the <code>JSON ARRAY</code> function.
* <p>
* <h3>Referencing <code>XYZ*Step</code> types directly from client code</h3>
* <p>
* It is usually not recommended to reference any <code>XYZ*Step</code> types
* directly from client code, or assign them to local variables. When writing
* dynamic SQL, creating a statement's components dynamically, and passing them
* to the DSL API statically is usually a better choice. See the manual's
* section about dynamic SQL for details: <a href=
* "https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql">https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql</a>.
* <p>
* Drawbacks of referencing the <code>XYZ*Step</code> types directly:
* <ul>
* <li>They're operating on mutable implementations (as of jOOQ 3.x)</li>
* <li>They're less composable and not easy to get right when dynamic SQL gets
* complex</li>
* <li>They're less readable</li>
* <li>They might have binary incompatible changes between minor releases</li>
* </ul>
*/
public interface JSONArrayNullStep<T> extends Field<T> {
@SuppressWarnings({ "unused" })
public interface JSONArrayNullStep<T> extends JSONArrayReturningStep<T> {
/**
* Add the <code>NULL ON NULL</code> clause to the <code>JSON ARRAY</code> function.
* <p>
* Include <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
Field<T> nullOnNull();
@NotNull
JSONArrayReturningStep<T> nullOnNull();
/**
* Add the <code>ABSENT ON NULL</code> clause to the <code>JSON ARRAY</code> function.
* <p>
* Exclude <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, POSTGRES })
Field<T> absentOnNull();
@NotNull
JSONArrayReturningStep<T> absentOnNull();
}

View File

@ -0,0 +1,76 @@
/*
* 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;
import static org.jooq.SQLDialect.*;
import java.util.*;
import org.jetbrains.annotations.*;
/**
* A step in the construction of the <code>JSON ARRAY</code> function.
* <p>
* <h3>Referencing <code>XYZ*Step</code> types directly from client code</h3>
* <p>
* It is usually not recommended to reference any <code>XYZ*Step</code> types
* directly from client code, or assign them to local variables. When writing
* dynamic SQL, creating a statement's components dynamically, and passing them
* to the DSL API statically is usually a better choice. See the manual's
* section about dynamic SQL for details: <a href=
* "https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql">https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql</a>.
* <p>
* Drawbacks of referencing the <code>XYZ*Step</code> types directly:
* <ul>
* <li>They're operating on mutable implementations (as of jOOQ 3.x)</li>
* <li>They're less composable and not easy to get right when dynamic SQL gets
* complex</li>
* <li>They're less readable</li>
* <li>They might have binary incompatible changes between minor releases</li>
* </ul>
*/
@SuppressWarnings({ "unused" })
public interface JSONArrayReturningStep<T> extends Field<T> {
/**
* Add the <code>RETURNING</code> clause to the <code>JSON ARRAY</code> function.
*/
@Support({ H2, MARIADB, MYSQL, POSTGRES })
@NotNull
Field<T> returning(DataType<?> returning);
}

View File

@ -58,19 +58,19 @@ import org.jooq.impl.DSL;
*
* @author Lukas Eder
*/
public interface JSONObjectAggNullStep<T> extends AggregateFilterStep<T> {
public interface JSONObjectAggNullStep<T> extends JSONObjectAggReturningStep<T> {
/**
* Include <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
AggregateFilterStep<T> nullOnNull();
JSONObjectAggReturningStep<T> nullOnNull();
/**
* Exclude <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
AggregateFilterStep<T> absentOnNull();
JSONObjectAggReturningStep<T> absentOnNull();
}

View File

@ -0,0 +1,70 @@
/*
* 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;
import org.jetbrains.annotations.*;
// ...
// ...
// ...
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.MARIADB;
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
import org.jooq.impl.DSL;
/**
* A step in the construction of {@link DSL#jsonObjectAgg(JSONEntry)} or
* {@link DSL#jsonbObjectAgg(JSONEntry)} functions where the
* <code>RETURNING</code> clause can be defined.
*
* @author Lukas Eder
*/
public interface JSONObjectAggReturningStep<T> extends AggregateFilterStep<T> {
/**
* Add a <code>RETURNING</code> clause to the <code>JSON_ARRAYAGG</code>
* function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
AggregateFilterStep<T> returning(DataType<?> returning);
}

View File

@ -37,41 +37,51 @@
*/
package org.jooq;
import static org.jooq.SQLDialect.*;
import java.util.*;
import org.jetbrains.annotations.*;
// ...
// ...
// ...
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.MARIADB;
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
import org.jooq.impl.DSL;
/**
* A step in the construction of {@link DSL#jsonObject(JSONEntry...)} or
* {@link DSL#jsonbObject(JSONEntry...)} functions where the <code>NULL</code>
* clause can be defined.
*
* @author Lukas Eder
* A step in the construction of the <code>JSON OBJECT</code> function.
* <p>
* <h3>Referencing <code>XYZ*Step</code> types directly from client code</h3>
* <p>
* It is usually not recommended to reference any <code>XYZ*Step</code> types
* directly from client code, or assign them to local variables. When writing
* dynamic SQL, creating a statement's components dynamically, and passing them
* to the DSL API statically is usually a better choice. See the manual's
* section about dynamic SQL for details: <a href=
* "https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql">https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql</a>.
* <p>
* Drawbacks of referencing the <code>XYZ*Step</code> types directly:
* <ul>
* <li>They're operating on mutable implementations (as of jOOQ 3.x)</li>
* <li>They're less composable and not easy to get right when dynamic SQL gets
* complex</li>
* <li>They're less readable</li>
* <li>They might have binary incompatible changes between minor releases</li>
* </ul>
*/
public interface JSONObjectNullStep<T> extends Field<T> {
@SuppressWarnings({ "unused" })
public interface JSONObjectNullStep<T> extends JSONObjectReturningStep<T> {
/**
* Add the <code>NULL ON NULL</code> clause to the <code>JSON OBJECT</code> function.
* <p>
* Include <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
Field<T> nullOnNull();
@NotNull
JSONObjectReturningStep<T> nullOnNull();
/**
* Add the <code>ABSENT ON NULL</code> clause to the <code>JSON OBJECT</code> function.
* <p>
* Exclude <code>NULL</code> values in output JSON.
*/
@NotNull
@Support({ H2, POSTGRES })
Field<T> absentOnNull();
@NotNull
JSONObjectReturningStep<T> absentOnNull();
}

View File

@ -0,0 +1,76 @@
/*
* 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;
import static org.jooq.SQLDialect.*;
import java.util.*;
import org.jetbrains.annotations.*;
/**
* A step in the construction of the <code>JSON OBJECT</code> function.
* <p>
* <h3>Referencing <code>XYZ*Step</code> types directly from client code</h3>
* <p>
* It is usually not recommended to reference any <code>XYZ*Step</code> types
* directly from client code, or assign them to local variables. When writing
* dynamic SQL, creating a statement's components dynamically, and passing them
* to the DSL API statically is usually a better choice. See the manual's
* section about dynamic SQL for details: <a href=
* "https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql">https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql</a>.
* <p>
* Drawbacks of referencing the <code>XYZ*Step</code> types directly:
* <ul>
* <li>They're operating on mutable implementations (as of jOOQ 3.x)</li>
* <li>They're less composable and not easy to get right when dynamic SQL gets
* complex</li>
* <li>They're less readable</li>
* <li>They might have binary incompatible changes between minor releases</li>
* </ul>
*/
@SuppressWarnings({ "unused" })
public interface JSONObjectReturningStep<T> extends Field<T> {
/**
* Add the <code>RETURNING</code> clause to the <code>JSON OBJECT</code> function.
*/
@Support({ H2, MARIADB, MYSQL, POSTGRES })
@NotNull
Field<T> returning(DataType<?> returning);
}

View File

@ -52,7 +52,7 @@ import org.jooq.impl.DSL;
*
* @author Lukas Eder
*/
public interface JSONValueOnStep<J> extends Field<J> {
public interface JSONValueOnStep<J> extends JSONValueReturningStep<J> {

View File

@ -0,0 +1,76 @@
/*
* 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;
import static org.jooq.SQLDialect.*;
import java.util.*;
import org.jetbrains.annotations.*;
/**
* A step in the construction of the <code>JSON VALUE</code> function.
* <p>
* <h3>Referencing <code>XYZ*Step</code> types directly from client code</h3>
* <p>
* It is usually not recommended to reference any <code>XYZ*Step</code> types
* directly from client code, or assign them to local variables. When writing
* dynamic SQL, creating a statement's components dynamically, and passing them
* to the DSL API statically is usually a better choice. See the manual's
* section about dynamic SQL for details: <a href=
* "https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql">https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql</a>.
* <p>
* Drawbacks of referencing the <code>XYZ*Step</code> types directly:
* <ul>
* <li>They're operating on mutable implementations (as of jOOQ 3.x)</li>
* <li>They're less composable and not easy to get right when dynamic SQL gets
* complex</li>
* <li>They're less readable</li>
* <li>They might have binary incompatible changes between minor releases</li>
* </ul>
*/
@SuppressWarnings({ "unused" })
public interface JSONValueReturningStep<T> extends Field<T> {
/**
* Add the <code>RETURNING</code> clause to the <code>JSON VALUE</code> function.
*/
@Support({ MARIADB, MYSQL, POSTGRES })
@NotNull
Field<T> returning(DataType<?> returning);
}

View File

@ -18389,6 +18389,100 @@ public class DSL {
return new Xmlforest(fields);
}
// -------------------------------------------------------------------------
// JSON functions
// -------------------------------------------------------------------------
/**
* The <code>JSON_ARRAY</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSON> jsonArray(Field<?>... fields) {
return new JSONArray<>(SQLDataType.JSON, Arrays.asList(fields));
}
/**
* The <code>JSON_ARRAY</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSON> jsonArray(Collection<? extends Field<?>> fields) {
return new JSONArray<>(SQLDataType.JSON, fields);
}
/**
* The <code>JSONB_ARRAY</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSONB> jsonbArray(Field<?>... fields) {
return new JSONArray<>(SQLDataType.JSONB, Arrays.asList(fields));
}
/**
* The <code>JSONB_ARRAY</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSONB> jsonbArray(Collection<? extends Field<?>> fields) {
return new JSONArray<>(SQLDataType.JSONB, fields);
}
/**
* The <code>JSON_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(Field<?>... entries) {
return new JSONObject<>(SQLDataType.JSON, Tools.jsonEntries(entries));
}
/**
* The <code>JSON_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(JSONEntry<?>... entries) {
return new JSONObject<>(SQLDataType.JSON, Arrays.asList(entries));
}
/**
* The <code>JSON_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(Collection<? extends JSONEntry<?>> entries) {
return new JSONObject<>(SQLDataType.JSON, entries);
}
/**
* The <code>JSONB_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(Field<?>... entries) {
return new JSONObject<>(SQLDataType.JSONB, Tools.jsonEntries(entries));
}
/**
* The <code>JSONB_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(JSONEntry<?>... entries) {
return new JSONObject<>(SQLDataType.JSONB, Arrays.asList(entries));
}
/**
* The <code>JSONB_OBJECT</code> function.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(Collection<? extends JSONEntry<?>> entries) {
return new JSONObject<>(SQLDataType.JSONB, entries);
}
@ -22228,7 +22322,7 @@ public class DSL {
@NotNull
@Support({ MARIADB, MYSQL, POSTGRES })
public static JSONValueOnStep<JSON> jsonValue(Field<JSON> json, Field<String> path) {
return new JSONValue<>(SQLDataType.JSON, json, path);
return new JSONValue<>(SQLDataType.JSON, json, path, null);
}
/**
@ -22246,43 +22340,7 @@ public class DSL {
@NotNull
@Support({ MARIADB, MYSQL, POSTGRES })
public static JSONValueOnStep<JSONB> jsonbValue(Field<JSONB> json, Field<String> path) {
return new JSONValue<>(SQLDataType.JSONB, json, path);
}
/**
* The JSON array constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSON> jsonArray(Field<?>... fields) {
return jsonArray(Arrays.asList(fields));
}
/**
* The JSON array constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSON> jsonArray(Collection<? extends Field<?>> fields) {
return new JSONArray<>(JSON, fields);
}
/**
* The JSONB array constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSONB> jsonbArray(Field<?>... fields) {
return jsonbArray(Arrays.asList(fields));
}
/**
* The JSONB array constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONArrayNullStep<JSONB> jsonbArray(Collection<? extends Field<?>> fields) {
return new JSONArray<>(JSONB, fields);
return new JSONValue<>(SQLDataType.JSONB, json, path, null);
}
/**
@ -22379,63 +22437,6 @@ public class DSL {
return jsonObject(Tools.EMPTY_JSONENTRY);
}
/**
* The JSON object constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(Field<?>... entries) {
return jsonObject(Tools.jsonEntries(entries));
}
/**
* The JSON object constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(JSONEntry<?>... entries) {
return jsonObject(Arrays.asList(entries));
}
/**
* The JSON object constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSON> jsonObject(Collection<? extends JSONEntry<?>> entries) {
return new JSONObject<>(JSON, entries);
}
/**
* The JSONB object constructor.
* <p>
* This is the same as calling {@link #jsonEntry(String, Field)} with
* {@link Field#getName()} as a key.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(Field<?>... entries) {
return jsonbObject(Tools.jsonEntries(entries));
}
/**
* The JSONB object constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(JSONEntry<?>... entries) {
return jsonbObject(Arrays.asList(entries));
}
/**
* The JSONB object constructor.
*/
@NotNull
@Support({ H2, MARIADB, MYSQL, POSTGRES })
public static JSONObjectNullStep<JSONB> jsonbObject(Collection<? extends JSONEntry<?>> entries) {
return new JSONObject<>(JSONB, entries);
}
/**
* The JSON array aggregate function.
*/

View File

@ -160,7 +160,7 @@ implements
private void accept0(Context<?> ctx) {
if (dropSchemaIfExists && !supportsIfExists(ctx))
tryCatch(ctx, DDLStatementType.DROP_SCHEMA, c -> accept0(c));
tryCatch(ctx, DDLStatementType.DROP_SCHEMA, c -> accept1(c));
else
accept1(ctx);
}

View File

@ -37,56 +37,71 @@
*/
package org.jooq.impl;
import static org.jooq.SQLDialect.H2;
import static org.jooq.impl.DSL.jsonArrayAgg;
import static org.jooq.impl.DSL.jsonbArrayAgg;
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.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.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.QueryPartListView.wrap;
import static org.jooq.impl.SQLDataType.JSON;
import static org.jooq.impl.DSL.*;
import static org.jooq.impl.Internal.*;
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.DataExtendedKey.*;
import static org.jooq.impl.Tools.DataKey.*;
import static org.jooq.SQLDialect.*;
import java.util.Collection;
import org.jooq.*;
import org.jooq.conf.*;
import org.jooq.impl.*;
import org.jooq.tools.*;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.JSONArrayNullStep;
import org.jooq.Row1;
import org.jooq.Table;
import org.jooq.impl.JSONNull.JSONNullType;
import java.util.*;
/**
* The JSON array constructor.
*
* @author Lukas Eder
* The <code>JSON ARRAY</code> statement.
*/
final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J> {
@SuppressWarnings({ "hiding", "rawtypes", "unchecked", "unused" })
final class JSONArray<T>
extends
AbstractField<T>
implements
JSONArrayNullStep<T>,
JSONArrayReturningStep<T>
{
/**
* Generated UID
*/
private static final long serialVersionUID = 1772007627336725780L;
private final QueryPartList<Field<?>> args;
private final JSONNullType nullType;
private static final long serialVersionUID = 1L;
JSONArray(DataType<J> type, Collection<? extends Field<?>> args) {
this(type, args, null);
private final DataType<T> type;
private final Collection<? extends Field<?>> fields;
private JSONOnNull onNull;
private DataType<?> returning;
JSONArray(
DataType<T> type,
Collection<? extends Field<?>> fields
) {
this(
type,
fields,
null,
null
);
}
JSONArray(DataType<J> type, Collection<? extends Field<?>> args, JSONNullType nullType) {
super(N_JSON_ARRAY, type);
JSONArray(
DataType<T> type,
Collection<? extends Field<?>> fields,
JSONOnNull onNull,
DataType<?> returning
) {
super(
N_JSON_ARRAY,
type
);
this.args = new QueryPartList<>(args);
this.nullType = nullType;
this.type = type;
this.fields = fields;
this.onNull = onNull;
this.returning = returning;
}
// -------------------------------------------------------------------------
@ -94,20 +109,29 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
// -------------------------------------------------------------------------
@Override
public final JSONArray<J> nullOnNull() {
return new JSONArray<>(getDataType(), args, NULL_ON_NULL);
public final JSONArray<T> nullOnNull() {
this.onNull = JSONOnNull.NULL_ON_NULL;
return this;
}
@Override
public final JSONArray<J> absentOnNull() {
return new JSONArray<>(getDataType(), args, ABSENT_ON_NULL);
public final JSONArray<T> absentOnNull() {
this.onNull = JSONOnNull.ABSENT_ON_NULL;
return this;
}
@Override
public final JSONArray<T> returning(DataType<?> returning) {
this.returning = returning;
return this;
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void accept(Context<?> ctx) {
switch (ctx.family()) {
@ -117,11 +141,12 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
case POSTGRES:
if (nullType == ABSENT_ON_NULL) {
Row1[] rows = new Row1[args.size()];
if (onNull == JSONOnNull.ABSENT_ON_NULL) {
Row1[] rows = new Row1[fields.size()];
for (int i = 0; i < rows.length; i++)
rows[i] = row(args.get(i));
int i = 0;
for (Field<?> field : fields)
rows[i++] = row(field);
Table<?> t = values(rows).as("t", "a");
Field<?> a = t.field("a");
@ -132,23 +157,51 @@ final class JSONArray<J> extends AbstractField<J> implements JSONArrayNullStep<J
));
}
else {
ctx.visit(getDataType() == JSON ? N_JSON_BUILD_ARRAY : N_JSONB_BUILD_ARRAY).sql('(').visit(args).sql(')');
ctx.visit(getDataType() == JSON ? N_JSON_BUILD_ARRAY : N_JSONB_BUILD_ARRAY).sql('(').visit(QueryPartCollectionView.wrap(fields)).sql(')');
}
break;
default: {
JSONNull jsonNull;
JSONReturning jsonReturning = new JSONReturning(returning);
// Workaround for https://github.com/h2database/h2database/issues/2496
if (ctx.family() == H2 && args.isEmpty())
jsonNull = new JSONNull(NULL_ON_NULL);
if (ctx.family() == H2 && fields.isEmpty())
jsonNull = new JSONNull(JSONOnNull.NULL_ON_NULL);
else
jsonNull = new JSONNull(nullType);
jsonNull = new JSONNull(onNull);
ctx.visit(K_JSON_ARRAY).sql('(').visit(
QueryPartListView.wrap(
QueryPartCollectionView.wrap((Collection<Field<?>>) fields).map(JSONEntryImpl.jsonCastMapper(ctx)),
jsonNull,
jsonReturning
).separator("")
).sql(')');
ctx.visit(K_JSON_ARRAY).sql('(').visit(wrap(wrap(args).map(jsonCastMapper(ctx)), jsonNull).separator("")).sql(')');
break;
}
}
}
// -------------------------------------------------------------------------
// The Object API
// -------------------------------------------------------------------------
@Override
public boolean equals(Object that) {
if (that instanceof JSONArray) {
return
StringUtils.equals(type, ((JSONArray) that).type) &&
StringUtils.equals(fields, ((JSONArray) that).fields) &&
StringUtils.equals(onNull, ((JSONArray) that).onNull) &&
StringUtils.equals(returning, ((JSONArray) that).returning)
;
}
else
return super.equals(that);
}
}

View File

@ -44,8 +44,8 @@ import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.groupConcat;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.JSONEntryImpl.jsonCast;
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.JSONOnNull.ABSENT_ON_NULL;
import static org.jooq.impl.JSONOnNull.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;
@ -57,13 +57,15 @@ import static org.jooq.impl.SQLDataType.VARCHAR;
import java.util.Collection;
import java.util.Set;
import org.jooq.AggregateFilterStep;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.JSONArrayAggOrderByStep;
import org.jooq.OrderField;
import org.jooq.SQLDialect;
import org.jooq.impl.JSONNull.JSONNullType;
import org.jetbrains.annotations.NotNull;
/**
@ -81,7 +83,8 @@ implements JSONArrayAggOrderByStep<J> {
private static final long serialVersionUID = 1772007627336725780L;
static final Set<SQLDialect> EMULATE_WITH_GROUP_CONCAT = SQLDialect.supportedBy(MARIADB, MYSQL);
private JSONNullType nullType;
private JSONOnNull onNull;
private DataType<?> returning;
JSONArrayAgg(DataType<J> type, Field<?> arg) {
super(false, N_JSON_ARRAYAGG, type, arg);
@ -117,7 +120,7 @@ implements JSONArrayAggOrderByStep<J> {
ctx.sql(')');
// TODO: What about a user-defined filter clause?
if (nullType == ABSENT_ON_NULL)
if (onNull == ABSENT_ON_NULL)
acceptFilterClause(ctx, arguments.get(0).isNotNull());
break;
@ -161,22 +164,32 @@ implements JSONArrayAggOrderByStep<J> {
ctx.visit(jsonCast(ctx, arguments.get(0)));
acceptOrderBy(ctx);
JSONNull jsonNull = new JSONNull(nullType);
JSONNull jsonNull = new JSONNull(onNull);
if (jsonNull.rendersContent(ctx))
ctx.sql(' ').visit(jsonNull);
JSONReturning jsonReturning = new JSONReturning(returning);
if (jsonReturning.rendersContent(ctx))
ctx.sql(' ').visit(jsonReturning);
ctx.sql(')');
}
@Override
public final JSONArrayAgg<J> nullOnNull() {
nullType = NULL_ON_NULL;
onNull = NULL_ON_NULL;
return this;
}
@Override
public final JSONArrayAgg<J> absentOnNull() {
nullType = ABSENT_ON_NULL;
onNull = ABSENT_ON_NULL;
return this;
}
@Override
public final JSONArrayAgg<J> returning(DataType<?> r) {
this.returning = r;
return this;
}

View File

@ -140,7 +140,7 @@ final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>,
}
}
static final Function<Field<?>, Field<?>> jsonCastMapper(final Context<?> ctx) {
static final Function<? super Field<?>, ? extends Field<?>> jsonCastMapper(final Context<?> ctx) {
return field -> jsonCast(ctx, field);
}

View File

@ -39,8 +39,8 @@ 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.JSONOnNull.ABSENT_ON_NULL;
import static org.jooq.impl.JSONOnNull.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;
@ -61,9 +61,9 @@ final class JSONNull extends AbstractQueryPart {
private static final long serialVersionUID = 3121287280045911346L;
static final Set<SQLDialect> NO_SUPPORT_ABSENT_ON_NULL = SQLDialect.supportedBy(MARIADB, MYSQL);
final JSONNullType type;
final JSONOnNull type;
JSONNull(JSONNullType type) {
JSONNull(JSONOnNull type) {
this.type = type;
}
@ -80,8 +80,4 @@ final class JSONNull extends AbstractQueryPart {
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
}
}

View File

@ -37,64 +37,71 @@
*/
package org.jooq.impl;
import static org.jooq.SQLDialect.H2;
// ...
import static org.jooq.impl.DSL.asterisk;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.jsonEntry;
import static org.jooq.impl.DSL.jsonObject;
import static org.jooq.impl.DSL.key;
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.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.Names.N_JSON_MERGE;
import static org.jooq.impl.Names.N_JSON_OBJECT;
import static org.jooq.impl.Names.N_T;
import static org.jooq.impl.QueryPartListView.wrap;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.DSL.*;
import static org.jooq.impl.Internal.*;
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.DataExtendedKey.*;
import static org.jooq.impl.Tools.DataKey.*;
import static org.jooq.SQLDialect.*;
import java.util.Collection;
import java.util.UUID;
import org.jooq.*;
import org.jooq.conf.*;
import org.jooq.impl.*;
import org.jooq.tools.*;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.JSONB;
import org.jooq.JSONEntry;
import org.jooq.JSONObjectNullStep;
import org.jooq.Name;
// ...
import org.jooq.impl.JSONNull.JSONNullType;
import java.util.*;
/**
* The JSON array constructor.
*
* @author Lukas Eder
* The <code>JSON OBJECT</code> statement.
*/
final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep<J> {
@SuppressWarnings({ "hiding", "rawtypes", "unchecked", "unused" })
final class JSONObject<T>
extends
AbstractField<T>
implements
JSONObjectNullStep<T>,
JSONObjectReturningStep<T>
{
/**
* Generated UID
*/
private static final long serialVersionUID = 1772007627336725780L;
private static final long serialVersionUID = 1L;
private final QueryPartList<JSONEntry<?>> args;
private final JSONNullType nullType;
private final DataType<T> type;
private final Collection<? extends JSONEntry<?>> entries;
private JSONOnNull onNull;
private DataType<?> returning;
JSONObject(DataType<J> type, Collection<? extends JSONEntry<?>> args) {
this(type, args, null);
JSONObject(
DataType<T> type,
Collection<? extends JSONEntry<?>> entries
) {
this(
type,
entries,
null,
null
);
}
JSONObject(DataType<J> type, Collection<? extends JSONEntry<?>> args, JSONNullType nullType) {
super(N_JSON_OBJECT, type);
JSONObject(
DataType<T> type,
Collection<? extends JSONEntry<?>> entries,
JSONOnNull onNull,
DataType<?> returning
) {
super(
N_JSON_OBJECT,
type
);
this.args = new QueryPartList<>(args);
this.nullType = nullType;
this.type = type;
this.entries = entries;
this.onNull = onNull;
this.returning = returning;
}
// -------------------------------------------------------------------------
@ -102,19 +109,29 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
// -------------------------------------------------------------------------
@Override
public final JSONObject<J> nullOnNull() {
return new JSONObject<>(getDataType(), args, NULL_ON_NULL);
public final JSONObject<T> nullOnNull() {
this.onNull = JSONOnNull.NULL_ON_NULL;
return this;
}
@Override
public final JSONObject<J> absentOnNull() {
return new JSONObject<>(getDataType(), args, ABSENT_ON_NULL);
public final JSONObject<T> absentOnNull() {
this.onNull = JSONOnNull.ABSENT_ON_NULL;
return this;
}
@Override
public final JSONObject<T> returning(DataType<?> returning) {
this.returning = returning;
return this;
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
@ -124,12 +141,12 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
case POSTGRES:
if (nullType == ABSENT_ON_NULL)
if (onNull == JSONOnNull.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(')');
ctx.visit(unquotedName(getDataType().getType() == JSONB.class ? "jsonb_build_object" : "json_build_object")).sql('(').visit(QueryPartCollectionView.wrap(entries)).sql(')');
if (nullType == ABSENT_ON_NULL)
if (onNull == JSONOnNull.ABSENT_ON_NULL)
ctx.sql(')');
break;
@ -170,29 +187,31 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
case MARIADB:
case MARIADB: {
JSONEntry<?> first;
// Workaround for https://jira.mariadb.org/browse/MDEV-13701
if (args.size() > 1) {
if (entries.size() > 1) {
ctx.visit(N_JSON_MERGE).sql('(').visit(inline("{}"))
.formatIndentStart();
for (JSONEntry<?> entry : args)
for (JSONEntry<?> entry : entries)
ctx.sql(',').formatSeparator().visit(jsonObject(entry));
ctx.formatIndentEnd()
.formatNewLine()
.sql(')');
}
else if (!args.isEmpty() && isJSONArray(args.get(0).value())) {
else if (!entries.isEmpty() && isJSONArray((first = entries.iterator().next()).value())) {
ctx.visit(jsonObject(
key(args.get(0).key()).value(DSL.field("{0}({1}, {2})", getDataType(), N_JSON_MERGE, inline("[]"), args.get(0).value()))
key(first.key()).value(DSL.field("{0}({1}, {2})", getDataType(), N_JSON_MERGE, inline("[]"), first.value()))
));
}
else
acceptStandard(ctx);
break;
}
default:
acceptStandard(ctx);
@ -208,18 +227,39 @@ final class JSONObject<J> extends AbstractField<J> implements JSONObjectNullStep
private final void acceptStandard(Context<?> ctx) {
JSONNull jsonNull;
JSONReturning jsonReturning = new JSONReturning(returning);
// Workaround for https://github.com/h2database/h2database/issues/2496
if (args.isEmpty() && ctx.family() == H2)
jsonNull = new JSONNull(NULL_ON_NULL);
if (entries.isEmpty() && ctx.family() == H2)
jsonNull = new JSONNull(JSONOnNull.NULL_ON_NULL);
else
jsonNull = new JSONNull(nullType);
jsonNull = new JSONNull(onNull);
ctx.visit(K_JSON_OBJECT).sql('(').visit(wrap(args, jsonNull).separator("")).sql(')');
ctx.visit(K_JSON_OBJECT).sql('(').visit(QueryPartListView.wrap(QueryPartCollectionView.wrap(entries), jsonNull, jsonReturning).separator("")).sql(')');
}
// -------------------------------------------------------------------------
// The Object API
// -------------------------------------------------------------------------
@Override
public boolean equals(Object that) {
if (that instanceof JSONObject) {
return
StringUtils.equals(type, ((JSONObject) that).type) &&
StringUtils.equals(entries, ((JSONObject) that).entries) &&
StringUtils.equals(onNull, ((JSONObject) that).onNull) &&
StringUtils.equals(returning, ((JSONObject) that).returning)
;
}
else
return super.equals(that);
}
}

View File

@ -43,8 +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.JSONNull.JSONNullType.ABSENT_ON_NULL;
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
import static org.jooq.impl.JSONOnNull.ABSENT_ON_NULL;
import static org.jooq.impl.JSONOnNull.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;
@ -56,7 +56,6 @@ import org.jooq.Field;
import org.jooq.JSON;
import org.jooq.JSONEntry;
import org.jooq.JSONObjectAggNullStep;
import org.jooq.impl.JSONNull.JSONNullType;
/**
@ -74,7 +73,8 @@ implements JSONObjectAggNullStep<J> {
private static final long serialVersionUID = 1772007627336725780L;
private final JSONEntry<?> entry;
private JSONNullType nullType;
private JSONOnNull onNull;
private DataType<?> returning;
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 (nullType == ABSENT_ON_NULL)
if (onNull == ABSENT_ON_NULL)
acceptGroupConcat(ctx);
@ -124,7 +124,7 @@ implements JSONObjectAggNullStep<J> {
ctx.sql(')');
// TODO: What about a user-defined filter clause?
if (nullType == ABSENT_ON_NULL)
if (onNull == ABSENT_ON_NULL)
acceptFilterClause(ctx, entry.value().isNotNull());
acceptOverClause(ctx);
@ -152,7 +152,7 @@ implements JSONObjectAggNullStep<J> {
break;
}
if (nullType == ABSENT_ON_NULL)
if (onNull == ABSENT_ON_NULL)
value = when(entry.value().isNull(), inline((String) null)).else_((Field) value);
}
@ -162,7 +162,7 @@ implements JSONObjectAggNullStep<J> {
inline('"'),
DSL.replace(entry.key(), inline('"'), inline("\\\"")),
inline("\":"),
nullType == ABSENT_ON_NULL ? value1 : DSL.coalesce(value1, inline("null"))
onNull == ABSENT_ON_NULL ? value1 : DSL.coalesce(value1, inline("null"))
)));
acceptOverClause(c);
}));
@ -173,23 +173,33 @@ implements JSONObjectAggNullStep<J> {
private final void acceptStandard(Context<?> ctx) {
ctx.visit(N_JSON_OBJECTAGG).sql('(').visit(entry);
JSONNull jsonNull = new JSONNull(nullType);
JSONNull jsonNull = new JSONNull(onNull);
if (jsonNull.rendersContent(ctx))
ctx.sql(' ').visit(jsonNull);
JSONReturning jsonReturning = new JSONReturning(returning);
if (jsonReturning.rendersContent(ctx))
ctx.sql(' ').visit(jsonReturning);
ctx.sql(')');
acceptOverClause(ctx);
}
@Override
public final JSONObjectAgg<J> nullOnNull() {
nullType = NULL_ON_NULL;
onNull = NULL_ON_NULL;
return this;
}
@Override
public final JSONObjectAgg<J> absentOnNull() {
nullType = ABSENT_ON_NULL;
onNull = ABSENT_ON_NULL;
return this;
}
@Override
public final JSONObjectAgg<J> returning(DataType<?> r) {
this.returning = r;
return this;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 org.jooq.*;
/**
* The <code>JSONOnNull</code>.
*/
enum JSONOnNull {
NULL_ON_NULL,
ABSENT_ON_NULL,
}

View File

@ -0,0 +1,80 @@
/*
* 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.H2;
import static org.jooq.SQLDialect.MARIADB;
import static org.jooq.SQLDialect.MYSQL;
import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.impl.Keywords.K_RETURNING;
import java.util.Set;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.SQLDialect;
/**
* @author Lukas Eder
*/
final class JSONReturning extends AbstractQueryPart {
/**
* Generated UID
*/
private static final long serialVersionUID = 3121287280045911346L;
static final Set<SQLDialect> NO_SUPPORT_RETURNING = SQLDialect.supportedBy(H2, MARIADB, MYSQL, POSTGRES);
final DataType<?> type;
JSONReturning(DataType<?> type) {
this.type = type;
}
@Override
public final boolean rendersContent(Context<?> ctx) {
return !NO_SUPPORT_RETURNING.contains(ctx.dialect()) && type != null;
}
@Override
public final void accept(Context<?> ctx) {
if (!NO_SUPPORT_RETURNING.contains(ctx.dialect()))
ctx.visit(K_RETURNING).sql(' ').sql(type.getCastTypeName(ctx.configuration()));
}
}

View File

@ -62,7 +62,6 @@ import org.jooq.JSONValueOnStep;
import org.jooq.Keyword;
// ...
import org.jooq.SQLDialect;
import org.jooq.conf.ParamType;
/**
@ -87,25 +86,22 @@ implements
private final Field<?> json;
private final Field<String> path;
private final DataType<?> returning;
private final Behaviour onError;
private final Field<?> onErrorDefault;
private final Behaviour onEmpty;
private final Field<?> onEmptyDefault;
private final Field<?> default_;
JSONValue(DataType<J> type, Field<?> json, Field<String> path) {
this(type, json, path, null, null, null, null, null);
JSONValue(DataType<J> type, Field<?> json, Field<String> path, DataType<?> returning) {
this(type, json, path, returning, null, null, null, null, null);
}
private JSONValue(
DataType<J> type,
Field<?> json,
Field<String> path,
DataType<?> returning,
Behaviour onError,
Field<?> onErrorDefault,
Behaviour onEmpty,
@ -116,13 +112,12 @@ implements
this.json = json;
this.path = path;
this.returning = returning;
this.onError = onError;
this.onErrorDefault = onErrorDefault;
this.onEmpty = onEmpty;
this.onEmptyDefault = onEmptyDefault;
this.default_ = default_;
}
@ -168,6 +163,11 @@ implements
@Override
public final JSONValue<J> returning(DataType<?> r) {
return new JSONValue<>(getDataType(), json, path, r, onError, onErrorDefault, onEmpty, onEmptyDefault, null);
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
@ -210,6 +210,9 @@ implements
if (returning != null)
ctx.separatorRequired(true).visit(new JSONReturning(returning));
ctx.sql(')');
break;
}

View File

@ -291,7 +291,6 @@ import static org.jooq.impl.DSL.splitPart;
import static org.jooq.impl.DSL.sql;
import static org.jooq.impl.DSL.sqrt;
import static org.jooq.impl.DSL.square;
// ...
import static org.jooq.impl.DSL.stddevPop;
import static org.jooq.impl.DSL.stddevSamp;
import static org.jooq.impl.DSL.sum;
@ -337,8 +336,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.JSONNull.JSONNullType.ABSENT_ON_NULL;
import static org.jooq.impl.JSONNull.JSONNullType.NULL_ON_NULL;
import static org.jooq.impl.JSONOnNull.ABSENT_ON_NULL;
import static org.jooq.impl.JSONOnNull.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;
@ -378,7 +377,6 @@ import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@ -497,10 +495,14 @@ import org.jooq.InsertValuesStepN;
import org.jooq.JSON;
import org.jooq.JSONArrayAggNullStep;
import org.jooq.JSONArrayAggOrderByStep;
import org.jooq.JSONArrayAggReturningStep;
import org.jooq.JSONArrayNullStep;
import org.jooq.JSONArrayReturningStep;
import org.jooq.JSONEntry;
import org.jooq.JSONObjectAggNullStep;
import org.jooq.JSONObjectAggReturningStep;
import org.jooq.JSONObjectNullStep;
import org.jooq.JSONObjectReturningStep;
import org.jooq.JSONTableColumnPathStep;
import org.jooq.JSONTableColumnsStep;
import org.jooq.JSONValueDefaultStep;
@ -595,7 +597,6 @@ 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.ScopeStack.Value;
import org.jooq.impl.XMLParse.DocumentOrContent;
import org.jooq.tools.StringUtils;
@ -8308,8 +8309,9 @@ final class ParserContext {
DataType<?> returning = parseJSONReturningIf();
parse(')');
return s1;
return returning == null ? s1 : s1.returning(returning);
}
return null;
@ -8339,6 +8341,10 @@ final class ParserContext {
return null;
}
private final DataType<?> parseJSONReturningIf() {
return parseKeywordIf("RETURNING") ? parseDataType() : null;
}
private final Field<?> parseFieldJSONArrayConstructorIf() {
if (parseFunctionNameIf("JSON_ARRAY")) {
parse('(');
@ -8346,21 +8352,24 @@ final class ParserContext {
return DSL.jsonArray();
List<Field<?>> result = null;
JSONNullType nullType = parseJSONNullTypeIf();
JSONOnNull onNull = parseJSONNullTypeIf();
DataType<?> returning = parseJSONReturningIf();
if (nullType == null) {
if (onNull == null && returning == null) {
result = parseFields();
nullType = parseJSONNullTypeIf();
onNull = parseJSONNullTypeIf();
returning = parseJSONReturningIf();
}
parse(')');
JSONArrayNullStep<JSON> a = result == null ? DSL.jsonArray() : DSL.jsonArray(result);
return nullType == NULL_ON_NULL
? a.nullOnNull()
: nullType == ABSENT_ON_NULL
? a.absentOnNull()
: a;
JSONArrayNullStep<?> s1 = result == null ? DSL.jsonArray() : DSL.jsonArray(result);
JSONArrayReturningStep<?> s2 = onNull == NULL_ON_NULL
? s1.nullOnNull()
: onNull == ABSENT_ON_NULL
? s1.absentOnNull()
: s1;
return returning == null ? s2 : s2.returning(returning);
}
return null;
@ -8369,18 +8378,23 @@ final class ParserContext {
private final Field<?> parseFieldJSONArrayAggIf() {
if (parseFunctionNameIf("JSON_ARRAYAGG")) {
Field<?> result;
JSONArrayAggOrderByStep<JSON> s1;
JSONArrayAggNullStep<JSON> s2;
JSONNullType nullType;
JSONArrayAggOrderByStep<?> s1;
JSONArrayAggNullStep<?> s2;
JSONArrayAggReturningStep<?> s3;
JSONOnNull onNull;
DataType<?> returning;
parse('(');
result = s2 = s1 = DSL.jsonArrayAgg(parseField());
result = s3 = s2 = s1 = DSL.jsonArrayAgg(parseField());
if (parseKeywordIf("ORDER BY"))
result = s2 = s1.orderBy(parseSortSpecification());
result = s3 = s2 = s1.orderBy(parseSortSpecification());
if ((nullType = parseJSONNullTypeIf()) != null)
result = nullType == ABSENT_ON_NULL ? s2.absentOnNull() : s2.nullOnNull();
if ((onNull = parseJSONNullTypeIf()) != null)
result = s3 = onNull == ABSENT_ON_NULL ? s2.absentOnNull() : s2.nullOnNull();
if ((returning = parseJSONReturningIf()) != null)
result = s3.returning(returning);
parse(')');
return result;
@ -8396,23 +8410,27 @@ final class ParserContext {
return DSL.jsonObject();
List<JSONEntry<?>> result = new ArrayList<>();
JSONNullType nullType = parseJSONNullTypeIf();
JSONOnNull onNull = parseJSONNullTypeIf();
DataType<?> returning = parseJSONReturningIf();
if (nullType == null) {
if (onNull == null && returning == null) {
do
result.add(parseJSONEntry());
while (parseIf(','));
nullType = parseJSONNullTypeIf();
onNull = parseJSONNullTypeIf();
returning = parseJSONReturningIf();
}
parse(')');
JSONObjectNullStep<JSON> o = DSL.jsonObject(result);
return nullType == NULL_ON_NULL
? o.nullOnNull()
: nullType == ABSENT_ON_NULL
? o.absentOnNull()
: o;
JSONObjectNullStep<?> s1 = DSL.jsonObject(result);
JSONObjectReturningStep<?> s2 = onNull == NULL_ON_NULL
? s1.nullOnNull()
: onNull == ABSENT_ON_NULL
? s1.absentOnNull()
: s1;
return returning == null ? s2 : s2.returning(returning);
}
return null;
@ -8421,14 +8439,19 @@ final class ParserContext {
private final Field<?> parseFieldJSONObjectAggIf() {
if (parseFunctionNameIf("JSON_OBJECTAGG")) {
Field<?> result;
JSONObjectAggNullStep<JSON> s1;
JSONNullType nullType;
JSONObjectAggNullStep<?> s1;
JSONObjectAggReturningStep<?> s2;
JSONOnNull onNull;
DataType<?> returning;
parse('(');
result = s1 = DSL.jsonObjectAgg(parseJSONEntry());
result = s2 = s1 = DSL.jsonObjectAgg(parseJSONEntry());
if ((nullType = parseJSONNullTypeIf()) != null)
result = nullType == ABSENT_ON_NULL ? s1.absentOnNull() : s1.nullOnNull();
if ((onNull = parseJSONNullTypeIf()) != null)
result = s2 = onNull == ABSENT_ON_NULL ? s1.absentOnNull() : s1.nullOnNull();
if ((returning = parseJSONReturningIf()) != null)
result = s2.returning(returning);
parse(')');
return result;
@ -8437,7 +8460,7 @@ final class ParserContext {
return null;
}
private final JSONNullType parseJSONNullTypeIf() {
private final JSONOnNull parseJSONNullTypeIf() {
if (parseKeywordIf("NULL ON NULL"))
return NULL_ON_NULL;
else if (parseKeywordIf("ABSENT ON NULL"))

View File

@ -2017,13 +2017,13 @@ final class Tools {
return result;
}
static final JSONEntry<?>[] jsonEntries(Field<?>... fields) {
static final List<JSONEntry<?>> jsonEntries(Field<?>... fields) {
if (fields == null)
return null;
JSONEntry<?>[] result = new JSONEntry[fields.length];
for (int i = 0; i < fields.length; i++)
result[i] = jsonEntry(fields[i]);
List<JSONEntry<?>> result = new ArrayList<>(fields.length);
for (Field<?> field : fields)
result.add(jsonEntry(field));
return result;
}