diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 0f35003e47..ef7663840c 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -151,6 +151,7 @@ extends GroupField, OrderField, FieldOrRow, + FieldOrRowOrSelect, FieldOrConstraint, TableElement { diff --git a/jOOQ/src/main/java/org/jooq/FieldOrRowOrSelect.java b/jOOQ/src/main/java/org/jooq/FieldOrRowOrSelect.java new file mode 100644 index 0000000000..c0ecb94ec0 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/FieldOrRowOrSelect.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +/** + * A common base type for {@link Field}, {@link Row}, and {@link Select} where + * DSL API accepts all types alike. + *

+ * Instances of this type cannot be created directly, only of its subtypes. + * + * @author Lukas Eder + */ +public /* sealed */ interface FieldOrRowOrSelect +extends + QueryPart +/* permits + Field, + Row, + Select */ +{ + +} diff --git a/jOOQ/src/main/java/org/jooq/Row.java b/jOOQ/src/main/java/org/jooq/Row.java index 8e307d91fb..6978830fe3 100644 --- a/jOOQ/src/main/java/org/jooq/Row.java +++ b/jOOQ/src/main/java/org/jooq/Row.java @@ -80,7 +80,12 @@ import org.jetbrains.annotations.ApiStatus.Experimental; * * @author Lukas Eder */ -public /* non-sealed */ interface Row extends Fields, FieldOrRow { +public /* non-sealed */ interface Row +extends + Fields, + FieldOrRow, + FieldOrRowOrSelect +{ /** * Get the degree of this row value expression. diff --git a/jOOQ/src/main/java/org/jooq/Select.java b/jOOQ/src/main/java/org/jooq/Select.java index 20f5d34c08..250c0164f4 100644 --- a/jOOQ/src/main/java/org/jooq/Select.java +++ b/jOOQ/src/main/java/org/jooq/Select.java @@ -97,7 +97,13 @@ import org.jetbrains.annotations.ApiStatus.Experimental; * @param The record type being returned by this query * @author Lukas Eder */ -public /* non-sealed */ interface Select extends ResultQuery, TableLike, FieldLike { +public /* non-sealed */ interface Select +extends + ResultQuery, + TableLike, + FieldLike, + FieldOrRowOrSelect +{ /** * Apply the UNION set operation. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java index 4456daa4a8..5c1be4bcc8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java @@ -46,7 +46,6 @@ import org.jooq.CloseableQuery; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Param; -import org.jooq.Query; import org.jooq.Record; import org.jooq.conf.ParamType; import org.jooq.impl.QOM.UProxy; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPartMap.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPartMap.java index 0b6b0ebb4f..21733c5d68 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPartMap.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPartMap.java @@ -49,8 +49,11 @@ import org.jooq.QueryPart; * @author Lukas Eder */ abstract class AbstractQueryPartMap -extends AbstractQueryPart -implements Map { +extends + AbstractQueryPart +implements + Map +{ private final Map map; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java index 546a129218..3a8acac997 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java @@ -43,10 +43,11 @@ import org.jooq.Configuration; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.FieldOrRow; +import org.jooq.FieldOrRowOrSelect; import org.jooq.Record; import org.jooq.StoreQuery; import org.jooq.Table; -import org.jooq.impl.QOM.UEmpty; import org.jooq.impl.QOM.UTransient; /** @@ -54,13 +55,18 @@ import org.jooq.impl.QOM.UTransient; * * @author Lukas Eder */ -abstract class AbstractStoreQuery extends AbstractDMLQuery implements StoreQuery { +abstract class AbstractStoreQuery +extends + AbstractDMLQuery +implements + StoreQuery +{ AbstractStoreQuery(Configuration configuration, WithImpl with, Table table) { super(configuration, with, table); } - protected abstract Map, Field> getValues(); + protected abstract Map getValues(); @SuppressWarnings({ "unchecked", "rawtypes" }) @Override @@ -87,7 +93,7 @@ abstract class AbstractStoreQuery extends AbstractDMLQuery else addValue(new UnknownField(getValues().size()), value); else - getValues().put(field, Tools.field(value, field)); + getValues().put((K) field, (V) Tools.field(value, field)); } final void addValue(Field field, int index, Field value) { @@ -97,7 +103,7 @@ abstract class AbstractStoreQuery extends AbstractDMLQuery else addValue(new UnknownField(getValues().size()), value); else - getValues().put(field, Tools.field(value, field)); + getValues().put((K) field, (V) Tools.field(value, field)); } static class UnknownField extends AbstractField implements UTransient { diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java index 206bec497e..b7606149b2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java @@ -37,25 +37,47 @@ */ package org.jooq.impl; +import static org.jooq.Clause.UPDATE_SET_ASSIGNMENT; +// ... +// ... +// ... +// ... +// ... +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; // ... // ... import static org.jooq.SQLDialect.POSTGRES; // ... // ... +// ... +// ... import static org.jooq.SQLDialect.SQLITE; // ... // ... +// ... +// ... import static org.jooq.SQLDialect.YUGABYTEDB; import static org.jooq.conf.WriteIfReadonly.IGNORE; import static org.jooq.conf.WriteIfReadonly.THROW; +import static org.jooq.impl.DSL.name; +import static org.jooq.impl.DSL.select; +import static org.jooq.impl.DSL.table; import static org.jooq.impl.DSL.when; +import static org.jooq.impl.Keywords.K_ROW; import static org.jooq.impl.Tools.anyMatch; import static org.jooq.impl.Tools.collect; +import static org.jooq.impl.Tools.fieldName; import static org.jooq.impl.Tools.filter; import static org.jooq.impl.Tools.flattenEntrySet; +import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.row0; +import static org.jooq.impl.Tools.unqualified; +import static org.jooq.impl.Tools.visitSubquery; import static org.jooq.impl.Tools.DataKey.DATA_ON_DUPLICATE_KEY_WHERE; +import java.util.List; import java.util.Map; import java.util.Set; @@ -63,10 +85,13 @@ import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Context; import org.jooq.Field; +import org.jooq.FieldOrRow; +import org.jooq.FieldOrRowOrSelect; // ... import org.jooq.RenderContext.CastMode; import org.jooq.Row; import org.jooq.SQLDialect; +import org.jooq.Select; import org.jooq.Table; import org.jooq.exception.DataTypeException; import org.jooq.impl.QOM.UNotYetImplemented; @@ -74,9 +99,18 @@ import org.jooq.impl.QOM.UNotYetImplemented; /** * @author Lukas Eder */ -final class FieldMapForUpdate extends AbstractQueryPartMap, Field> implements UNotYetImplemented { - private static final Set CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - private static final Set NO_SUPPORT_QUALIFY = SQLDialect.supportedBy(POSTGRES, SQLITE, YUGABYTEDB); +final class FieldMapForUpdate extends AbstractQueryPartMap implements UNotYetImplemented { + + static final Set CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); + static final Set NO_SUPPORT_QUALIFY = SQLDialect.supportedBy(POSTGRES, SQLITE, YUGABYTEDB); + + + + + + + static final Set SUPPORT_RVE_SET = SQLDialect.supportedBy(H2, HSQLDB, POSTGRES, YUGABYTEDB); + static final Set REQUIRE_RVE_ROW_CLAUSE = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); private final Table table; private final Clause assignmentClause; @@ -86,7 +120,6 @@ final class FieldMapForUpdate extends AbstractQueryPartMap, Field> i this.assignmentClause = assignmentClause; } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public final void accept(Context ctx) { if (size() > 0) { @@ -106,24 +139,29 @@ final class FieldMapForUpdate extends AbstractQueryPartMap, Field> i if (!CASTS_NEEDED.contains(ctx.dialect())) ctx.castMode(CastMode.NEVER); - for (Entry, Field> entry : removeReadonly(ctx, flattenEntrySet(entrySet(), true))) { + entryLoop: + for (Entry entry : flattenEntrySet(entrySet(), true)) { + FieldOrRow key = entry.getKey(); + FieldOrRowOrSelect value = entry.getValue(); + + + + + + + + + + + + + + if (!"".equals(separator)) ctx.sql(separator) .formatSeparator(); - ctx.start(assignmentClause) - .qualify(supportsQualify, c -> c.visit(entry.getKey())) - .sql(" = "); - - // [#8479] Emulate WHERE clause using CASE - Condition condition = (Condition) ctx.data(DATA_ON_DUPLICATE_KEY_WHERE); - if (condition != null) - ctx.visit(when(condition, (Field) entry.getValue()).else_(entry.getKey())); - else - ctx.visit(entry.getValue()); - - ctx.end(assignmentClause); - + acceptAssignmentClause(ctx, supportsQualify, key, value); separator = ","; } @@ -134,12 +172,114 @@ final class FieldMapForUpdate extends AbstractQueryPartMap, Field> i ctx.sql("[ no fields are updated ]"); } - static final Iterable, Field>> removeReadonly(Context ctx, Iterable, Field>> it) { + @SuppressWarnings("unchecked") + private final void acceptAssignmentClause( + Context ctx, + boolean supportsQualify, + FieldOrRow key, + FieldOrRowOrSelect value + ) { + ctx.start(assignmentClause); + + // A multi-row update was specified + if (key instanceof Row) { Row multiRow = (Row) key; + Row multiValue = value instanceof Row ? (Row) value : null; + Select multiSelect = value instanceof Select ? (Select) value : null; + + // [#6884] This syntax can be emulated trivially, if the RHS is not a SELECT subquery + if (multiValue != null && !SUPPORT_RVE_SET.contains(ctx.dialect())) { + FieldMapForUpdate map = new FieldMapForUpdate(table(), UPDATE_SET_ASSIGNMENT); + + for (int i = 0; i < multiRow.size(); i++) { + Field k = multiRow.field(i); + Field v = multiValue.field(i); + + map.put(k, Tools.field(v, k)); + } + + toSQLUpdateMap(ctx, map); + } - return it; + + + + + + + + + + + + else { + Row row = removeReadonly(ctx, multiRow); + + ctx.start(UPDATE_SET_ASSIGNMENT) + .formatIndentStart() + .formatSeparator() + .qualify(false, c -> c.visit(row)) + .sql(" = "); + + // Some dialects don't really support row value expressions on the + // right hand side of a SET clause + if (multiValue != null + + + + ) { + + // [#6763] Incompatible change in PostgreSQL 10 requires ROW() constructor for + // single-degree rows. Let's just always render it, here. + if (REQUIRE_RVE_ROW_CLAUSE.contains(ctx.dialect())) + ctx.visit(K_ROW).sql(" "); + + ctx.visit(removeReadonly(ctx, multiRow, multiValue)); + } + + // Subselects or subselect emulations of row value expressions + else if (multiSelect != null) { + Select select; + + if (multiValue != null) + select = select(removeReadonly(ctx, multiRow, multiValue).fields()); + + + + + else + select = multiSelect; + + visitSubquery(ctx, select, false, false, false); + } + + ctx.formatIndentEnd().end(UPDATE_SET_ASSIGNMENT); + } + } + + // A regular (non-multi-row) update was specified + else { + ctx.qualify(supportsQualify, c -> c.visit(key)) + .sql(" = "); + + // [#8479] Emulate WHERE clause using CASE + Condition condition = (Condition) ctx.data(DATA_ON_DUPLICATE_KEY_WHERE); + if (condition != null) + ctx.visit(when(condition, (Field) value).else_(key)); + else + ctx.visit(value); + } + + ctx.end(assignmentClause); + } + + static final void toSQLUpdateMap(Context ctx, FieldMapForUpdate updateMap) { + ctx.formatIndentStart() + .formatSeparator() + .visit(updateMap) + .formatIndentEnd(); } static final Row removeReadonly(Context ctx, Row row) { @@ -177,4 +317,5 @@ final class FieldMapForUpdate extends AbstractQueryPartMap, Field> i + } diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java index 8823d6089d..d19e9f8a25 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java @@ -38,7 +38,6 @@ package org.jooq.impl; import static java.lang.Boolean.TRUE; -import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static org.jooq.Clause.FIELD_ROW; import static org.jooq.Clause.INSERT_SELECT; @@ -56,10 +55,8 @@ import static org.jooq.impl.Keywords.K_VALUES; import static org.jooq.impl.QueryPartCollectionView.wrap; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.anyMatch; -import static org.jooq.impl.Tools.collect; import static org.jooq.impl.Tools.filter; import static org.jooq.impl.Tools.flatten; -import static org.jooq.impl.Tools.flattenCollection; import static org.jooq.impl.Tools.lazy; import static org.jooq.impl.Tools.BooleanDataKey.DATA_EMULATE_BULK_INSERT_RETURNING; @@ -67,7 +64,6 @@ import java.util.AbstractList; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -83,21 +79,19 @@ import org.jooq.Configuration; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.FieldOrRow; import org.jooq.Param; // ... import org.jooq.Record; import org.jooq.RenderContext.CastMode; import org.jooq.SQLDialect; import org.jooq.Select; -import org.jooq.SelectJoinStep; import org.jooq.Table; import org.jooq.conf.WriteIfReadonly; import org.jooq.exception.DataTypeException; import org.jooq.impl.AbstractStoreQuery.UnknownField; import org.jooq.impl.QOM.UNotYetImplemented; -import org.jetbrains.annotations.NotNull; - /** * @author Lukas Eder */ @@ -219,7 +213,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple .end(INSERT_SELECT); } - static final Set> keysAndComputedOnClient(Set> keys, Table table) { + static final Set keysAndComputedOnClient(Set keys, Table table) { @@ -289,6 +283,8 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple + + diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java index 5bf3ffb857..c114a1cfa4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java @@ -56,7 +56,6 @@ import org.jooq.FieldLike; import org.jooq.InsertOnConflictConditionStep; import org.jooq.InsertOnConflictWhereIndexPredicateStep; import org.jooq.InsertOnDuplicateSetMoreStep; -import org.jooq.InsertQuery; import org.jooq.InsertResultStep; import org.jooq.InsertSetMoreStep; import org.jooq.InsertSetStep; @@ -822,21 +821,21 @@ final class InsertImpl void addValue(InsertQuery delegate, Field field, int index, Object object) { + private final void addValue(InsertQueryImpl delegate, Field field, int index, Object object) { // [#1343] Only convert non-jOOQ objects // [#8606] The column index is relevant when adding a value to a plain SQL multi row INSERT // statement that does not have any field list. if (object instanceof Field) - ((AbstractStoreQuery) delegate).addValue(field, index, (Field) object); + delegate.addValue(field, index, (Field) object); else if (object instanceof FieldLike) - ((AbstractStoreQuery) delegate).addValue(field, index, ((FieldLike) object).asField()); + delegate.addValue(field, index, ((FieldLike) object).asField()); else if (field != null) - ((AbstractStoreQuery) delegate).addValue(field, index, field.getDataType().convert(object)); + delegate.addValue(field, index, field.getDataType().convert(object)); // [#4629] Plain SQL INSERT INTO t VALUES (a, b, c) statements don't know the insert columns else - ((AbstractStoreQuery) delegate).addValue(field, index, (T) object); + delegate.addValue(field, index, (T) object); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index 349ed7d119..1e62f3b372 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -48,7 +48,6 @@ import static org.jooq.Clause.INSERT_INSERT_INTO; import static org.jooq.Clause.INSERT_ON_DUPLICATE_KEY_UPDATE; import static org.jooq.Clause.INSERT_ON_DUPLICATE_KEY_UPDATE_ASSIGNMENT; import static org.jooq.Clause.INSERT_RETURNING; -import static org.jooq.Clause.INSERT_SELECT; // ... // ... import static org.jooq.SQLDialect.DERBY; @@ -102,7 +101,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Collectors; import org.jooq.Clause; import org.jooq.Condition; @@ -112,7 +110,6 @@ import org.jooq.Context; import org.jooq.Field; import org.jooq.Identity; import org.jooq.InsertQuery; -import org.jooq.Merge; import org.jooq.MergeNotMatchedStep; import org.jooq.MergeOnConditionStep; import org.jooq.Name; @@ -134,7 +131,13 @@ import org.jooq.tools.StringUtils; /** * @author Lukas Eder */ -final class InsertQueryImpl extends AbstractStoreQuery implements InsertQuery, UNotYetImplemented { +final class InsertQueryImpl +extends + AbstractStoreQuery, Field> +implements + InsertQuery, + UNotYetImplemented +{ private static final Clause[] CLAUSES = { INSERT }; private static final Set SUPPORT_INSERT_IGNORE = SQLDialect.supportedBy(MARIADB, MYSQL); diff --git a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java index 568dfbff75..f95877532e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java @@ -109,6 +109,8 @@ import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Field; +import org.jooq.FieldOrRow; +import org.jooq.FieldOrRowOrSelect; import org.jooq.MergeKeyStep1; import org.jooq.MergeKeyStep10; import org.jooq.MergeKeyStep11; @@ -1627,11 +1629,13 @@ implements if (update == null) update = new MatchedClause(noCondition()); - for (Entry, Field> e : m.updateMap.entrySet()) { - Field exp = update.updateMap.get(e.getKey()); + for (Entry e : m.updateMap.entrySet()) { + FieldOrRowOrSelect exp = update.updateMap.get(e.getKey()); if (exp instanceof CaseConditionStepImpl) ((CaseConditionStepImpl) exp).when(negate.and(condition), e.getValue()); + + // [#10523] [#13325] TODO: ClassCastException when we're using Row here, once supported else update.updateMap.put(e.getKey(), when(negate.and(condition), (Field) e.getValue()).else_(e.getKey())); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 61c3f49f65..1025a89551 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -268,6 +268,7 @@ import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; import org.jooq.Field; import org.jooq.FieldOrRow; +import org.jooq.FieldOrRowOrSelect; import org.jooq.Fields; import org.jooq.ForeignKey; import org.jooq.JSON; @@ -5676,6 +5677,13 @@ final class Tools { return map(orderFields, (OrderField f) -> field(f)); } + static final List> fields(FieldOrRow fr) { + if (fr instanceof Field) + return singletonList((Field) fr); + else + return asList(((Row) fr).fields()); + } + static final Field unalias(Field field) { Field result = aliased(field); return result != null ? result : field; @@ -6099,23 +6107,25 @@ final class Tools { * set iterable, making sure no duplicate keys resulting from overlapping * embeddables will be produced. */ - static final Iterable, Field>> flattenEntrySet( - final Iterable, Field>> iterable, + static final Iterable> flattenEntrySet( + final Iterable> iterable, final boolean removeDuplicates ) { // [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms // [#11729] Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=572873 return () -> new FlatteningIterator<>(iterable.iterator(), (e, duplicates) -> { - if (e.getKey() instanceof EmbeddableTableField) { - List, Field>> result = new ArrayList<>(); - Field[] keys = embeddedFields(e.getKey()); - Field[] values = embeddedFields(e.getValue()); + + // [#9879] [#13325] TODO: Support also UPDATE .. SET ROW = ... + if (e.getKey() instanceof EmbeddableTableField) { EmbeddableTableField key = (EmbeddableTableField) e.getKey(); + List> result = new ArrayList<>(); + Field[] keys = embeddedFields(key); + Field[] values = embeddedFields((Field) e.getValue()); for (int i = 0; i < keys.length; i++) - result.add(new SimpleImmutableEntry, Field>( + result.add(new SimpleImmutableEntry( keys[i], values[i] )); diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index dcc5c39b31..b871b8eb0f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -52,7 +52,6 @@ import static org.jooq.Clause.UPDATE_WHERE; // ... import static org.jooq.SQLDialect.CUBRID; // ... -// ... import static org.jooq.SQLDialect.DERBY; // ... import static org.jooq.SQLDialect.FIREBIRD; @@ -68,8 +67,6 @@ import static org.jooq.SQLDialect.POSTGRES; // ... // ... // ... -// ... -// ... import static org.jooq.SQLDialect.SQLITE; // ... // ... @@ -77,30 +74,22 @@ import static org.jooq.SQLDialect.SQLITE; // ... import static org.jooq.SQLDialect.YUGABYTEDB; import static org.jooq.conf.SettingsTools.getExecuteUpdateWithoutWhere; -import static org.jooq.conf.WriteIfReadonly.THROW; import static org.jooq.impl.DSL.name; import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.trueCondition; -import static org.jooq.impl.FieldMapForUpdate.removeReadonly; import static org.jooq.impl.FieldMapsForInsert.keysAndComputedOnClient; import static org.jooq.impl.Keywords.K_FROM; import static org.jooq.impl.Keywords.K_LIMIT; import static org.jooq.impl.Keywords.K_ORDER_BY; -import static org.jooq.impl.Keywords.K_ROW; import static org.jooq.impl.Keywords.K_SET; import static org.jooq.impl.Keywords.K_UPDATE; import static org.jooq.impl.Keywords.K_WHERE; -import static org.jooq.impl.Tools.EMPTY_FIELD; -import static org.jooq.impl.Tools.fieldName; -import static org.jooq.impl.Tools.map; -import static org.jooq.impl.Tools.unqualified; -import static org.jooq.impl.Tools.visitSubquery; +import static org.jooq.impl.Tools.anyMatch; +import static org.jooq.impl.Tools.findAny; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -110,6 +99,8 @@ import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Field; +import org.jooq.FieldOrRow; +import org.jooq.FieldOrRowOrSelect; import org.jooq.Operator; import org.jooq.OrderField; // ... @@ -166,13 +157,18 @@ import org.jooq.Select; import org.jooq.Table; import org.jooq.TableLike; import org.jooq.UpdateQuery; -import org.jooq.exception.DataTypeException; import org.jooq.impl.QOM.UNotYetImplemented; /** * @author Lukas Eder */ -final class UpdateQueryImpl extends AbstractStoreQuery implements UpdateQuery, UNotYetImplemented { +final class UpdateQueryImpl +extends + AbstractStoreQuery +implements + UpdateQuery, + UNotYetImplemented +{ private static final Clause[] CLAUSES = { UPDATE }; @@ -182,13 +178,6 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl - - - - - private static final Set SUPPORT_RVE_SET = SQLDialect.supportedBy(H2, HSQLDB, POSTGRES, YUGABYTEDB); - private static final Set REQUIRE_RVE_ROW_CLAUSE = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - // LIMIT is not supported at all private static final Set NO_SUPPORT_LIMIT = SQLDialect.supportedUntil(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, POSTGRES, SQLITE, YUGABYTEDB); @@ -198,9 +187,6 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl private final FieldMapForUpdate updateMap; private final TableList from; private final ConditionProviderImpl condition; - private Row multiRow; - private Row multiValue; - private Select multiSelect; private final SortFieldList orderBy; private Field limit; @@ -453,13 +439,11 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl final void addValues0(Row row, Row value) { - multiRow = row; - multiValue = value; + updateMap.put(row, value); } final void addValues0(Row row, Select select) { - multiRow = row; - multiSelect = select; + updateMap.put(row, select); } @Override @@ -564,9 +548,6 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl - - - @@ -580,9 +561,6 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl u.setReturning(returning); u.updateMap.putAll(updateMap); - u.multiRow = multiRow; - u.multiSelect = multiSelect; - u.multiValue = multiValue; u.from.addAll(from); u.condition.setWhere(condition.getWhere()); u.orderBy.addAll(orderBy); @@ -622,86 +600,7 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl .visit(K_SET) .separatorRequired(true); - // A multi-row update was specified - if (multiRow != null) { - - // [#6884] This syntax can be emulated trivially, if the RHS is not a SELECT subquery - if (multiValue != null && !SUPPORT_RVE_SET.contains(ctx.dialect())) { - FieldMapForUpdate map = new FieldMapForUpdate(table(), UPDATE_SET_ASSIGNMENT); - - for (int i = 0; i < multiRow.size(); i++) { - Field k = multiRow.field(i); - Field v = multiValue.field(i); - - map.put(k, Tools.field(v, k)); - } - - toSQLUpdateMap(ctx, map); - } - - - - - - - - - - - - - - - - else { - Row row = removeReadonly(ctx, multiRow); - - ctx.start(UPDATE_SET_ASSIGNMENT) - .formatIndentStart() - .formatSeparator() - .qualify(false, c -> c.visit(row)) - .sql(" = "); - - // Some dialects don't really support row value expressions on the - // right hand side of a SET clause - if (multiValue != null - - - - ) { - - // [#6763] Incompatible change in PostgreSQL 10 requires ROW() constructor for - // single-degree rows. Let's just always render it, here. - if (REQUIRE_RVE_ROW_CLAUSE.contains(ctx.dialect())) - ctx.visit(K_ROW).sql(" "); - - ctx.visit(removeReadonly(ctx, multiRow, multiValue)); - } - - // Subselects or subselect emulations of row value expressions - else { - Select select; - - if (multiValue != null) - select = select(removeReadonly(ctx, multiRow, multiValue).fields()); - - - - - else - select = multiSelect; - - visitSubquery(ctx, select, false, false, false); - } - - ctx.formatIndentEnd().end(UPDATE_SET_ASSIGNMENT); - } - } - - // A regular (non-multi-row) update was specified - else - toSQLUpdateMap(ctx, updateMap); - + FieldMapForUpdate.toSQLUpdateMap(ctx, updateMap); ctx.end(UPDATE_SET); @@ -763,32 +662,6 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl ctx.end(UPDATE_RETURNING); } - private static final void toSQLUpdateMap(Context ctx, FieldMapForUpdate updateMap) { - ctx.formatIndentStart() - .formatSeparator() - .visit(updateMap) - .formatIndentEnd(); - } - - - - - - - - - - - - - - - - - - - - private final void acceptFrom(Context ctx) { ctx.start(UPDATE_FROM); @@ -801,6 +674,8 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl + + f = from; if (!f.isEmpty()) @@ -823,7 +698,7 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl if (!condition.hasWhere()) executeWithoutWhere("UPDATE without WHERE", getExecuteUpdateWithoutWhere(configuration().settings())); - return updateMap.size() > 0 || multiRow != null; + return updateMap.size() > 0; } @@ -835,4 +710,11 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl + + + + + + + }