diff --git a/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java b/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java new file mode 100644 index 0000000000..bc7bd2a105 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java @@ -0,0 +1,77 @@ +/* + * 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; + +import java.lang.reflect.Array; + +import org.jooq.impl.AbstractConverter; + +/** + * @author Lukas Eder + */ +@SuppressWarnings("unchecked") +final class ArrayComponentConverter extends AbstractConverter { + + private final Converter converter; + + public ArrayComponentConverter(Converter converter) { + super((Class) converter.fromType().getComponentType(), (Class) converter.toType().getComponentType()); + + this.converter = converter; + } + + @Override + public final U from(T t) { + if (t == null) + return null; + + T[] a = (T[]) Array.newInstance(fromType(), 1); + a[0] = t; + return converter.from(a)[0]; + } + + @Override + public final T to(U u) { + if (u == null) + return null; + + U[] a = (U[]) Array.newInstance(fromType(), 1); + a[0] = u; + return converter.to(a)[0]; + } +} diff --git a/jOOQ/src/main/java/org/jooq/ArrayConverter.java b/jOOQ/src/main/java/org/jooq/ArrayConverter.java new file mode 100644 index 0000000000..cbf9bfbb2f --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/ArrayConverter.java @@ -0,0 +1,72 @@ +/* + * 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; + +import static org.jooq.impl.Internal.arrayType; + +import org.jooq.impl.AbstractConverter; +import org.jooq.tools.Convert; + +/** + * A {@link Converter} that can convert arrays based on a delegate converter + * that can convert the array base types. + * + * @author Lukas Eder + */ +final class ArrayConverter extends AbstractConverter { + + final Converter converter; + final Converter inverse; + + public ArrayConverter(Converter converter) { + super(arrayType(converter.fromType()), arrayType(converter.toType())); + + this.converter = converter; + this.inverse = Converters.inverse(converter); + } + + @Override + public final U[] from(T[] t) { + return Convert.convertArray(t, converter); + } + + @Override + public final T[] to(U[] t) { + return Convert.convertArray(t, inverse); + } +} diff --git a/jOOQ/src/main/java/org/jooq/Converters.java b/jOOQ/src/main/java/org/jooq/Converters.java index 10a1256895..232824fd35 100644 --- a/jOOQ/src/main/java/org/jooq/Converters.java +++ b/jOOQ/src/main/java/org/jooq/Converters.java @@ -37,9 +37,6 @@ */ package org.jooq; -import static org.jooq.impl.Internal.arrayType; -import static org.jooq.tools.Convert.convertArray; - import java.io.Serializable; import java.util.function.Function; @@ -132,15 +129,26 @@ public class Converters extends AbstractConverter { return Converter.of(converter.toType(), converter.fromType(), converter::to, converter::from); } + /** + * Create a converter that can convert arrays with the component types being + * the argument converter's types. + */ public static Converter forArrays(final Converter converter) { - final Converter inverse = inverse(converter); - - return Converter.of(arrayType(converter.fromType()), arrayType(converter.toType()), - t -> convertArray(t, converter), - u -> convertArray(u, inverse) - ); + return new ArrayConverter<>(converter); } + /** + * Create a converter that can convert component types based on the argument + * converter, which converts array types. + */ + public static Converter forArrayComponents(final Converter converter) { + if (converter instanceof ArrayConverter) + return ((ArrayConverter) converter).converter; + else + return new ArrayComponentConverter<>(converter); + } + + Converters(Converter... chain) { super(chain[0].fromType(), chain[chain.length - 1].toType()); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java index a9fb7745df..750528070a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java @@ -526,7 +526,7 @@ implements } @Override - public final DataType getArrayDataType() { + public /* non-final */ DataType getArrayDataType() { return new ArrayDataType<>(this); } diff --git a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java index bd6de1f8d0..27d36edc2c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java @@ -45,6 +45,7 @@ import org.jooq.CharacterSet; import org.jooq.Collation; import org.jooq.Configuration; import org.jooq.Converter; +import org.jooq.Converters; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Nullability; @@ -55,6 +56,7 @@ import org.jooq.SQLDialect; import org.jooq.impl.QOM.GenerationOption; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * A DataType used for converted types using {@link Converter} @@ -132,6 +134,25 @@ final class ConvertedDataType extends AbstractDataTypeX { return (DataType) delegate.getSQLDataType(); } + @Override + public final DataType getArrayDataType() { + return delegate.getArrayDataType().asConvertedDataType(Converters.forArrays(binding.converter())); + } + + @Override + public final DataType getArrayComponentDataType() { + DataType d = delegate.getArrayComponentDataType(); + + return d == null ? null : d.asConvertedDataType(Converters.forArrayComponents((Converter) binding.converter())); + } + + @Override + public final Class getArrayComponentType() { + DataType d = getArrayComponentDataType(); + + return d == null ? null : d.getType(); + } + @Override public final DataType getDataType(Configuration configuration) { return (DataType) delegate.getDataType(configuration); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 36da206960..440a0a341b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -671,7 +671,12 @@ public class DefaultBinding implements Binding { // Type-specific subclasses API // ----------------------------------------------------------------------------------------------------------------- - abstract static class AbstractBinding implements org.jooq.Binding { + /** + * An internal binding class for default data type binding implementations. + *

+ * This base class can be safely assumed to not leak into custom bindings. + */ + abstract static class InternalBinding implements org.jooq.Binding { static final Set NEEDS_PRECISION_SCALE_ON_BIGDECIMAL = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB); static final Set REQUIRES_JSON_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); static final Set NO_SUPPORT_ENUM_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); @@ -685,7 +690,7 @@ public class DefaultBinding implements Binding { final Converter converter; final boolean attachable; - AbstractBinding(DataType dataType, Converter converter) { + InternalBinding(DataType dataType, Converter converter) { this.dataType = dataType; this.converter = converter; @@ -1078,16 +1083,16 @@ public class DefaultBinding implements Binding { } } - static final class DelegatingBinding extends AbstractBinding { + static final class DelegatingBinding extends InternalBinding { private final Converter delegatingConverter; - private final AbstractBinding delegatingBinding; + private final InternalBinding delegatingBinding; DelegatingBinding( DataType originalDataType, Converter delegatingConverter, Converter originalConverter, - Function, ? extends AbstractBinding> f + Function, ? extends InternalBinding> f ) { super(originalDataType, originalConverter); @@ -1141,7 +1146,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultArrayBinding extends AbstractBinding { + static final class DefaultArrayBinding extends InternalBinding { private static final Set REQUIRES_ARRAY_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); @@ -1564,7 +1569,7 @@ public class DefaultBinding implements Binding { - static final class DefaultBigDecimalBinding extends AbstractBinding { + static final class DefaultBigDecimalBinding extends InternalBinding { private static final Set BIND_AS_STRING = SQLDialect.supportedBy(SQLITE); DefaultBigDecimalBinding(DataType dataType, Converter converter) { @@ -1625,7 +1630,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultBigIntegerBinding extends AbstractBinding { + static final class DefaultBigIntegerBinding extends InternalBinding { private static final Set BIND_AS_STRING = SQLDialect.supportedBy(SQLITE); DefaultBigIntegerBinding(DataType dataType, Converter converter) { @@ -1689,7 +1694,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultBlobBinding extends AbstractBinding { + static final class DefaultBlobBinding extends InternalBinding { DefaultBlobBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -1751,7 +1756,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultBooleanBinding extends AbstractBinding { + static final class DefaultBooleanBinding extends InternalBinding { private static final Set BIND_AS_1_0 = SQLDialect.supportedBy(FIREBIRD, SQLITE); @@ -1879,7 +1884,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultByteBinding extends AbstractBinding { + static final class DefaultByteBinding extends InternalBinding { DefaultByteBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -1939,7 +1944,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultBytesBinding extends AbstractBinding { + static final class DefaultBytesBinding extends InternalBinding { // [#12956] Starting from H2 2.0, we can't use byte[] for BLOB anymore, if they're // larger than 1MB @@ -2152,7 +2157,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultClobBinding extends AbstractBinding { + static final class DefaultClobBinding extends InternalBinding { DefaultClobBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -2199,7 +2204,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultDateBinding extends AbstractBinding { + static final class DefaultDateBinding extends InternalBinding { private static final Set INLINE_AS_STRING_LITERAL = SQLDialect.supportedBy(SQLITE); DefaultDateBinding(DataType dataType, Converter converter) { @@ -2393,7 +2398,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultDayToSecondBinding extends AbstractBinding { + static final class DefaultDayToSecondBinding extends InternalBinding { private static final Set REQUIRE_PG_INTERVAL = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); private static final Set REQUIRE_STANDARD_INTERVAL = SQLDialect.supportedBy(H2); @@ -2488,7 +2493,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultDoubleBinding extends AbstractBinding { + static final class DefaultDoubleBinding extends InternalBinding { static final Set REQUIRES_LITERAL_CAST = SQLDialect.supportedBy(H2); DefaultDoubleBinding(DataType dataType, Converter converter) { @@ -2657,7 +2662,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultEnumTypeBinding extends AbstractBinding { + static final class DefaultEnumTypeBinding extends InternalBinding { private static final Set REQUIRE_ENUM_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); DefaultEnumTypeBinding(DataType dataType, Converter converter) { @@ -2748,7 +2753,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultFloatBinding extends AbstractBinding { + static final class DefaultFloatBinding extends InternalBinding { DefaultFloatBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -2840,7 +2845,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultIntegerBinding extends AbstractBinding { + static final class DefaultIntegerBinding extends InternalBinding { DefaultIntegerBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -2897,7 +2902,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultLongBinding extends AbstractBinding { + static final class DefaultLongBinding extends InternalBinding { DefaultLongBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -3130,7 +3135,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultOffsetDateTimeBinding extends AbstractBinding { + static final class DefaultOffsetDateTimeBinding extends InternalBinding { DefaultOffsetDateTimeBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -3321,7 +3326,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultOffsetTimeBinding extends AbstractBinding { + static final class DefaultOffsetTimeBinding extends InternalBinding { DefaultOffsetTimeBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -3435,7 +3440,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultInstantBinding extends AbstractBinding { + static final class DefaultInstantBinding extends InternalBinding { private static final Converter CONVERTER = Converter.ofNullable( OffsetDateTime.class, @@ -3493,7 +3498,7 @@ public class DefaultBinding implements Binding { } } - static final class CommercialOnlyBinding extends AbstractBinding { + static final class CommercialOnlyBinding extends InternalBinding { CommercialOnlyBinding(DataType dataType, Converter converter) { super(dataType, converter); } @@ -3532,7 +3537,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultOtherBinding extends AbstractBinding { + static final class DefaultOtherBinding extends InternalBinding { DefaultOtherBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -3541,7 +3546,7 @@ public class DefaultBinding implements Binding { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override final void set0(BindingSetStatementContext ctx, Object value) throws SQLException { - AbstractBinding b = (AbstractBinding) binding(DefaultDataType.getDataType(ctx.dialect(), value.getClass())); + InternalBinding b = (InternalBinding) binding(DefaultDataType.getDataType(ctx.dialect(), value.getClass())); // [#7370] Prevent a stack overflow error on unsupported data types if (b instanceof DefaultOtherBinding) @@ -3630,7 +3635,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultRowIdBinding extends AbstractBinding { + static final class DefaultRowIdBinding extends InternalBinding { DefaultRowIdBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -3667,7 +3672,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultRecordBinding extends AbstractBinding { + static final class DefaultRecordBinding extends InternalBinding { private static final Set REQUIRE_RECORD_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); DefaultRecordBinding(DataType dataType, Converter converter) { @@ -3880,7 +3885,7 @@ public class DefaultBinding implements Binding { // which would cause a StackOverflowError, here! else if (type != converter.fromType()) { Converter c = (Converter) converter; - return c.from(pgFromString(ctx, field("converted_field", ((ConvertedDataType) field.getDataType()).delegate), string)); + return c.from(pgFromString(ctx, field("converted_field", ((ConvertedDataType) field.getDataType()).delegate()), string)); } throw new UnsupportedOperationException("Class " + type + " is not supported"); @@ -3963,7 +3968,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultResultBinding extends AbstractBinding, U> { + static final class DefaultResultBinding extends InternalBinding, U> { DefaultResultBinding(DataType> dataType, Converter, U> converter) { super(dataType, converter); @@ -4058,7 +4063,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultShortBinding extends AbstractBinding { + static final class DefaultShortBinding extends InternalBinding { DefaultShortBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -4115,7 +4120,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultStringBinding extends AbstractBinding { + static final class DefaultStringBinding extends InternalBinding { DefaultStringBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -4195,7 +4200,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultNStringBinding extends AbstractBinding { + static final class DefaultNStringBinding extends InternalBinding { private final DefaultStringBinding fallback; DefaultNStringBinding(DataType dataType, Converter converter) { @@ -4290,7 +4295,7 @@ public class DefaultBinding implements Binding { } } - static final class DefaultTimeBinding extends AbstractBinding { + static final class DefaultTimeBinding extends InternalBinding { DefaultTimeBinding(DataType