[#2530] Support for row value expressions

This commit is contained in:
lukaseder 2019-02-25 10:49:19 +01:00
parent 54c5bc3138
commit a32df83fc0
7 changed files with 99 additions and 23 deletions

View File

@ -64,11 +64,14 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
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.Keywords.K_AND;
import static org.jooq.impl.Keywords.K_BETWEEN;
import static org.jooq.impl.Keywords.K_NOT;
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;
@ -79,6 +82,7 @@ import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.QueryPartInternal;
import org.jooq.RowN;
import org.jooq.SQLDialect;
/**
@ -133,10 +137,23 @@ final class BetweenCondition<T> extends AbstractCondition implements BetweenAndS
}
private final QueryPartInternal delegate(Configuration configuration) {
if (symmetric && NO_SUPPORT_SYMMETRIC.contains(configuration.family()))
return not
? (QueryPartInternal) field.notBetween(minValue, maxValue).and(field.notBetween(maxValue, minValue))
: (QueryPartInternal) field.between(minValue, maxValue).or(field.between(maxValue, minValue));
if (isEmbeddable(field) && isEmbeddable(minValue) && isEmbeddable(maxValue)) {
RowN f = row(embeddedFields(field));
RowN min = row(embeddedFields(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
return new Native();
}

View File

@ -39,6 +39,7 @@ package org.jooq.impl;
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.BooleanDataKey.DATA_LOCK_ROWS_FOR_UPDATE;
@ -1699,14 +1700,23 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> implements Cu
private final <T> void setValue(AbstractRecord record, Field<T> field, int index) throws SQLException {
try {
T value;
Field<?>[] nested = null;
Class<? extends AbstractRecord> recordType = null;
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())
.operate(new CursorRecordInitialiser(emulatedFields, offset + index));
if (nested != null) {
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 {
rsContext.index(offset + index + 1);

View File

@ -56,9 +56,12 @@ import static org.jooq.SQLDialect.FIREBIRD;
// ...
import static org.jooq.conf.ParamType.INDEXED;
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.Keywords.K_AND;
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 java.util.AbstractList;
@ -70,6 +73,7 @@ import org.jooq.Clause;
import org.jooq.Comparator;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.RowN;
import org.jooq.SQLDialect;
/**
@ -100,6 +104,25 @@ final class InCondition<T> extends AbstractCondition {
@Override
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);
if (list.size() == 0) {

View File

@ -61,7 +61,10 @@ import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.impl.DSL.condition;
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.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;
import java.util.EnumSet;
@ -110,11 +113,14 @@ final class IsDistinctFrom<T> extends AbstractCondition {
* clause.
*/
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,
// optimally using INTERSECT...
// [#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)
? (QueryPartInternal) notExists(select(lhs.as("x")).intersect(select(rhs.as("x"))))
: (QueryPartInternal) exists(select(lhs.as("x")).intersect(select(rhs.as("x"))));

View File

@ -41,8 +41,11 @@ package org.jooq.impl;
import static org.jooq.Clause.CONDITION;
import static org.jooq.Clause.CONDITION_IS_NOT_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_NULL;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.isEmbeddable;
import org.jooq.Clause;
import org.jooq.Context;
@ -67,7 +70,13 @@ final class IsNull extends AbstractCondition {
@Override
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

View File

@ -4778,22 +4778,33 @@ final class Tools {
return array == null || array.length == 0;
}
static final boolean isEmbeddable(Object object) {
return object instanceof EmbeddableTableField
|| object instanceof Val && ((Val<?>) object).value instanceof EmbeddableRecord
|| object instanceof EmbeddableRecord;
static final boolean isEmbeddable(Field<?> field) {
return field instanceof EmbeddableTableField
|| field instanceof Val && ((Val<?>) field).value instanceof EmbeddableRecord;
}
static final Field<?>[] embeddedFields(Object object) {
return object instanceof EmbeddableTableField
? ((EmbeddableTableField<?, ?>) object).fields
: object instanceof Val && ((Val<?>) object).value instanceof EmbeddableRecord
? ((EmbeddableRecord<?>) ((Val<?>) object).value).valuesRow().fields()
: object instanceof EmbeddableRecord
? ((EmbeddableRecord<?>) object).valuesRow().fields()
@SuppressWarnings("unchecked")
static final Field<?>[] embeddedFields(Field<?> field) {
return field instanceof EmbeddableTableField
? ((EmbeddableTableField<?, ?>) field).fields
: field instanceof Val && ((Val<?>) field).value instanceof EmbeddableRecord
? ((EmbeddableRecord<?>) ((Val<?>) field).value).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;
}
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}.
*/

View File

@ -58,7 +58,7 @@ import org.jooq.tools.StringUtils;
*/
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) {
super(value, type);
@ -74,7 +74,7 @@ final class Val<T> extends AbstractParam<T> {
@Override
public void accept(Context<?> ctx) {
if (value instanceof EmbeddableRecord) {
if (EmbeddableRecord.class.isAssignableFrom(getType())) {
Object previous = ctx.data(DATA_LIST_ALREADY_INDENTED);
ctx.data(DATA_LIST_ALREADY_INDENTED, true);