diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 6f378f09a9..71ea00fdfc 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -6547,6 +6547,47 @@ public abstract class jOOQAbstractTest< column = 0; + // STDDEV_POP(), STDDEV_SAMP(), VAR_POP(), VAR_SAMP() + result = + create().select(TBook_ID(), + TBook_ID().stddevPopOver() + .partitionByOne(), + TBook_ID().stddevSampOver() + .partitionByOne(), + TBook_ID().varPopOver() + .partitionByOne(), + TBook_ID().varSampOver() + .partitionByOne(), + + TBook_ID().stddevPopOver() + .partitionBy(TBook_AUTHOR_ID()), + TBook_ID().stddevSampOver() + .partitionBy(TBook_AUTHOR_ID()), + TBook_ID().varPopOver() + .partitionBy(TBook_AUTHOR_ID()), + TBook_ID().varSampOver() + .partitionBy(TBook_AUTHOR_ID())) + .from(TBook()) + .orderBy(TBook_ID().asc()) + .fetch(); + + // Overall STDDEV_POP(), STDDEV_SAMP(), VAR_POP(), VAR_SAMP() + assertEquals("1.118", result.getValueAsString(0, 1).substring(0, 5)); + assertEquals(1.25, result.getValueAsDouble(0, 3)); + + // Partitioned STDDEV_POP(), STDDEV_SAMP(), VAR_POP(), VAR_SAMP() + assertEquals(0.5, result.getValueAsDouble(0, 5)); + assertEquals(0.25, result.getValueAsDouble(0, 7)); + + // DB2 only knows STDDEV_POP / VAR_POP + if (getDialect() != SQLDialect.DB2) { + assertEquals("1.290", result.getValueAsString(0, 2).substring(0, 5)); + assertEquals("1.666", result.getValueAsString(0, 4).substring(0, 5)); + assertEquals("0.707", result.getValueAsString(0, 6).substring(0, 5)); + assertEquals(0.5, result.getValueAsDouble(0, 8)); + } + + column = 0; if (getDialect() == SQLDialect.SQLSERVER) { log.info("SKIPPING", "ROWS UNBOUNDED PRECEDING and similar tests"); return; diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 79c4f17f8e..72f4c5b09e 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -833,6 +833,38 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider lag(int offset, Field defaultValue); + /** + * The stddev_pop(field) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + WindowPartitionByStep stddevPopOver(); + + /** + * The stddev_samp(field) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + WindowPartitionByStep stddevSampOver(); + + /** + * The var_pop(field) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + WindowPartitionByStep varPopOver(); + + /** + * The var_samp(field) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + WindowPartitionByStep varSampOver(); + // ------------------------------------------------------------------------ // String functions created from this field // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index d9a1a4ffcc..1acacbad08 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -367,6 +367,26 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im return new WindowFunction("lag", getDataType(), this, literal(offset), defaultValue); } + @Override + public final WindowPartitionByStep stddevPopOver() { + return new WindowFunction(Term.STDDEV_POP, SQLDataType.NUMERIC, this); + } + + @Override + public final WindowPartitionByStep stddevSampOver() { + return new WindowFunction(Term.STDDEV_SAMP, SQLDataType.NUMERIC, this); + } + + @Override + public final WindowPartitionByStep varPopOver() { + return new WindowFunction(Term.VAR_POP, SQLDataType.NUMERIC, this); + } + + @Override + public final WindowPartitionByStep varSampOver() { + return new WindowFunction(Term.VAR_SAMP, SQLDataType.NUMERIC, this); + } + // ------------------------------------------------------------------------ // Functions created from this field // ------------------------------------------------------------------------ @@ -408,22 +428,22 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im @Override public final Field stddevPop() { - return new StddevPop(this); + return new Function(Term.STDDEV_POP, SQLDataType.NUMERIC, this); } @Override public final Field stddevSamp() { - return new StddevSamp(this); + return new Function(Term.STDDEV_SAMP, SQLDataType.NUMERIC, this); } @Override public final Field varPop() { - return new VarPop(this); + return new Function(Term.VAR_POP, SQLDataType.NUMERIC, this); } @Override public final Field varSamp() { - return new VarSamp(this); + return new Function(Term.VAR_SAMP, SQLDataType.NUMERIC, this); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Function.java b/jOOQ/src/main/java/org/jooq/impl/Function.java index 46484c023c..568d5b439c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Function.java +++ b/jOOQ/src/main/java/org/jooq/impl/Function.java @@ -47,20 +47,30 @@ import org.jooq.DataType; import org.jooq.Field; import org.jooq.NamedQueryPart; import org.jooq.RenderContext; +import org.jooq.SQLDialect; /** * @author Lukas Eder */ class Function extends AbstractField { - private static final long serialVersionUID = 347252741712134044L; + private static final long serialVersionUID = 347252741712134044L; private final List> arguments; + private final Term term; Function(String name, DataType type, Field... arguments) { super(name, type); this.arguments = Arrays.asList(arguments); + this.term = null; + } + + Function(Term term, DataType type, Field... arguments) { + super(term.name().toLowerCase(), type); + + this.arguments = Arrays.asList(arguments); + this.term = term; } @Override @@ -71,7 +81,7 @@ class Function extends AbstractField { @Override public final void toSQL(RenderContext context) { context.sql(getFNPrefix()); - context.sql(getName()); + context.sql(getFNName(context.getDialect())); context.sql(getArgumentListDelimiter(context, "(")); String separator = ""; @@ -86,6 +96,15 @@ class Function extends AbstractField { context.sql(getFNSuffix()); } + private final String getFNName(SQLDialect dialect) { + if (term != null) { + return term.translate(dialect); + } + else { + return getName(); + } + } + /** * Subclasses may override this method to add an additional prefix in the * function SQL string diff --git a/jOOQ/src/main/java/org/jooq/impl/StddevSamp.java b/jOOQ/src/main/java/org/jooq/impl/StddevSamp.java deleted file mode 100644 index b98e384774..0000000000 --- a/jOOQ/src/main/java/org/jooq/impl/StddevSamp.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOQ" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.jooq.impl; - -import java.math.BigDecimal; - -import org.jooq.Configuration; -import org.jooq.Field; - -/** - * @author Lukas Eder - */ -class StddevSamp extends AbstractFunction { - - /** - * Generated UID - */ - private static final long serialVersionUID = -7273879239726265322L; - - StddevSamp(Field arg) { - super("stddev_samp", SQLDataType.NUMERIC, arg); - } - - @Override - final Field getFunction0(Configuration configuration) { - switch (configuration.getDialect()) { - case DB2: - return new Function("stddev", SQLDataType.NUMERIC, getArguments()); - - case SQLSERVER: - return new Function("stdev", SQLDataType.NUMERIC, getArguments()); - - default: - return new Function("stddev_samp", SQLDataType.NUMERIC, getArguments()); - } - } -} diff --git a/jOOQ/src/main/java/org/jooq/impl/StddevPop.java b/jOOQ/src/main/java/org/jooq/impl/Term.java similarity index 53% rename from jOOQ/src/main/java/org/jooq/impl/StddevPop.java rename to jOOQ/src/main/java/org/jooq/impl/Term.java index fd0c55cb3e..7c184950aa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/StddevPop.java +++ b/jOOQ/src/main/java/org/jooq/impl/Term.java @@ -35,36 +35,66 @@ */ package org.jooq.impl; -import java.math.BigDecimal; - -import org.jooq.Configuration; -import org.jooq.Field; +import org.jooq.SQLDialect; /** + * The term dictionary enumerates standard expressions and their + * dialect-specific variants if applicable + * * @author Lukas Eder */ -class StddevPop extends AbstractFunction { +enum Term { - /** - * Generated UID - */ - private static final long serialVersionUID = -7273879239726265322L; + STDDEV_POP, + STDDEV_SAMP, + VAR_POP, + VAR_SAMP - StddevPop(Field arg) { - super("stddev_pop", SQLDataType.NUMERIC, arg); - } + ; - @Override - final Field getFunction0(Configuration configuration) { - switch (configuration.getDialect()) { - case DB2: - return new Function("stddev", SQLDataType.NUMERIC, getArguments()); + public final String translate(SQLDialect dialect) { + switch (this) { + case STDDEV_POP: + switch (dialect) { + case DB2: + return "stddev"; + case SQLSERVER: + return "stdevp"; + default: + return "stddev_pop"; + } - case SQLSERVER: - return new Function("stdevp", SQLDataType.NUMERIC, getArguments()); + case STDDEV_SAMP: + switch (dialect) { + case DB2: + return "stddev"; + case SQLSERVER: + return "stdev"; + default: + return "stddev_samp"; + } - default: - return new Function("stddev_pop", SQLDataType.NUMERIC, getArguments()); + case VAR_POP: + switch (dialect) { + case DB2: + return "variance"; + case SQLSERVER: + return "varp"; + default: + return "var_pop"; + } + + case VAR_SAMP: + switch (dialect) { + case DB2: + return "variance"; + case SQLSERVER: + return "var"; + default: + return "var_samp"; + } } + + return null; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/VarPop.java b/jOOQ/src/main/java/org/jooq/impl/VarPop.java deleted file mode 100644 index 026e39ff3e..0000000000 --- a/jOOQ/src/main/java/org/jooq/impl/VarPop.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOQ" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.jooq.impl; - -import java.math.BigDecimal; - -import org.jooq.Configuration; -import org.jooq.Field; - -/** - * @author Lukas Eder - */ -class VarPop extends AbstractFunction { - - /** - * Generated UID - */ - private static final long serialVersionUID = -7273879239726265322L; - - VarPop(Field arg) { - super("var_pop", SQLDataType.NUMERIC, arg); - } - - @Override - final Field getFunction0(Configuration configuration) { - switch (configuration.getDialect()) { - case DB2: - return new Function("variance", SQLDataType.NUMERIC, getArguments()); - - case SQLSERVER: - return new Function("varp", SQLDataType.NUMERIC, getArguments()); - - default: - return new Function("var_pop", SQLDataType.NUMERIC, getArguments()); - } - } -} diff --git a/jOOQ/src/main/java/org/jooq/impl/VarSamp.java b/jOOQ/src/main/java/org/jooq/impl/VarSamp.java deleted file mode 100644 index 9a2b2f7734..0000000000 --- a/jOOQ/src/main/java/org/jooq/impl/VarSamp.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOQ" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.jooq.impl; - -import java.math.BigDecimal; - -import org.jooq.Configuration; -import org.jooq.Field; - -/** - * @author Lukas Eder - */ -class VarSamp extends AbstractFunction { - - /** - * Generated UID - */ - private static final long serialVersionUID = -7273879239726265322L; - - VarSamp(Field arg) { - super("var_samp", SQLDataType.NUMERIC, arg); - } - - @Override - final Field getFunction0(Configuration configuration) { - switch (configuration.getDialect()) { - case DB2: - return new Function("variance", SQLDataType.NUMERIC, getArguments()); - - case SQLSERVER: - return new Function("var", SQLDataType.NUMERIC, getArguments()); - - default: - return new Function("var_samp", SQLDataType.NUMERIC, getArguments()); - } - } -} diff --git a/jOOQ/src/main/java/org/jooq/impl/WindowFunction.java b/jOOQ/src/main/java/org/jooq/impl/WindowFunction.java index bbfad9a90a..cdb366e043 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WindowFunction.java +++ b/jOOQ/src/main/java/org/jooq/impl/WindowFunction.java @@ -73,6 +73,8 @@ implements */ private static final long serialVersionUID = 5505202722420252635L; + private final Term term; + private final FieldList arguments; private final FieldList partitionBy; private final SortFieldList orderBy; @@ -90,6 +92,16 @@ implements this.partitionBy = new FieldList(); this.orderBy = new SortFieldList(); this.arguments = new FieldList(Arrays.asList(arguments)); + this.term = null; + } + + public WindowFunction(Term term, DataType type, Field... arguments) { + super(term.name().toLowerCase(), type); + + this.partitionBy = new FieldList(); + this.orderBy = new SortFieldList(); + this.arguments = new FieldList(Arrays.asList(arguments)); + this.term = term; } // ------------------------------------------------------------------------- @@ -103,7 +115,7 @@ implements @Override public final void toSQL(RenderContext context) { - context.sql(getName()); + context.sql(getFNName(context.getDialect())); context.sql("("); if (!arguments.isEmpty()) { @@ -190,6 +202,15 @@ implements context.sql(")"); } + private final String getFNName(SQLDialect dialect) { + if (term != null) { + return term.translate(dialect); + } + else { + return getName(); + } + } + private void toSQLRows(RenderContext context, Integer rows) { if (rows == Integer.MIN_VALUE) { context.sql("unbounded preceding");