[jOOQ/jOOQ#11829] Add DataType.getRow() to track nested ROW types and allow access to their Field<?>[]
This commit is contained in:
parent
ea80188d20
commit
f2580592dd
@ -75,6 +75,7 @@ import java.util.function.Function;
|
||||
|
||||
import org.jooq.Converters.UnknownType;
|
||||
import org.jooq.exception.DataTypeException;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.jooq.impl.SQLDataType;
|
||||
import org.jooq.tools.Convert;
|
||||
import org.jooq.types.DayToSecond;
|
||||
@ -148,6 +149,13 @@ public interface DataType<T> extends Named {
|
||||
@Nullable
|
||||
Domain<T> getDomain();
|
||||
|
||||
/**
|
||||
* Get the nested record's {@link Row} definition, if this is a
|
||||
* {@link #isRecord()}, or <code>NULL</code> otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
Row getRow();
|
||||
|
||||
/**
|
||||
* Retrieve the Java type associated with ARRAYs of this data type.
|
||||
*/
|
||||
@ -889,6 +897,15 @@ public interface DataType<T> extends Named {
|
||||
*/
|
||||
boolean isUDT();
|
||||
|
||||
/**
|
||||
* Whether this data type is a nested record type.
|
||||
* <p>
|
||||
* This is true for anonymous, structural nested record types constructed
|
||||
* with {@link DSL#row(SelectField...)} or for nominal nested record types,
|
||||
* such as {@link #isUDT()} or {@link #isEmbeddable()}.
|
||||
*/
|
||||
boolean isRecord();
|
||||
|
||||
/**
|
||||
* Whether this data type is an enum type.
|
||||
*/
|
||||
|
||||
@ -85,13 +85,17 @@ import org.jooq.Name;
|
||||
import org.jooq.Nullability;
|
||||
// ...
|
||||
import org.jooq.QualifiedRecord;
|
||||
import org.jooq.RecordType;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.XML;
|
||||
import org.jooq.tools.Convert;
|
||||
import org.jooq.types.Interval;
|
||||
import org.jooq.types.UNumber;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@ -478,6 +482,11 @@ abstract class AbstractDataType<T> extends AbstractNamed implements DataType<T>
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public /* non-final */ Row getRow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -646,6 +655,11 @@ abstract class AbstractDataType<T> extends AbstractNamed implements DataType<T>
|
||||
return QualifiedRecord.class.isAssignableFrom(tType0());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isRecord() {
|
||||
return Record.class.isAssignableFrom(tType0());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isEnum() {
|
||||
return EnumType.class.isAssignableFrom(tType0());
|
||||
@ -676,6 +690,7 @@ abstract class AbstractDataType<T> extends AbstractNamed implements DataType<T>
|
||||
abstract String castTypeSuffix0();
|
||||
abstract String castTypeName0();
|
||||
abstract Class<?> tType0();
|
||||
abstract Class<T> uType0();
|
||||
abstract Integer precision0();
|
||||
abstract Integer scale0();
|
||||
abstract Integer length0();
|
||||
|
||||
@ -51,7 +51,7 @@ import org.jooq.Nullability;
|
||||
*/
|
||||
final class ArrayDataType<T> extends DefaultDataType<T[]> {
|
||||
|
||||
final DataType<T> elementType;
|
||||
final DataType<T> elementType;
|
||||
|
||||
public ArrayDataType(DataType<T> elementType) {
|
||||
super(null, elementType.getArrayType(), elementType.getTypeName(), elementType.getCastTypeName());
|
||||
@ -63,7 +63,7 @@ final class ArrayDataType<T> extends DefaultDataType<T[]> {
|
||||
* [#3225] Performant constructor for creating derived types.
|
||||
*/
|
||||
ArrayDataType(
|
||||
DefaultDataType<T[]> t,
|
||||
AbstractDataType<T[]> t,
|
||||
DataType<T> elementType,
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
@ -94,7 +94,7 @@ final class ArrayDataType<T> extends DefaultDataType<T[]> {
|
||||
) {
|
||||
return new ArrayDataType<>(
|
||||
this,
|
||||
(DefaultDataType<T>) elementType,
|
||||
(AbstractDataType<T>) elementType,
|
||||
newPrecision,
|
||||
newScale,
|
||||
newLength,
|
||||
|
||||
@ -45,6 +45,8 @@ import org.jooq.Converter;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Nullability;
|
||||
import org.jooq.RecordType;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.SQLDialect;
|
||||
|
||||
/**
|
||||
@ -55,8 +57,8 @@ import org.jooq.SQLDialect;
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
final class ConvertedDataType<T, U> extends AbstractDataTypeX<U> {
|
||||
|
||||
private final AbstractDataTypeX<T> delegate;
|
||||
private final Binding<? super T, U> binding;
|
||||
final AbstractDataTypeX<T> delegate;
|
||||
final Binding<? super T, U> binding;
|
||||
|
||||
ConvertedDataType(AbstractDataTypeX<T> delegate, Binding<? super T, U> binding) {
|
||||
super(delegate.getQualifiedName(), delegate.getCommentPart());
|
||||
@ -92,6 +94,11 @@ final class ConvertedDataType<T, U> extends AbstractDataTypeX<U> {
|
||||
).asConvertedDataType(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Row getRow() {
|
||||
return delegate.getRow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DataType<U> getSQLDataType() {
|
||||
return (DataType<U>) delegate.getSQLDataType();
|
||||
@ -167,6 +174,11 @@ final class ConvertedDataType<T, U> extends AbstractDataTypeX<U> {
|
||||
return delegate.tType0();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<U> uType0() {
|
||||
return binding.converter().toType();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Integer precision0() {
|
||||
return delegate.precision0();
|
||||
|
||||
@ -258,6 +258,11 @@ final class DataTypeProxy<T> extends AbstractDataType<T> {
|
||||
return type.tType0();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<T> uType0() {
|
||||
return type.uType0();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Integer precision0() {
|
||||
return defaultIfNull(overridePrecision, type.precision0());
|
||||
|
||||
@ -232,6 +232,8 @@ import org.jooq.types.YearToMonth;
|
||||
import org.jooq.types.YearToSecond;
|
||||
import org.jooq.util.postgres.PostgresUtils;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// ...
|
||||
|
||||
// ...
|
||||
@ -3430,12 +3432,9 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
render.sql("::").visit(((TableRecord<?>) value).getTable().getQualifiedName());
|
||||
}
|
||||
|
||||
private static final <T> T pgFromString(Class<T> type, String string) {
|
||||
return pgFromString(Converters.identity(type), string);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final <T> T pgFromString(Converter<?, T> converter, String string) {
|
||||
private static final <T> T pgFromString(Field<T> field, String string) {
|
||||
Converter<?, T> converter = field.getConverter();
|
||||
Class<T> type = Reflect.wrapper(converter.toType());
|
||||
|
||||
if (string == null)
|
||||
@ -3495,7 +3494,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
else if (type == UUID.class)
|
||||
return (T) UUID.fromString(string);
|
||||
else if (type.isArray())
|
||||
return (T) pgNewArray(type, string);
|
||||
return (T) pgNewArray(field, type, string);
|
||||
|
||||
|
||||
|
||||
@ -3506,7 +3505,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#11812] UDTRecords/TableRecords or InternalRecords that don't have an explicit converter
|
||||
&& (!InternalRecord.class.isAssignableFrom(type) || type == converter.fromType()))
|
||||
return (T) pgNewRecord(type, null, string);
|
||||
return (T) pgNewRecord(type, (AbstractRow<?>) field.getDataType().getRow(), string);
|
||||
else if (type == Object.class)
|
||||
return (T) string;
|
||||
|
||||
@ -3514,7 +3513,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
// which would cause a StackOverflowError, here!
|
||||
else if (type != converter.fromType()) {
|
||||
Converter<Object, T> c = (Converter<Object, T>) converter;
|
||||
return c.from(pgFromString(c.fromType(), string));
|
||||
return c.from(pgFromString(field("converted_field", ((ConvertedDataType<?, ?>) field.getDataType()).delegate), string));
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Class " + type + " is not supported");
|
||||
@ -3532,7 +3531,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
* @return The converted {@link UDTRecord}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static final Record pgNewRecord(Class<?> type, AbstractRow<Record> fields, final Object object) {
|
||||
static final Record pgNewRecord(Class<?> type, AbstractRow<?> fields, final Object object) {
|
||||
if (object == null)
|
||||
return null;
|
||||
|
||||
@ -3548,9 +3547,9 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
// - Temporal data
|
||||
// - Everything else: VARCHAR
|
||||
if (fields == null && Record.class.isAssignableFrom(type))
|
||||
fields = (AbstractRow<Record>) Tools.row0(Tools.fields(values.size(), SQLDataType.VARCHAR));
|
||||
fields = Tools.row0(Tools.fields(values.size(), SQLDataType.VARCHAR));
|
||||
|
||||
return Tools.newRecord(true, (Class<Record>) type, fields)
|
||||
return Tools.newRecord(true, (Class<Record>) type, (AbstractRow<Record>) fields)
|
||||
.operate(record -> {
|
||||
Row row = record.fieldsRow();
|
||||
|
||||
@ -3562,7 +3561,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
}
|
||||
|
||||
private static final <T> void pgSetValue(Record record, Field<T> field, String value) {
|
||||
record.set(field, pgFromString(field.getConverter(), value));
|
||||
record.set(field, pgFromString(field, value));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3574,21 +3573,24 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
* @param string A String representation of an array
|
||||
* @return The converted array
|
||||
*/
|
||||
private static final Object[] pgNewArray(Class<?> type, String string) {
|
||||
private static final Object[] pgNewArray(Field<?> field, Class<?> type, String string) {
|
||||
if (string == null)
|
||||
return null;
|
||||
|
||||
try {
|
||||
Class<?> component = type.getComponentType();
|
||||
|
||||
return Tools.map(
|
||||
toPGArray(string),
|
||||
v -> pgFromString(component, v),
|
||||
size -> (Object[]) java.lang.reflect.Array.newInstance(component, size)
|
||||
v -> pgFromString(field("array_element", field.getDataType().getArrayComponentDataType()), v),
|
||||
size -> (Object[]) java.lang.reflect.Array.newInstance(type.getComponentType(), size)
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new DataTypeException("Error while creating array", e);
|
||||
|
||||
// [#11823]
|
||||
if (type.getComponentType().getSimpleName().equals("UnknownType"))
|
||||
throw new DataTypeException("Error while creating array for UnknownType. Please provide an explicit Class<U> type to your converter, see https://github.com/jOOQ/jOOQ/issues/11823", e);
|
||||
else
|
||||
throw new DataTypeException("Error while creating array", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +360,7 @@ public class DefaultDataType<T> extends AbstractDataTypeX<T> {
|
||||
* [#3225] Performant constructor for creating derived types.
|
||||
*/
|
||||
DefaultDataType(
|
||||
DefaultDataType<T> t,
|
||||
AbstractDataType<T> t,
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
Integer length,
|
||||
@ -372,14 +372,14 @@ public class DefaultDataType<T> extends AbstractDataTypeX<T> {
|
||||
) {
|
||||
super(t.getQualifiedName(), NO_COMMENT);
|
||||
|
||||
this.dialect = t.dialect;
|
||||
this.sqlDataType = t.sqlDataType;
|
||||
this.uType = t.uType;
|
||||
this.tType = t.tType;
|
||||
this.typeName = t.typeName;
|
||||
this.castTypeName = t.castTypeName;
|
||||
this.castTypePrefix = t.castTypePrefix;
|
||||
this.castTypeSuffix = t.castTypeSuffix;
|
||||
this.dialect = t.getDialect();
|
||||
this.sqlDataType = t.getSQLDataType();
|
||||
this.uType = t.uType0();
|
||||
this.tType = t.tType0();
|
||||
this.typeName = t.typeName0();
|
||||
this.castTypeName = t.castTypeName0();
|
||||
this.castTypePrefix = t.castTypePrefix0();
|
||||
this.castTypeSuffix = t.castTypeSuffix0();
|
||||
|
||||
this.nullability = nullability;
|
||||
this.collation = collation;
|
||||
@ -392,9 +392,9 @@ public class DefaultDataType<T> extends AbstractDataTypeX<T> {
|
||||
|
||||
// [#10362] User bindings and/or converters need to be retained
|
||||
this.binding =
|
||||
t.binding instanceof org.jooq.impl.DefaultBinding.AbstractBinding
|
||||
? binding(this, (Converter<T, T>) t.binding.converter())
|
||||
: t.binding;
|
||||
t.getBinding() instanceof org.jooq.impl.DefaultBinding.AbstractBinding
|
||||
? binding(this, (Converter<T, T>) t.getBinding().converter())
|
||||
: t.getBinding();
|
||||
}
|
||||
|
||||
private static final Integer integerPrecision(Class<?> type, Integer precision) {
|
||||
@ -528,6 +528,11 @@ public class DefaultDataType<T> extends AbstractDataTypeX<T> {
|
||||
return tType;
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<T> uType0() {
|
||||
return uType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SQLDialect getDialect() {
|
||||
return dialect;
|
||||
|
||||
120
jOOQ/src/main/java/org/jooq/impl/RecordDataType.java
Normal file
120
jOOQ/src/main/java/org/jooq/impl/RecordDataType.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Tools.recordType;
|
||||
|
||||
import org.jooq.CharacterSet;
|
||||
import org.jooq.Collation;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Nullability;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordType;
|
||||
import org.jooq.Row;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A wrapper for anonymous array data types
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class RecordDataType<R extends Record> extends DefaultDataType<R> {
|
||||
|
||||
final Row row;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public RecordDataType(Row row) {
|
||||
// [#11829] TODO: Implement this correctly for UDTRecord, TableRecord, and EmbeddableRecord
|
||||
super(null, (Class<R>) recordType(row.size()), "record", "record");
|
||||
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3225] Performant constructor for creating derived types.
|
||||
*/
|
||||
RecordDataType(
|
||||
DefaultDataType<R> t,
|
||||
Row row,
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
Integer length,
|
||||
Nullability nullability,
|
||||
Collation collation,
|
||||
CharacterSet characterSet,
|
||||
boolean identity,
|
||||
Field<R> defaultValue
|
||||
) {
|
||||
super(t, precision, scale, length, nullability, collation, characterSet, identity, defaultValue);
|
||||
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
DefaultDataType<R> construct(
|
||||
Integer newPrecision,
|
||||
Integer newScale,
|
||||
Integer newLength,
|
||||
Nullability
|
||||
newNullability,
|
||||
Collation newCollation,
|
||||
CharacterSet newCharacterSet,
|
||||
boolean newIdentity,
|
||||
Field<R> newDefaultValue
|
||||
) {
|
||||
return new RecordDataType<>(
|
||||
this,
|
||||
row,
|
||||
newPrecision,
|
||||
newScale,
|
||||
newLength,
|
||||
newNullability,
|
||||
newCollation,
|
||||
newCharacterSet,
|
||||
newIdentity,
|
||||
(Field) newDefaultValue
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Row getRow() {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,6 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.Record;
|
||||
@ -99,7 +98,7 @@ final class RowField<ROW extends Row, REC extends Record> extends AbstractField<
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
RowField(final ROW row, Name as) {
|
||||
super(as, (DataType) SQLDataType.RECORD, CommentImpl.NO_COMMENT, binding(fromNullable(
|
||||
super(as, new RecordDataType<>(row), CommentImpl.NO_COMMENT, binding(fromNullable(
|
||||
Object.class,
|
||||
(Class<REC>) Tools.recordType(row.size()),
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user