From 82a565f5665bb96d74a58c60c81a723c3c960afb Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 8 Dec 2021 11:22:36 +0100 Subject: [PATCH] [jOOQ/jOOQ#12704] DefaultRecordUnmapper.IterableUnmapper uses name based field access instead of index based ones, leading to ambiguities --- .../java/org/jooq/impl/AbstractRecord.java | 72 +++++++++---------- .../org/jooq/impl/DefaultRecordUnmapper.java | 24 +++---- .../main/java/org/jooq/impl/FieldsImpl.java | 16 ----- jOOQ/src/main/java/org/jooq/impl/Tools.java | 14 ++++ .../main/java/org/jooq/impl/XMLHandler.java | 2 +- 5 files changed, 59 insertions(+), 69 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index 15d0112371..477e609fe8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -408,6 +408,10 @@ abstract class AbstractRecord extends AbstractStore implements Record { public final void set(Field field, T value) { int index = fields.indexOf(field); + set(field, index, value); + } + + final void set(Field field, int index, T value) { if (index >= 0) set(index, field, value); else if (Tools.nonReplacingEmbeddable(field)) { @@ -904,109 +908,93 @@ abstract class AbstractRecord extends AbstractStore implements Record { return (@NotNull E) mapper.map(this); } - private final void from0(Object source, FieldsImpl f) { + private final void from0(Object source, int[] targetIndexMapping) { if (source == null) return; // [#2520] TODO: Benchmark this from() method. There's probably a better implementation - from(Tools.configuration(this).recordUnmapperProvider().provide(source.getClass(), f).unmap(prepareArrayForUnmap(source, f))); + from( + Tools.configuration(this).recordUnmapperProvider().provide(source.getClass(), (FieldsImpl) fields.fields).unmap(source), + targetIndexMapping + ); // [#2700] [#3582] If a POJO attribute is NULL, but the column is NOT NULL // then we should let the database apply DEFAULT values resetChangedOnNotNull(this); } - private final Object prepareArrayForUnmap(Object source, FieldsImpl f) { - if (source instanceof Object[]) { Object[] array = (Object[]) source; - if (array.length != f.size()) { - Object[] result = new Object[f.size()]; - - for (int i = 0; i < result.length; i++) { - int index = fields.indexOf(f.field(i)); - result[i] = index >= 0 && index < array.length ? array[index] : null; - } - - return result; - } - else - return source; - } - else - return source; - } - @Override public final void from(Object source) { - from0(source, fields.fields); + from0(source, null); } @Override public final void from(Object source, Field... f) { - from0(source, new FieldsImpl(f)); + from0(source, fields.fields.indexesOf(f)); } @Override public final void from(Object source, String... fieldNames) { - from(source, fields(fieldNames)); + from0(source, fields.fields.indexesOf(fieldNames)); } @Override public final void from(Object source, Name... fieldNames) { - from(source, fields(fieldNames)); + from0(source, fields.fields.indexesOf(fieldNames)); } @Override public final void from(Object source, int... fieldIndexes) { - from(source, fields(fieldIndexes)); + from0(source, fieldIndexes); } @Override public final void fromMap(Map map) { - from(map, fields()); + from(map); } @Override public final void fromMap(Map map, Field... f) { - from0(map, new FieldsImpl(f)); + from(map, f); } @Override public final void fromMap(Map map, String... fieldNames) { - fromMap(map, fields(fieldNames)); + from(map, fieldNames); } @Override public final void fromMap(Map map, Name... fieldNames) { - fromMap(map, fields(fieldNames)); + from(map, fieldNames); } @Override public final void fromMap(Map map, int... fieldIndexes) { - fromMap(map, fields(fieldIndexes)); + from(map, fieldIndexes); } @Override public final void fromArray(Object... array) { - fromArray(array, fields()); + from(array); } @Override public final void fromArray(Object[] array, Field... f) { - from0(array, new FieldsImpl(f)); + from(array, f); } @Override public final void fromArray(Object[] array, String... fieldNames) { - fromArray(array, fields(fieldNames)); + from(array, fieldNames); } @Override public final void fromArray(Object[] array, Name... fieldNames) { - fromArray(array, fields(fieldNames)); + from(array, fieldNames); } @Override public final void fromArray(Object[] array, int... fieldIndexes) { - fromArray(array, fields(fieldIndexes)); + from(array, fieldIndexes); } /** @@ -1022,6 +1010,18 @@ abstract class AbstractRecord extends AbstractStore implements Record { } } + final void from(Record source, int[] indexMapping) { + int s = source.size(); + int t = indexMapping == null ? s : indexMapping.length; + + for (int i = 0; i < s && i < t; i++) { + int j = indexMapping == null ? i : indexMapping[i]; + + if (source.field(j) != null && source.changed(j)) + Tools.setValue(this, field(j), j, source, j); + } + } + // ------------------------------------------------------------------------- // Formatting methods // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordUnmapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordUnmapper.java index 07a73bc3ea..4596c9a2e5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordUnmapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordUnmapper.java @@ -135,19 +135,15 @@ public class DefaultRecordUnmapper implements RecordUnmappe private final class ArrayUnmapper implements RecordUnmapper { - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked" }) @Override public final R unmap(E source) { if (source instanceof Object[]) { Object[] array = (Object[]) source; int size = rowType.size(); - Record record = newRecord(); + AbstractRecord record = (AbstractRecord) newRecord(); - for (int i = 0; i < size && i < array.length; i++) { - Field field = rowType.field(i); - - if (rowType.field(field) != null) - Tools.setValue(record, field, array[i]); - } + for (int i = 0; i < size && i < array.length; i++) + Tools.setValue(record, rowType.field(i), i, array[i]); return (R) record; } @@ -158,20 +154,16 @@ public class DefaultRecordUnmapper implements RecordUnmappe private final class IterableUnmapper implements RecordUnmapper { - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked" }) @Override public final R unmap(E source) { if (source instanceof Iterable) { Iterable iterable = (Iterable) source; Iterator it = iterable.iterator(); int size = rowType.size(); - Record record = newRecord(); + AbstractRecord record = (AbstractRecord) newRecord(); - for (int i = 0; i < size && it.hasNext(); i++) { - Field field = rowType.field(i); - - if (rowType.field(field) != null) - Tools.setValue(record, field, it.next()); - } + for (int i = 0; i < size && it.hasNext(); i++) + Tools.setValue(record, rowType.field(i), i, it.next()); return (R) record; } diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java index 20bb6e900e..b2fc29748d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java @@ -49,9 +49,7 @@ import java.sql.SQLWarning; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import org.jooq.Configuration; @@ -63,7 +61,6 @@ import org.jooq.Name; import org.jooq.Record; import org.jooq.RecordMapper; import org.jooq.RecordType; -import org.jooq.Result; import org.jooq.Row; import org.jooq.SelectField; import org.jooq.Table; @@ -473,10 +470,6 @@ final class FieldsImpl extends AbstractQueryPart implements Re return dataType(indexOrFail(this, fieldName)); } - /** - * @deprecated - 3.14.5 - [#11058] - These are used for the deprecated interning feature only. - */ - @Deprecated final int[] indexesOf(Field... f) { int[] result = new int[f.length]; @@ -486,10 +479,6 @@ final class FieldsImpl extends AbstractQueryPart implements Re return result; } - /** - * @deprecated - 3.14.5 - [#11058] - These are used for the deprecated interning feature only. - */ - @Deprecated final int[] indexesOf(String... fieldNames) { int[] result = new int[fieldNames.length]; @@ -499,10 +488,6 @@ final class FieldsImpl extends AbstractQueryPart implements Re return result; } - /** - * @deprecated - 3.14.5 - [#11058] - These are used for the deprecated interning feature only. - */ - @Deprecated final int[] indexesOf(Name... fieldNames) { int[] result = new int[fieldNames.length]; @@ -512,7 +497,6 @@ final class FieldsImpl extends AbstractQueryPart implements Re return result; } - @Override public final void accept(Context ctx) { ctx.visit(wrap(fields)); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 669de623fc..c80f650ad4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -3154,6 +3154,13 @@ final class Tools { setValue(target, targetField, source.get(sourceField)); } + /** + * Type-safely copy a value from one record to another + */ + static final void setValue(AbstractRecord target, Field targetField, int targetIndex, Record source, int sourceIndex) { + setValue(target, targetField, targetIndex, source.get(sourceIndex)); + } + /** * Type-safely set a value to a record */ @@ -3161,6 +3168,13 @@ final class Tools { target.set(targetField, targetField.getDataType().convert(value)); } + /** + * Type-safely set a value to a record + */ + static final void setValue(AbstractRecord target, Field targetField, int targetIndex, Object value) { + target.set(targetField, targetIndex, targetField.getDataType().convert(value)); + } + /** * [#2591] Type-safely copy a value from one record to another, preserving flags. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java index 84d8cb9e60..bd4fe63753 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java @@ -105,7 +105,7 @@ final class XMLHandler extends DefaultHandler { XMLHandler(DSLContext ctx, AbstractRow row, Class recordType) { this.ctx = ctx; this.states = new ArrayDeque<>(); - s = new State<>(row, recordType); + this.s = new State<>(row, recordType); } Result read(String string) {