[jOOQ/jOOQ#12028] Add MULTISET_AGG aggregate and window function support
This commit is contained in:
parent
1526cab8ee
commit
887518ee24
@ -244,13 +244,7 @@ public class Settings
|
||||
protected Boolean emulateOnDuplicateKeyUpdateOnPrimaryKeyOnly = false;
|
||||
@XmlElement(defaultValue = "DEFAULT")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected NestedCollectionEmulation emulateList = NestedCollectionEmulation.DEFAULT;
|
||||
@XmlElement(defaultValue = "DEFAULT")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected NestedCollectionEmulation emulateMultiset = NestedCollectionEmulation.DEFAULT;
|
||||
@XmlElement(defaultValue = "DEFAULT")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected NestedCollectionEmulation emulateSet = NestedCollectionEmulation.DEFAULT;
|
||||
@XmlElement(defaultValue = "LOG_DEBUG")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected ExecuteWithoutWhere executeUpdateWithoutWhere = ExecuteWithoutWhere.LOG_DEBUG;
|
||||
@ -2281,23 +2275,7 @@ public class Settings
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>LIST</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public NestedCollectionEmulation getEmulateList() {
|
||||
return emulateList;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>LIST</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public void setEmulateList(NestedCollectionEmulation value) {
|
||||
this.emulateList = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>MULTISET</code> support should be emulated.
|
||||
* [#3884] How <code>MULTISET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public NestedCollectionEmulation getEmulateMultiset() {
|
||||
@ -2305,29 +2283,13 @@ public class Settings
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>MULTISET</code> support should be emulated.
|
||||
* [#3884] How <code>MULTISET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public void setEmulateMultiset(NestedCollectionEmulation value) {
|
||||
this.emulateMultiset = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>SET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public NestedCollectionEmulation getEmulateSet() {
|
||||
return emulateSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>SET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public void setEmulateSet(NestedCollectionEmulation value) {
|
||||
this.emulateSet = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#6771] Specifies whether UPDATE statements are allowed to be executed lacking a WHERE clause. This has no effect on rendering the statements SQL string.
|
||||
*
|
||||
@ -3676,16 +3638,7 @@ public class Settings
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>LIST</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public Settings withEmulateList(NestedCollectionEmulation value) {
|
||||
setEmulateList(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>MULTISET</code> support should be emulated.
|
||||
* [#3884] How <code>MULTISET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public Settings withEmulateMultiset(NestedCollectionEmulation value) {
|
||||
@ -3693,15 +3646,6 @@ public class Settings
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3884] Whether <code>SET</code> support should be emulated.
|
||||
*
|
||||
*/
|
||||
public Settings withEmulateSet(NestedCollectionEmulation value) {
|
||||
setEmulateSet(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#6771] Specifies whether UPDATE statements are allowed to be executed lacking a WHERE clause. This has no effect on rendering the statements SQL string.
|
||||
*
|
||||
@ -4090,9 +4034,7 @@ public class Settings
|
||||
builder.append("inListPadBase", inListPadBase);
|
||||
builder.append("delimiter", delimiter);
|
||||
builder.append("emulateOnDuplicateKeyUpdateOnPrimaryKeyOnly", emulateOnDuplicateKeyUpdateOnPrimaryKeyOnly);
|
||||
builder.append("emulateList", emulateList);
|
||||
builder.append("emulateMultiset", emulateMultiset);
|
||||
builder.append("emulateSet", emulateSet);
|
||||
builder.append("executeUpdateWithoutWhere", executeUpdateWithoutWhere);
|
||||
builder.append("executeDeleteWithoutWhere", executeDeleteWithoutWhere);
|
||||
builder.append("interpreterDialect", interpreterDialect);
|
||||
@ -4921,15 +4863,6 @@ public class Settings
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (emulateList == null) {
|
||||
if (other.emulateList!= null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emulateList.equals(other.emulateList)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (emulateMultiset == null) {
|
||||
if (other.emulateMultiset!= null) {
|
||||
return false;
|
||||
@ -4939,15 +4872,6 @@ public class Settings
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (emulateSet == null) {
|
||||
if (other.emulateSet!= null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emulateSet.equals(other.emulateSet)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (executeUpdateWithoutWhere == null) {
|
||||
if (other.executeUpdateWithoutWhere!= null) {
|
||||
return false;
|
||||
@ -5338,9 +5262,7 @@ public class Settings
|
||||
result = ((prime*result)+((inListPadBase == null)? 0 :inListPadBase.hashCode()));
|
||||
result = ((prime*result)+((delimiter == null)? 0 :delimiter.hashCode()));
|
||||
result = ((prime*result)+((emulateOnDuplicateKeyUpdateOnPrimaryKeyOnly == null)? 0 :emulateOnDuplicateKeyUpdateOnPrimaryKeyOnly.hashCode()));
|
||||
result = ((prime*result)+((emulateList == null)? 0 :emulateList.hashCode()));
|
||||
result = ((prime*result)+((emulateMultiset == null)? 0 :emulateMultiset.hashCode()));
|
||||
result = ((prime*result)+((emulateSet == null)? 0 :emulateSet.hashCode()));
|
||||
result = ((prime*result)+((executeUpdateWithoutWhere == null)? 0 :executeUpdateWithoutWhere.hashCode()));
|
||||
result = ((prime*result)+((executeDeleteWithoutWhere == null)? 0 :executeDeleteWithoutWhere.hashCode()));
|
||||
result = ((prime*result)+((interpreterDialect == null)? 0 :interpreterDialect.hashCode()));
|
||||
|
||||
@ -60,6 +60,7 @@ import static org.jooq.impl.QueryPartCollectionView.wrap;
|
||||
import static org.jooq.impl.SQLDataType.DOUBLE;
|
||||
import static org.jooq.impl.SQLDataType.NUMERIC;
|
||||
import static org.jooq.impl.Tools.camelCase;
|
||||
import static org.jooq.impl.Tools.isEmpty;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -369,6 +370,15 @@ implements
|
||||
return DSL.nullif(fo(function), (Field<U>) zero());
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this aggregate function's <code>ORDER BY</code>,
|
||||
* <code>FILTER</code> and <code>OVER</code> clauses to an argument
|
||||
* aggregate function.
|
||||
*/
|
||||
final <U> Field<U> ofo(AbstractAggregateFunction<U> function) {
|
||||
return fo(isEmpty(withinGroupOrderBy) ? function : function.orderBy(withinGroupOrderBy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this aggregate function's <code>FILTER</code> and <code>OVER</code>
|
||||
* clauses to an argument aggregate function.
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
// ...
|
||||
import static org.jooq.impl.Names.N_ARRAY_AGG;
|
||||
|
||||
import org.jooq.Context;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
128
jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java
Normal file
128
jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.impl.DSL.jsonArray;
|
||||
import static org.jooq.impl.DSL.jsonArrayAgg;
|
||||
import static org.jooq.impl.DSL.jsonObject;
|
||||
import static org.jooq.impl.DSL.jsonbArray;
|
||||
import static org.jooq.impl.DSL.jsonbArrayAgg;
|
||||
import static org.jooq.impl.DSL.jsonbObject;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.selectFrom;
|
||||
import static org.jooq.impl.DSL.xmlagg;
|
||||
import static org.jooq.impl.DSL.xmlelement;
|
||||
import static org.jooq.impl.Keywords.K_MULTISET;
|
||||
import static org.jooq.impl.Names.N_ARRAY_AGG;
|
||||
import static org.jooq.impl.Names.N_MULTISET_AGG;
|
||||
import static org.jooq.impl.Names.N_RECORD;
|
||||
import static org.jooq.impl.Names.N_RESULT;
|
||||
import static org.jooq.impl.Tools.emulateMultiset;
|
||||
import static org.jooq.impl.Tools.map;
|
||||
import static org.jooq.impl.Tools.visitSubquery;
|
||||
|
||||
import org.jooq.AggregateFunction;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.SelectField;
|
||||
import org.jooq.Table;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class MultisetAgg<R extends Record> extends DefaultAggregateFunction<Result<R>> {
|
||||
|
||||
private final AbstractRow<R> row;
|
||||
|
||||
MultisetAgg(boolean distinct, SelectField<R> row) {
|
||||
super(distinct, N_MULTISET_AGG, new MultisetDataType<>((AbstractRow<R>) row, null), (((AbstractRow<R>) row).fields()));
|
||||
|
||||
this.row = (AbstractRow<R>) row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
switch (emulateMultiset(ctx.configuration())) {
|
||||
case JSON: {
|
||||
ctx.visit(ofo((AbstractAggregateFunction<?>) jsonArrayAgg(jsonObject(row.fields()))));
|
||||
break;
|
||||
}
|
||||
|
||||
case JSONB: {
|
||||
ctx.visit(ofo((AbstractAggregateFunction<?>) jsonbArrayAgg(jsonObject(row.fields()))));
|
||||
break;
|
||||
}
|
||||
|
||||
case XML: {
|
||||
ctx.visit(xmlelement(N_RESULT,
|
||||
ofo((AbstractAggregateFunction<?>)
|
||||
xmlagg(xmlelement(N_RECORD,
|
||||
map(row.fields(), f -> xmlelement(f.getUnqualifiedName(), f))
|
||||
))
|
||||
)
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
case NATIVE:
|
||||
ctx.visit(N_MULTISET_AGG).sql('(');
|
||||
acceptArguments1(ctx, new QueryPartListView<>(arguments.get(0)));
|
||||
acceptOrderBy(ctx);
|
||||
ctx.sql(')');
|
||||
acceptFilterClause(ctx);
|
||||
acceptOverClause(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,8 +39,6 @@ package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Tools.CTX;
|
||||
import static org.jooq.impl.Tools.newRecord;
|
||||
import static org.jooq.impl.Tools.recordType;
|
||||
import static org.jooq.impl.Tools.row0;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -51,8 +49,6 @@ import org.jooq.Nullability;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.impl.AbstractRecord.TransferRecordState;
|
||||
|
||||
/**
|
||||
* A wrapper for anonymous multiset data types.
|
||||
|
||||
@ -248,6 +248,7 @@ final class Names {
|
||||
static final Name N_MODE = unquotedName("mode");
|
||||
static final Name N_MUL = unquotedName("mul");
|
||||
static final Name N_MULTISET = unquotedName("multiset");
|
||||
static final Name N_MULTISET_AGG = unquotedName("multiset_agg");
|
||||
static final Name N_NANO100_BETWEEN = unquotedName("nano100_between");
|
||||
static final Name N_NEWID = unquotedName("newid");
|
||||
static final Name N_NEXTVAL = unquotedName("nextval");
|
||||
|
||||
@ -10422,6 +10422,8 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
over = filter = parseOrderedSetFunctionIf();
|
||||
if (filter == null && !basic)
|
||||
over = filter = parseArrayAggFunctionIf();
|
||||
if (filter == null && !basic)
|
||||
over = filter = parseMultisetAggFunctionIf();
|
||||
if (filter == null && !basic)
|
||||
over = filter = parseXMLAggFunctionIf();
|
||||
if (filter == null && !basic)
|
||||
@ -10847,6 +10849,24 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
private final AggregateFilterStep<?> parseMultisetAggFunctionIf() {
|
||||
if (parseKeywordIf("MULTISET_AGG")) {
|
||||
parse('(');
|
||||
|
||||
List<Field<?>> fields = parseList(',', ParseContext::parseField);
|
||||
List<SortField<?>> sort = null;
|
||||
|
||||
if (parseKeywordIf("ORDER BY"))
|
||||
sort = parseList(',', ParseContext::parseSortField);
|
||||
|
||||
parse(')');
|
||||
ArrayAggOrderByStep<?> s1 = multisetAgg(fields);
|
||||
return sort == null ? s1 : s1.orderBy(sort);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private final List<SortField<?>> parseWithinGroupN() {
|
||||
return parseWithinGroupN(false);
|
||||
}
|
||||
|
||||
@ -552,16 +552,8 @@ jOOQ queries, for which no specific fetchSize value was specified.]]></jxb:javad
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#6462] Use only the primary key to emulate MySQL's INSERT .. ON DUPLICATE KEY UPDATE statement. In MySQL, the statement considers all unique keys for duplicates to apply an update rather than an insert. Earlier versions of jOOQ considered only the PRIMARY KEY. This flag can be turned on to maintain backwards compatibility.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="emulateList" type="jooq-runtime:NestedCollectionEmulation" minOccurs="0" maxOccurs="1" default="DEFAULT">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#3884] Whether <code>LIST</code> support should be emulated.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="emulateMultiset" type="jooq-runtime:NestedCollectionEmulation" minOccurs="0" maxOccurs="1" default="DEFAULT">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#3884] Whether <code>MULTISET</code> support should be emulated.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="emulateSet" type="jooq-runtime:NestedCollectionEmulation" minOccurs="0" maxOccurs="1" default="DEFAULT">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#3884] Whether <code>SET</code> support should be emulated.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#3884] How <code>MULTISET</code> support should be emulated.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="executeUpdateWithoutWhere" type="jooq-runtime:ExecuteWithoutWhere" minOccurs="0" maxOccurs="1" default="LOG_DEBUG">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user