Merge branch 'main' of github.com:jOOQ/jOOQ

This commit is contained in:
Lukas Eder 2024-04-25 07:43:42 +02:00
commit acb9ccdcd8
10 changed files with 639 additions and 3 deletions

View File

@ -0,0 +1,69 @@
/*
* 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
*
* https://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: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
/**
* An ordered-set aggregate function.
* <p>
* An ordered-set aggregate function is an aggregate function with a mandatory
* Oracle-specific <code>WITHIN GROUP (ORDER BY )</code> clause. An example is
* <code>LISTAGG</code>: <pre><code>
* SELECT LISTAGG(TITLE, ', ')
* WITHIN GROUP (ORDER BY TITLE)
* FROM T_BOOK
* GROUP BY AUTHOR_ID
* </code></pre> The above function groups books by author and aggregates titles
* into a concatenated string.
* <p>
* Ordered-set aggregate functions can be further converted into window functions
* using the <code>OVER(PARTITION BY )</code> clause. For example: <pre><code>
* SELECT LISTAGG(TITLE, ', ')
* WITHIN GROUP (ORDER BY TITLE)
* OVER (PARTITION BY AUTHOR_ID)
* FROM T_BOOK
* </code></pre>
*
* @author Lukas Eder
*/
public interface OptionallyOrderedAggregateFunction<T>
extends
OrderedAggregateFunction<T>,
AggregateFilterStep<T>
{
}

View File

