From 7e132ce7cfcf1872e1615df1a611e5c8590d0ca7 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 7 Mar 2025 09:41:54 +0100 Subject: [PATCH] [jOOQ/jOOQ#18098] Record::key produces unattached records --- .../java/org/jooq/impl/AbstractDMLQuery.java | 4 +-- .../java/org/jooq/impl/AbstractRecord.java | 14 ++++---- .../main/java/org/jooq/impl/CursorImpl.java | 4 +-- .../java/org/jooq/impl/DefaultBinding.java | 6 ++-- .../java/org/jooq/impl/DefaultDSLContext.java | 8 ++--- .../org/jooq/impl/DefaultRecordMapper.java | 4 +-- .../org/jooq/impl/DefaultRecordUnmapper.java | 2 +- .../main/java/org/jooq/impl/FieldsImpl.java | 2 +- .../main/java/org/jooq/impl/JSONReader.java | 6 ++-- .../main/java/org/jooq/impl/ListHandler.java | 2 +- .../java/org/jooq/impl/MultisetDataType.java | 2 +- jOOQ/src/main/java/org/jooq/impl/R2DBC.java | 10 +++++- .../java/org/jooq/impl/RecordDataType.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Tools.java | 33 ++++++------------- .../org/jooq/impl/UpdatableRecordImpl.java | 17 ++++++++-- .../main/java/org/jooq/impl/XMLHandler.java | 4 +-- 16 files changed, 63 insertions(+), 57 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index 0bf36d43f1..6ffb593ed4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -1434,9 +1434,9 @@ abstract class AbstractDMLQuery extends AbstractRowCountQuery ((Result) getResult()).add( Tools.newRecord( true, + originalConfiguration, AbstractRecord.class, - fields, - originalConfiguration) + fields) .operate(record -> { record.values[0] = id; record.originals[0] = id; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index f580854df1..9795155747 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -208,7 +208,7 @@ implements return (T) get(index); else if (nonReplacingEmbeddable(field)) return (T) Tools - .newRecord(fetched, ((EmbeddableTableField) field).recordType) + .newRecord(fetched, configuration(), ((EmbeddableTableField) field).recordType) .operate(new TransferRecordState<>(embeddedFields(field))); else throw Tools.indexFail(fields, field); @@ -402,7 +402,7 @@ implements */ @Override public Record original() { - return Tools.newRecord(fetched, (Class) getClass(), fields, configuration()) + return Tools.newRecord(fetched, configuration(), (Class) getClass(), fields) .operate(record -> { for (int i = 0; i < originals.length; i++) record.values[i] = record.originals[i] = originals[i]; @@ -419,7 +419,7 @@ implements return (T) original(index); else if (nonReplacingEmbeddable(field)) return (T) Tools - .newRecord(fetched, ((EmbeddableTableField) field).recordType) + .newRecord(fetched, configuration(), ((EmbeddableTableField) field).recordType) .operate(((AbstractRecord) original()).new TransferRecordState<>(embeddedFields(field))); else throw Tools.indexFail(fields, field); @@ -686,7 +686,7 @@ implements @Override public final Record into(Field... f) { - return Tools.newRecord(fetched, Record.class, Tools.row0(f), configuration()).operate(new TransferRecordState(f)); + return Tools.newRecord(fetched, configuration(), Record.class, Tools.row0(f)).operate(new TransferRecordState(f)); } @@ -834,15 +834,15 @@ implements @Override public final R into(Table table) { - return Tools.newRecord(fetched, table, configuration()).operate(new TransferRecordState<>(table.fields())); + return Tools.newRecord(fetched, configuration(), table).operate(new TransferRecordState<>(table.fields())); } final R intoRecord(R record) { - return Tools.newRecord(fetched, () -> record, configuration()).operate(new TransferRecordState<>(null)); + return Tools.newRecord(fetched, configuration(), () -> record).operate(new TransferRecordState<>(null)); } final R intoRecord(Class type) { - return (R) Tools.newRecord(fetched, type, fields, configuration()).operate(new TransferRecordState<>(null)); + return (R) Tools.newRecord(fetched, configuration(), type, fields).operate(new TransferRecordState<>(null)); } class TransferRecordState implements ThrowingFunction { diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index a825aa1fe5..c47c5cbe00 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -1342,7 +1342,7 @@ final class CursorImpl extends AbstractCursor { ); @SuppressWarnings("unchecked") - private final RecordDelegate recordDelegate = Tools.newRecord(true, (Supplier) factory, ((DefaultExecuteContext) ctx).originalConfiguration()); + private final RecordDelegate recordDelegate = Tools.newRecord(true, ((DefaultExecuteContext) ctx).originalConfiguration(), (Supplier) factory); @Override public final boolean hasNext() { @@ -1547,7 +1547,7 @@ final class CursorImpl extends AbstractCursor { ); - value = (T) Tools.newRecord(true, (Class) recordType, (AbstractRow) nested, ((DefaultExecuteContext) ctx).originalConfiguration()) + value = (T) Tools.newRecord(true, ((DefaultExecuteContext) ctx).originalConfiguration(), (Class) recordType, (AbstractRow) nested) .operate(operation); // [#7100] TODO: Is there a more elegant way to do this? diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 19812a7bbf..f5ce196aee 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -4534,7 +4534,7 @@ public class DefaultBinding implements Binding { // [#17074] Do this only if requested (e.g. native ROW may be generated despite JSON MULTISET emulation at the top level) if (skipDegree1 && row.size() == 1 && emulateMultiset(ctx.configuration()) != NestedCollectionEmulation.NATIVE) { result = new ResultImpl<>(ctx.configuration(), row); - result.add(newRecord(true, (Class) type.getRecordType(), row, ctx.configuration()).operate(r -> { + result.add(newRecord(true, ctx.configuration(), (Class) type.getRecordType(), row).operate(r -> { DefaultBindingGetResultSetContext c = new DefaultBindingGetResultSetContext<>(ctx.executeContext(), ctx.resultSet(), ctx.index()); r.field(0).getBinding().get((BindingGetResultSetContext) c); r.fromArray(c.value()); @@ -4730,7 +4730,7 @@ public class DefaultBinding implements Binding { if (fields == null && Record.class.isAssignableFrom(type)) fields = Tools.row0(Tools.fields(values.size(), SQLDataType.VARCHAR)); - return Tools.newRecord(true, (Class) type, (AbstractRow) fields) + return Tools.newRecord(true, originalConfiguration(ctx), (Class) type, (AbstractRow) fields) .operate(r -> { Row row = r.fieldsRow(); @@ -4896,7 +4896,7 @@ public class DefaultBinding implements Binding { Configuration c = originalConfiguration(ctx); Result result = new ResultImpl<>(c, row); - result.add(newRecord(true, recordType, row, c).operate(r -> { + result.add(newRecord(true, c, recordType, row).operate(r -> { r.from(asList(s)); r.touched(false); return r; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index f6c64e2ae6..ac94f43242 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -4869,7 +4869,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri @Override public Record newRecord(Field... fields) { - return Tools.newRecord(false, RecordImplN.class, Tools.row0(fields), configuration()).operate(null); + return Tools.newRecord(false, configuration(), RecordImplN.class, Tools.row0(fields)).operate(null); } @Override @@ -4993,17 +4993,17 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri @Override public > R newRecord(UDT type) { - return Tools.newRecord(false, type, configuration()).operate(null); + return Tools.newRecord(false, configuration(), type).operate(null); } @Override public R newRecord(Table table) { - return Tools.newRecord(false, table, configuration()).operate(null); + return Tools.newRecord(false, configuration(), table).operate(null); } @Override public R newRecord(Table table, final Object source) { - return Tools.newRecord(false, table, configuration()) + return Tools.newRecord(false, configuration(), table) .operate(record -> { record.from(source); return record; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java index 521dde2764..ff88978bbf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -845,7 +845,7 @@ public class DefaultRecordMapper implements RecordMapper { NestedMappingInfo nestedMappingInfo = nestedMappingInfos.get(prefix); nestedMappingInfo.row = Tools.row0(list); - nestedMappingInfo.recordDelegate = newRecord(true, recordType(nestedMappingInfo.row.size()), nestedMappingInfo.row, configuration); + nestedMappingInfo.recordDelegate = newRecord(true, configuration, recordType(nestedMappingInfo.row.size()), nestedMappingInfo.row); for (java.lang.reflect.Field member : getMatchingMembers(configuration, type, prefix, true)) nestedMappingInfo.mappers.add( @@ -1128,7 +1128,7 @@ public class DefaultRecordMapper implements RecordMapper implements RecordUnmappe } private final Record newRecord() { - return Tools.newRecord(false, recordType, row, configuration).operate(null); + return Tools.newRecord(false, configuration, recordType, row).operate(null); } private static final void setValue(Record record, Object source, java.lang.reflect.Field member, Field field, ConverterContext converterContext) diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java index e476b7a739..dbbffb475a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java @@ -190,7 +190,7 @@ implements public final RecordMapper mapper(Field[] f) { AbstractRow row = Tools.row0(f == null ? EMPTY_FIELD : f); - return r -> newRecord(false, AbstractRecord.class, row, r.configuration()).operate(x -> { + return r -> newRecord(false, r.configuration(), AbstractRecord.class, row).operate(x -> { for (Field field : row.fields.fields) Tools.copyValue((AbstractRecord) x, field, r, field); diff --git a/jOOQ/src/main/java/org/jooq/impl/JSONReader.java b/jOOQ/src/main/java/org/jooq/impl/JSONReader.java index 13938c4497..47f0554f0b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JSONReader.java +++ b/jOOQ/src/main/java/org/jooq/impl/JSONReader.java @@ -191,7 +191,7 @@ final class JSONReader { ) : null; - result.add(newRecord(true, recordType, actualRow, ctx.configuration()).operate(r -> { + result.add(newRecord(true, ctx.configuration(), recordType, actualRow).operate(r -> { if (multiset) r.from(list); else @@ -218,7 +218,7 @@ final class JSONReader { if (record == null) result.add(null); else - result.add(newRecord(true, recordType, actualRow, ctx.configuration()).operate(r -> { + result.add(newRecord(true, ctx.configuration(), recordType, actualRow).operate(r -> { r.from(record); r.touched(false); return r; @@ -297,7 +297,7 @@ final class JSONReader { List l = (List) record.get(i); patchRecord(ctx, multiset, actualRow, l); - record.set(i, newRecord(true, recordType, actualRow, ctx.configuration()).operate(r -> { + record.set(i, newRecord(true, ctx.configuration(), recordType, actualRow).operate(r -> { r.from(l); r.touched(false); return r; diff --git a/jOOQ/src/main/java/org/jooq/impl/ListHandler.java b/jOOQ/src/main/java/org/jooq/impl/ListHandler.java index 2772f8fbca..bef78b10b6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ListHandler.java +++ b/jOOQ/src/main/java/org/jooq/impl/ListHandler.java @@ -71,7 +71,7 @@ final class ListHandler { Result result = new ResultImpl<>(ctx.configuration(), row); for (Object o : list) - result.add(newRecord(true, recordType, row, ctx.configuration()).operate(r -> { + result.add(newRecord(true, ctx.configuration(), recordType, row).operate(r -> { Object[] attributes = o instanceof Struct s ? s.getAttributes() diff --git a/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java b/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java index 2f069ac026..e028e2cf54 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java @@ -168,7 +168,7 @@ final class MultisetDataType extends DefaultDataType ResultImpl result = new ResultImpl<>(CONFIG.get(), row); for (Object record : l) - result.add(newRecord(true, recordType, row, CONFIG.get()) + result.add(newRecord(true, CONFIG.get(), recordType, row) .operate(r -> { // [#12014] TODO: Fix this and remove workaround diff --git a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java index a250824bd1..4143a645c9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java +++ b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java @@ -431,7 +431,15 @@ final class R2DBC { // TODO: This call is duplicated from CursorImpl and related classes. // Refactor this call to make sure code is re-used, especially when // ExecuteListener lifecycle management is implemented - RecordDelegate delegate = Tools.newRecord(true, recordFactory(null, (Class) query.getRecordType(), (AbstractRow) Tools.row0(fields)), query.configuration()); + RecordDelegate delegate = Tools.newRecord( + true, + query.configuration(), + recordFactory( + null, + (Class) query.getRecordType(), + (AbstractRow) Tools.row0(fields) + ) + ); // TODO: What data to pass here? DefaultBindingGetResultSetContext ctx = new DefaultBindingGetResultSetContext( diff --git a/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java b/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java index 74b4120cbc..f744e40a0c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java @@ -172,7 +172,7 @@ final class RecordDataType extends DefaultDataType { || object instanceof List || object instanceof Struct ) { - return newRecord(true, getRecordType(), row, CONFIG.get()) + return newRecord(true, CONFIG.get(), getRecordType(), row) .operate(r -> { // [#12014] TODO: Fix this and remove workaround diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 305dcbead9..cc50502086 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -1382,55 +1382,42 @@ final class Tools { + /** * Create a new record */ - static final RecordDelegate newRecord(boolean fetched, Class type) { - return newRecord(fetched, type, null); - } - - /** - * Create a new record. - */ - static final RecordDelegate newRecord(boolean fetched, Class type, AbstractRow fields) { - return newRecord(fetched, type, fields, null); + static final RecordDelegate newRecord(boolean fetched, Configuration configuration, Class type) { + return newRecord(fetched, configuration, type, null); } /** * Create a new {@link Table} or {@link UDT} record. */ - static final RecordDelegate newRecord(boolean fetched, RecordQualifier type) { - return newRecord(fetched, type, null); - } - - /** - * Create a new {@link Table} or {@link UDT} record. - */ - static final RecordDelegate newRecord(boolean fetched, RecordQualifier type, Configuration configuration) { + static final RecordDelegate newRecord(boolean fetched, Configuration configuration, RecordQualifier type) { return newRecord( fetched, + configuration, recordFactory( type, type.getRecordType(), (AbstractRow) type.fieldsRow() - ), - configuration + ) ); } /** * Create a new record. */ - static final RecordDelegate newRecord(boolean fetched, Class type, AbstractRow fields, Configuration configuration) { - return newRecord(fetched, recordFactory(null, type, fields), configuration); + static final RecordDelegate newRecord(boolean fetched, Configuration configuration, Class type, AbstractRow fields) { + return newRecord(fetched, configuration, recordFactory(null, type, fields)); } /** * Create a new record. */ - static final RecordDelegate newRecord(boolean fetched, Supplier factory, Configuration configuration) { + static final RecordDelegate newRecord(boolean fetched, Configuration configuration, Supplier factory) { return new RecordDelegate<>(configuration, factory, fetched); } @@ -3839,7 +3826,7 @@ final class Tools { */ @SuppressWarnings("unchecked") static final String getMappedUDTName(Scope scope, Class> type) { - return getMappedUDTName(scope, Tools.newRecord(false, (Class>) type).operate(null)); + return getMappedUDTName(scope, Tools.newRecord(false, scope.configuration(), (Class>) type).operate(null)); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java index 413fbe617e..4eb075d116 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java @@ -100,7 +100,12 @@ import org.jooq.tools.StringUtils; * @author Lukas Eder */ @org.jooq.Internal -public class UpdatableRecordImpl> extends TableRecordImpl implements UpdatableRecord { +public class UpdatableRecordImpl> +extends TableRecordImpl +implements + UpdatableRecord +{ + private static final JooqLogger log = JooqLogger.getLogger(UpdatableRecordImpl.class); private static final Set NO_SUPPORT_FOR_UPDATE = SQLDialect.supportedBy(SQLITE); private static final Set NO_SUPPORT_MERGE_RETURNING = SQLDialect.supportedBy(DERBY, IGNITE); @@ -111,7 +116,13 @@ public class UpdatableRecordImpl> extends TableReco @Override public Record key() { - AbstractRecord result = Tools.newRecord(fetched, AbstractRecord.class, (AbstractRow) Tools.row0(getPrimaryKey().getFieldsArray())).operate(null); + AbstractRecord result = Tools.newRecord( + fetched, + configuration(), + AbstractRecord.class, + (AbstractRow) Tools.row0(getPrimaryKey().getFieldsArray()) + ).operate(null); + result.setValues(result.fields.fields.fields, this); return result; } @@ -465,7 +476,7 @@ public class UpdatableRecordImpl> extends TableReco // [#3359] The "fetched" flag must be set to false to enforce INSERT statements on // subsequent store() calls - when Settings.updatablePrimaryKeys is set. // R vs Record casting is needed in Java 8 it seems - return (R) Tools.newRecord(false, (Table) (Table) getTable(), configuration()) + return (R) Tools.newRecord(false, configuration(), (Table) (Table) getTable()) .operate((Record copy) -> { // Copy all fields. This marks them all as isChanged, which is important diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java index 0bc49c7273..eea8f2542b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java @@ -288,7 +288,7 @@ final class XMLHandler extends DefaultHandler { s.inRecord--; initResult(); - s.result.add(newRecord(true, s.recordType, s.row, ctx.configuration()).operate(s::into)); + s.result.add(newRecord(true, ctx.configuration(), s.recordType, s.row).operate(s::into)); s.values.clear(); s.column = 0; } @@ -304,7 +304,7 @@ final class XMLHandler extends DefaultHandler { Field f = peek.row.field(peek.column); if ("record".equalsIgnoreCase(qName) && f.getDataType().isRecord()) { - peek.values.add(newRecord(true, s.recordType, s.row, ctx.configuration()).operate(s::into)); + peek.values.add(newRecord(true, ctx.configuration(), s.recordType, s.row).operate(s::into)); s = states.pop(); s.inRecord--; break x;