[jOOQ/jOOQ#9879] Support client side computed embeddables in UPSERT

This commit is contained in:
Lukas Eder 2022-04-20 17:42:52 +02:00
parent b17ec92d98
commit 2f064052e8
4 changed files with 42 additions and 25 deletions

View File

@ -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;

View File

@ -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<Record> insertSelect(Context<?> ctx) {
final Select<Record> insertSelect(Context<?> ctx, GeneratorStatementType statementType) {
Select<Record> select = null;
Map<Field<?>, List<Field<?>>> v = valuesFlattened(ctx);
Map<Field<?>, List<Field<?>>> v = valuesFlattened(ctx, statementType);
for (int i = 0; i < rows; i++) {
int row = i;
Select<Record> 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<Field<?>> list : valuesFlattened(ctx).values()) {
for (List<Field<?>> 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<Field<?>> fields = keysFlattened(ctx);
Set<Field<?>> fields = keysFlattened(ctx, GeneratorStatementType.INSERT);
@ -619,27 +619,36 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
return it;
}
final Set<Field<?>> keysFlattened(Context<?> ctx) {
final Set<Field<?>> keysFlattened(Context<?> ctx, GeneratorStatementType statementType) {
// [#9864] TODO: Refactor and optimise these flattening algorithms
return valuesFlattened(ctx).keySet();
return valuesFlattened(ctx, statementType).keySet();
}
final Map<Field<?>, List<Field<?>>> valuesFlattened(Context<?> ctx) {
final Map<Field<?>, List<Field<?>>> valuesFlattened(Context<?> ctx, GeneratorStatementType statementType) {
Map<Field<?>, List<Field<?>>> result = new LinkedHashMap<>();
// [#2530] [#6124] [#10481] TODO: Shortcut for performance, when there are no embeddables
Set<Field<?>> overlapping = null;
for (Entry<Field<?>, List<Field<?>>> entry : removeReadonly(ctx, values.entrySet(), Entry::getKey)) {
Field<?> key = entry.getKey();
DataType<?> keyType = key.getDataType();
List<Field<?>> value = entry.getValue();
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
if (entry.getKey().getDataType().isEmbeddable()) {
List<Iterator<? extends Field<?>>> value = new ArrayList<>(entry.getValue().size());
if (keyType.isEmbeddable()) {
List<Iterator<? extends Field<?>>> 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<Field<?>> list = new ArrayList<>(entry.getValue().size());
List<Field<?>> list = new ArrayList<>(value.size());
for (Iterator<? extends Field<?>> v : value)
for (Iterator<? extends Field<?>> 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;

View File

@ -629,11 +629,12 @@ implements
if (select != null) {
Set<Field<?>> 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<Record> rows = null;
Set<Field<?>> fields = insertMaps.keysFlattened(ctx);
Set<Field<?>> 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<Field<?>> k = insertMaps.keysFlattened(ctx);
Set<Field<?>> k = insertMaps.keysFlattened(ctx, null);
Collection<Field<?>> 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.

View File

@ -6069,16 +6069,17 @@ final class Tools {
return filter(asList(array), predicate);
}
static final List<Field<?>> flattenFieldOrRow(FieldOrRow fr) {
static final Iterable<Field<?>> flattenFieldOrRow(FieldOrRow fr) {
if (fr instanceof Field)
return singletonList((Field<?>) fr);
return flatten((Field<?>) fr);
else
return asList(((Row) fr).fields());
}
static final <C extends Collection<Field<?>>> C flattenFieldOrRows(Collection<? extends FieldOrRow> frs, C c) {
for (FieldOrRow fr : frs)
c.addAll(flattenFieldOrRow(fr));
for (Field<?> f : flattenFieldOrRow(fr))
c.add(f);
return c;
}