From 2f064052e869e7f2ddf119e305b106bd8380785d Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 20 Apr 2022 17:42:52 +0200 Subject: [PATCH] [jOOQ/jOOQ#9879] Support client side computed embeddables in UPSERT --- .../java/org/jooq/impl/AbstractDMLQuery.java | 1 + .../org/jooq/impl/FieldMapsForInsert.java | 49 ++++++++++++------- .../java/org/jooq/impl/InsertQueryImpl.java | 10 ++-- jOOQ/src/main/java/org/jooq/impl/Tools.java | 7 +-- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index 4bf7666bac..f0ba880e59 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -136,6 +136,7 @@ import org.jooq.Delete; import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; import org.jooq.Field; +import org.jooq.GeneratorStatementType; import org.jooq.Identity; import org.jooq.Insert; import org.jooq.Name; diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java index 53893f58b8..b760e28af4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java @@ -192,7 +192,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple case FIREBIRD: { - toSQLInsertSelect(ctx, insertSelect(ctx)); + toSQLInsertSelect(ctx, insertSelect(ctx, GeneratorStatementType.INSERT)); break; } @@ -313,10 +313,10 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple - final Select insertSelect(Context ctx) { + final Select insertSelect(Context ctx, GeneratorStatementType statementType) { Select select = null; - Map, List>> v = valuesFlattened(ctx); + Map, List>> v = valuesFlattened(ctx, statementType); for (int i = 0; i < rows; i++) { int row = i; Select iteration = DSL.select(Tools.map(v.values(), l -> l.get(row))); @@ -357,7 +357,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple String separator = ""; int i = 0; - for (List> list : valuesFlattened(ctx).values()) { + for (List> list : valuesFlattened(ctx, GeneratorStatementType.INSERT).values()) { ctx.sql(separator); if (indent) @@ -597,7 +597,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple } // [#989] Avoid qualifying fields in INSERT field declaration - Set> fields = keysFlattened(ctx); + Set> fields = keysFlattened(ctx, GeneratorStatementType.INSERT); @@ -619,27 +619,36 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple return it; } - final Set> keysFlattened(Context ctx) { + final Set> keysFlattened(Context ctx, GeneratorStatementType statementType) { // [#9864] TODO: Refactor and optimise these flattening algorithms - return valuesFlattened(ctx).keySet(); + return valuesFlattened(ctx, statementType).keySet(); } - final Map, List>> valuesFlattened(Context ctx) { + final Map, List>> valuesFlattened(Context ctx, GeneratorStatementType statementType) { Map, List>> result = new LinkedHashMap<>(); // [#2530] [#6124] [#10481] TODO: Shortcut for performance, when there are no embeddables Set> overlapping = null; for (Entry, List>> entry : removeReadonly(ctx, values.entrySet(), Entry::getKey)) { + Field key = entry.getKey(); + DataType keyType = key.getDataType(); + List> value = entry.getValue(); // [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms - if (entry.getKey().getDataType().isEmbeddable()) { - List>> value = new ArrayList<>(entry.getValue().size()); + if (keyType.isEmbeddable()) { + List>> valueFlattened = new ArrayList<>(value.size()); + + for (Field v : value) + valueFlattened.add(flatten(v).iterator()); + + for (Field k : flatten(key)) { + + + + - for (Field f : entry.getValue()) - value.add(flatten(f).iterator()); - for (Field key : flatten(entry.getKey())) { @@ -650,17 +659,23 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple { - List> list = new ArrayList<>(entry.getValue().size()); + List> list = new ArrayList<>(value.size()); - for (Iterator> v : value) + for (Iterator> v : valueFlattened) list.add(v.hasNext() ? v.next() : null); - result.put(key, list); + result.put(k, list); } } } + + + + + + else - result.put(entry.getKey(), entry.getValue()); + result.put(key, value); } return result; diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index f3dc7126c3..797202918a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -629,11 +629,12 @@ implements if (select != null) { + Set> keysFlattened = insertMaps.keysFlattened(ctx, GeneratorStatementType.INSERT); // [#2995] Prevent the generation of wrapping parentheses around the // INSERT .. SELECT statement's SELECT because they would be // interpreted as the (missing) INSERT column list's parens. - if (insertMaps.keysFlattened(ctx).size() == 0) + if (keysFlattened.size() == 0) ctx.data(DATA_INSERT_SELECT_WITHOUT_INSERT_COLUMN_LIST, true); @@ -662,7 +663,6 @@ implements - // [#8353] TODO: Support overlapping embeddables toSQLInsertSelect(ctx, s); @@ -768,7 +768,7 @@ implements if (!keys.isEmpty()) { Select rows = null; - Set> fields = insertMaps.keysFlattened(ctx); + Set> fields = insertMaps.keysFlattened(ctx, GeneratorStatementType.INSERT); // [#10989] INSERT .. SELECT .. ON DUPLICATE KEY IGNORE if (select != null) { @@ -822,7 +822,7 @@ implements || !table().getKeys().isEmpty()) { Table t = null; - Set> k = insertMaps.keysFlattened(ctx); + Set> k = insertMaps.keysFlattened(ctx, null); Collection> f = null; if (!NO_SUPPORT_SUBQUERY_IN_MERGE_USING.contains(ctx.dialect())) { @@ -832,7 +832,7 @@ implements // [#11770] [#11880] Single row inserts also do, in some dialects Select s = select != null ? select - : insertMaps.insertSelect(ctx); + : insertMaps.insertSelect(ctx, null); // [#8937] With DEFAULT VALUES, there is no SELECT. Create one from // known DEFAULT expressions, or use NULL. diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 2ba93ea1d1..f5541f95a7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -6069,16 +6069,17 @@ final class Tools { return filter(asList(array), predicate); } - static final List> flattenFieldOrRow(FieldOrRow fr) { + static final Iterable> flattenFieldOrRow(FieldOrRow fr) { if (fr instanceof Field) - return singletonList((Field) fr); + return flatten((Field) fr); else return asList(((Row) fr).fields()); } static final >> C flattenFieldOrRows(Collection frs, C c) { for (FieldOrRow fr : frs) - c.addAll(flattenFieldOrRow(fr)); + for (Field f : flattenFieldOrRow(fr)) + c.add(f); return c; }