diff --git a/jOOQ-test/src/org/jooq/test/jOOQOracleTest.java b/jOOQ-test/src/org/jooq/test/jOOQOracleTest.java index f735111946..f7ba7d5cdc 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQOracleTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQOracleTest.java @@ -83,6 +83,7 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Timestamp; import java.util.Arrays; +import java.util.Collections; import org.jooq.ArrayRecord; import org.jooq.DataType; @@ -1244,7 +1245,7 @@ public class jOOQOracleTest extends jOOQAbstractTest< } @Test - public void testKeepDenseRank() { + public void testOracleKeepDenseRank() { assertEquals( Arrays.asList(3, 7), Arrays.asList( @@ -1255,4 +1256,42 @@ public class jOOQOracleTest extends jOOQAbstractTest< .fetchOne() .into(Integer[].class))); } + + @Test + public void testOraclePartitionedOuterJoin() { + + // Maybe, find a more sensible query for the test case...? + Result result1 = + create().select( + TAuthor_FIRST_NAME(), + TBook_TITLE()) + .from(TAuthor() + .leftOuterJoin(TBook()) + .partitionBy(TBook_TITLE()) + .on(TAuthor_ID().equal(TBook_AUTHOR_ID()))) + .orderBy( + TAuthor_FIRST_NAME(), + TBook_ID()) + .fetch(); + + assertEquals(8, result1.size()); + assertEquals(BOOK_TITLES, result1.getValues(TBook_TITLE()).subList(0, 4)); + assertEquals(Collections.nCopies(4, "George"), result1.getValues(TAuthor_FIRST_NAME()).subList(0, 4)); + assertEquals(Collections.nCopies(4, "Paulo"), result1.getValues(TAuthor_FIRST_NAME()).subList(4, 8)); + + Result result2 = + create().select( + TAuthor_FIRST_NAME(), + TBook_TITLE()) + .from(TAuthor()) + .leftOuterJoin(TBook()) + .partitionBy(TBook_TITLE()) + .on(TAuthor_ID().equal(TBook_AUTHOR_ID())) + .orderBy( + TAuthor_FIRST_NAME(), + TBook_ID()) + .fetch(); + + assertEquals(result1, result2); + } } diff --git a/jOOQ/src/main/java/org/jooq/SelectJoinPartitionByStep.java b/jOOQ/src/main/java/org/jooq/SelectJoinPartitionByStep.java new file mode 100644 index 0000000000..ecd471ac3b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/SelectJoinPartitionByStep.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2009-2012, 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; + +import static org.jooq.SQLDialect.ORACLE; + +import java.util.Collection; + +/** + * This type is used for the {@link Select}'s DSL API when selecting generic + * {@link Record} types. + *

+ * Example:

+ * -- get all authors' first and last names, and the number
+ * -- of books they've written in German, if they have written
+ * -- more than five books in German in the last three years
+ * -- (from 2011), and sort those authors by last names
+ * -- limiting results to the second and third row
+ *
+ *   SELECT T_AUTHOR.FIRST_NAME, T_AUTHOR.LAST_NAME, COUNT(*)
+ *     FROM T_AUTHOR
+ *     JOIN T_BOOK ON T_AUTHOR.ID = T_BOOK.AUTHOR_ID
+ *    WHERE T_BOOK.LANGUAGE = 'DE'
+ *      AND T_BOOK.PUBLISHED > '2008-01-01'
+ * GROUP BY T_AUTHOR.FIRST_NAME, T_AUTHOR.LAST_NAME
+ *   HAVING COUNT(*) > 5
+ * ORDER BY T_AUTHOR.LAST_NAME ASC NULLS FIRST
+ *    LIMIT 2
+ *   OFFSET 1
+ *      FOR UPDATE
+ *       OF FIRST_NAME, LAST_NAME
+ *       NO WAIT
+ * 
Its equivalent in jOOQ
+ * create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
+ *       .from(T_AUTHOR)
+ *       .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID))
+ *       .where(TBook.LANGUAGE.equal("DE"))
+ *       .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')))
+ *       .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
+ *       .having(create.count().greaterThan(5))
+ *       .orderBy(TAuthor.LAST_NAME.asc().nullsFirst())
+ *       .limit(2)
+ *       .offset(1)
+ *       .forUpdate()
+ *       .of(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
+ *       .noWait();
+ * 
Refer to the manual for more details + * + * @author Lukas Eder + */ +public interface SelectJoinPartitionByStep extends SelectOnStep { + + /** + * Add a PARTITION BY clause to the right hand side of the + * OUTER JOIN keywords + * + * @see TablePartitionByStep#partitionBy(Field...) + */ + @Support(ORACLE) + SelectOnStep partitionBy(Field... fields); + + /** + * Add a PARTITION BY clause to the right hand side of the + * OUTER JOIN keywords + * + * @see TablePartitionByStep#partitionBy(Collection) + */ + @Support(ORACLE) + SelectOnStep partitionBy(Collection> fields); + +} diff --git a/jOOQ/src/main/java/org/jooq/SelectJoinStep.java b/jOOQ/src/main/java/org/jooq/SelectJoinStep.java index c9c07b2ed5..cbeffafecf 100644 --- a/jOOQ/src/main/java/org/jooq/SelectJoinStep.java +++ b/jOOQ/src/main/java/org/jooq/SelectJoinStep.java @@ -205,7 +205,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#leftOuterJoin(TableLike) */ @Support - SelectOnStep leftOuterJoin(TableLike table); + SelectJoinPartitionByStep leftOuterJoin(TableLike table); /** * Convenience method to LEFT OUTER JOIN a table to the last @@ -221,7 +221,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#leftOuterJoin(String) */ @Support - SelectOnStep leftOuterJoin(String sql); + SelectJoinPartitionByStep leftOuterJoin(String sql); /** * Convenience method to LEFT OUTER JOIN a table to the last @@ -237,7 +237,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#leftOuterJoin(String, Object...) */ @Support - SelectOnStep leftOuterJoin(String sql, Object... bindings); + SelectJoinPartitionByStep leftOuterJoin(String sql, Object... bindings); /** * Convenience method to RIGHT OUTER JOIN a table to the last @@ -249,7 +249,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#rightOuterJoin(TableLike) */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - SelectOnStep rightOuterJoin(TableLike table); + SelectJoinPartitionByStep rightOuterJoin(TableLike table); /** * Convenience method to RIGHT OUTER JOIN a table to the last @@ -267,7 +267,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#rightOuterJoin(String) */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - SelectOnStep rightOuterJoin(String sql); + SelectJoinPartitionByStep rightOuterJoin(String sql); /** * Convenience method to RIGHT OUTER JOIN a table to the last @@ -285,7 +285,7 @@ public interface SelectJoinStep extends SelectWhereStep { * @see Table#rightOuterJoin(String, Object...) */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - SelectOnStep rightOuterJoin(String sql, Object... bindings); + SelectJoinPartitionByStep rightOuterJoin(String sql, Object... bindings); /** * Convenience method to FULL OUTER JOIN a table to the last diff --git a/jOOQ/src/main/java/org/jooq/SelectQuery.java b/jOOQ/src/main/java/org/jooq/SelectQuery.java index aa8f9d035c..21e55bf7f8 100644 --- a/jOOQ/src/main/java/org/jooq/SelectQuery.java +++ b/jOOQ/src/main/java/org/jooq/SelectQuery.java @@ -107,6 +107,21 @@ public interface SelectQuery extends Select, ConditionProvider, OrderPro @Support void addJoin(TableLike table, JoinType type, Condition... conditions); + /** + * Joins the existing table product to a new table using a condition + *

+ * This adds a PARTITION BY clause to the right hand side of a + * OUTER JOIN expression. + * + * @param table The joined table + * @param type The type of join + * @param conditions The joining conditions + * @param partitionBy The PARTITION BY expression + * @see TablePartitionByStep + */ + @Support(ORACLE) + void addJoin(TableLike table, JoinType type, Condition[] conditions, Field[] partitionBy); + /** * Joins the existing table product to a new table with a USING * clause diff --git a/jOOQ/src/main/java/org/jooq/Table.java b/jOOQ/src/main/java/org/jooq/Table.java index 1773f0108f..6996f9cec9 100644 --- a/jOOQ/src/main/java/org/jooq/Table.java +++ b/jOOQ/src/main/java/org/jooq/Table.java @@ -235,7 +235,7 @@ public interface Table extends org.jooq.Type, AliasProvider * LEFT OUTER JOIN a table to this table. */ @Support - TableOnStep leftOuterJoin(TableLike table); + TablePartitionByStep leftOuterJoin(TableLike table); /** * LEFT OUTER JOIN a table to this table. @@ -248,7 +248,7 @@ public interface Table extends org.jooq.Type, AliasProvider * @see Factory#table(String) */ @Support - TableOnStep leftOuterJoin(String sql); + TablePartitionByStep leftOuterJoin(String sql); /** * LEFT OUTER JOIN a table to this table. @@ -261,7 +261,7 @@ public interface Table extends org.jooq.Type, AliasProvider * @see Factory#table(String, Object...) */ @Support - TableOnStep leftOuterJoin(String sql, Object... bindings); + TablePartitionByStep leftOuterJoin(String sql, Object... bindings); /** * RIGHT OUTER JOIN a table to this table. @@ -269,7 +269,7 @@ public interface Table extends org.jooq.Type, AliasProvider * This is only possible where the underlying RDBMS supports it */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - TableOnStep rightOuterJoin(TableLike table); + TablePartitionByStep rightOuterJoin(TableLike table); /** * RIGHT OUTER JOIN a table to this table. @@ -284,7 +284,7 @@ public interface Table extends org.jooq.Type, AliasProvider * @see Factory#table(String) */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - TableOnStep rightOuterJoin(String sql); + TablePartitionByStep rightOuterJoin(String sql); /** * RIGHT OUTER JOIN a table to this table. @@ -299,7 +299,7 @@ public interface Table extends org.jooq.Type, AliasProvider * @see Factory#table(String, Object...) */ @Support({ ASE, CUBRID, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE }) - TableOnStep rightOuterJoin(String sql, Object... bindings); + TablePartitionByStep rightOuterJoin(String sql, Object... bindings); /** * FULL OUTER JOIN a table to this table. diff --git a/jOOQ/src/main/java/org/jooq/TablePartitionByStep.java b/jOOQ/src/main/java/org/jooq/TablePartitionByStep.java new file mode 100644 index 0000000000..5b45e191d7 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/TablePartitionByStep.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2009-2012, 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; + +import static org.jooq.SQLDialect.ORACLE; + +import java.util.Collection; + +/** + * An intermediate type for the construction of a partitioned + * {@link SQLDialect#ORACLE} OUTER JOIN clause. + *

+ * This step allows for adding Oracle-specific PARTITION BY clauses + * to the right of an OUTER JOIN keyword. See the Oracle + * documentation for more details here: http://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm# + * i2054062 + * + * @author Lukas Eder + */ +public interface TablePartitionByStep extends TableOnStep { + + /** + * Add a PARTITION BY clause to the right hand side of the + * OUTER JOIN keywords + */ + @Support(ORACLE) + TableOnStep partitionBy(Field... fields); + + /** + * Add a PARTITION BY clause to the right hand side of the + * OUTER JOIN keywords + */ + @Support(ORACLE) + TableOnStep partitionBy(Collection> fields); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index 51260e21dd..51cdfa3f45 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -55,6 +55,7 @@ import org.jooq.Table; import org.jooq.TableField; import org.jooq.TableLike; import org.jooq.TableOnStep; +import org.jooq.TablePartitionByStep; abstract class AbstractTable extends AbstractFieldProviderQueryPart implements Table { @@ -199,32 +200,32 @@ abstract class AbstractTable extends AbstractFieldProviderQuer } @Override - public final TableOnStep leftOuterJoin(TableLike table) { + public final TablePartitionByStep leftOuterJoin(TableLike table) { return new JoinTable(this, table, JoinType.LEFT_OUTER_JOIN); } @Override - public final TableOnStep leftOuterJoin(String sql) { + public final TablePartitionByStep leftOuterJoin(String sql) { return leftOuterJoin(table(sql)); } @Override - public final TableOnStep leftOuterJoin(String sql, Object... bindings) { + public final TablePartitionByStep leftOuterJoin(String sql, Object... bindings) { return leftOuterJoin(table(sql, bindings)); } @Override - public final TableOnStep rightOuterJoin(TableLike table) { + public final TablePartitionByStep rightOuterJoin(TableLike table) { return new JoinTable(this, table, JoinType.RIGHT_OUTER_JOIN); } @Override - public final TableOnStep rightOuterJoin(String sql) { + public final TablePartitionByStep rightOuterJoin(String sql) { return rightOuterJoin(table(sql)); } @Override - public final TableOnStep rightOuterJoin(String sql, Object... bindings) { + public final TablePartitionByStep rightOuterJoin(String sql, Object... bindings) { return rightOuterJoin(table(sql, bindings)); } diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index 9a2df32025..da321cdf48 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -73,6 +73,7 @@ import org.jooq.TableField; import org.jooq.TableLike; import org.jooq.TableOnConditionStep; import org.jooq.TableOnStep; +import org.jooq.TablePartitionByStep; import org.jooq.exception.DataAccessException; /** @@ -80,7 +81,7 @@ import org.jooq.exception.DataAccessException; * * @author Lukas Eder */ -class JoinTable extends AbstractTable implements TableOnStep, TableOnConditionStep { +class JoinTable extends AbstractTable implements TableOnConditionStep, TablePartitionByStep { /** * Generated UID @@ -89,6 +90,7 @@ class JoinTable extends AbstractTable implements TableOnStep, TableOnCon private final Table lhs; private final Table rhs; + private final FieldList rhsPartitionBy; private final JoinType type; private final ConditionProviderImpl condition; @@ -99,6 +101,7 @@ class JoinTable extends AbstractTable implements TableOnStep, TableOnCon this.lhs = lhs.asTable(); this.rhs = rhs.asTable(); + this.rhsPartitionBy = new FieldList(); this.type = type; this.condition = new ConditionProviderImpl(); @@ -133,6 +136,15 @@ class JoinTable extends AbstractTable implements TableOnStep, TableOnCon .sql(rhs) .sql(rhs instanceof JoinTable ? ")" : ""); + // [#1645] The Oracle PARTITION BY clause can be put to the right of an + // OUTER JOINed table + if (!rhsPartitionBy.isEmpty()) { + context.formatSeparator() + .keyword(" partition by (") + .sql(rhsPartitionBy) + .sql(")"); + } + // CROSS JOIN and NATURAL JOIN do not have any condition clauses if (!asList(CROSS_JOIN, NATURAL_JOIN, @@ -233,7 +245,7 @@ class JoinTable extends AbstractTable implements TableOnStep, TableOnCon @Override public final void bind(BindContext context) throws DataAccessException { - context.bind(lhs).bind(rhs); + context.bind(lhs).bind(rhs).bind((QueryPart) rhsPartitionBy); if (!using.isEmpty()) { context.bind((QueryPart) using); @@ -272,6 +284,17 @@ class JoinTable extends AbstractTable implements TableOnStep, TableOnCon // Join API // ------------------------------------------------------------------------ + @Override + public final TableOnStep partitionBy(Field... fields) { + return partitionBy(Util.list(fields)); + } + + @Override + public final TableOnStep partitionBy(Collection> fields) { + rhsPartitionBy.addAll(fields); + return this; + } + @Override public final JoinTable on(Condition... conditions) { condition.addConditions(conditions); diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java index 569ac6d302..4925c50481 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java @@ -56,6 +56,7 @@ import org.jooq.SelectConditionStep; import org.jooq.SelectConnectByConditionStep; import org.jooq.SelectForUpdateOfStep; import org.jooq.SelectHavingConditionStep; +import org.jooq.SelectJoinPartitionByStep; import org.jooq.SelectJoinStep; import org.jooq.SelectOffsetStep; import org.jooq.SelectOnConditionStep; @@ -77,7 +78,7 @@ class SelectImpl extends AbstractDelegatingSelect implements // Cascading interface implementations for Select behaviour SelectSelectStep, - SelectOnStep, + SelectJoinPartitionByStep, SelectOnConditionStep, SelectConditionStep, SelectConnectByConditionStep, @@ -95,6 +96,11 @@ class SelectImpl extends AbstractDelegatingSelect implements */ private transient TableLike joinTable; + /** + * A temporary member holding a join partition by expression + */ + private transient Field[] joinPartitionBy; + /** * A temporary member holding a join type */ @@ -573,8 +579,9 @@ class SelectImpl extends AbstractDelegatingSelect implements conditionStep = ConditionStep.ON; joinConditions = new ConditionProviderImpl(); joinConditions.addConditions(conditions); - getQuery().addJoin(joinTable, joinType, joinConditions); + getQuery().addJoin(joinTable, joinType, new Condition[] { joinConditions }, joinPartitionBy); joinTable = null; + joinPartitionBy = null; joinType = null; return this; } @@ -594,6 +601,7 @@ class SelectImpl extends AbstractDelegatingSelect implements conditionStep = ConditionStep.ON; getQuery().addJoinOnKey(joinTable, joinType); joinTable = null; + joinPartitionBy = null; joinType = null; return this; } @@ -603,6 +611,7 @@ class SelectImpl extends AbstractDelegatingSelect implements conditionStep = ConditionStep.ON; getQuery().addJoinOnKey(joinTable, joinType, keyFields); joinTable = null; + joinPartitionBy = null; joinType = null; return this; } @@ -612,6 +621,7 @@ class SelectImpl extends AbstractDelegatingSelect implements conditionStep = ConditionStep.ON; getQuery().addJoinOnKey(joinTable, joinType, key); joinTable = null; + joinPartitionBy = null; joinType = null; return this; @@ -626,6 +636,7 @@ class SelectImpl extends AbstractDelegatingSelect implements public final SelectImpl using(Collection> fields) { getQuery().addJoinUsing(joinTable, joinType, fields); joinTable = null; + joinPartitionBy = null; joinType = null; return this; } @@ -654,6 +665,7 @@ class SelectImpl extends AbstractDelegatingSelect implements conditionStep = ConditionStep.ON; joinTable = table; joinType = type; + joinPartitionBy = null; joinConditions = null; return this; } @@ -681,6 +693,7 @@ class SelectImpl extends AbstractDelegatingSelect implements private final SelectImpl simpleJoin0(TableLike table, JoinType type) { getQuery().addJoin(table, type); joinTable = null; + joinPartitionBy = null; joinType = null; return this; } @@ -765,6 +778,17 @@ class SelectImpl extends AbstractDelegatingSelect implements return naturalRightOuterJoin(table(sql, bindings)); } + @Override + public final SelectImpl partitionBy(Field... fields) { + joinPartitionBy = fields; + return this; + } + + @Override + public final SelectImpl partitionBy(Collection> fields) { + return partitionBy(fields.toArray(new Field[fields.size()])); + } + /** * The {@link SelectImpl} current condition step *

diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 17cab9bc11..81578e57fa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -140,6 +140,11 @@ class SelectQueryImpl extends AbstractSubSelect implements SelectQuery { @Override public final void addJoin(TableLike table, JoinType type, Condition... conditions) { + addJoin(table, type, conditions, null); + } + + @Override + public final void addJoin(TableLike table, JoinType type, Condition[] conditions, Field[] partitionBy) { // TODO: This and similar methods should be refactored, patterns extracted... int index = getFrom().size() - 1; @@ -150,10 +155,10 @@ class SelectQueryImpl extends AbstractSubSelect implements SelectQuery { joined = getFrom().get(index).join(table).on(conditions); break; case LEFT_OUTER_JOIN: - joined = getFrom().get(index).leftOuterJoin(table).on(conditions); + joined = getFrom().get(index).leftOuterJoin(table).partitionBy(partitionBy).on(conditions); break; case RIGHT_OUTER_JOIN: - joined = getFrom().get(index).rightOuterJoin(table).on(conditions); + joined = getFrom().get(index).rightOuterJoin(table).partitionBy(partitionBy).on(conditions); break; case FULL_OUTER_JOIN: joined = getFrom().get(index).fullOuterJoin(table).on(conditions); diff --git a/jOOQ/src/main/java/org/jooq/impl/Util.java b/jOOQ/src/main/java/org/jooq/impl/Util.java index 4d43efe9df..0f2449db89 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Util.java +++ b/jOOQ/src/main/java/org/jooq/impl/Util.java @@ -53,7 +53,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.regex.Pattern; @@ -193,6 +195,14 @@ final class Util { } } + /** + * Use this rather than {@link Arrays#asList(Object...)} for + * null-safety + */ + static final List list(T[] array) { + return array == null ? Collections.emptyList() : Arrays.asList(array); + } + /** * [#1005] Convert values from the VALUES clause to appropriate * values as specified by the INTO clause's column list.