@ -93,7 +93,7 @@ public interface OrderedAggregateFunction<T> {
/**
* Add an <code>WITHIN GROUP (ORDER BY )</code> clause to the ordered
* aggregate function
* aggregate function.
*/
@NotNull
@Support({ CUBRID, DUCKDB, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, TRINO, YUGABYTEDB })
@ -101,9 +101,23 @@ public interface OrderedAggregateFunction<T> {
/**
* Add an <code>WITHIN GROUP (ORDER BY )</code> clause to the ordered
* aggregate function
* aggregate function.
*/
@NotNull
@Support({ CUBRID, DUCKDB, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, TRINO, YUGABYTEDB })
AggregateFilterStep<T> withinGroupOrderBy(Collection<? extends OrderField<?>> fields);
/**
* Add an <code>ORDER BY </code> clause to the ordered aggregate function.
*/
@NotNull
@Support({ CUBRID, DUCKDB, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, TRINO, YUGABYTEDB })
AggregateFilterStep<T> orderBy(OrderField<?>... fields);
/**
* Add an <code>ORDER BY </code> clause to the ordered aggregate function.
*/
@NotNull
@Support({ CUBRID, DUCKDB, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, TRINO, YUGABYTEDB })
AggregateFilterStep<T> orderBy(Collection<? extends OrderField<?>> fields);
}

View File

@ -304,6 +304,7 @@ import org.jooq.Name;
import org.jooq.Name.Quoted;
import org.jooq.Null;
import org.jooq.Operator;
import org.jooq.OptionallyOrderedAggregateFunction;
import org.jooq.OrderField;
import org.jooq.OrderedAggregateFunction;
import org.jooq.OrderedAggregateFunctionOfDeferredType;
@ -24617,6 +24618,20 @@ public class DSL {
return new Max<>(field, true);
}
/**
* The <code>MAX_BY</code> function.
* <p>
* Evaluate <code>value</code> at the row having the maximum value for <code>by</code>.
*
* @param value The returned value.
* @param by The expression to use to evaluate the maximum.
*/
@NotNull
@Support({ DUCKDB, TRINO })
public static <T> OptionallyOrderedAggregateFunction<T> maxBy(Field<T> value, Field<?> by) {
return new MaxBy<>(value, by);
}
/**
* The <code>MEDIAN</code> function.
*/
@ -24644,6 +24659,20 @@ public class DSL {
return new Min<>(field, true);
}
/**
* The <code>MIN_BY</code> function.
* <p>
* Evaluate <code>value</code> at the row having the minimum value for <code>by</code>.
*
* @param value The returned value.
* @param by The expression to use to evaluate the minimum
*/
@NotNull
@Support({ DUCKDB, TRINO })
public static <T> OptionallyOrderedAggregateFunction<T> minBy(Field<T> value, Field<?> by) {
return new MinBy<>(value, by);
}
/**
* The <code>PRODUCT</code> function.
* <p>

View File

@ -1418,6 +1418,13 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
t = byte[][].class;
}
// [#16585] Another HSQLDB bug regarding LocalTime:
// See also: https://sourceforge.net/p/hsqldb/bugs/1702/
else if (t == LocalTime[].class) {
a = (Object[]) Convert.convertArray(a, Time[].class);
t = Time[].class;
}
ctx.statement().setArray(ctx.index(), new MockArray(ctx.family(), a, t));
break;
}

View File

@ -0,0 +1,207 @@
/*
* 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
*
* https://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: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
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.ExtendedDataKey.*;
import static org.jooq.impl.Tools.SimpleDataKey.*;
import static org.jooq.SQLDialect.*;
import org.jooq.*;
import org.jooq.Function1;
import org.jooq.Record;
import org.jooq.conf.*;
import org.jooq.tools.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
/**
* The <code>MAX BY</code> statement.
*/
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
final class MaxBy<T>
extends
AbstractAggregateFunction<T>
implements
QOM.MaxBy<T>
{
MaxBy(
Field<T> value,
Field<?> by
) {
super(
false,
N_MAX_BY,
Tools.nullSafeDataType(value),
nullSafeNotNull(value, (DataType) OTHER),
nullSafeNotNull(by, OTHER)
);
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
default:
acceptFunctionName(ctx);
ctx.sql('(');
acceptArguments0(ctx);
acceptOrderBy(ctx);
ctx.sql(')');
acceptFilterClause(ctx);
acceptOverClause(ctx);
break;
}
}
@Override
final void acceptFunctionName(Context<?> ctx) {
switch (ctx.family()) {
case CLICKHOUSE:
ctx.visit(N_argMax);
break;
default:
super.acceptFunctionName(ctx);
break;
}
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
@SuppressWarnings("unchecked")
@Override
public final Field<T> $value() {
return (Field<T>) getArgument(0);
}
@Override
public final Field<?> $by() {
return getArgument(1);
}
@Override
public final QOM.MaxBy<T> $value(Field<T> newValue) {
return $constructor().apply(newValue, $by());
}
@Override
public final QOM.MaxBy<T> $by(Field<?> newValue) {
return $constructor().apply($value(), newValue);
}
public final Function2<? super Field<T>, ? super Field<?>, ? extends QOM.MaxBy<T>> $constructor() {
return (a1, a2) -> new MaxBy<>(a1, a2);
}
// -------------------------------------------------------------------------
// XXX: The Object API
// -------------------------------------------------------------------------
@Override
public boolean equals(Object that) {
if (that instanceof QOM.MaxBy<?> o) {
return
StringUtils.equals($value(), o.$value()) &&
StringUtils.equals($by(), o.$by())
;
}
else
return super.equals(that);
}
}

View File

@ -0,0 +1,206 @@
/*
* 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
*
* https://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: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
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.ExtendedDataKey.*;
import static org.jooq.impl.Tools.SimpleDataKey.*;
import static org.jooq.SQLDialect.*;
import org.jooq.*;
import org.jooq.Function1;
import org.jooq.Record;
import org.jooq.conf.*;
import org.jooq.tools.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
/**
* The <code>MIN BY</code> statement.
*/
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
final class MinBy<T>
extends
AbstractAggregateFunction<T>
implements
QOM.MinBy<T>
{
MinBy(
Field<T> value,
Field<?> by
) {
super(
false,
N_MIN_BY,
Tools.nullSafeDataType(value),
nullSafeNotNull(value, (DataType) OTHER),
nullSafeNotNull(by, OTHER)
);
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
default:
acceptFunctionName(ctx);
ctx.sql('(');
acceptArguments0(ctx);
acceptOrderBy(ctx);
ctx.sql(')');
acceptFilterClause(ctx);
acceptOverClause(ctx);
break;
}
}
@Override
final void acceptFunctionName(Context<?> ctx) {
switch (ctx.family()) {
case CLICKHOUSE:
ctx.visit(N_argMin);
break;
default:
super.acceptFunctionName(ctx);
break;
}
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
@SuppressWarnings("unchecked")
@Override
public final Field<T> $value() {
return (Field<T>) getArgument(0);
}
@Override
public final Field<?> $by() {
return getArgument(1);
}
@Override
public final QOM.MinBy<T> $value(Field<T> newValue) {
return $constructor().apply(newValue, $by());
}
@Override
public final QOM.MinBy<T> $by(Field<?> newValue) {
return $constructor().apply($value(), newValue);
}
public final Function2<? super Field<T>, ? super Field<?>, ? extends QOM.MinBy<T>> $constructor() {
return (a1, a2) -> new MinBy<>(a1, a2);
}
// -------------------------------------------------------------------------
// XXX: The Object API
// -------------------------------------------------------------------------
@Override
public boolean equals(Object that) {
if (that instanceof QOM.MinBy<?> o) {
return
StringUtils.equals($value(), o.$value()) &&
StringUtils.equals($by(), o.$by())
;
}
else
return super.equals(that);
}
}

View File

@ -69,6 +69,8 @@ final class Names {
static final Name N_ADD_YEARS = systemName("add_years");
static final Name N_ANY = systemName("any");
static final Name N_ARBITRARY = systemName("arbitrary");
static final Name N_argMax = systemName("argMax");
static final Name N_argMin = systemName("argMin");
static final Name N_ARRAY = systemName("array");
static final Name N_ARRAY_AGG = systemName("array_agg");
static final Name N_ARRAY_CONSTRUCT = systemName("array_construct");
@ -521,9 +523,11 @@ final class Names {
static final Name N_LTRIM = systemName("ltrim");
static final Name N_MAP_KEYS = systemName("map_keys");
static final Name N_MAX = systemName("max");
static final Name N_MAX_BY = systemName("max_by");
static final Name N_MD5 = systemName("md5");
static final Name N_MEDIAN = systemName("median");
static final Name N_MIN = systemName("min");
static final Name N_MIN_BY = systemName("min_by");
static final Name N_NEWID = systemName("newid");
static final Name N_NONE_MATCH = systemName("none_match");
static final Name N_NULLIF = systemName("nullif");

View File

@ -254,6 +254,7 @@ import static org.jooq.impl.DSL.log10;
import static org.jooq.impl.DSL.lower;
import static org.jooq.impl.DSL.ltrim;
import static org.jooq.impl.DSL.max;
import static org.jooq.impl.DSL.maxBy;
import static org.jooq.impl.DSL.maxDistinct;
import static org.jooq.impl.DSL.md5;
import static org.jooq.impl.DSL.median;
@ -261,6 +262,7 @@ import static org.jooq.impl.DSL.microsecond;
import static org.jooq.impl.DSL.millennium;
import static org.jooq.impl.DSL.millisecond;
import static org.jooq.impl.DSL.min;
import static org.jooq.impl.DSL.minBy;
import static org.jooq.impl.DSL.minDistinct;
import static org.jooq.impl.DSL.minute;
import static org.jooq.impl.DSL.mode;
@ -663,6 +665,7 @@ import org.jooq.MergeMatchedWhereStep;
import org.jooq.MergeUsingStep;
import org.jooq.Meta;
import org.jooq.Name;
import org.jooq.OptionallyOrderedAggregateFunction;
import org.jooq.Name.Quoted;
import org.jooq.OrderedAggregateFunction;
import org.jooq.OrderedAggregateFunctionOfDeferredType;
@ -12149,6 +12152,8 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
over = filter = agg = parseBinarySetFunctionIf();
if (filter == null && !basic)
over = filter = parseOrderedSetFunctionIf();
if (filter == null && !basic)
over = filter = parseMinMaxByFunctionIf();
if (filter == null && !basic)
over = filter = parseArrayAggFunctionIf();
if (filter == null && !basic)
@ -12536,6 +12541,31 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return null;
}
private final AggregateFilterStep<?> parseMinMaxByFunctionIf() {
boolean minBy = parseFunctionNameIf("MIN_BY", "ARG_MIN", "argMin");
boolean maxBy = !minBy && parseFunctionNameIf("MAX_BY", "ARG_MAX", "argMax");
if (minBy || maxBy) {
parse('(');
parseSetQuantifier();
Field<?> f1 = parseField();
parse(',');
Field<?> f2 = parseField();
List<SortField<?>> sort = null;
if (parseKeywordIf("ORDER BY"))
sort = parseList(',', c -> c.parseSortField());
parse(')');
OptionallyOrderedAggregateFunction<?> s1 = minBy ? minBy(f1, f2) : maxBy(f1, f2);
return sort == null ? s1 : s1.orderBy(sort);
}
return null;
}
private final AggregateFilterStep<?> parseArrayAggFunctionIf() {
if (parseKeywordIf("ARRAY_AGG", "groupArray")) {
parse('(');

View File

@ -8027,6 +8027,41 @@ public final class QOM {
@NotNull Max<T> $distinct(boolean distinct);
}
/**
* The <code>MAX BY</code> function.
* <p>
* Evaluate <code>value</code> at the row having the maximum value for <code>by</code>.
*/
public /*sealed*/ interface MaxBy<T>
extends
org.jooq.OptionallyOrderedAggregateFunction<T>
//permits
// MaxBy
{
/**
* The returned value.
*/
@NotNull Field<T> $value();
/**
* The expression to use to evaluate the maximum.
*/
@NotNull Field<?> $by();
/**
* The returned value.
*/
@CheckReturnValue
@NotNull MaxBy<T> $value(Field<T> value);
/**
* The expression to use to evaluate the maximum.
*/
@CheckReturnValue
@NotNull MaxBy<T> $by(Field<?> by);
}
/**
* The <code>MEDIAN</code> function.
*/
@ -8058,6 +8093,41 @@ public final class QOM {
@NotNull Min<T> $distinct(boolean distinct);
}
/**
* The <code>MIN BY</code> function.
* <p>
* Evaluate <code>value</code> at the row having the minimum value for <code>by</code>.
*/
public /*sealed*/ interface MinBy<T>
extends
org.jooq.OptionallyOrderedAggregateFunction<T>
//permits
// MinBy
{
/**
* The returned value.
*/
@NotNull Field<T> $value();
/**
* The expression to use to evaluate the minimum
*/
@NotNull Field<?> $by();
/**
* The returned value.
*/
@CheckReturnValue
@NotNull MinBy<T> $value(Field<T> value);
/**
* The expression to use to evaluate the minimum
*/
@CheckReturnValue
@NotNull MinBy<T> $by(Field<?> by);
}
/**
* The <code>PRODUCT</code> function.
* <p>

View File

@ -30,7 +30,7 @@
<hsqldb.version>2.7.2</hsqldb.version>
<!-- JDBC drivers for jOOQ-xyz-extensions modules and vendor-specific API access -->
<postgres.version>42.7.2</postgres.version>
<postgres.version>42.7.3</postgres.version>
<sqlserver.version>12.4.2.jre11</sqlserver.version>
<oracle.version>23.3.0.23.09</oracle.version>