Support overlapping embeddables
This commit is contained in:
parent
e90a9abd76
commit
061e425340
@ -1057,8 +1057,6 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -88,12 +88,4 @@ public interface ColumnDefinition extends TypedElementDefinition<TableDefinition
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -41,7 +41,6 @@ package org.jooq.meta;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.tools.JooqLogger;
|
||||
@ -58,8 +57,7 @@ public class DefaultColumnDefinition
|
||||
private static final JooqLogger log = JooqLogger.getLogger(DefaultColumnDefinition.class);
|
||||
private final int position;
|
||||
private final boolean isIdentity;
|
||||
private transient List<EmbeddableDefinition> containedInEmbeddables;
|
||||
private transient EmbeddableDefinition replacedByEmbeddable;
|
||||
private transient List<EmbeddableDefinition> replacedByEmbeddables;
|
||||
|
||||
public DefaultColumnDefinition(TableDefinition table, String name, int position, DataTypeDefinition type,
|
||||
boolean isIdentity, String comment) {
|
||||
@ -124,20 +122,6 @@ public class DefaultColumnDefinition
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -101,7 +101,7 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> {
|
||||
if (!CASTS_NEEDED.contains(ctx.dialect()))
|
||||
ctx.castMode(CastMode.NEVER);
|
||||
|
||||
for (Entry<Field<?>, Field<?>> entry : flattenEntrySet(entrySet())) {
|
||||
for (Entry<Field<?>, Field<?>> entry : flattenEntrySet(entrySet(), true)) {
|
||||
if (!"".equals(separator))
|
||||
ctx.sql(separator)
|
||||
.formatSeparator();
|
||||
|
||||
@ -46,6 +46,11 @@ import static org.jooq.SQLDialect.POSTGRES;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.Keywords.K_DEFAULT_VALUES;
|
||||
import static org.jooq.impl.Keywords.K_VALUES;
|
||||
import static org.jooq.impl.Tools.collect;
|
||||
import static org.jooq.impl.Tools.flatten;
|
||||
import static org.jooq.impl.Tools.flattenCollection;
|
||||
import static org.jooq.impl.Tools.isEmbeddable;
|
||||
import static org.jooq.impl.Tools.lazy;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_EMULATE_BULK_INSERT_RETURNING;
|
||||
|
||||
import java.util.AbstractList;
|
||||
@ -86,6 +91,9 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
|
||||
final Table<?> table;
|
||||
final Map<Field<?>, Field<?>> empty;
|
||||
// Depending on whether embeddable types are allowed, this data structure
|
||||
// needs to be flattened with duplicates removed, prior to consumption
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
final Map<Field<?>, List<Field<?>>> values;
|
||||
int rows;
|
||||
int nextRow = -1;
|
||||
@ -225,10 +233,11 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
final Select<Record> insertSelect() {
|
||||
Select<Record> select = null;
|
||||
|
||||
Map<Field<?>, List<Field<?>>> v = valuesFlattened();
|
||||
for (int row = 0; row < rows; row++) {
|
||||
List<Field<?>> fields = new ArrayList<>(values.size());
|
||||
List<Field<?>> fields = new ArrayList<>(v.size());
|
||||
|
||||
for (List<Field<?>> list : values.values())
|
||||
for (List<Field<?>> list : v.values())
|
||||
fields.add(list.get(row));
|
||||
|
||||
Select<Record> iteration = DSL.select(fields);
|
||||
@ -269,7 +278,7 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
String separator = "";
|
||||
int i = 0;
|
||||
|
||||
for (List<Field<?>> list : values.values()) {
|
||||
for (List<Field<?>> list : valuesFlattened().values()) {
|
||||
ctx.sql(separator);
|
||||
|
||||
if (indent)
|
||||
@ -289,6 +298,8 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.visit(list.get(row));
|
||||
separator = ", ";
|
||||
}
|
||||
@ -502,7 +513,7 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
return;
|
||||
|
||||
// [#2995] Do not generate empty column lists.
|
||||
if (values.size() == 0)
|
||||
if (values.isEmpty())
|
||||
return;
|
||||
|
||||
// [#4629] Do not generate column lists for unknown columns
|
||||
@ -519,9 +530,45 @@ final class FieldMapsForInsert extends AbstractQueryPart {
|
||||
// [#989] Avoid qualifying fields in INSERT field declaration
|
||||
boolean qualify = ctx.qualify();
|
||||
ctx.qualify(false)
|
||||
.visit(new QueryPartCollectionView<>(values.keySet()))
|
||||
.visit(new QueryPartCollectionView<>(collect(flattenCollection(values.keySet(), true))))
|
||||
.qualify(qualify);
|
||||
|
||||
ctx.sql(')');
|
||||
}
|
||||
|
||||
final Map<Field<?>, List<Field<?>>> valuesFlattened() {
|
||||
Map<Field<?>, List<Field<?>>> result = new LinkedHashMap<>();
|
||||
|
||||
// [#2530] [#6124] [#10481] TODO: Shortcut for performance, when there are no embeddables
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
Set<Field<?>> overlapping = null;
|
||||
for (Entry<Field<?>, List<Field<?>>> entry : values.entrySet()) {
|
||||
if (isEmbeddable(entry.getKey())) {
|
||||
List<Iterator<? extends Field<?>>> value = new ArrayList<>(entry.getValue().size());
|
||||
|
||||
for (Field<?> f : entry.getValue())
|
||||
value.add(flatten(f).iterator());
|
||||
|
||||
for (Field<?> key : flatten(entry.getKey())) {
|
||||
if ((overlapping = lazy(overlapping)).add(key)) {
|
||||
List<Field<?>> list = new ArrayList<>(entry.getValue().size());
|
||||
|
||||
for (Iterator<? extends Field<?>> v : value)
|
||||
if (v.hasNext())
|
||||
list.add(v.next());
|
||||
|
||||
result.put(key, list);
|
||||
}
|
||||
else
|
||||
for (Iterator<? extends Field<?>> v : value)
|
||||
if (v.hasNext())
|
||||
v.next();
|
||||
}
|
||||
}
|
||||
else
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,6 +668,7 @@ final class InsertQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
|
||||
// [#8353] TODO: Support overlapping embeddables
|
||||
ctx.formatSeparator()
|
||||
.start(INSERT_SELECT)
|
||||
.visit(select)
|
||||
|
||||
@ -5358,10 +5358,23 @@ final class Tools {
|
||||
}
|
||||
}
|
||||
|
||||
static final <E> List<E> collect(Iterable<E> iterable) {
|
||||
if (iterable instanceof List)
|
||||
return (List<E>) iterable;
|
||||
|
||||
List<E> result = new ArrayList<>();
|
||||
for (E e : iterable)
|
||||
result.add(e);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten out an {@link EmbeddableTableField}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <E extends Field<?>> Iterable<E> flatten(final E field) {
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
return new Iterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
@ -5369,12 +5382,18 @@ final class Tools {
|
||||
|
||||
if (field instanceof EmbeddableTableField)
|
||||
return new FlatteningIterator<E>(it) {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
List<E> flatten(E e) {
|
||||
return (List<E>) Arrays.asList(((EmbeddableTableField<?, ?>) e).fields);
|
||||
}
|
||||
};
|
||||
else if (field instanceof Val && ((Val<?>) field).getValue() instanceof EmbeddableRecord)
|
||||
return new FlatteningIterator<E>(it) {
|
||||
@Override
|
||||
List<E> flatten(E e) {
|
||||
return (List<E>) Arrays.asList(embeddedFields(field));
|
||||
}
|
||||
};
|
||||
else
|
||||
return it;
|
||||
}
|
||||
@ -5385,16 +5404,33 @@ final class Tools {
|
||||
* Flatten out {@link EmbeddableTableField} elements contained in an
|
||||
* ordinary iterable.
|
||||
*/
|
||||
static final <E extends Field<?>> Iterable<E> flattenCollection(final Iterable<E> iterable) {
|
||||
static final <E extends Field<?>> Iterable<E> flattenCollection(
|
||||
final Iterable<E> iterable,
|
||||
final boolean removeDuplicates
|
||||
) {
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
return new Iterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new FlatteningIterator<E>(iterable.iterator()) {
|
||||
Set<Field<?>> overlapping = null;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
List<E> flatten(E e) {
|
||||
if (e instanceof EmbeddableTableField)
|
||||
return (List<E>) Arrays.asList(((EmbeddableTableField<?, ?>) e).fields);
|
||||
if (e instanceof EmbeddableTableField) {
|
||||
if (removeDuplicates) {
|
||||
List<E> result = new ArrayList<>();
|
||||
|
||||
for (Field<?> field : ((EmbeddableTableField<?, ?>) e).fields)
|
||||
if ((overlapping = lazy(overlapping)).add(field))
|
||||
result.add((E) field);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return (List<E>) Arrays.asList(((EmbeddableTableField<?, ?>) e).fields);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -5404,14 +5440,21 @@ final class Tools {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten out {@link EmbeddableTableField} elements contained in an
|
||||
* entry set iterable.
|
||||
* Flatten out {@link EmbeddableTableField} elements contained in an entry
|
||||
* set iterable, making sure no duplicate keys resulting from overlapping
|
||||
* embeddables will be produced.
|
||||
*/
|
||||
static final <E extends Entry<Field<?>, Field<?>>> Iterable<E> flattenEntrySet(final Iterable<E> iterable) {
|
||||
static final <E extends Entry<Field<?>, Field<?>>> Iterable<E> flattenEntrySet(
|
||||
final Iterable<E> iterable,
|
||||
final boolean removeDuplicates
|
||||
) {
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
return new Iterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new FlatteningIterator<E>(iterable.iterator()) {
|
||||
Set<Field<?>> overlapping = null;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
List<E> flatten(E e) {
|
||||
@ -5421,9 +5464,10 @@ final class Tools {
|
||||
Field<?>[] values = embeddedFields(e.getValue());
|
||||
|
||||
for (int i = 0; i < keys.length; i++)
|
||||
result.add((E) new SimpleImmutableEntry<Field<?>, Field<?>>(
|
||||
keys[i], values[i]
|
||||
));
|
||||
if (!removeDuplicates || (overlapping = lazy(overlapping)).add(keys[i]))
|
||||
result.add((E) new SimpleImmutableEntry<Field<?>, Field<?>>(
|
||||
keys[i], values[i]
|
||||
));
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -5435,6 +5479,10 @@ final class Tools {
|
||||
};
|
||||
}
|
||||
|
||||
static final <T> Set<T> lazy(Set<T> set) {
|
||||
return set == null ? new HashSet<>() : set;
|
||||
}
|
||||
|
||||
/**
|
||||
* A base implementation for {@link EmbeddableTableField} flattening
|
||||
* iterators with a default implementation for {@link Iterator#remove()} for
|
||||
|
||||
Loading…
Reference in New Issue
Block a user