diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index a04662c8b3..c7d0e86772 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -45,6 +45,7 @@ import static org.jooq.impl.Factory.cast; import static org.jooq.impl.Factory.castNull; import static org.jooq.impl.Factory.count; import static org.jooq.impl.Factory.countOver; +import static org.jooq.impl.Factory.cube; import static org.jooq.impl.Factory.cumeDistOver; import static org.jooq.impl.Factory.currentDate; import static org.jooq.impl.Factory.currentTime; @@ -61,6 +62,7 @@ import static org.jooq.impl.Factory.percentRankOver; import static org.jooq.impl.Factory.pi; import static org.jooq.impl.Factory.rand; import static org.jooq.impl.Factory.rankOver; +import static org.jooq.impl.Factory.rollup; import static org.jooq.impl.Factory.rowNumberOver; import static org.jooq.impl.Factory.table; import static org.jooq.impl.Factory.trueCondition; @@ -2966,6 +2968,108 @@ public abstract class jOOQAbstractTest< assertEquals("Paulo", result.getValue(0, VLibrary_AUTHOR()).substring(0, 5)); } + @Test + public void testGroupByCubeRollup() throws Exception { + switch (getDialect()) { + case ASE: + case DERBY: + case H2: + case HSQLDB: + case INGRES: + case MYSQL: + case POSTGRES: + case SQLITE: + log.info("SKIPPING", "Group by CUBE / ROLLUP tests"); + return; + } + + Result result; + + result = create().select( + TBook_ID(), + TBook_AUTHOR_ID()) + .from(TBook()) + .groupBy(cube( + TBook_ID(), + TBook_AUTHOR_ID())) + .orderBy( + TBook_ID().asc().nullsFirst(), + TBook_AUTHOR_ID().asc().nullsFirst()).fetch(); + + assertEquals(11, result.size()); + assertNull(result.getValue(0, 0)); + assertNull(result.getValue(0, 1)); + + assertNull(result.getValue(1, 0)); + assertEquals(1, result.getValue(1, 1)); + + assertNull(result.getValue(2, 0)); + assertEquals(2, result.getValue(2, 1)); + + assertEquals(1, result.getValue(3, 0)); + assertNull(result.getValue(3, 1)); + + assertEquals(1, result.getValue(4, 0)); + assertEquals(1, result.getValue(4, 1)); + + assertEquals(2, result.getValue(5, 0)); + assertNull(result.getValue(5, 1)); + + assertEquals(2, result.getValue(6, 0)); + assertEquals(1, result.getValue(6, 1)); + + assertEquals(3, result.getValue(7, 0)); + assertNull(result.getValue(7, 1)); + + assertEquals(3, result.getValue(8, 0)); + assertEquals(2, result.getValue(8, 1)); + + assertEquals(4, result.getValue(9, 0)); + assertNull(result.getValue(9, 1)); + + assertEquals(4, result.getValue(10, 0)); + assertEquals(2, result.getValue(10, 1)); + + result = create().select( + TBook_ID(), + TBook_AUTHOR_ID()) + .from(TBook()) + .groupBy(rollup( + TBook_ID(), + TBook_AUTHOR_ID())) + .orderBy( + TBook_ID().asc().nullsFirst(), + TBook_AUTHOR_ID().asc().nullsFirst()).fetch(); + + assertEquals(9, result.size()); + assertNull(result.getValue(0, 0)); + assertNull(result.getValue(0, 1)); + + assertEquals(1, result.getValue(1, 0)); + assertNull(result.getValue(1, 1)); + + assertEquals(1, result.getValue(2, 1)); + assertEquals(1, result.getValue(2, 1)); + + assertEquals(2, result.getValue(3, 0)); + assertNull(result.getValue(3, 1)); + + assertEquals(2, result.getValue(4, 0)); + assertEquals(1, result.getValue(4, 1)); + + assertEquals(3, result.getValue(5, 0)); + assertNull(result.getValue(5, 1)); + + assertEquals(3, result.getValue(6, 0)); + assertEquals(2, result.getValue(6, 1)); + + assertEquals(4, result.getValue(7, 0)); + assertNull(result.getValue(7, 1)); + + assertEquals(4, result.getValue(8, 0)); + assertEquals(2, result.getValue(8, 1)); + } + @Test public void testHavingWithoutGrouping() throws Exception { try { @@ -4997,9 +5101,9 @@ public abstract class jOOQAbstractTest< record = create().select(tomorrow, ts, yesterday).fetchOne(); // Ingres truncates milliseconds. Ignore this fact - assertEquals(24 * 60 * 60, - (record.getValue(ts).getTime() / 1000 - record.getValue(yesterday).getTime() / 1000)); assertEquals(24 * 60 * 60 + 60 * 60, + (record.getValue(ts).getTime() / 1000 - record.getValue(yesterday).getTime() / 1000)); + assertEquals(24 * 60 * 60, (record.getValue(tomorrow).getTime() / 1000 - record.getValue(ts).getTime() / 1000)); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 5d9dbee087..3fff2a7f8c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -202,18 +202,6 @@ public class Factory implements Configuration { return mapping; } - // ------------------------------------------------------------------------- - // Access to the loader API - // ------------------------------------------------------------------------- - - /** - * Create a new Loader object to load data from a CSV or XML - * source - */ - public final > LoaderOptionsStep loadInto(Table table) { - return new LoaderImpl(this, table); - } - // ------------------------------------------------------------------------- // RenderContext and BindContext accessors // ------------------------------------------------------------------------- @@ -310,6 +298,18 @@ public class Factory implements Configuration { } } + // ------------------------------------------------------------------------- + // Access to the loader API + // ------------------------------------------------------------------------- + + /** + * Create a new Loader object to load data from a CSV or XML + * source + */ + public final > LoaderOptionsStep loadInto(Table table) { + return new LoaderImpl(this, table); + } + // ------------------------------------------------------------------------- // Conversion of objects into tables // ------------------------------------------------------------------------- @@ -906,6 +906,10 @@ public class Factory implements Configuration { return new SQLResultQuery(this, sql, bindings).fetchOne(); } + // ------------------------------------------------------------------------- + // JDBC convenience methods + // ------------------------------------------------------------------------- + /** * Fetch all data from a JDBC {@link ResultSet} and transform it to a jOOQ * {@link Result}. After fetching all data, the JDBC ResultSet will be @@ -1610,7 +1614,7 @@ public class Factory implements Configuration { } // ------------------------------------------------------------------------- - // Global Field factory + // Global Field and Function factory // ------------------------------------------------------------------------- /** @@ -1755,6 +1759,146 @@ public class Factory implements Configuration { return (Field[]) castFields; } + /** + * Create a ROLLUP(fiel1, field2, .., fieldn) grouping field + *

+ * This has been observed to work with the following databases: + *

    + *
  • DB2
  • + *
  • Oracle
  • + *
  • SQL Server
  • + *
  • Sybase SQL Anywhere
  • + *
+ *

+ * Please check the SQL Server documentation for a very nice explanation of + * CUBE, ROLLUP, and GROUPING SETS + * clauses in grouping contexts: http://msdn.microsoft.com/en-US/library/bb522495.aspx + * + * @param fields The fields that are part of the ROLLUP + * function + * @return A field to be used in a GROUP BY clause + */ + public static Field rollup(Field... fields) { + return function("rollup", Object.class, fields); + } + + /** + * Create a CUBE(fiel1, field2, .., fieldn) grouping field + *

+ * This has been observed to work with the following databases: + *

    + *
  • DB2
  • + *
  • Oracle
  • + *
  • SQL Server
  • + *
  • Sybase SQL Anywhere
  • + *
+ *

+ * Please check the SQL Server documentation for a very nice explanation of + * CUBE, ROLLUP, and GROUPING SETS + * clauses in grouping contexts: http://msdn.microsoft.com/en-US/library/bb522495.aspx + * + * @param fields The fields that are part of the CUBE + * function + * @return A field to be used in a GROUP BY clause + */ + public static Field cube(Field... fields) { + return function("cube", Object.class, fields); + } + + // ------------------------------------------------------------------------- + // Aggregate and window functions + // ------------------------------------------------------------------------- + + /** + * Get the count(*) function + * + * @see Field#count() + * @see Field#countDistinct() + */ + public static Field count() { + return field("*", Integer.class).count(); + } + + /** + * The count(*) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + * + * @see Field#countOver() + */ + public static WindowPartitionByStep countOver() { + return field("*", Integer.class).countOver(); + } + + /** + * The row_number() over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep rowNumberOver() { + return new WindowFunction("row_number", SQLDataType.INTEGER); + } + + /** + * The rank_over() over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep rankOver() { + return new WindowFunction("rank", SQLDataType.INTEGER); + } + + /** + * The dense_rank() over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep denseRankOver() { + return new WindowFunction("dense_rank", SQLDataType.INTEGER); + } + + /** + * The precent_rank() over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep percentRankOver() { + return new WindowFunction("percent_rank", SQLDataType.NUMERIC); + } + + /** + * The cume_dist() over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep cumeDistOver() { + return new WindowFunction("cume_dist", SQLDataType.NUMERIC); + } + + /** + * The ntile([number]) over ([analytic clause]) function. + *

+ * Window functions are supported in DB2, Postgres, Oracle, SQL Server and + * Sybase. + */ + public static WindowPartitionByStep ntile(int number) { + return new WindowFunction("ntile", SQLDataType.NUMERIC, field("" + number, Integer.class)); + } + + // ------------------------------------------------------------------------- + // Bind values + // ------------------------------------------------------------------------- + /** * Get a constant value *

@@ -2129,92 +2273,6 @@ public class Factory implements Configuration { return new Euler(); } - // ------------------------------------------------------------------------- - // Aggregate and window functions - // ------------------------------------------------------------------------- - - /** - * Get the count(*) function - * - * @see Field#count() - * @see Field#countDistinct() - */ - public static Field count() { - return field("*", Integer.class).count(); - } - - /** - * The count(*) over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - * - * @see Field#countOver() - */ - public static WindowPartitionByStep countOver() { - return field("*", Integer.class).countOver(); - } - - /** - * The row_number() over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep rowNumberOver() { - return new WindowFunction("row_number", SQLDataType.INTEGER); - } - - /** - * The rank_over() over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep rankOver() { - return new WindowFunction("rank", SQLDataType.INTEGER); - } - - /** - * The dense_rank() over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep denseRankOver() { - return new WindowFunction("dense_rank", SQLDataType.INTEGER); - } - - /** - * The precent_rank() over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep percentRankOver() { - return new WindowFunction("percent_rank", SQLDataType.NUMERIC); - } - - /** - * The cume_dist() over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep cumeDistOver() { - return new WindowFunction("cume_dist", SQLDataType.NUMERIC); - } - - /** - * The ntile([number]) over ([analytic clause]) function. - *

- * Window functions are supported in DB2, Postgres, Oracle, SQL Server and - * Sybase. - */ - public static WindowPartitionByStep ntile(int number) { - return new WindowFunction("ntile", SQLDataType.NUMERIC, field("" + number, Integer.class)); - } - // ------------------------------------------------------------------------- // Pseudo fields and date time functions // -------------------------------------------------------------------------