[#2665] Implement SPI for RenderContext and BindContext listening to

allow for custom SQL transformation

 * Added tests for IN predicate
 * Added tests for EXISTS predicate
This commit is contained in:
Lukas Eder 2013-08-08 17:47:46 +02:00
parent 1a5d8e13c3
commit a79b7bfeb0
5 changed files with 198 additions and 9 deletions

View File

@ -69,7 +69,7 @@ public enum Clause {
/**
* A complete table reference.
* <p>
* This "clause" surrounds a complete table reference as it can be encountered
* This clause surrounds a complete table reference as it can be encountered
* in
* <ul>
* <li> {@link #SELECT_FROM}</li>
@ -119,31 +119,128 @@ public enum Clause {
// Clauses used in a any type of statement to model condition references
// -------------------------------------------------------------------------
/**
* A condition expression.
*/
CONDITION,
/**
* A <code>NULL</code> condition.
* <p>
* This clause surrounds a {@link #FIELD}.
*/
CONDITION_IS_NULL,
/**
* A <code>NOT NULL</code> condition.
* <p>
* This clause surrounds a {@link #FIELD}.
*/
CONDITION_IS_NOT_NULL,
// TODO: Should operators be distinguished?
// - LIKE predicate
// - Subselect predicates
// - RVE predicates
// - Quantified predicates
CONDITION_COMPARISON,
/**
* A <code>BEWEEN</code> condition.
* <p>
* This clause surrounds three {@link #FIELD} clauses.
*/
CONDITION_BETWEEN,
/**
* A <code>BEWEEN SYMMETRIC</code> condition.
* <p>
* This clause surrounds three {@link #FIELD} clauses.
*/
CONDITION_BETWEEN_SYMMETRIC,
/**
* A <code>NOT BEWEEN</code> condition.
* <p>
* This clause surrounds three {@link #FIELD} clauses.
*/
CONDITION_NOT_BETWEEN,
/**
* A <code>NOT BEWEEN SYMMETRIC</code> condition.
* <p>
* This clause surrounds three {@link #FIELD} clauses.
*/
CONDITION_NOT_BETWEEN_SYMMETRIC,
/**
* A <code>DISTINCT</code> condition.
* <p>
* This clause surrounds two {@link #FIELD} clauses.
*/
CONDITION_IS_DISTINCT,
/**
* A <code>NOT DISTINCT</code> condition.
* <p>
* This clause surrounds two {@link #FIELD} clauses.
*/
CONDITION_IS_NOT_DISTINCT,
/**
* An <code>OVERLAPS</code> condition.
* <p>
* This clause surrounds two {@link #FIELD} clauses.
*/
CONDITION_OVERLAPS,
/**
* A combined condition using <code>AND</code>.
* <p>
* This clause surrounds several {@link #CONDITION} clauses.
*/
CONDITION_AND,
/**
* A combined condition using <code>OR</code>.
* <p>
* This clause surrounds several {@link #CONDITION} clauses.
*/
CONDITION_OR,
/**
* A <code>NOT</code> condition.
* <p>
* This clause surrounds a {@link #CONDITION} clause.
*/
CONDITION_NOT,
/**
* An <code>IN</code> condition.
* <p>
* This clause surrounds two or more {@link #FIELD} clauses.
*/
CONDITION_IN,
/**
* A <code>NOT IN</code> condition.
* <p>
* This clause surrounds two or more {@link #FIELD} clauses.
*/
CONDITION_NOT_IN,
/**
* An <code>EXISTS</code> condition.
* <p>
* This clause surrounds a {@link #SELECT} clause.
*/
CONDITION_EXISTS,
/**
* A <code>NOT EXISTS</code> condition.
* <p>
* This clause surrounds a {@link #SELECT} clause.
*/
CONDITION_NOT_EXISTS,
// -------------------------------------------------------------------------
@ -153,7 +250,7 @@ public enum Clause {
/**
* A complete <code>SELECT</code> statement or a subselect.
* <p>
* This "clause" surrounds a complete <code>SELECT</code> statement, a
* This clause surrounds a complete <code>SELECT</code> statement, a
* subselect, or a set operation, such as
* <ul>
* <li> {@link #SELECT_UNION}</li>
@ -301,7 +398,7 @@ public enum Clause {
* This clause surrounds
* <ul>
* <li>the <code>RETURNING</code> keyword</li>
* <li>several {@link #FIELD} "clauses"</li>
* <li>several {@link #FIELD} clauses</li>
* </ul>
*/
UPDATE_RETURNING,

View File

@ -45,7 +45,6 @@ import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Param;
import org.jooq.Query;
import org.jooq.QueryPartInternal;
import org.jooq.RenderContext;
import org.jooq.conf.ParamType;
@ -100,10 +99,8 @@ abstract class AbstractDelegatingQuery<Q extends Query> extends AbstractQueryPar
@Override
public final Clause[] clauses(Context<?> ctx) {
if (delegate instanceof QueryPartInternal) {
return ((QueryPartInternal) delegate).clauses(null);
}
// Delegate queries don't emit clauses themselves.
return null;
}

View File

@ -78,7 +78,7 @@ class SQLQuery extends AbstractQuery {
@Override
public final Clause[] clauses(Context<?> ctx) {
if (delegate instanceof QueryPartInternal) {
return ((QueryPartInternal) delegate).clauses(null);
return ((QueryPartInternal) delegate).clauses(ctx);
}
return null;

View File

@ -84,7 +84,7 @@ class SQLResultQuery extends AbstractResultQuery<Record> {
@Override
public final Clause[] clauses(Context<?> ctx) {
if (delegate instanceof QueryPartInternal) {
return ((QueryPartInternal) delegate).clauses(null);
return ((QueryPartInternal) delegate).clauses(ctx);
}
return null;

View File

@ -43,15 +43,27 @@ import static org.jooq.Clause.CONDITION_AND;
import static org.jooq.Clause.CONDITION_BETWEEN;
import static org.jooq.Clause.CONDITION_BETWEEN_SYMMETRIC;
import static org.jooq.Clause.CONDITION_COMPARISON;
import static org.jooq.Clause.CONDITION_EXISTS;
import static org.jooq.Clause.CONDITION_IN;
import static org.jooq.Clause.CONDITION_IS_NOT_NULL;
import static org.jooq.Clause.CONDITION_IS_NULL;
import static org.jooq.Clause.CONDITION_NOT_BETWEEN;
import static org.jooq.Clause.CONDITION_NOT_BETWEEN_SYMMETRIC;
import static org.jooq.Clause.CONDITION_NOT_EXISTS;
import static org.jooq.Clause.CONDITION_NOT_IN;
import static org.jooq.Clause.CONDITION_OR;
import static org.jooq.Clause.FIELD;
import static org.jooq.Clause.FIELD_REFERENCE;
import static org.jooq.Clause.FIELD_ROW;
import static org.jooq.Clause.FIELD_VALUE;
import static org.jooq.Clause.SELECT;
import static org.jooq.Clause.SELECT_CONNECT_BY;
import static org.jooq.Clause.SELECT_FROM;
import static org.jooq.Clause.SELECT_GROUP_BY;
import static org.jooq.Clause.SELECT_HAVING;
import static org.jooq.Clause.SELECT_ORDER_BY;
import static org.jooq.Clause.SELECT_SELECT;
import static org.jooq.Clause.SELECT_START_WITH;
import static org.jooq.Clause.SELECT_WHERE;
import static org.jooq.Clause.TABLE;
import static org.jooq.Clause.TABLE_REFERENCE;
@ -62,8 +74,11 @@ import static org.jooq.Clause.UPDATE_SET_ASSIGNMENT;
import static org.jooq.Clause.UPDATE_UPDATE;
import static org.jooq.Clause.UPDATE_WHERE;
import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.notExists;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.selectOne;
import static org.jooq.impl.DSL.using;
import static org.jooq.impl.DSL.val;
import static org.jooq.impl.DefaultVisitListenerProvider.providers;
@ -377,6 +392,86 @@ public class VisitContextTest extends AbstractTest {
));
}
@Test
public void test_CONDITION_IN() {
ctx.render(FIELD_ID1.in(1, 2));
assertEvents(asList(
asList(CONDITION),
asList(CONDITION, CONDITION_IN),
asList(CONDITION, CONDITION_IN, FIELD),
asList(CONDITION, CONDITION_IN, FIELD, FIELD_REFERENCE),
asList(CONDITION, CONDITION_IN, FIELD),
asList(CONDITION, CONDITION_IN, FIELD, FIELD_VALUE),
asList(CONDITION, CONDITION_IN, FIELD),
asList(CONDITION, CONDITION_IN, FIELD, FIELD_VALUE)
));
}
@Test
public void test_CONDITION_NOT_IN() {
ctx.render(FIELD_ID1.notIn(1, 2));
assertEvents(asList(
asList(CONDITION),
asList(CONDITION, CONDITION_NOT_IN),
asList(CONDITION, CONDITION_NOT_IN, FIELD),
asList(CONDITION, CONDITION_NOT_IN, FIELD, FIELD_REFERENCE),
asList(CONDITION, CONDITION_NOT_IN, FIELD),
asList(CONDITION, CONDITION_NOT_IN, FIELD, FIELD_VALUE),
asList(CONDITION, CONDITION_NOT_IN, FIELD),
asList(CONDITION, CONDITION_NOT_IN, FIELD, FIELD_VALUE)
));
}
@Test
public void test_CONDITION_EXISTS() {
// Omit "dual" with Postgres
ctx.configuration().set(POSTGRES);
ctx.render(exists(selectOne()));
assertEvents(asList(
asList(CONDITION),
asList(CONDITION, CONDITION_EXISTS),
asList(CONDITION, CONDITION_EXISTS, SELECT),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_SELECT),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_SELECT, FIELD),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_SELECT, FIELD, FIELD_VALUE),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_FROM),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_WHERE),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_START_WITH),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_CONNECT_BY),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_GROUP_BY),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_HAVING),
asList(CONDITION, CONDITION_EXISTS, SELECT, SELECT_ORDER_BY)
));
}
@Test
public void test_CONDITION_NOT_EXISTS() {
// Omit "dual" with Postgres
ctx.configuration().set(POSTGRES);
ctx.render(notExists(selectOne()));
assertEvents(asList(
asList(CONDITION),
asList(CONDITION, CONDITION_NOT_EXISTS),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_SELECT),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_SELECT, FIELD),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_SELECT, FIELD, FIELD_VALUE),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_FROM),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_WHERE),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_START_WITH),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_CONNECT_BY),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_GROUP_BY),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_HAVING),
asList(CONDITION, CONDITION_NOT_EXISTS, SELECT, SELECT_ORDER_BY)
));
}
@Test
public void test_CONDITION_BETWEEN() {
ctx.render(FIELD_ID1.between(1).and(2));