Skip to content

Commit

Permalink
[#2530] Support for row value expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaseder committed Feb 25, 2019
1 parent 54c5bc3 commit a32df83
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 25 deletions.
25 changes: 21 additions & 4 deletions jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java
Expand Up @@ -64,11 +64,14 @@
// ... // ...
// ... // ...
import static org.jooq.impl.DSL.nullSafe; import static org.jooq.impl.DSL.nullSafe;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.val; import static org.jooq.impl.DSL.val;
import static org.jooq.impl.Keywords.K_AND; import static org.jooq.impl.Keywords.K_AND;
import static org.jooq.impl.Keywords.K_BETWEEN; import static org.jooq.impl.Keywords.K_BETWEEN;
import static org.jooq.impl.Keywords.K_NOT; import static org.jooq.impl.Keywords.K_NOT;
import static org.jooq.impl.Keywords.K_SYMMETRIC; import static org.jooq.impl.Keywords.K_SYMMETRIC;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;


import java.util.EnumSet; import java.util.EnumSet;


Expand All @@ -79,6 +82,7 @@
import org.jooq.Context; import org.jooq.Context;
import org.jooq.Field; import org.jooq.Field;
import org.jooq.QueryPartInternal; import org.jooq.QueryPartInternal;
import org.jooq.RowN;
import org.jooq.SQLDialect; import org.jooq.SQLDialect;


/** /**
Expand Down Expand Up @@ -133,10 +137,23 @@ public final Clause[] clauses(Context<?> ctx) {
} }


private final QueryPartInternal delegate(Configuration configuration) { private final QueryPartInternal delegate(Configuration configuration) {
if (symmetric && NO_SUPPORT_SYMMETRIC.contains(configuration.family())) if (isEmbeddable(field) && isEmbeddable(minValue) && isEmbeddable(maxValue)) {
return not RowN f = row(embeddedFields(field));
? (QueryPartInternal) field.notBetween(minValue, maxValue).and(field.notBetween(maxValue, minValue)) RowN min = row(embeddedFields(minValue));
: (QueryPartInternal) field.between(minValue, maxValue).or(field.between(maxValue, minValue)); RowN max = row(embeddedFields(maxValue));

return (QueryPartInternal) (not
? symmetric
? f.notBetweenSymmetric(min).and(max)
: f.notBetween(min).and(max)
: symmetric
? f.betweenSymmetric(min).and(max)
: f.between(min).and(max));
}
else if (symmetric && NO_SUPPORT_SYMMETRIC.contains(configuration.family()))
return (QueryPartInternal) (not
? field.notBetween(minValue, maxValue).and(field.notBetween(maxValue, minValue))
: field.between(minValue, maxValue).or(field.between(maxValue, minValue)));
else else
return new Native(); return new Native();
} }
Expand Down
18 changes: 14 additions & 4 deletions jOOQ/src/main/java/org/jooq/impl/CursorImpl.java
Expand Up @@ -39,6 +39,7 @@


import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
// ... // ...
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.recordFactory; import static org.jooq.impl.Tools.recordFactory;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_LOCK_ROWS_FOR_UPDATE; import static org.jooq.impl.Tools.BooleanDataKey.DATA_LOCK_ROWS_FOR_UPDATE;


Expand Down Expand Up @@ -1699,14 +1700,23 @@ public AbstractRecord operate(AbstractRecord record) throws SQLException {
private final <T> void setValue(AbstractRecord record, Field<T> field, int index) throws SQLException { private final <T> void setValue(AbstractRecord record, Field<T> field, int index) throws SQLException {
try { try {
T value; T value;
Field<?>[] nested = null;
Class<? extends AbstractRecord> recordType = null;


if (field instanceof RowField) { if (field instanceof RowField) {
Field<?>[] emulatedFields = ((RowField<?, ?>) field).emulatedFields(); nested = ((RowField<?, ?>) field).emulatedFields();
recordType = RecordImpl.class;
}
else if (field instanceof EmbeddableTableField) {
nested = embeddedFields(field);
recordType = (Class<AbstractRecord>) ((EmbeddableTableField<?, ?>) field).recordType;
}


value = (T) Tools.newRecord(true, RecordImpl.class, emulatedFields, ((DefaultExecuteContext) ctx).originalConfiguration()) if (nested != null) {
.operate(new CursorRecordInitialiser(emulatedFields, offset + index)); value = (T) Tools.newRecord(true, recordType, nested, ((DefaultExecuteContext) ctx).originalConfiguration())
.operate(new CursorRecordInitialiser(nested, offset + index));


offset += emulatedFields.length - 1; offset += nested.length - 1;
} }
else { else {
rsContext.index(offset + index + 1); rsContext.index(offset + index + 1);
Expand Down
23 changes: 23 additions & 0 deletions jOOQ/src/main/java/org/jooq/impl/InCondition.java
Expand Up @@ -56,9 +56,12 @@
// ... // ...
import static org.jooq.conf.ParamType.INDEXED; import static org.jooq.conf.ParamType.INDEXED;
import static org.jooq.impl.DSL.falseCondition; import static org.jooq.impl.DSL.falseCondition;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.trueCondition; import static org.jooq.impl.DSL.trueCondition;
import static org.jooq.impl.Keywords.K_AND; import static org.jooq.impl.Keywords.K_AND;
import static org.jooq.impl.Keywords.K_OR; import static org.jooq.impl.Keywords.K_OR;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;
import static org.jooq.tools.StringUtils.defaultIfNull; import static org.jooq.tools.StringUtils.defaultIfNull;


import java.util.AbstractList; import java.util.AbstractList;
Expand All @@ -70,6 +73,7 @@
import org.jooq.Comparator; import org.jooq.Comparator;
import org.jooq.Context; import org.jooq.Context;
import org.jooq.Field; import org.jooq.Field;
import org.jooq.RowN;
import org.jooq.SQLDialect; import org.jooq.SQLDialect;


/** /**
Expand Down Expand Up @@ -100,6 +104,25 @@ public final Clause[] clauses(Context<?> ctx) {


@Override @Override
public final void accept(Context<?> ctx) { public final void accept(Context<?> ctx) {
if (isEmbeddable(field))
if (comparator == IN)
ctx.visit(row(embeddedFields(field)).in(rows()));
else
ctx.visit(row(embeddedFields(field)).notIn(rows()));
else
accept0(ctx);
}

private final RowN[] rows() {
RowN[] result = new RowN[values.length];

for (int i = 0; i < values.length; i++)
result[i] = row(embeddedFields(values[i]));

return result;
}

private final void accept0(Context<?> ctx) {
List<Field<?>> list = Arrays.asList(values); List<Field<?>> list = Arrays.asList(values);


if (list.size() == 0) { if (list.size() == 0) {
Expand Down
8 changes: 7 additions & 1 deletion jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java
Expand Up @@ -61,7 +61,10 @@
import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.exists; import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.notExists; 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.select;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;


import java.util.EnumSet; import java.util.EnumSet;


Expand Down Expand Up @@ -110,11 +113,14 @@ public final void accept(Context<?> ctx) {
* clause. * clause.
*/ */
private final QueryPartInternal delegate(Configuration configuration) { private final QueryPartInternal delegate(Configuration configuration) {
if (isEmbeddable(lhs) && isEmbeddable(rhs)) {
return (QueryPartInternal) row(embeddedFields(lhs)).compare(comparator, row(embeddedFields(rhs)));
}


// [#3511] These dialects need to emulate the IS DISTINCT FROM predicate, // [#3511] These dialects need to emulate the IS DISTINCT FROM predicate,
// optimally using INTERSECT... // optimally using INTERSECT...
// [#7222] [#7224] Make sure the columns are aliased // [#7222] [#7224] Make sure the columns are aliased
if (EMULATE_DISTINCT_PREDICATE.contains(configuration.family())) { else if (EMULATE_DISTINCT_PREDICATE.contains(configuration.family())) {
return (comparator == IS_DISTINCT_FROM) return (comparator == IS_DISTINCT_FROM)
? (QueryPartInternal) notExists(select(lhs.as("x")).intersect(select(rhs.as("x")))) ? (QueryPartInternal) notExists(select(lhs.as("x")).intersect(select(rhs.as("x"))))
: (QueryPartInternal) exists(select(lhs.as("x")).intersect(select(rhs.as("x")))); : (QueryPartInternal) exists(select(lhs.as("x")).intersect(select(rhs.as("x"))));
Expand Down
11 changes: 10 additions & 1 deletion jOOQ/src/main/java/org/jooq/impl/IsNull.java
Expand Up @@ -41,8 +41,11 @@
import static org.jooq.Clause.CONDITION; import static org.jooq.Clause.CONDITION;
import static org.jooq.Clause.CONDITION_IS_NOT_NULL; import static org.jooq.Clause.CONDITION_IS_NOT_NULL;
import static org.jooq.Clause.CONDITION_IS_NULL; import static org.jooq.Clause.CONDITION_IS_NULL;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.Keywords.K_IS_NOT_NULL; import static org.jooq.impl.Keywords.K_IS_NOT_NULL;
import static org.jooq.impl.Keywords.K_IS_NULL; import static org.jooq.impl.Keywords.K_IS_NULL;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;


import org.jooq.Clause; import org.jooq.Clause;
import org.jooq.Context; import org.jooq.Context;
Expand All @@ -67,7 +70,13 @@ final class IsNull extends AbstractCondition {


@Override @Override
public final void accept(Context<?> ctx) { public final void accept(Context<?> ctx) {
ctx.visit(field).sql(' ').visit(isNull ? K_IS_NULL : K_IS_NOT_NULL); if (isEmbeddable(field))
if (isNull)
ctx.visit(row(embeddedFields(field)).isNull());
else
ctx.visit(row(embeddedFields(field)).isNotNull());
else
ctx.visit(field).sql(' ').visit(isNull ? K_IS_NULL : K_IS_NOT_NULL);
} }


@Override @Override
Expand Down
37 changes: 24 additions & 13 deletions jOOQ/src/main/java/org/jooq/impl/Tools.java
Expand Up @@ -4778,22 +4778,33 @@ static final boolean isEmpty(Object[] array) {
return array == null || array.length == 0; return array == null || array.length == 0;
} }


static final boolean isEmbeddable(Object object) { static final boolean isEmbeddable(Field<?> field) {
return object instanceof EmbeddableTableField return field instanceof EmbeddableTableField
|| object instanceof Val && ((Val<?>) object).value instanceof EmbeddableRecord || field instanceof Val && ((Val<?>) field).value instanceof EmbeddableRecord;
|| object instanceof EmbeddableRecord; }
}

@SuppressWarnings("unchecked")
static final Field<?>[] embeddedFields(Object object) { static final Field<?>[] embeddedFields(Field<?> field) {
return object instanceof EmbeddableTableField return field instanceof EmbeddableTableField
? ((EmbeddableTableField<?, ?>) object).fields ? ((EmbeddableTableField<?, ?>) field).fields
: object instanceof Val && ((Val<?>) object).value instanceof EmbeddableRecord : field instanceof Val && ((Val<?>) field).value instanceof EmbeddableRecord
? ((EmbeddableRecord<?>) ((Val<?>) object).value).valuesRow().fields() ? ((EmbeddableRecord<?>) ((Val<?>) field).value).valuesRow().fields()
: object instanceof EmbeddableRecord
? ((EmbeddableRecord<?>) object).valuesRow().fields() // It's an embeddable type, but it is null
: field instanceof Val && EmbeddableRecord.class.isAssignableFrom(field.getType())
? newInstance((Class<? extends EmbeddableRecord<?>>) field.getType()).valuesRow().fields()
: null; : null;
} }


private static final EmbeddableRecord<?> newInstance(Class<? extends EmbeddableRecord<?>> type) {
try {
return type.getConstructor().newInstance();
}
catch (Exception e) {
throw new MappingException("Cannot create EmbeddableRecord type", e);
}
}

/** /**
* Flatten out an {@link EmbeddableTableField}. * Flatten out an {@link EmbeddableTableField}.
*/ */
Expand Down
4 changes: 2 additions & 2 deletions jOOQ/src/main/java/org/jooq/impl/Val.java
Expand Up @@ -58,7 +58,7 @@
*/ */
final class Val<T> extends AbstractParam<T> { final class Val<T> extends AbstractParam<T> {


private static final long serialVersionUID = 6807729087019209084L; private static final long serialVersionUID = 6807729087019209084L;


Val(T value, DataType<T> type) { Val(T value, DataType<T> type) {
super(value, type); super(value, type);
Expand All @@ -74,7 +74,7 @@ final class Val<T> extends AbstractParam<T> {


@Override @Override
public void accept(Context<?> ctx) { public void accept(Context<?> ctx) {
if (value instanceof EmbeddableRecord) { if (EmbeddableRecord.class.isAssignableFrom(getType())) {
Object previous = ctx.data(DATA_LIST_ALREADY_INDENTED); Object previous = ctx.data(DATA_LIST_ALREADY_INDENTED);


ctx.data(DATA_LIST_ALREADY_INDENTED, true); ctx.data(DATA_LIST_ALREADY_INDENTED, true);
Expand Down

0 comments on commit a32df83

Please sign in to comment.