diff --git a/jOOQ/src/main/java/org/jooq/ForeignKey.java b/jOOQ/src/main/java/org/jooq/ForeignKey.java index a25685ab2e..894c17ace5 100644 --- a/jOOQ/src/main/java/org/jooq/ForeignKey.java +++ b/jOOQ/src/main/java/org/jooq/ForeignKey.java @@ -90,8 +90,8 @@ public interface ForeignKey extends Key { * Fetch a parent record of a given record through this foreign key *

* This returns a parent record referenced by a given record through this - * foreign key. If no parent record was found, this returns - * null + * foreign key, as if fetching from {@link #parent(Record)}. If no parent + * record was found, this returns null * * @throws DataAccessException if something went wrong executing the query * @see TableRecord#fetchParent(ForeignKey) @@ -103,7 +103,8 @@ public interface ForeignKey extends Key { * Fetch parent records of a given set of record through this foreign key *

* This returns parent records referenced by any record in a given set of - * records through this foreign key. + * records through this foreign key, as if fetching from + * {@link #parents(Record...)}. * * @throws DataAccessException if something went wrong executing the query * @see TableRecord#fetchParent(ForeignKey) @@ -115,7 +116,8 @@ public interface ForeignKey extends Key { * Fetch parent records of a given set of record through this foreign key *

* This returns parent records referenced by any record in a given set of - * records through this foreign key. + * records through this foreign key, as if fetching from + * {@link #parents(Collection)}. * * @throws DataAccessException if something went wrong executing the query * @see TableRecord#fetchParent(ForeignKey) @@ -127,7 +129,7 @@ public interface ForeignKey extends Key { * Fetch child records of a given record through this foreign key *

* This returns childs record referencing a given record through this - * foreign key + * foreign key, as if fetching from {@link #children(Record)}. * * @throws DataAccessException if something went wrong executing the query * @see UpdatableRecord#fetchChild(ForeignKey) @@ -140,7 +142,8 @@ public interface ForeignKey extends Key { * Fetch child records of a given set of records through this foreign key *

* This returns childs record referencing any record in a given set of - * records through this foreign key + * records through this foreign key, as if fetching from + * {@link #children(Record...)}. * * @throws DataAccessException if something went wrong executing the query * @see UpdatableRecord#fetchChild(ForeignKey) @@ -153,7 +156,8 @@ public interface ForeignKey extends Key { * Fetch child records of a given set of records through this foreign key *

* This returns childs record referencing any record in a given set of - * records through this foreign key + * records through this foreign key, as if fetching from + * {@link #children(Collection)}. * * @throws DataAccessException if something went wrong executing the query * @see UpdatableRecord#fetchChild(ForeignKey) @@ -161,4 +165,46 @@ public interface ForeignKey extends Key { */ @NotNull Result fetchChildren(Collection records) throws DataAccessException; + + /** + * Get a table expression representing the parent of a record, given this + * foreign key. + */ + @NotNull + Table parent(R record); + + /** + * Get a table expression representing the parents of a record, given this + * foreign key. + */ + @NotNull + Table parents(R... records); + + /** + * Get a table expression representing the parents of a record, given this + * foreign key. + */ + @NotNull + Table parents(Collection records); + + /** + * Get a table expression representing the children of a record, given this + * foreign key. + */ + @NotNull + Table children(O record); + + /** + * Get a table expression representing the children of a record, given this + * foreign key. + */ + @NotNull + Table children(O... records); + + /** + * Get a table expression representing the children of a record, given this + * foreign key. + */ + @NotNull + Table children(Collection records); } diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index 6b812b1663..1e65c1fb67 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -3196,7 +3196,8 @@ public interface Result extends List, Attachable, Formattab // ------------------------------------------------------------------------ /** - * Fetch parent records of this record, given a foreign key. + * Fetch parent records of this record, given a foreign key, as if fetching + * from {@link #parents(ForeignKey)}. * * @throws DataAccessException if something went wrong executing the query. * @see ForeignKey#fetchParent(Record) @@ -3207,7 +3208,8 @@ public interface Result extends List, Attachable, Formattab > Result fetchParents(ForeignKey key) throws DataAccessException; /** - * Fetch child records of this record, given a foreign key. + * Fetch child records of this record, given a foreign key, as if fetching + * from {@link #children(ForeignKey)}. * * @throws DataAccessException if something went wrong executing the query. * @see ForeignKey#fetchChildren(java.util.Collection) @@ -3217,6 +3219,20 @@ public interface Result extends List, Attachable, Formattab @NotNull > Result fetchChildren(ForeignKey key) throws DataAccessException; + /** + * Get a table expression representing the parents of all of this result's + * records, given a foreign key. + */ + @NotNull + > Table parents(ForeignKey key); + + /** + * Get a table expression representing the children of all of this result's + * records, given a foreign key. + */ + @NotNull + > Table children(ForeignKey key); + // ------------------------------------------------------------------------ // Specialisations of Attachable methods // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/TableRecord.java b/jOOQ/src/main/java/org/jooq/TableRecord.java index cddd0b69d2..e4b612f12c 100644 --- a/jOOQ/src/main/java/org/jooq/TableRecord.java +++ b/jOOQ/src/main/java/org/jooq/TableRecord.java @@ -106,8 +106,8 @@ public interface TableRecord> extends Record { * Fetch a parent record of this record, given a foreign key. *

* This returns a parent record referenced by this record through a given - * foreign key. If no parent record was found, this returns - * null + * foreign key, as if fetching from {@link #parent(ForeignKey)}. If no + * parent record was found, this returns null * * @throws DataAccessException if something went wrong executing the query * @see ForeignKey#fetchParent(Record) @@ -118,6 +118,14 @@ public interface TableRecord> extends Record { @Support > O fetchParent(ForeignKey key) throws DataAccessException; + /** + * Get a table expression representing the parent of this record, given a + * foreign key. + */ + @NotNull + @Support + > Table parent(ForeignKey key); + @NotNull @Override R with(Field field, T value); diff --git a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java index 83d1804218..7a4edd7cff 100644 --- a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java +++ b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java @@ -582,7 +582,8 @@ public interface UpdatableRecord> extends TableReco * Fetch a child record of this record, given a foreign key. *

* This returns a child record referencing this record through a given - * foreign key. If no child record was found, this returns null + * foreign key, as if fetching from {@link #children(ForeignKey)}.. If no + * child record was found, this returns null. * * @throws DataAccessException if something went wrong executing the query * @throws TooManyRowsException if the query returned more than one record @@ -597,8 +598,8 @@ public interface UpdatableRecord> extends TableReco /** * Fetch child records of this record, given a foreign key. *

- * This returns childs record referencing this record through a given - * foreign key. + * This returns child records referencing this record through a given + * foreign key, as if fetching from {@link #children(ForeignKey)}. * * @throws DataAccessException if something went wrong executing the query * @see ForeignKey#fetchChildren(java.util.Collection) @@ -608,4 +609,12 @@ public interface UpdatableRecord> extends TableReco @NotNull @Support > Result fetchChildren(ForeignKey key) throws DataAccessException; + + /** + * Get a table expression representing the children of this record, given a + * foreign key. + */ + @NotNull + @Support + > Table children(ForeignKey key); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java b/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java index cb8c6b6b67..c7d06eb9e0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java @@ -52,7 +52,7 @@ import org.jooq.TableOptions; /** * @author Lukas Eder */ -final class DerivedTable extends AbstractTable { +class DerivedTable extends AbstractTable { private static final long serialVersionUID = 6272398035926615668L; diff --git a/jOOQ/src/main/java/org/jooq/impl/InlineDerivedTable.java b/jOOQ/src/main/java/org/jooq/impl/InlineDerivedTable.java new file mode 100644 index 0000000000..50114a1f37 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/InlineDerivedTable.java @@ -0,0 +1,71 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +package org.jooq.impl; + +import static org.jooq.impl.DSL.selectFrom; + +import org.jooq.Condition; +import org.jooq.Record; +import org.jooq.Table; + +/** + * @author Lukas Eder + */ +final class InlineDerivedTable extends DerivedTable { + + private static final long serialVersionUID = 6272398035926615668L; + + private final Table table; + private final Condition where; + + InlineDerivedTable(Table table, Condition where) { + super(selectFrom(table).where(where)); + + this.table = table; + this.where = where; + } + + final Table table() { + return table; + } + + final Condition condition() { + return where; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java b/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java index 689e5447bc..931eda2118 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java @@ -106,6 +106,14 @@ final class ReferenceImpl extends AbstractKe return fetchParents(list(records)); } + @Override + public final Result fetchParents(Collection records) { + if (records == null || records.size() == 0) + return new ResultImpl<>(new DefaultConfiguration(), uk.getFields()); + else + return extractDSLContext(records).selectFrom(parents(records)).fetch(); + } + @Override public final Result fetchChildren(O record) { return fetchChildren(list(record)); @@ -117,48 +125,59 @@ final class ReferenceImpl extends AbstractKe return fetchChildren(list(records)); } - @Override - public final Result fetchParents(Collection records) { - if (records == null || records.size() == 0) - return new ResultImpl<>(new DefaultConfiguration(), uk.getFields()); - else - return fetch(records, uk.getTable(), uk.getFieldsArray(), getFieldsArray()); - } - @Override public final Result fetchChildren(Collection records) { if (records == null || records.size() == 0) return new ResultImpl<>(new DefaultConfiguration(), getFields()); else - return fetch(records, getTable(), getFieldsArray(), uk.getFieldsArray()); + return extractDSLContext(records).selectFrom(children(records)).fetch(); + } + + @Override + public final Table parent(R record) { + return parents(list(record)); + } + + @SafeVarargs + @Override + public final Table parents(R... records) { + return parents(list(records)); + } + + @Override + public final Table parents(Collection records) { + return table(records, uk.getTable(), uk.getFieldsArray(), getFieldsArray()); + } + + @Override + public final Table children(O record) { + return children(list(record)); + } + + @SafeVarargs + @Override + public final Table children(O... records) { + return children(list(records)); + } + + @Override + public final Table children(Collection records) { + return table(records, getTable(), getFieldsArray(), uk.getFieldsArray()); } - /** - * Do the actual fetching - */ @SuppressWarnings("unchecked") - private static Result fetch( + private static Table table( Collection records, Table table, TableField[] fields1, - TableField[] fields2) { - - // Use regular predicates - if (fields1.length == 1) { - return extractDSLContext(records) - .selectFrom(table) - .where(((Field) fields1[0]).in(extractValues(records, fields2[0]))) - .fetch(); - } - - // Use row value expressions - else { - return extractDSLContext(records) - .selectFrom(table) - .where(row(fields1).in(extractRows(records, fields2))) - .fetch(); - } - + TableField[] fields2 + ) { + return new InlineDerivedTable<>( + table, + fields1.length == 1 + ? ((Field) fields1[0]).in(extractValues(records, fields2[0])) + : row(fields1).in(extractRows(records, fields2)) + ); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index a7502f0dfd..bf26dbc78c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -1496,6 +1496,16 @@ final class ResultImpl extends AbstractCursor implements Re return key.fetchChildren(this); } + @Override + public final > Table parents(ForeignKey key) { + return key.parents(this); + } + + @Override + public final > Table children(ForeignKey key) { + return key.children(this); + } + // ------------------------------------------------------------------------- // XXX Object API // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index ded69c44de..c8962a7517 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -1654,6 +1654,8 @@ final class SelectQueryImpl extends AbstractResultQuery imp + tablelist = transformInlineDerivedTables(tablelist, where); + context.formatSeparator() .visit(K_FROM) .separatorRequired(true) @@ -1903,6 +1905,97 @@ final class SelectQueryImpl extends AbstractResultQuery imp } } + private final boolean hasOuterJoins(TableList tablelist) { + for (Table table : tablelist) + if (table instanceof JoinTable && hasOuterJoins((JoinTable) table)) + return true; + + return false; + } + + private final boolean hasOuterJoins(JoinTable join) { + switch (join.type) { + case CROSS_JOIN: + case JOIN: + case NATURAL_JOIN: + case STRAIGHT_JOIN: + return join.lhs instanceof JoinTable && hasOuterJoins((JoinTable) join.lhs) + || join.rhs instanceof JoinTable && hasOuterJoins((JoinTable) join.rhs); + + default: + return true; + } + } + + private final boolean hasInlineDerivedTables(TableList tablelist) { + for (Table table : tablelist) + if (table instanceof InlineDerivedTable) + return true; + else if (table instanceof JoinTable && hasInlineDerivedTables((JoinTable) table)) + return true; + + return false; + } + + private final boolean hasInlineDerivedTables(JoinTable join) { + return join.lhs instanceof InlineDerivedTable + || join.rhs instanceof InlineDerivedTable + || join.lhs instanceof JoinTable && hasInlineDerivedTables((JoinTable) join.lhs) + || join.rhs instanceof JoinTable && hasInlineDerivedTables((JoinTable) join.rhs); + } + + private final TableList transformInlineDerivedTables(TableList tablelist, ConditionProviderImpl where) { + + // [#10353] A first implementation supports this feature only when there are no outer joins + if (hasOuterJoins(tablelist) || !hasInlineDerivedTables(tablelist)) + return tablelist; + + TableList result = new TableList(); + + for (Table table : tablelist) + transformInlineDerivedTable0(table, result, where); + + return result; + } + + private final void transformInlineDerivedTable0(Table table, TableList result, ConditionProviderImpl where) { + if (table instanceof InlineDerivedTable) { + InlineDerivedTable t = (InlineDerivedTable) table; + + result.add(t.table()); + where.addConditions(t.condition()); + } + else if (table instanceof JoinTable) { + result.add(transformInlineDerivedTables0(table, where)); + } + else + result.add(table); + } + + private final Table transformInlineDerivedTables0(Table table, ConditionProviderImpl where) { + if (table instanceof InlineDerivedTable) { + InlineDerivedTable t = (InlineDerivedTable) table; + + where.addConditions(t.condition()); + return t.table(); + } + else if (table instanceof JoinTable) { + JoinTable join = (JoinTable) table; + JoinTable result = new JoinTable( + transformInlineDerivedTables0(join.lhs, where), + transformInlineDerivedTables0(join.rhs, where), + join.type + ); + + if (!join.using.isEmpty()) + return result.using(join.using); + else + return result.on(join.condition); + } + else + return table; + } + diff --git a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java index fc5e161678..439e9cae6b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java @@ -151,6 +151,12 @@ public class TableRecordImpl> extends AbstractRecord im return key.fetchParent((R) this); } + @SuppressWarnings("unchecked") + @Override + public final > Table parent(ForeignKey key) { + return key.parent((R) this); + } + @Override public final int insert() { return insert(fields.fields.fields); diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java index 56d9c2647e..4e12707cb0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java @@ -115,6 +115,12 @@ public class UpdatableRecordImpl> extends TableReco return key.fetchChildren((R) this); } + @SuppressWarnings("unchecked") + @Override + public final > Table children(ForeignKey key) { + return key.children((R) this); + } + @Override final UniqueKey getPrimaryKey() { return getTable().getPrimaryKey();