[#1202] Add support for the relational division operation: A.divideBy(B).on(A.ID.equal(B.A_ID)).returning(A.X, A.Y, ...)
This commit is contained in:
parent
e914d2d037
commit
86402653ed
@ -40,6 +40,7 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.jooq.impl.Factory.avg;
|
||||
import static org.jooq.impl.Factory.count;
|
||||
import static org.jooq.impl.Factory.field;
|
||||
import static org.jooq.impl.Factory.literal;
|
||||
import static org.jooq.impl.Factory.max;
|
||||
import static org.jooq.impl.Factory.sum;
|
||||
@ -49,6 +50,7 @@ import static org.jooq.impl.Factory.val;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableRecord;
|
||||
import org.jooq.UpdatableRecord;
|
||||
import org.jooq.test.BaseTest;
|
||||
@ -208,4 +210,40 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, D, T, U, I, IPK, T658, T725, T639
|
||||
asList(1, 2, 0, 0, 0),
|
||||
asList(result3.get(0).into(Integer[].class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelationalDivision() {
|
||||
|
||||
// Books and bookstores. There's only one book that is contained in
|
||||
// every bookstore:
|
||||
// ----------------------------------------------------------------
|
||||
int id =
|
||||
create().select()
|
||||
.from(TBookToBookStore()
|
||||
.divideBy(TBookStore())
|
||||
.on(TBookToBookStore_BOOK_STORE_NAME().equal(TBookStore_NAME()))
|
||||
.returning(TBookToBookStore_BOOK_ID()))
|
||||
.fetchOne(0, Integer.class);
|
||||
|
||||
assertEquals(3, id);
|
||||
|
||||
// Test removing some bookstores in nested selects
|
||||
Table<?> notAllBookStores =
|
||||
create().select()
|
||||
.from(TBookStore())
|
||||
.where(TBookStore_NAME().notEqual("Buchhandlung im Volkshaus"))
|
||||
.asTable("not_all_bookstores");
|
||||
|
||||
Result<?> result =
|
||||
create().select()
|
||||
.from(TBookToBookStore()
|
||||
.divideBy(notAllBookStores)
|
||||
.on(TBookToBookStore_BOOK_STORE_NAME().equal(notAllBookStores.getField(TBookStore_NAME())))
|
||||
.returning(TBookToBookStore_BOOK_ID(), field("'abc'").as("abc")))
|
||||
.orderBy(1)
|
||||
.fetch();
|
||||
|
||||
assertEquals(asList((Object) 1, "abc"), asList(result.get(0).intoArray()));
|
||||
assertEquals(asList((Object) 3, "abc"), asList(result.get(1).intoArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1345,6 +1345,11 @@ public abstract class jOOQAbstractTest<
|
||||
new ExoticTests(this).testPivotClause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelationalDivision() throws Exception {
|
||||
new ExoticTests(this).testRelationalDivision();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteListenerOnResultQuery() throws Exception {
|
||||
new ExecuteListenerTests(this).testExecuteListenerOnResultQuery();
|
||||
|
||||
159
jOOQ/src/main/java/org/jooq/DivideByOnConditionStep.java
Normal file
159
jOOQ/src/main/java/org/jooq/DivideByOnConditionStep.java
Normal file
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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 org.jooq.impl.Factory;
|
||||
|
||||
/**
|
||||
* An intermediate type for the construction of a relational division. This type
|
||||
* can be used as a convenience type for connecting more conditions.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public interface DivideByOnConditionStep extends DivideByReturningStep {
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#AND} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep and(Condition condition);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#AND} operator.
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep and(String sql);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#AND} operator.
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String, Object...)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep and(String sql, Object... bindings);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with a negated other one using
|
||||
* the {@link Operator#AND} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep andNot(Condition condition);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with an <code>EXISTS</code>
|
||||
* clause using the {@link Operator#AND} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep andExists(Select<?> select);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with a <code>NOT EXISTS</code>
|
||||
* clause using the {@link Operator#AND} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep andNotExists(Select<?> select);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#OR} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep or(Condition condition);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#OR} operator.
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep or(String sql);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with another one using the
|
||||
* {@link Operator#OR} operator.
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String, Object...)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep or(String sql, Object... bindings);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with a negated other one using
|
||||
* the {@link Operator#OR} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep orNot(Condition condition);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with an <code>EXISTS</code>
|
||||
* clause using the {@link Operator#OR} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep orExists(Select<?> select);
|
||||
|
||||
/**
|
||||
* Combine the currently assembled conditions with a <code>NOT EXISTS</code>
|
||||
* clause using the {@link Operator#OR} operator.
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep orNotExists(Select<?> select);
|
||||
}
|
||||
78
jOOQ/src/main/java/org/jooq/DivideByOnStep.java
Normal file
78
jOOQ/src/main/java/org/jooq/DivideByOnStep.java
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 org.jooq.impl.Factory;
|
||||
|
||||
/**
|
||||
* An intermediate type for the construction of a relational division
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public interface DivideByOnStep {
|
||||
|
||||
/**
|
||||
* Add a division condition to the <code>DIVIDE BY</code> clause
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep on(Condition... conditions);
|
||||
|
||||
/**
|
||||
* Add a division condition to the <code>DIVIDE BY</code> clause
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep on(String sql);
|
||||
|
||||
/**
|
||||
* Add a division condition to the <code>DIVIDE BY</code> clause
|
||||
* <p>
|
||||
* <b>NOTE</b>: When inserting plain SQL into jOOQ objects, you must
|
||||
* guarantee syntax integrity. You may also create the possibility of
|
||||
* malicious SQL injection. Be sure to properly use bind variables and/or
|
||||
* escape literals when concatenated into SQL clauses!
|
||||
*
|
||||
* @see Factory#condition(String, Object...)
|
||||
*/
|
||||
@Support
|
||||
DivideByOnConditionStep on(String sql, Object... bindings);
|
||||
}
|
||||
58
jOOQ/src/main/java/org/jooq/DivideByReturningStep.java
Normal file
58
jOOQ/src/main/java/org/jooq/DivideByReturningStep.java
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 java.util.Collection;
|
||||
|
||||
/**
|
||||
* An intermediate type for the construction of a relational division
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public interface DivideByReturningStep {
|
||||
|
||||
/**
|
||||
* Specify the fields that you want the division to return from the dividend
|
||||
*/
|
||||
@Support
|
||||
Table<Record> returning(Field<?>... fields);
|
||||
|
||||
/**
|
||||
* Specify the fields that you want the division to return from the dividend
|
||||
*/
|
||||
@Support
|
||||
Table<Record> returning(Collection<? extends Field<?>> fields);
|
||||
}
|
||||
@ -132,6 +132,55 @@ public interface Table<R extends Record> extends Type<R>, AliasProvider<Table<R>
|
||||
@Support({ ORACLE })
|
||||
PivotForStep pivot(Collection<? extends Field<?>> aggregateFunctions);
|
||||
|
||||
/**
|
||||
* Create a new <code>TABLE</code> reference from this table, applying
|
||||
* relational division.
|
||||
* <p>
|
||||
* Relational division is the inverse of a cross join operation. The
|
||||
* following is an approximate definition of a relational division:
|
||||
* <code><pre>
|
||||
* Assume the following cross join / cartesian product
|
||||
* C = A × B
|
||||
*
|
||||
* Then it can be said that
|
||||
* A = C ÷ B
|
||||
* B = C ÷ A
|
||||
* </pre></code>
|
||||
* <p>
|
||||
* With jOOQ, you can simplify using relational divisions by using the
|
||||
* following syntax: <code><pre>
|
||||
* C.divideBy(B).on(C.ID.equal(B.C_ID)).returning(C.TEXT)
|
||||
* </pre></code>
|
||||
* <p>
|
||||
* The above roughly translates to <code><pre>
|
||||
* SELECT DISTINCT C.TEXT FROM C "c1"
|
||||
* WHERE NOT EXISTS (
|
||||
* SELECT 1 FROM B
|
||||
* WHERE NOT EXISTS (
|
||||
* SELECT 1 FROM C "c2"
|
||||
* WHERE "c2".TEXT = "c1".TEXT
|
||||
* AND "c2".ID = B.C_ID
|
||||
* )
|
||||
* )
|
||||
* </pre></code>
|
||||
* <p>
|
||||
* Or in plain text: Find those TEXT values in C whose ID's correspond to
|
||||
* all ID's in B.
|
||||
* <p>
|
||||
* For more information about relational division and some nice, real-life
|
||||
* examples, see
|
||||
* <ul>
|
||||
* <li><a
|
||||
* href="http://en.wikipedia.org/wiki/Relational_algebra#Division">http://en.wikipedia.org/wiki/Relational_algebra#Division</a></li>
|
||||
* <li><a href=
|
||||
* "http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/"
|
||||
* >http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/</a></li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This has been observed to work with all dialects
|
||||
*/
|
||||
DivideByOnStep divideBy(Table<?> divisor);
|
||||
|
||||
/**
|
||||
* <code>INNER JOIN</code> a table to this table.
|
||||
*/
|
||||
|
||||
@ -43,6 +43,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.DivideByOnStep;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.Identity;
|
||||
@ -71,7 +72,7 @@ abstract class AbstractTable<R extends Record> extends AbstractType<R> implement
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// TableLike API
|
||||
// XXX: TableLike API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@ -85,7 +86,7 @@ abstract class AbstractTable<R extends Record> extends AbstractType<R> implement
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Table API
|
||||
// XXX: Table API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -137,7 +138,7 @@ abstract class AbstractTable<R extends Record> extends AbstractType<R> implement
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// PIVOT API
|
||||
// XXX: PIVOT API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@ -151,7 +152,16 @@ abstract class AbstractTable<R extends Record> extends AbstractType<R> implement
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// JOIN API
|
||||
// XXX: DIVISION API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final DivideByOnStep divideBy(Table<?> divisor) {
|
||||
return new DivideBy(this, divisor);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: JOIN API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
|
||||
286
jOOQ/src/main/java/org/jooq/impl/DivideBy.java
Normal file
286
jOOQ/src/main/java/org/jooq/impl/DivideBy.java
Normal file
@ -0,0 +1,286 @@
|
||||
/**
|
||||
* 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.impl;
|
||||
|
||||
import static org.jooq.impl.Factory.condition;
|
||||
import static org.jooq.impl.Factory.exists;
|
||||
import static org.jooq.impl.Factory.notExists;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Attachable;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Condition;
|
||||
import org.jooq.ConditionProvider;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DivideByOnConditionStep;
|
||||
import org.jooq.DivideByOnStep;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Operator;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RenderContext;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.Table;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class DivideBy
|
||||
extends AbstractTable<Record>
|
||||
implements
|
||||
DivideByOnStep,
|
||||
DivideByOnConditionStep {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = -4999896035630325449L;
|
||||
|
||||
private final Table<?> dividend;
|
||||
private final Table<?> divisor;
|
||||
private final ConditionProviderImpl condition;
|
||||
private final FieldList returning;
|
||||
|
||||
DivideBy(Table<?> dividend, Table<?> divisor) {
|
||||
super("division");
|
||||
|
||||
this.dividend = dividend;
|
||||
this.divisor = divisor;
|
||||
|
||||
this.condition = new ConditionProviderImpl();
|
||||
this.returning = new FieldList();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: Table API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final Class<? extends Record> getRecordType() {
|
||||
return RecordImpl.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean declaresTables() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
context.sql(table(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {
|
||||
context.bind(table(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the relational division operation into SQL.
|
||||
* <p>
|
||||
* Various nice examples of how this can be achieved are found here: <a
|
||||
* href=
|
||||
* "http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/"
|
||||
* >http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-
|
||||
* sql-of-relational-division/</a>
|
||||
*/
|
||||
private final Table<?> table(Configuration configuration) {
|
||||
Factory create = create(configuration);
|
||||
|
||||
ConditionProviderImpl selfJoin = new ConditionProviderImpl();
|
||||
FieldList select = new FieldList();
|
||||
Table<?> outer = dividend.as("dividend");
|
||||
|
||||
for (Field<?> field : returning) {
|
||||
Field<?> outerField = outer.getField(field);
|
||||
|
||||
// Fields from the RETURNING clause are added AS-IS to the SELECT
|
||||
// statement, if they're not contained in the dividend table
|
||||
if (outerField == null) {
|
||||
select.add(field);
|
||||
}
|
||||
|
||||
// Fields from the RETURNING clause need proper aliasing if they're
|
||||
// contained in the dividend table
|
||||
else {
|
||||
select.add(outerField);
|
||||
selfJoin(selfJoin, outer, dividend, field);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply relational division using double-nested NOT EXISTS clauses
|
||||
// There are more efficient ways to express division in SQL, but those
|
||||
// are hard to simulate with jOOQ
|
||||
return create.selectDistinct(select)
|
||||
.from(outer)
|
||||
.whereNotExists(create
|
||||
.selectOne()
|
||||
.from(divisor)
|
||||
.whereNotExists(create
|
||||
.selectOne()
|
||||
.from(dividend)
|
||||
.where(selfJoin)
|
||||
.and(condition)))
|
||||
.asTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracted method for type-safety
|
||||
*/
|
||||
private final <T> void selfJoin(ConditionProvider selfJoin, Table<?> outer, Table<?> inner, Field<T> field) {
|
||||
Field<T> outerField = outer.getField(field);
|
||||
Field<T> innerField = inner.getField(field);
|
||||
|
||||
if (outerField == null || innerField == null) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
selfJoin.addConditions(outerField.equal(innerField));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Table<Record> as(String alias) {
|
||||
return new TableAlias<Record>(this, alias, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final FieldList getFieldList() {
|
||||
return new FieldList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<Attachable> getAttachables0() {
|
||||
return getAttachables(dividend, divisor, condition, returning);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: DivideBy API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final DivideBy on(Condition... conditions) {
|
||||
condition.addConditions(conditions);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy on(String sql) {
|
||||
and(sql);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy on(String sql, Object... bindings) {
|
||||
and(sql, bindings);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy returning(Field<?>... fields) {
|
||||
return returning(Arrays.asList(fields));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy returning(Collection<? extends Field<?>> fields) {
|
||||
returning.addAll(fields);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy and(Condition c) {
|
||||
condition.addConditions(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy and(String sql) {
|
||||
return and(condition(sql));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy and(String sql, Object... bindings) {
|
||||
return and(condition(sql, bindings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy andNot(Condition c) {
|
||||
return and(c.not());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy andExists(Select<?> select) {
|
||||
return and(exists(select));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy andNotExists(Select<?> select) {
|
||||
return and(notExists(select));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy or(Condition c) {
|
||||
condition.addConditions(Operator.OR, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy or(String sql) {
|
||||
return or(condition(sql));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy or(String sql, Object... bindings) {
|
||||
return or(condition(sql, bindings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy orNot(Condition c) {
|
||||
return or(c.not());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy orExists(Select<?> select) {
|
||||
return or(exists(select));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DivideBy orNotExists(Select<?> select) {
|
||||
return or(notExists(select));
|
||||
}
|
||||
}
|
||||
@ -89,7 +89,7 @@ implements
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Table API
|
||||
// XXX: Table API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@ -318,7 +318,7 @@ implements
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Pivot API
|
||||
// XXX: Pivot API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user