From a32df83fc01365306a2ddf1986bc0eff941d4f1e Mon Sep 17 00:00:00 2001 From: lukaseder Date: Mon, 25 Feb 2019 10:49:19 +0100 Subject: [PATCH] [#2530] Support for row value expressions --- .../java/org/jooq/impl/BetweenCondition.java | 25 +++++++++++--- .../main/java/org/jooq/impl/CursorImpl.java | 18 +++++++--- .../main/java/org/jooq/impl/InCondition.java | 23 +++++++++++++ .../java/org/jooq/impl/IsDistinctFrom.java | 8 ++++- jOOQ/src/main/java/org/jooq/impl/IsNull.java | 11 ++++++- jOOQ/src/main/java/org/jooq/impl/Tools.java | 33 ++++++++++++------- jOOQ/src/main/java/org/jooq/impl/Val.java | 4 +-- 7 files changed, 99 insertions(+), 23 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java index d6140bded8..cc580cee11 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java @@ -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 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(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index b569e2f2b6..df5a30bd77 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -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 extends AbstractCursor implements Cu private final void setValue(AbstractRecord record, Field field, int index) throws SQLException { try { T value; + Field[] nested = null; + Class 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) ((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); diff --git a/jOOQ/src/main/java/org/jooq/impl/InCondition.java b/jOOQ/src/main/java/org/jooq/impl/InCondition.java index a658de5919..26d4865a51 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/InCondition.java @@ -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 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> list = Arrays.asList(values); if (list.size() == 0) { diff --git a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java index f0dfaf6649..912d456923 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java @@ -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 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")))); diff --git a/jOOQ/src/main/java/org/jooq/impl/IsNull.java b/jOOQ/src/main/java/org/jooq/impl/IsNull.java index f408ff0d30..b863cbef1e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsNull.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsNull.java @@ -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 diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 669c518da7..611ee47ba6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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>) field.getType()).valuesRow().fields() : null; } + private static final EmbeddableRecord newInstance(Class> type) { + try { + return type.getConstructor().newInstance(); + } + catch (Exception e) { + throw new MappingException("Cannot create EmbeddableRecord type", e); + } + } + /** * Flatten out an {@link EmbeddableTableField}. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java index fecd452098..51ec8a5250 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Val.java +++ b/jOOQ/src/main/java/org/jooq/impl/Val.java @@ -58,7 +58,7 @@ import org.jooq.tools.StringUtils; */ final class Val extends AbstractParam { - private static final long serialVersionUID = 6807729087019209084L; + private static final long serialVersionUID = 6807729087019209084L; Val(T value, DataType type) { super(value, type); @@ -74,7 +74,7 @@ final class Val extends AbstractParam { @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);