[#2530] Support for row value expressions
This commit is contained in:
parent
54c5bc3138
commit
a32df83fc0
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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"))));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}.
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user