- [#7297] Support deriving a WindowDefinition from another WindowDefinition
- [#7663] Referenced WindowDefinitions should not be inlined in MySQL, Sybase SQL Anywhere
This commit is contained in:
lukaseder 2018-07-10 18:46:05 +02:00
parent e203490c3b
commit 53bd929f5e
6 changed files with 255 additions and 33 deletions

View File

@ -60,6 +60,6 @@ package org.jooq;
*
* @author Lukas Eder
*/
public interface WindowDefinition extends QueryPart {
public interface WindowDefinition extends WindowSpecificationOrderByStep {
}

View File

@ -72,6 +72,7 @@ import static org.jooq.impl.Keywords.K_RESPECT_NULLS;
import static org.jooq.impl.Keywords.K_SEPARATOR;
import static org.jooq.impl.Keywords.K_WHERE;
import static org.jooq.impl.Keywords.K_WITHIN_GROUP;
import static org.jooq.impl.SelectQueryImpl.SUPPORT_WINDOW_CLAUSE;
import static org.jooq.impl.Term.ARRAY_AGG;
import static org.jooq.impl.Term.LIST_AGG;
import static org.jooq.impl.Term.MEDIAN;
@ -131,13 +132,13 @@ class Function<T> extends AbstractField<T> implements
{
private static final long serialVersionUID = 347252741712134044L;
private static final EnumSet<SQLDialect> SUPPORT_ARRAY_AGG = EnumSet.of(HSQLDB, POSTGRES);
private static final EnumSet<SQLDialect> SUPPORT_GROUP_CONCAT = EnumSet.of(CUBRID, H2, HSQLDB, MARIADB, MYSQL, SQLITE);
private static final EnumSet<SQLDialect> SUPPORT_STRING_AGG = EnumSet.of(POSTGRES);
private static final EnumSet<SQLDialect> SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES);
private static final long serialVersionUID = 347252741712134044L;
private static final EnumSet<SQLDialect> SUPPORT_ARRAY_AGG = EnumSet.of(HSQLDB, POSTGRES);
private static final EnumSet<SQLDialect> SUPPORT_GROUP_CONCAT = EnumSet.of(CUBRID, H2, HSQLDB, MARIADB, MYSQL, SQLITE);
private static final EnumSet<SQLDialect> SUPPORT_STRING_AGG = EnumSet.of(POSTGRES);
private static final EnumSet<SQLDialect> SUPPORT_NO_PARENS_WINDOW_REFERENCE = EnumSet.of(MYSQL, POSTGRES);
static final Field<Integer> ASTERISK = DSL.field("*", Integer.class);
static final Field<Integer> ASTERISK = DSL.field("*", Integer.class);
// Mutually exclusive attributes: super.getName(), this.name, this.term
private final Name name;
@ -372,9 +373,9 @@ class Function<T> extends AbstractField<T> implements
return DSL.sql("({0})", windowSpecification);
// [#3727] Referenced WindowDefinitions that contain a frame clause
// shouldn't be referenced from within parentheses (in PostgreSQL)
// shouldn't be referenced from within parentheses (in MySQL and PostgreSQL)
if (windowDefinition != null)
if ( POSTGRES == ctx.family())
if (SUPPORT_NO_PARENS_WINDOW_REFERENCE.contains(ctx.family()))
return windowDefinition;
else
return DSL.sql("({0})", windowDefinition);
@ -644,7 +645,10 @@ class Function<T> extends AbstractField<T> implements
@Override
public final WindowFinalStep<T> over(WindowSpecification specification) {
this.windowSpecification = (WindowSpecificationImpl) specification;
this.windowSpecification = specification instanceof WindowSpecificationImpl
? (WindowSpecificationImpl) specification
: new WindowSpecificationImpl((WindowDefinitionImpl) specification);
return this;
}

View File

@ -1254,7 +1254,7 @@ final class ParserImpl implements Parser {
Name name = parseIdentifier(ctx);
parseKeyword(ctx, "AS");
parse(ctx, '(');
result.add(name.as(parseWindowSpecificationIf(ctx, true)));
result.add(name.as(parseWindowSpecificationIf(ctx, null, true)));
parse(ctx, ')');
}
while (parseIf(ctx, ','));
@ -1262,13 +1262,16 @@ final class ParserImpl implements Parser {
return result;
}
private static final WindowSpecification parseWindowSpecificationIf(ParserContext ctx, boolean orderByAllowed) {
private static final WindowSpecification parseWindowSpecificationIf(ParserContext ctx, Name windowName, boolean orderByAllowed) {
final WindowSpecificationOrderByStep s1;
final WindowSpecificationRowsStep s2;
final WindowSpecificationRowsAndStep s3;
final WindowSpecificationExcludeStep s4;
final WindowSpecification result;
s1 = parseKeywordIf(ctx, "PARTITION BY")
s1 = windowName != null
? windowName.as()
: parseKeywordIf(ctx, "PARTITION BY")
? partitionBy(parseFields(ctx))
: null;
@ -1454,20 +1457,29 @@ final class ParserImpl implements Parser {
if (parseKeywordIf(ctx, "EXCLUDE"))
if (parseKeywordIf(ctx, "CURRENT ROW"))
return s4.excludeCurrentRow();
result = s4.excludeCurrentRow();
else if (parseKeywordIf(ctx, "TIES"))
return s4.excludeTies();
result = s4.excludeTies();
else if (parseKeywordIf(ctx, "GROUP"))
return s4.excludeGroup();
result = s4.excludeGroup();
else if (parseKeywordIf(ctx, "NO OTHERS"))
return s4.excludeNoOthers();
result = s4.excludeNoOthers();
else
throw ctx.expected("CURRENT ROW", "TIES", "GROUP", "NO OTHERS");
else
return s4;
result = s4;
}
else
return s2;
result = s2;
if (result != null)
return result;
else if (windowName != null)
return null;
else if ((windowName = parseIdentifierIf(ctx)) != null)
return parseWindowSpecificationIf(ctx, windowName, orderByAllowed);
else
return null;
}
private static final Delete<?> parseDelete(ParserContext ctx, WithImpl with) {
@ -6336,11 +6348,7 @@ final class ParserImpl implements Parser {
Object result;
if (parseIf(ctx, '(')) {
result = parseWindowSpecificationIf(ctx, orderByAllowed);
if (result == null)
result = parseIdentifierIf(ctx);
result = parseWindowSpecificationIf(ctx, null, orderByAllowed);
parse(ctx, ')');
}
else {

View File

@ -202,7 +202,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
private static final EnumSet<SQLDialect> NO_SUPPORT_FOR_UPDATE = EnumSet.of(CUBRID);
private static final EnumSet<SQLDialect> NO_SUPPORT_FOR_UPDATE_QUALIFIED = EnumSet.of(DERBY, FIREBIRD, H2, HSQLDB);
private static final EnumSet<SQLDialect> SUPPORT_SELECT_INTO = EnumSet.of(HSQLDB, POSTGRES);
private static final EnumSet<SQLDialect> SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES);
static final EnumSet<SQLDialect> SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES);
private static final EnumSet<SQLDialect> REQUIRES_FROM_CLAUSE = EnumSet.of(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL);
private static final EnumSet<SQLDialect> EMULATE_EMPTY_GROUP_BY_OTHER = EnumSet.of(FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE);

View File

@ -37,15 +37,21 @@
*/
package org.jooq.impl;
// ...
import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.SelectQueryImpl.SUPPORT_WINDOW_CLAUSE;
import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS;
import java.util.Collection;
import org.jooq.Clause;
import org.jooq.Context;
import org.jooq.Name;
import org.jooq.OrderField;
import org.jooq.WindowDefinition;
import org.jooq.WindowSpecification;
import org.jooq.WindowSpecificationExcludeStep;
import org.jooq.WindowSpecificationRowsAndStep;
import org.jooq.WindowSpecificationRowsStep;
/**
* @author Lukas Eder
@ -87,12 +93,34 @@ final class WindowDefinitionImpl extends AbstractQueryPart implements WindowDefi
// Outside the WINDOW clause, only few dialects actually support
// referencing WINDOW definitions
else if ( ctx.family() == POSTGRES)
else if (SUPPORT_WINDOW_CLAUSE.contains(ctx.family())) {
ctx.visit(name);
}
// When emulating, just repeat the window specification
else if (window != null)
else if (window != null) {
ctx.visit(window);
}
// Try looking up the window specification from the context
else {
@SuppressWarnings("unchecked")
QueryPartList<WindowDefinition> windows = (QueryPartList<WindowDefinition>) ctx.data(DATA_WINDOW_DEFINITIONS);
renderContextDefinitionOrName:
if (windows != null) {
for (WindowDefinition w : windows) {
if (((WindowDefinitionImpl) w).getName().equals(name)) {
ctx.visit(w);
break renderContextDefinitionOrName;
}
}
// Default to printing the name
ctx.visit(name);
}
}
}
@Override
@ -104,4 +132,168 @@ final class WindowDefinitionImpl extends AbstractQueryPart implements WindowDefi
public final Clause[] clauses(Context<?> ctx) {
return null;
}
// ------------------------------------------------------------------------
// XXX: WindowSpecification API
// ------------------------------------------------------------------------
@Override
public final WindowSpecificationRowsStep orderBy(OrderField<?>... fields) {
return new WindowSpecificationImpl(this).orderBy(fields);
}
@Override
public final WindowSpecificationRowsStep orderBy(Collection<? extends OrderField<?>> fields) {
return new WindowSpecificationImpl(this).orderBy(fields);
}
@Override
public final WindowSpecificationExcludeStep rowsUnboundedPreceding() {
return new WindowSpecificationImpl(this).rowsUnboundedPreceding();
}
@Override
public final WindowSpecificationExcludeStep rowsPreceding(int number) {
return new WindowSpecificationImpl(this).rowsPreceding(number);
}
@Override
public final WindowSpecificationExcludeStep rowsCurrentRow() {
return new WindowSpecificationImpl(this).rowsCurrentRow();
}
@Override
public final WindowSpecificationExcludeStep rowsUnboundedFollowing() {
return new WindowSpecificationImpl(this).rowsUnboundedFollowing();
}
@Override
public final WindowSpecificationExcludeStep rowsFollowing(int number) {
return new WindowSpecificationImpl(this).rowsFollowing(number);
}
@Override
public final WindowSpecificationRowsAndStep rowsBetweenUnboundedPreceding() {
return new WindowSpecificationImpl(this).rowsBetweenUnboundedPreceding();
}
@Override
public final WindowSpecificationRowsAndStep rowsBetweenPreceding(int number) {
return new WindowSpecificationImpl(this).rowsBetweenPreceding(number);
}
@Override
public final WindowSpecificationRowsAndStep rowsBetweenCurrentRow() {
return new WindowSpecificationImpl(this).rowsBetweenCurrentRow();
}
@Override
public final WindowSpecificationRowsAndStep rowsBetweenUnboundedFollowing() {
return new WindowSpecificationImpl(this).rowsBetweenUnboundedFollowing();
}
@Override
public final WindowSpecificationRowsAndStep rowsBetweenFollowing(int number) {
return new WindowSpecificationImpl(this).rowsBetweenFollowing(number);
}
@Override
public final WindowSpecificationExcludeStep rangeUnboundedPreceding() {
return new WindowSpecificationImpl(this).rangeUnboundedPreceding();
}
@Override
public final WindowSpecificationExcludeStep rangePreceding(int number) {
return new WindowSpecificationImpl(this).rangePreceding(number);
}
@Override
public final WindowSpecificationExcludeStep rangeCurrentRow() {
return new WindowSpecificationImpl(this).rangeCurrentRow();
}
@Override
public final WindowSpecificationExcludeStep rangeUnboundedFollowing() {
return new WindowSpecificationImpl(this).rangeUnboundedFollowing();
}
@Override
public final WindowSpecificationExcludeStep rangeFollowing(int number) {
return new WindowSpecificationImpl(this).rangeFollowing(number);
}
@Override
public final WindowSpecificationRowsAndStep rangeBetweenUnboundedPreceding() {
return new WindowSpecificationImpl(this).rangeBetweenUnboundedPreceding();
}
@Override
public final WindowSpecificationRowsAndStep rangeBetweenPreceding(int number) {
return new WindowSpecificationImpl(this).rangeBetweenPreceding(number);
}
@Override
public final WindowSpecificationRowsAndStep rangeBetweenCurrentRow() {
return new WindowSpecificationImpl(this).rangeBetweenCurrentRow();
}
@Override
public final WindowSpecificationRowsAndStep rangeBetweenUnboundedFollowing() {
return new WindowSpecificationImpl(this).rangeBetweenUnboundedFollowing();
}
@Override
public final WindowSpecificationRowsAndStep rangeBetweenFollowing(int number) {
return new WindowSpecificationImpl(this).rangeBetweenFollowing(number);
}
@Override
public final WindowSpecificationExcludeStep groupsUnboundedPreceding() {
return new WindowSpecificationImpl(this).groupsUnboundedPreceding();
}
@Override
public final WindowSpecificationExcludeStep groupsPreceding(int number) {
return new WindowSpecificationImpl(this).groupsPreceding(number);
}
@Override
public final WindowSpecificationExcludeStep groupsCurrentRow() {
return new WindowSpecificationImpl(this).groupsCurrentRow();
}
@Override
public final WindowSpecificationExcludeStep groupsUnboundedFollowing() {
return new WindowSpecificationImpl(this).groupsUnboundedFollowing();
}
@Override
public final WindowSpecificationExcludeStep groupsFollowing(int number) {
return new WindowSpecificationImpl(this).groupsFollowing(number);
}
@Override
public final WindowSpecificationRowsAndStep groupsBetweenUnboundedPreceding() {
return new WindowSpecificationImpl(this).groupsBetweenUnboundedPreceding();
}
@Override
public final WindowSpecificationRowsAndStep groupsBetweenPreceding(int number) {
return new WindowSpecificationImpl(this).groupsBetweenPreceding(number);
}
@Override
public final WindowSpecificationRowsAndStep groupsBetweenCurrentRow() {
return new WindowSpecificationImpl(this).groupsBetweenCurrentRow();
}
@Override
public final WindowSpecificationRowsAndStep groupsBetweenUnboundedFollowing() {
return new WindowSpecificationImpl(this).groupsBetweenUnboundedFollowing();
}
@Override
public final WindowSpecificationRowsAndStep groupsBetweenFollowing(int number) {
return new WindowSpecificationImpl(this).groupsBetweenFollowing(number);
}
}

View File

@ -95,6 +95,7 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
private static final long serialVersionUID = 2996016924769376361L;
private static final EnumSet<SQLDialect> OMIT_PARTITION_BY_ONE = EnumSet.of(CUBRID, MYSQL);
private final WindowDefinitionImpl windowDefinition;
private final QueryPartList<Field<?>> partitionBy;
private final SortFieldList orderBy;
private Integer frameStart;
@ -104,6 +105,11 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
private boolean partitionByOne;
WindowSpecificationImpl() {
this(null);
}
WindowSpecificationImpl(WindowDefinitionImpl windowDefinition) {
this.windowDefinition = windowDefinition;
this.partitionBy = new QueryPartList<Field<?>>();
this.orderBy = new SortFieldList();
}
@ -112,6 +118,18 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
public final void accept(Context<?> ctx) {
String glue = "";
if (windowDefinition != null) {
boolean declareWindows = ctx.declareWindows();
ctx.sql(glue)
.declareWindows(false)
.visit(windowDefinition)
.declareWindows(declareWindows);
glue = " ";
}
if (!partitionBy.isEmpty()) {
// Ignore PARTITION BY 1 clause. These databases erroneously map the
@ -432,25 +450,25 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
}
@Override
public WindowSpecificationFinalStep excludeCurrentRow() {
public final WindowSpecificationFinalStep excludeCurrentRow() {
exclude = CURRENT_ROW;
return this;
}
@Override
public WindowSpecificationFinalStep excludeGroup() {
public final WindowSpecificationFinalStep excludeGroup() {
exclude = GROUP;
return this;
}
@Override
public WindowSpecificationFinalStep excludeTies() {
public final WindowSpecificationFinalStep excludeTies() {
exclude = TIES;
return this;
}
@Override
public WindowSpecificationFinalStep excludeNoOthers() {
public final WindowSpecificationFinalStep excludeNoOthers() {
exclude = NO_OTHERS;
return this;
}