[#1535] Generate dummy ORDER BY clause for ranking functions on databases that require them
This commit is contained in:
parent
da3ef5f7eb
commit
0b7af62876
@ -76,6 +76,16 @@ import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.impl.Term.CUME_DIST;
|
||||
import static org.jooq.impl.Term.DENSE_RANK;
|
||||
import static org.jooq.impl.Term.FIRST_VALUE;
|
||||
import static org.jooq.impl.Term.LAG;
|
||||
import static org.jooq.impl.Term.LAST_VALUE;
|
||||
import static org.jooq.impl.Term.LEAD;
|
||||
import static org.jooq.impl.Term.NTH_VALUE;
|
||||
import static org.jooq.impl.Term.NTILE;
|
||||
import static org.jooq.impl.Term.PERCENT_RANK;
|
||||
import static org.jooq.impl.Term.RANK;
|
||||
import static org.jooq.impl.Term.ROW_NUMBER;
|
||||
import static org.jooq.impl.Tools.EMPTY_FIELD;
|
||||
import static org.jooq.impl.Tools.EMPTY_QUERYPART;
|
||||
@ -18170,7 +18180,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static WindowOverStep<Integer> rank() {
|
||||
return new org.jooq.impl.Function<Integer>("rank", SQLDataType.INTEGER);
|
||||
return new org.jooq.impl.Function<Integer>(RANK, SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18178,7 +18188,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static WindowOverStep<Integer> denseRank() {
|
||||
return new org.jooq.impl.Function<Integer>("dense_rank", SQLDataType.INTEGER);
|
||||
return new org.jooq.impl.Function<Integer>(DENSE_RANK, SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18186,7 +18196,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static WindowOverStep<BigDecimal> percentRank() {
|
||||
return new org.jooq.impl.Function<BigDecimal>("percent_rank", SQLDataType.NUMERIC);
|
||||
return new org.jooq.impl.Function<BigDecimal>(PERCENT_RANK, SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18194,7 +18204,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static WindowOverStep<BigDecimal> cumeDist() {
|
||||
return new org.jooq.impl.Function<BigDecimal>("cume_dist", SQLDataType.NUMERIC);
|
||||
return new org.jooq.impl.Function<BigDecimal>(CUME_DIST, SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18202,7 +18212,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static WindowOverStep<Integer> ntile(int number) {
|
||||
return new org.jooq.impl.Function<Integer>("ntile", SQLDataType.INTEGER, inline(number));
|
||||
return new org.jooq.impl.Function<Integer>(NTILE, SQLDataType.INTEGER, inline(number));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18226,7 +18236,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> firstValue(Field<T> field) {
|
||||
return new org.jooq.impl.Function<T>("first_value", nullSafeDataType(field), nullSafe(field));
|
||||
return new org.jooq.impl.Function<T>(FIRST_VALUE, nullSafeDataType(field), nullSafe(field));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18234,7 +18244,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lastValue(Field<T> field) {
|
||||
return new org.jooq.impl.Function<T>("last_value", nullSafeDataType(field), nullSafe(field));
|
||||
return new org.jooq.impl.Function<T>(LAST_VALUE, nullSafeDataType(field), nullSafe(field));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18250,7 +18260,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowFromFirstLastStep<T> nthValue(Field<T> field, Field<Integer> nth) {
|
||||
return new org.jooq.impl.Function<T>("nth_value", nullSafeDataType(field), nullSafe(field), nullSafe(nth));
|
||||
return new org.jooq.impl.Function<T>(NTH_VALUE, nullSafeDataType(field), nullSafe(field), nullSafe(nth));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18258,7 +18268,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lead(Field<T> field) {
|
||||
return new LeadLag<T>("lead", nullSafe(field));
|
||||
return new LeadLag<T>(LEAD, nullSafe(field));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18266,7 +18276,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lead(Field<T> field, int offset) {
|
||||
return new LeadLag<T>("lead", nullSafe(field), offset);
|
||||
return new LeadLag<T>(LEAD, nullSafe(field), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18286,7 +18296,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lead(Field<T> field, int offset, Field<T> defaultValue) {
|
||||
return new LeadLag<T>("lead", nullSafe(field), offset, nullSafe(defaultValue));
|
||||
return new LeadLag<T>(LEAD, nullSafe(field), offset, nullSafe(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18294,7 +18304,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lag(Field<T> field) {
|
||||
return new LeadLag<T>("lag", nullSafe(field));
|
||||
return new LeadLag<T>(LAG, nullSafe(field));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18302,7 +18312,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MARIADB, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lag(Field<T> field, int offset) {
|
||||
return new LeadLag<T>("lag", nullSafe(field), offset);
|
||||
return new LeadLag<T>(LAG, nullSafe(field), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18322,7 +18332,7 @@ public class DSL {
|
||||
*/
|
||||
@Support({ CUBRID, FIREBIRD_3_0, H2, MYSQL_8_0, POSTGRES, SQLITE })
|
||||
public static <T> WindowIgnoreNullsStep<T> lag(Field<T> field, int offset, Field<T> defaultValue) {
|
||||
return new LeadLag<T>("lag", nullSafe(field), offset, nullSafe(defaultValue));
|
||||
return new LeadLag<T>(LAG, nullSafe(field), offset, nullSafe(defaultValue));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -85,6 +85,7 @@ import static org.jooq.impl.Term.MEDIAN;
|
||||
import static org.jooq.impl.Term.MODE;
|
||||
import static org.jooq.impl.Term.PRODUCT;
|
||||
import static org.jooq.impl.Term.ROW_NUMBER;
|
||||
import static org.jooq.impl.Tools.DataKey.DATA_RANKING_FUNCTION;
|
||||
import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -419,13 +420,42 @@ class Function<T> extends AbstractField<T> implements
|
||||
return;
|
||||
|
||||
// [#1524] Don't render this clause where it is not supported
|
||||
if (term == ROW_NUMBER && ctx.configuration().dialect() == HSQLDB)
|
||||
if (term == ROW_NUMBER && ctx.family() == HSQLDB)
|
||||
return;
|
||||
|
||||
Boolean ranking = false;
|
||||
Boolean previous = null;
|
||||
|
||||
if (term != null) {
|
||||
switch (term) {
|
||||
case CUME_DIST:
|
||||
case DENSE_RANK:
|
||||
case FIRST_VALUE:
|
||||
case LAG:
|
||||
case LEAD:
|
||||
case LAST_VALUE:
|
||||
case NTH_VALUE:
|
||||
case NTILE:
|
||||
case PERCENT_RANK:
|
||||
case RANK:
|
||||
case ROW_NUMBER:
|
||||
ranking = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.sql(' ')
|
||||
.visit(K_OVER)
|
||||
.sql(' ')
|
||||
.visit(window);
|
||||
.sql(' ');
|
||||
|
||||
previous = (Boolean) ctx.data(DATA_RANKING_FUNCTION, ranking);
|
||||
|
||||
ctx.visit(window);
|
||||
|
||||
if (previous != null)
|
||||
ctx.data(DATA_RANKING_FUNCTION, previous);
|
||||
else
|
||||
ctx.data().remove(DATA_RANKING_FUNCTION);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@ -54,12 +54,12 @@ final class LeadLag<T> extends Function<T> {
|
||||
*/
|
||||
private static final long serialVersionUID = 7292087943334025737L;
|
||||
|
||||
private final String function;
|
||||
private final Term function;
|
||||
private final Field<T> field;
|
||||
private final int offset;
|
||||
private final Field<T> defaultValue;
|
||||
|
||||
LeadLag(String function, Field<T> field) {
|
||||
LeadLag(Term function, Field<T> field) {
|
||||
super(function, field.getDataType(), field);
|
||||
|
||||
this.function = function;
|
||||
@ -68,7 +68,7 @@ final class LeadLag<T> extends Function<T> {
|
||||
this.defaultValue = null;
|
||||
}
|
||||
|
||||
LeadLag(String function, Field<T> field, int offset) {
|
||||
LeadLag(Term function, Field<T> field, int offset) {
|
||||
super(function, field.getDataType(), field, inline(offset));
|
||||
|
||||
this.function = function;
|
||||
@ -77,7 +77,7 @@ final class LeadLag<T> extends Function<T> {
|
||||
this.defaultValue = null;
|
||||
}
|
||||
|
||||
LeadLag(String function, Field<T> field, int offset, Field<T> defaultValue) {
|
||||
LeadLag(Term function, Field<T> field, int offset, Field<T> defaultValue) {
|
||||
super(function, field.getDataType(), field, inline(offset), defaultValue);
|
||||
|
||||
this.function = function;
|
||||
|
||||
@ -48,12 +48,7 @@ import org.jooq.SQLDialect;
|
||||
*/
|
||||
enum Term {
|
||||
|
||||
ARRAY_AGG {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
return "array_agg";
|
||||
}
|
||||
},
|
||||
ARRAY_AGG,
|
||||
ATAN2 {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
@ -127,11 +122,12 @@ enum Term {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CUME_DIST,
|
||||
DENSE_RANK,
|
||||
FIRST_VALUE,
|
||||
LAG,
|
||||
LAST_VALUE,
|
||||
LEAD,
|
||||
LIST_AGG {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
@ -168,12 +164,7 @@ enum Term {
|
||||
return "listagg";
|
||||
}
|
||||
},
|
||||
MEDIAN {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
return "median";
|
||||
}
|
||||
},
|
||||
MEDIAN,
|
||||
MODE {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
@ -187,6 +178,8 @@ enum Term {
|
||||
}
|
||||
}
|
||||
},
|
||||
NTH_VALUE,
|
||||
NTILE,
|
||||
OCTET_LENGTH {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
@ -214,12 +207,9 @@ enum Term {
|
||||
return "octet_length";
|
||||
}
|
||||
},
|
||||
PRODUCT {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
return "product";
|
||||
}
|
||||
},
|
||||
PERCENT_RANK,
|
||||
PRODUCT,
|
||||
RANK,
|
||||
ROW_NUMBER {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
@ -318,10 +308,12 @@ enum Term {
|
||||
|
||||
;
|
||||
|
||||
private final Name name;
|
||||
private final Name name;
|
||||
private final String translation;
|
||||
|
||||
private Term() {
|
||||
this.name = DSL.name(name());
|
||||
this.translation = name().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -334,7 +326,11 @@ enum Term {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the term to its dialect-specific variant
|
||||
* Translate the term to its dialect-specific variant.
|
||||
*
|
||||
* @param dialect The dialect to translate to
|
||||
*/
|
||||
abstract String translate(SQLDialect dialect);
|
||||
String translate(SQLDialect dialect) {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,6 +497,11 @@ final class Tools {
|
||||
* [#7467] In SQL Server, the TOP clause is sometimes preferred over the FETCH clause.
|
||||
*/
|
||||
DATA_PREFER_TOP_OVER_FETCH,
|
||||
|
||||
/**
|
||||
* [#1535] We're currently generating the window specification of a ranking function.
|
||||
*/
|
||||
DATA_RANKING_FUNCTION,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -37,13 +37,20 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.one;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.Keywords.K_AND;
|
||||
import static org.jooq.impl.Keywords.K_BETWEEN;
|
||||
import static org.jooq.impl.Keywords.K_CURRENT_ROW;
|
||||
@ -54,6 +61,7 @@ import static org.jooq.impl.Keywords.K_PARTITION_BY;
|
||||
import static org.jooq.impl.Keywords.K_PRECEDING;
|
||||
import static org.jooq.impl.Keywords.K_UNBOUNDED_FOLLOWING;
|
||||
import static org.jooq.impl.Keywords.K_UNBOUNDED_PRECEDING;
|
||||
import static org.jooq.impl.Tools.DataKey.DATA_RANKING_FUNCTION;
|
||||
import static org.jooq.impl.WindowSpecificationImpl.Exclude.CURRENT_ROW;
|
||||
import static org.jooq.impl.WindowSpecificationImpl.Exclude.GROUP;
|
||||
import static org.jooq.impl.WindowSpecificationImpl.Exclude.NO_OTHERS;
|
||||
@ -93,8 +101,9 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 2996016924769376361L;
|
||||
private static final EnumSet<SQLDialect> OMIT_PARTITION_BY_ONE = EnumSet.of(CUBRID, MYSQL, SQLITE);
|
||||
private static final long serialVersionUID = 2996016924769376361L;
|
||||
private static final EnumSet<SQLDialect> OMIT_PARTITION_BY_ONE = EnumSet.of(CUBRID, MYSQL, SQLITE);
|
||||
private static final EnumSet<SQLDialect> REQUIRES_ORDER_BY_IN_RANKING = EnumSet.of(H2);
|
||||
|
||||
private final WindowDefinitionImpl windowDefinition;
|
||||
private final QueryPartList<Field<?>> partitionBy;
|
||||
@ -154,6 +163,13 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
|
||||
|
||||
glue = " ";
|
||||
}
|
||||
else if (TRUE.equals(ctx.data(DATA_RANKING_FUNCTION)) && REQUIRES_ORDER_BY_IN_RANKING.contains(ctx.family())) {
|
||||
ctx.sql(glue)
|
||||
.visit(K_ORDER_BY).sql(' ')
|
||||
.visit(field(select(one())));
|
||||
|
||||
glue = " ";
|
||||
}
|
||||
|
||||
if (frameStart != null) {
|
||||
ctx.sql(glue);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user