diff --git a/jOOQ/src/main/java/org/jooq/WindowDefinition.java b/jOOQ/src/main/java/org/jooq/WindowDefinition.java index 6f2c0a682c..555fa6ef25 100644 --- a/jOOQ/src/main/java/org/jooq/WindowDefinition.java +++ b/jOOQ/src/main/java/org/jooq/WindowDefinition.java @@ -60,6 +60,6 @@ package org.jooq; * * @author Lukas Eder */ -public interface WindowDefinition extends QueryPart { +public interface WindowDefinition extends WindowSpecificationOrderByStep { } diff --git a/jOOQ/src/main/java/org/jooq/impl/Function.java b/jOOQ/src/main/java/org/jooq/impl/Function.java index baa78c9386..acb677b476 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Function.java +++ b/jOOQ/src/main/java/org/jooq/impl/Function.java @@ -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 extends AbstractField implements { - private static final long serialVersionUID = 347252741712134044L; - private static final EnumSet SUPPORT_ARRAY_AGG = EnumSet.of(HSQLDB, POSTGRES); - private static final EnumSet SUPPORT_GROUP_CONCAT = EnumSet.of(CUBRID, H2, HSQLDB, MARIADB, MYSQL, SQLITE); - private static final EnumSet SUPPORT_STRING_AGG = EnumSet.of(POSTGRES); - private static final EnumSet SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES); + private static final long serialVersionUID = 347252741712134044L; + private static final EnumSet SUPPORT_ARRAY_AGG = EnumSet.of(HSQLDB, POSTGRES); + private static final EnumSet SUPPORT_GROUP_CONCAT = EnumSet.of(CUBRID, H2, HSQLDB, MARIADB, MYSQL, SQLITE); + private static final EnumSet SUPPORT_STRING_AGG = EnumSet.of(POSTGRES); + private static final EnumSet SUPPORT_NO_PARENS_WINDOW_REFERENCE = EnumSet.of(MYSQL, POSTGRES); - static final Field ASTERISK = DSL.field("*", Integer.class); + static final Field ASTERISK = DSL.field("*", Integer.class); // Mutually exclusive attributes: super.getName(), this.name, this.term private final Name name; @@ -372,9 +373,9 @@ class Function extends AbstractField 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 extends AbstractField implements @Override public final WindowFinalStep over(WindowSpecification specification) { - this.windowSpecification = (WindowSpecificationImpl) specification; + this.windowSpecification = specification instanceof WindowSpecificationImpl + ? (WindowSpecificationImpl) specification + : new WindowSpecificationImpl((WindowDefinitionImpl) specification); + return this; } diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 32f25e827e..a64ae97ba9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -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 { diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 82342d371f..a680d91919 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -202,7 +202,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp private static final EnumSet NO_SUPPORT_FOR_UPDATE = EnumSet.of(CUBRID); private static final EnumSet NO_SUPPORT_FOR_UPDATE_QUALIFIED = EnumSet.of(DERBY, FIREBIRD, H2, HSQLDB); private static final EnumSet SUPPORT_SELECT_INTO = EnumSet.of(HSQLDB, POSTGRES); - private static final EnumSet SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES); + static final EnumSet SUPPORT_WINDOW_CLAUSE = EnumSet.of(MYSQL, POSTGRES); private static final EnumSet REQUIRES_FROM_CLAUSE = EnumSet.of(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL); private static final EnumSet EMULATE_EMPTY_GROUP_BY_OTHER = EnumSet.of(FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE); diff --git a/jOOQ/src/main/java/org/jooq/impl/WindowDefinitionImpl.java b/jOOQ/src/main/java/org/jooq/impl/WindowDefinitionImpl.java index a9aa94b3bf..b8c61b3eb0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WindowDefinitionImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/WindowDefinitionImpl.java @@ -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 windows = (QueryPartList) 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> 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); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/WindowSpecificationImpl.java b/jOOQ/src/main/java/org/jooq/impl/WindowSpecificationImpl.java index 9c8e6e0f5f..6d3d1d824b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WindowSpecificationImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/WindowSpecificationImpl.java @@ -95,6 +95,7 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements private static final long serialVersionUID = 2996016924769376361L; private static final EnumSet OMIT_PARTITION_BY_ONE = EnumSet.of(CUBRID, MYSQL); + private final WindowDefinitionImpl windowDefinition; private final QueryPartList> 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>(); 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; }