diff --git a/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java b/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java index dfcee83d13..1c18e514c7 100644 --- a/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java +++ b/jOOQ/src/main/java/org/jooq/ArrayComponentConverter.java @@ -39,39 +39,39 @@ package org.jooq; import java.lang.reflect.Array; -import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; /** * @author Lukas Eder */ @SuppressWarnings("unchecked") -final class ArrayComponentConverter extends AbstractConverter { +final class ArrayComponentConverter extends AbstractScopedConverter { - final Converter converter; + final ScopedConverter converter; - public ArrayComponentConverter(Converter converter) { + public ArrayComponentConverter(ScopedConverter converter) { super((Class) converter.fromType().getComponentType(), (Class) converter.toType().getComponentType()); this.converter = converter; } @Override - public final U from(T t) { + public final U from(T t, ConverterScope scope) { if (t == null) return null; T[] a = (T[]) Array.newInstance(fromType(), 1); a[0] = t; - return converter.from(a)[0]; + return converter.from(a, scope)[0]; } @Override - public final T to(U u) { + public final T to(U u, ConverterScope scope) { if (u == null) return null; U[] a = (U[]) Array.newInstance(fromType(), 1); a[0] = u; - return converter.to(a)[0]; + return converter.to(a, scope)[0]; } } diff --git a/jOOQ/src/main/java/org/jooq/ArrayConverter.java b/jOOQ/src/main/java/org/jooq/ArrayConverter.java index cbf9bfbb2f..269a905f00 100644 --- a/jOOQ/src/main/java/org/jooq/ArrayConverter.java +++ b/jOOQ/src/main/java/org/jooq/ArrayConverter.java @@ -39,7 +39,7 @@ package org.jooq; import static org.jooq.impl.Internal.arrayType; -import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; import org.jooq.tools.Convert; /** @@ -48,12 +48,12 @@ import org.jooq.tools.Convert; * * @author Lukas Eder */ -final class ArrayConverter extends AbstractConverter { +final class ArrayConverter extends AbstractScopedConverter { - final Converter converter; - final Converter inverse; + final ScopedConverter converter; + final ScopedConverter inverse; - public ArrayConverter(Converter converter) { + public ArrayConverter(ScopedConverter converter) { super(arrayType(converter.fromType()), arrayType(converter.toType())); this.converter = converter; @@ -61,12 +61,12 @@ final class ArrayConverter extends AbstractConverter { } @Override - public final U[] from(T[] t) { + public final U[] from(T[] t, ConverterScope scope) { return Convert.convertArray(t, converter); } @Override - public final T[] to(U[] t) { + public final T[] to(U[] t, ConverterScope scope) { return Convert.convertArray(t, inverse); } } diff --git a/jOOQ/src/main/java/org/jooq/BindingScope.java b/jOOQ/src/main/java/org/jooq/BindingScope.java index 8c6fe0f5ac..9fd29dfe72 100644 --- a/jOOQ/src/main/java/org/jooq/BindingScope.java +++ b/jOOQ/src/main/java/org/jooq/BindingScope.java @@ -98,4 +98,9 @@ import java.sql.SQLOutput; */ public interface BindingScope extends Scope { + /** + * Get the {@link ConverterScope} from the context of this + * {@link BindingScope}. + */ + ConverterScope converterScope(); } diff --git a/jOOQ/src/main/java/org/jooq/Converter.java b/jOOQ/src/main/java/org/jooq/Converter.java index 47fb648a12..b1c8f0b8f2 100644 --- a/jOOQ/src/main/java/org/jooq/Converter.java +++ b/jOOQ/src/main/java/org/jooq/Converter.java @@ -45,6 +45,7 @@ import java.util.function.Function; import org.jooq.exception.DataTypeException; import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; import org.jooq.impl.SQLDataType; import org.jetbrains.annotations.NotNull; @@ -171,15 +172,15 @@ public interface Converter extends Serializable { Function from, Function to ) { - return new AbstractConverter(fromType, toType) { + return new AbstractScopedConverter(fromType, toType) { @Override - public final U from(T t) { + public final U from(T t, ConverterScope scope) { return from.apply(t); } @Override - public final T to(U u) { + public final T to(U u, ConverterScope scope) { return to.apply(u); } }; diff --git a/jOOQ/src/main/java/org/jooq/ConverterScope.java b/jOOQ/src/main/java/org/jooq/ConverterScope.java new file mode 100644 index 0000000000..b3a9b5b58b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/ConverterScope.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * A {@link Scope} that models the life cycle of a data type conversion call to + * {@link Converter#from(Object)} or {@link Converter#to(Object)}. + *

+ * Implementations + */ +public interface ConverterScope extends Scope { + +} diff --git a/jOOQ/src/main/java/org/jooq/Converters.java b/jOOQ/src/main/java/org/jooq/Converters.java index a4502a3ca2..232ce2ef43 100644 --- a/jOOQ/src/main/java/org/jooq/Converters.java +++ b/jOOQ/src/main/java/org/jooq/Converters.java @@ -38,10 +38,12 @@ package org.jooq; import java.io.Serializable; +import java.util.function.BiFunction; import java.util.function.Function; import org.jooq.exception.DataTypeException; import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; import org.jooq.impl.IdentityConverter; import org.jooq.impl.SQLDataType; @@ -57,15 +59,15 @@ import org.jetbrains.annotations.NotNull; * @author Lukas Eder */ @SuppressWarnings({ "rawtypes", "unchecked" }) -public class Converters extends AbstractConverter { +public final class Converters extends AbstractScopedConverter { - final Converter[] chain; + final ScopedConverter[] chain; /** * Create an identity converter. */ @NotNull - public static Converter identity(final Class type) { + public static ScopedConverter identity(final Class type) { return new IdentityConverter(type); } @@ -77,7 +79,7 @@ public class Converters extends AbstractConverter { */ @Deprecated(forRemoval = true, since = "3.14") @NotNull - public static Converter of() { + public static ScopedConverter of() { return new Converters(); } @@ -89,44 +91,63 @@ public class Converters extends AbstractConverter { */ @Deprecated(forRemoval = true, since = "3.14") @NotNull - public static Converter of(Converter converter) { - return new Converters(converter); + public static ScopedConverter of(Converter converter) { + return new Converters(ScopedConverter.scoped(converter)); } /** * Chain two converters. */ @NotNull - public static Converter of(Converter c1, Converter c2) { - return new Converters(c1, c2); + public static ScopedConverter of(Converter c1, Converter c2) { + return new Converters( + ScopedConverter.scoped(c1), + ScopedConverter.scoped(c2) + ); } /** * Chain three converters. */ @NotNull - public static Converter of(Converter c1, Converter c2, Converter c3) { - return new Converters(c1, c2, c3); + public static ScopedConverter of(Converter c1, Converter c2, Converter c3) { + return new Converters( + ScopedConverter.scoped(c1), + ScopedConverter.scoped(c2), + ScopedConverter.scoped(c3) + ); } /** * Chain four converters. */ @NotNull - public static Converter of(Converter c1, Converter c2, Converter c3, Converter c4) { - return new Converters(c1, c2, c3, c4); + public static ScopedConverter of(Converter c1, Converter c2, Converter c3, Converter c4) { + return new Converters( + ScopedConverter.scoped(c1), + ScopedConverter.scoped(c2), + ScopedConverter.scoped(c3), + ScopedConverter.scoped(c4) + ); } /** * Inverse a converter. */ public static Converter inverse(final Converter converter) { + return inverse(ScopedConverter.scoped(converter)); + } - // [#11099] Allow instanceof checks on IdentityConverter for performance reasons - if (converter instanceof IdentityConverter) - return (Converter) converter; + /** + * Inverse a converter. + */ + public static ScopedConverter inverse(final ScopedConverter converter) { + + // [#11099] Allow instanceof checks on IdentityConverter for performance reasons + if (converter instanceof IdentityConverter) + return (ScopedConverter) converter; else - return Converter.of(converter.toType(), converter.fromType(), converter::to, converter::from); + return ScopedConverter.of(converter.toType(), converter.fromType(), converter::to, converter::from); } /** @@ -134,6 +155,14 @@ public class Converters extends AbstractConverter { * the argument converter's types. */ public static Converter forArrays(Converter converter) { + return forArrays(ScopedConverter.scoped(converter)); + } + + /** + * Create a converter that can convert arrays with the component types being + * the argument converter's types. + */ + public static ScopedConverter forArrays(ScopedConverter converter) { if (converter instanceof ArrayComponentConverter a) return a.converter; else @@ -145,35 +174,42 @@ public class Converters extends AbstractConverter { * converter, which converts array types. */ public static Converter forArrayComponents(Converter converter) { + return forArrayComponents(ScopedConverter.scoped(converter)); + } + + /** + * Create a converter that can convert component types based on the argument + * converter, which converts array types. + */ + public static Converter forArrayComponents(ScopedConverter converter) { if (converter instanceof ArrayConverter a) return a.converter; else return new ArrayComponentConverter<>(converter); } - - Converters(Converter... chain) { + Converters(ScopedConverter... chain) { super(chain[0].fromType(), chain[chain.length - 1].toType()); this.chain = chain; } @Override - public final U from(T t) { + public final U from(T t, ConverterScope scope) { Object result = t; for (int i = 0; i < chain.length; i++) - result = chain[i].from(result); + result = chain[i].from(result, scope); return (U) result; } @Override - public final T to(U u) { + public final T to(U u, ConverterScope scope) { Object result = u; for (int i = chain.length - 1; i >= 0; i--) - result = chain[i].to(result); + result = chain[i].to(result, scope); return (T) result; } @@ -201,6 +237,12 @@ public class Converters extends AbstractConverter { : f.apply(t) : t -> t == null ? null : f.apply(t); } + static final BiFunction nullable(BiFunction f) { + return f instanceof Serializable + ? (BiFunction & Serializable) (t, x) -> t == null ? null + : f.apply(t, x) : (t, x) -> t == null ? null : f.apply(t, x); + } + static final Function notImplemented() { return t -> { throw new DataTypeException("Conversion function not implemented"); }; } diff --git a/jOOQ/src/main/java/org/jooq/DataType.java b/jOOQ/src/main/java/org/jooq/DataType.java index f61ee27825..070c9b29df 100644 --- a/jOOQ/src/main/java/org/jooq/DataType.java +++ b/jOOQ/src/main/java/org/jooq/DataType.java @@ -139,7 +139,7 @@ public interface DataType extends Named { * Get the converter associated with this data type. */ @NotNull - Converter getConverter(); + ScopedConverter getConverter(); /** * Retrieve the Java type associated with this data type. diff --git a/jOOQ/src/main/java/org/jooq/ExecuteContext.java b/jOOQ/src/main/java/org/jooq/ExecuteContext.java index 7692d97bdc..e206316a65 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteContext.java @@ -71,6 +71,12 @@ import org.jetbrains.annotations.Nullable; */ public interface ExecuteContext extends Scope { + /** + * Get a {@link ConverterScope} for the scope of this + * {@link ExecuteContext}. + */ + ConverterScope converterScope(); + /** * The connection to be used in this execute context. *

diff --git a/jOOQ/src/main/java/org/jooq/ExecuteScope.java b/jOOQ/src/main/java/org/jooq/ExecuteScope.java index d9d83411ed..f9ab9d87fe 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteScope.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteScope.java @@ -44,6 +44,12 @@ import org.jetbrains.annotations.Nullable; */ public interface ExecuteScope extends Scope { + /** + * Get the {@link ConverterScope} from the context of this + * {@link ExecuteScope}. + */ + ConverterScope converterScope(); + /** * The {@link ExecuteContext} that created this scope. * diff --git a/jOOQ/src/main/java/org/jooq/ScopedConverter.java b/jOOQ/src/main/java/org/jooq/ScopedConverter.java new file mode 100644 index 0000000000..a8ef16f455 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/ScopedConverter.java @@ -0,0 +1,186 @@ +/* + * 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.Converters.nullable; +import static org.jooq.impl.Internal.converterScope; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A special type of {@link Converter} with alternative + * {@link #from(Object, ConverterScope)} and {@link #to(Object, ConverterScope)} + * methods. + *

+ * This special converter type can be used wherever an ordinary + * {@link Converter} is used. jOOQ internal call sites will call the alternative + * {@link #from(Object, ConverterScope)} and {@link #to(Object, ConverterScope)} + * methods, instead of {@link #from(Object)} and {@link #to(Object)}, allowing + * for accessing global {@link Configuration#data()} content. + */ +public interface ScopedConverter extends Converter { + + /** + * Construct a new converter from functions. + * + * @param the database type. + * @param the user type. + * @param fromType The database type. + * @param toType The user type. + * @param from A function converting from T to U when reading from the + * database. + * @param to A function converting from U to T when writing to the database. + * @return The converter. + * @see Converter + */ + @NotNull + static ScopedConverter of( + Class fromType, + Class toType, + BiFunction from, + BiFunction to + ) { + return new AbstractScopedConverter(fromType, toType) { + + @Override + public final U from(T t, ConverterScope scope) { + return from.apply(t, scope); + } + + @Override + public final T to(U u, ConverterScope scope) { + return to.apply(u, scope); + } + }; + } + + /** + * Construct a new converter from functions. + *

+ * This works like {@link Converter#of(Class, Class, Function, Function)}, + * except that both conversion {@link Function}s are decorated with a + * function that always returns null for null + * inputs. + *

+ * Example: + *

+ *


+     * Converter<String, Integer> converter =
+     *   Converter.ofNullable(String.class, Integer.class, Integer::parseInt, Object::toString);
+     *
+     * // No exceptions thrown
+     * assertNull(converter.from(null));
+     * assertNull(converter.to(null));
+     * 
+ * + * @param the database type + * @param the user type + * @param fromType The database type + * @param toType The user type + * @param from A function converting from T to U when reading from the + * database. + * @param to A function converting from U to T when writing to the database. + * @return The converter. + * @see Converter + */ + @NotNull + static ScopedConverter ofNullable( + Class fromType, + Class toType, + BiFunction from, + BiFunction to + ) { + return of(fromType, toType, nullable(from), nullable(to)); + } + + /** + * Turn a {@link Converter} into a {@link ScopedConverter}. + */ + @NotNull + static ScopedConverter scoped(Converter converter) { + if (converter instanceof ScopedConverter s) + return s; + else + return new AbstractScopedConverter(converter.fromType(), converter.toType()) { + @Override + public U from(T t, ConverterScope scope) { + return converter.from(t); + } + + @Override + public T to(U u, ConverterScope scope) { + return converter.to(u); + } + }; + } + + /** + * Read and convert a database object to a user object. + * + * @param databaseObject The database object. + * @param scope The scope of this conversion. + * @return The user object. + */ + U from(T databaseObject, ConverterScope scope); + + /** + * Convert and write a user object to a database object. + * + * @param userObject The user object. + * @param scope The scope of this conversion. + * @return The database object. + */ + T to(U userObject, ConverterScope scope); + + @Override + default T to(U userObject) { + return to(userObject, converterScope()); + } + + @Override + default U from(T databaseObject) { + return from(databaseObject, converterScope()); + } +} diff --git a/jOOQ/src/main/java/org/jooq/Typed.java b/jOOQ/src/main/java/org/jooq/Typed.java index 9de1fe623f..3f6905fde6 100644 --- a/jOOQ/src/main/java/org/jooq/Typed.java +++ b/jOOQ/src/main/java/org/jooq/Typed.java @@ -62,7 +62,7 @@ public interface Typed extends QueryPart { * the generated object. */ @NotNull - Converter getConverter(); + ScopedConverter getConverter(); /** * The object's underlying {@link Binding}. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index 826965c234..6ed63b5683 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -78,6 +78,7 @@ import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Context; +import org.jooq.ConverterScope; import org.jooq.DSLContext; import org.jooq.ExecuteContext; import org.jooq.Field; @@ -235,6 +236,11 @@ abstract class AbstractContext> extends AbstractScope imple // ExecuteScope API // ------------------------------------------------------------------------ + @Override + public final ConverterScope converterScope() { + return ctx != null ? ctx.converterScope() : Tools.converterScope(configuration()); + } + @Override public final ExecuteContext executeContext() { return ctx; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java index 1a59507160..428b61f895 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java @@ -106,6 +106,7 @@ import org.jooq.Result; import org.jooq.Row; import org.jooq.SQLDialect; import org.jooq.Schema; +import org.jooq.ScopedConverter; import org.jooq.Table; import org.jooq.XML; import org.jooq.impl.QOM.GenerationLocation; @@ -114,6 +115,8 @@ import org.jooq.impl.QOM.UEmpty; import org.jooq.types.Interval; import org.jooq.types.UNumber; +import org.jetbrains.annotations.NotNull; + // ... /** @@ -563,8 +566,8 @@ implements } @Override - public final Converter getConverter() { - return getBinding().converter(); + public final ScopedConverter getConverter() { + return (@NotNull ScopedConverter) ScopedConverter.scoped(getBinding().converter()); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractExecuteScope.java b/jOOQ/src/main/java/org/jooq/impl/AbstractExecuteScope.java index d6286f5680..2e7ad9a024 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractExecuteScope.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractExecuteScope.java @@ -37,6 +37,7 @@ */ package org.jooq.impl; +import org.jooq.ConverterScope; import org.jooq.ExecuteContext; import org.jooq.ExecuteScope; @@ -55,6 +56,11 @@ abstract class AbstractExecuteScope extends AbstractScope implements ExecuteScop this.ctx = ctx; } + @Override + public final ConverterScope converterScope() { + return ctx.converterScope(); + } + @Override public ExecuteContext executeContext() { return ctx; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index 948a5bb0e4..bb52caa0d8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -40,9 +40,11 @@ package org.jooq.impl; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static org.jooq.ScopedConverter.scoped; import static org.jooq.conf.SettingsTools.updatablePrimaryKeys; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.converterOrFail; +import static org.jooq.impl.Tools.converterScope; import static org.jooq.impl.Tools.embeddedFields; import static org.jooq.impl.Tools.indexFail; import static org.jooq.impl.Tools.indexOrFail; @@ -102,7 +104,6 @@ import org.jooq.XMLFormat; import org.jooq.exception.IOException; import org.jooq.exception.InvalidResultException; import org.jooq.exception.MappingException; -import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; import org.jetbrains.annotations.NotNull; @@ -335,12 +336,12 @@ abstract class AbstractRecord extends AbstractStore implements Record { @Override public final U get(Field field, Class type) { Object t = get(field); - return (U) converterOrFail(this, t, (Class) field.getType(), type).from(t); + return (U) converterOrFail(this, t, (Class) field.getType(), type).from(t, converterScope(this)); } @Override public final U get(Field field, Converter converter) { - return converter.from(get(field)); + return scoped(converter).from(get(field), converterScope(this)); } @Override @@ -351,7 +352,7 @@ abstract class AbstractRecord extends AbstractStore implements Record { @Override public final U get(int index, Class type) { Object t = get(index); - return (U) converterOrFail(this, t, (Class) field(safeIndex(index)).getType(), type).from(t); + return (U) converterOrFail(this, t, (Class) field(safeIndex(index)).getType(), type).from(t, converterScope(this)); } @Override @@ -474,7 +475,7 @@ abstract class AbstractRecord extends AbstractStore implements Record { @Override public final void set(Field field, U value, Converter converter) { - set(field, converter.to(value)); + set(field, scoped(converter).to(value, converterScope(this))); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRow.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRow.java index 4b8e3d79b9..015d5bd0fb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRow.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRow.java @@ -64,6 +64,7 @@ import org.jooq.Record; import org.jooq.Row; import org.jooq.Row1; import org.jooq.Row2; +import org.jooq.ScopedConverter; import org.jooq.SelectField; // ... import org.jooq.impl.QOM.UnmodifiableList; @@ -151,7 +152,7 @@ abstract class AbstractRow extends AbstractQueryPart implement } @Override - public final Converter getConverter() { + public final ScopedConverter getConverter() { return rf().getConverter(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractScopedConverter.java b/jOOQ/src/main/java/org/jooq/impl/AbstractScopedConverter.java new file mode 100644 index 0000000000..b482c75fba --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractScopedConverter.java @@ -0,0 +1,55 @@ +/* + * 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 org.jooq.ScopedConverter; + +/** + * @author Lukas Eder + */ +public abstract class AbstractScopedConverter extends AbstractConverter implements ScopedConverter { + + public AbstractScopedConverter(Class fromType, Class toType) { + super(fromType, toType); + } + + @Override + public String toString() { + return "ScopedConverter [ " + fromType().getName() + " -> " + toType().getName() + " ]"; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index af2668df12..407bbffa8e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -107,6 +107,7 @@ import org.jooq.Row; import org.jooq.RowId; import org.jooq.SQL; import org.jooq.Schema; +import org.jooq.ScopedConverter; import org.jooq.Select; import org.jooq.SelectField; import org.jooq.Table; @@ -203,7 +204,7 @@ implements } @Override - public final Converter getConverter() { + public final ScopedConverter getConverter() { return getDataType().getConverter(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java index e4897ca66d..5a3b3a22cf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java @@ -46,6 +46,7 @@ import org.jooq.DataType; import org.jooq.Field; import org.jooq.Generator; import org.jooq.Name; +import org.jooq.ScopedConverter; import org.jooq.Typed; /** @@ -71,7 +72,7 @@ abstract class AbstractTypedNamed extends AbstractNamed implements Typed { // ------------------------------------------------------------------------- @Override - public final Converter getConverter() { + public final ScopedConverter getConverter() { return getDataType().getConverter(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java b/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java index fcd3972709..0ff686a796 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java @@ -56,6 +56,7 @@ import jakarta.xml.bind.annotation.XmlRootElement; import javax.xml.namespace.QName; import org.jooq.Converter; +import org.jooq.ConverterScope; import org.jooq.XML; /** @@ -80,21 +81,21 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding { return converter; } - private static final class XMLasObjectConverter implements Converter { + private static final class XMLasObjectConverter extends AbstractScopedConverter { - Class type; - XmlRootElement root; - transient JAXBContext ctx; + XmlRootElement root; + transient JAXBContext ctx; private XMLasObjectConverter(Class type) { - this.type = type; + super(XML.class, type); + this.root = type.getAnnotation(XmlRootElement.class); this.ctx = initCtx(); } private final JAXBContext initCtx() { try { - return JAXBContext.newInstance(type); + return JAXBContext.newInstance(toType()); } catch (JAXBException e) { throw new DataBindingException(e); @@ -102,15 +103,15 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding { } @Override - public T from(XML t) { + public T from(XML t, ConverterScope scope) { if (t == null) return null; - return JAXB.unmarshal(new StringReader("" + t), type); + return JAXB.unmarshal(new StringReader("" + t), toType()); } @Override - public XML to(T u) { + public XML to(T u, ConverterScope scope) { if (u == null) return null; @@ -118,9 +119,8 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding { StringWriter s = new StringWriter(); Object o = u; - if (root == null) { - o = new JAXBElement<>(new QName(decapitalize(type.getSimpleName())), type, u); - } + if (root == null) + o = new JAXBElement<>(new QName(decapitalize(toType().getSimpleName())), toType(), u); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FRAGMENT, true); @@ -132,16 +132,6 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding { } } - @Override - public Class fromType() { - return XML.class; - } - - @Override - public Class toType() { - return type; - } - private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Convert.java b/jOOQ/src/main/java/org/jooq/impl/Convert.java index 12859ce1d7..bc2b8b789e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Convert.java +++ b/jOOQ/src/main/java/org/jooq/impl/Convert.java @@ -40,7 +40,9 @@ package org.jooq.impl; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static org.jooq.ScopedConverter.scoped; import static org.jooq.impl.Internal.arrayType; +import static org.jooq.impl.Internal.converterScope; import static org.jooq.impl.Tools.configuration; import static org.jooq.impl.Tools.emulateMultiset; import static org.jooq.tools.reflect.Reflect.accessible; @@ -90,11 +92,10 @@ import java.util.Set; import java.util.UUID; import java.util.regex.Pattern; -import jakarta.xml.bind.JAXB; - // ... import org.jooq.Converter; import org.jooq.ConverterProvider; +import org.jooq.ConverterScope; import org.jooq.EnumType; import org.jooq.Field; import org.jooq.JSON; @@ -124,6 +125,8 @@ import org.jooq.types.ULong; import org.jooq.types.UShort; import org.jooq.util.xml.jaxb.InformationSchema; +import jakarta.xml.bind.JAXB; + /** * Utility methods for type conversions *

@@ -400,7 +403,7 @@ final class Convert { } static final U[] convertCollection(Collection from, Class to){ - return new ConvertAll(to).from(from); + return new ConvertAll(to).from(from, converterScope()); } /** @@ -430,10 +433,10 @@ final class Convert { Class fromType = converter.fromType(); if (fromType == Object.class) - return converter.from((T) from); + return scoped(converter).from((T) from, converterScope()); ConvertAll convertAll = new ConvertAll<>(fromType); - return converter.from(convertAll.from(from)); + return scoped(converter).from(convertAll.from(from, converterScope()), converterScope()); } /** @@ -544,7 +547,7 @@ final class Convert { List result = new ArrayList<>(collection.size()); for (Object o : collection) - result.add(convert(all.from(o), converter)); + result.add(convert(all.from(o, converterScope()), converter)); return result; } @@ -557,17 +560,20 @@ final class Convert { /** * The converter to convert them all. */ - private static class ConvertAll implements Converter { + private static final class ConvertAll extends AbstractScopedConverter { private final Class toClass; + @SuppressWarnings("unchecked") ConvertAll(Class toClass) { + super(Object.class, (Class) toClass); + this.toClass = toClass; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override - public U from(Object from) { + public U from(Object from, ConverterScope scope) { if (from == null) { // [#936] If types are converted to primitives, the result must not @@ -608,7 +614,7 @@ final class Convert { // [#12557] Anything can be unwrapped from Optional else if (fromClass == Optional.class) - return from(((Optional) from).orElse(null)); + return from(((Optional) from).orElse(null), scope); // Regular checks else if (fromClass == byte[].class) { @@ -1396,21 +1402,10 @@ final class Convert { } @Override - public Object to(U to) { + public Object to(U to, ConverterScope scope) { return to; } - @Override - public Class fromType() { - return Object.class; - } - - @SuppressWarnings("unchecked") - @Override - public Class toType() { - return (Class) toClass; - } - /** * Convert a long timestamp (millis) to any date type. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java index 5523271f34..ee7fc2b7b1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java @@ -37,6 +37,8 @@ */ package org.jooq.impl; +import static org.jooq.impl.Internal.converterScope; + import java.util.List; import java.util.Map; @@ -54,6 +56,7 @@ import org.jooq.Record; import org.jooq.Result; import org.jooq.Row; import org.jooq.SQLDialect; +import org.jooq.ScopedConverter; import org.jooq.exception.DataTypeException; import org.jooq.impl.DefaultBinding.InternalBinding; import org.jooq.impl.QOM.GenerationLocation; @@ -306,7 +309,7 @@ final class ConvertedDataType extends AbstractDataTypeX { // [#3200] Try to convert arbitrary objects to T else - return ((Converter) getConverter()).from(delegate.convert(object)); + return ((ScopedConverter) getConverter()).from(delegate.convert(object), converterScope()); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 1d44e984f7..7a081f372a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -84,6 +84,7 @@ import org.jooq.Field; // ... import org.jooq.Record; import org.jooq.Result; +import org.jooq.ScopedConverter; import org.jooq.exception.ControlFlowSignal; import org.jooq.tools.JooqLogger; import org.jooq.tools.jdbc.JDBC41ResultSet; @@ -1570,7 +1571,7 @@ final class CursorImpl extends AbstractCursor { // [#7100] TODO: Is there a more elegant way to do this? if (f != field) - value = ((Converter) field.getConverter()).from(value); + value = ((ScopedConverter) field.getConverter()).from(value, ctx.converterScope()); offset += operation.offset - nestedOffset + nested.size() - 1; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DateToLocalDateConverter.java b/jOOQ/src/main/java/org/jooq/impl/DateToLocalDateConverter.java index feebcdc9a2..94df5355f9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateToLocalDateConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateToLocalDateConverter.java @@ -42,6 +42,7 @@ import java.time.LocalDate; import java.util.function.Function; import org.jooq.Converter; +import org.jooq.ConverterScope; /** * @author Lukas Eder @@ -51,19 +52,19 @@ import org.jooq.Converter; * Converter.ofNullable(Date.class, LocalDate.class, Date::toLocalDate, Date::valueOf). */ @Deprecated -public final class DateToLocalDateConverter extends AbstractConverter { +public final class DateToLocalDateConverter extends AbstractScopedConverter { public DateToLocalDateConverter() { super(Date.class, LocalDate.class); } @Override - public final LocalDate from(Date t) { + public final LocalDate from(Date t, ConverterScope scope) { return t == null ? null : t.toLocalDate(); } @Override - public final Date to(LocalDate u) { + public final Date to(LocalDate u, ConverterScope scope) { return u == null ? null : Date.valueOf(u); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 4611e5d964..1c4b9da007 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -82,6 +82,7 @@ import static org.jooq.SQLDialect.SQLITE; // ... // ... import static org.jooq.SQLDialect.YUGABYTEDB; +import static org.jooq.ScopedConverter.scoped; import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.Convert.convert; import static org.jooq.impl.Convert.patchIso8601Timestamp; @@ -211,6 +212,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -223,11 +225,13 @@ import org.jooq.BindingGetSQLInputContext; import org.jooq.BindingGetStatementContext; import org.jooq.BindingRegisterContext; import org.jooq.BindingSQLContext; +import org.jooq.BindingScope; import org.jooq.BindingSetSQLOutputContext; import org.jooq.BindingSetStatementContext; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Converter; +import org.jooq.ConverterScope; import org.jooq.Converters; import org.jooq.DataType; import org.jooq.EnumType; @@ -250,6 +254,7 @@ import org.jooq.RowId; import org.jooq.SQLDialect; import org.jooq.Schema; import org.jooq.Scope; +import org.jooq.ScopedConverter; import org.jooq.Source; import org.jooq.Spatial; import org.jooq.TableRecord; @@ -370,31 +375,31 @@ public class DefaultBinding implements Binding { else if (type == LocalDate.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Date.class, LocalDate.class, - (Function & Serializable) Date::toLocalDate, - (Function & Serializable) Date::valueOf + ScopedConverter.ofNullable(Date.class, LocalDate.class, + (BiFunction & Serializable) (t, x) -> t.toLocalDate(), + (BiFunction & Serializable) (t, x) -> Date.valueOf(t) ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultDateBinding<>(DATE, c) ); else if (type == LocalDateTime.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Timestamp.class, LocalDateTime.class, - (Function & Serializable) Timestamp::toLocalDateTime, - (Function & Serializable) Timestamp::valueOf + ScopedConverter.ofNullable(Timestamp.class, LocalDateTime.class, + (BiFunction & Serializable) (t, x) -> t.toLocalDateTime(), + (BiFunction & Serializable) (t, x) -> Timestamp.valueOf(t) ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultTimestampBinding<>(TIMESTAMP, c) ); else if (type == LocalTime.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Time.class, LocalTime.class, - (Function & Serializable) Time::toLocalTime, - (Function & Serializable) Time::valueOf + ScopedConverter.ofNullable(Time.class, LocalTime.class, + (BiFunction & Serializable) (t, x) -> t.toLocalTime(), + (BiFunction & Serializable) (t, x) -> Time.valueOf(t) ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultTimeBinding<>(TIME, c) ); else if (type == Long.class || type == long.class) @@ -424,41 +429,41 @@ public class DefaultBinding implements Binding { else if (type == UByte.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Short.class, UByte.class, - (Function & Serializable) UByte::valueOf, - (Function & Serializable) UByte::shortValue + ScopedConverter.ofNullable(Short.class, UByte.class, + (BiFunction & Serializable) (t, x) -> UByte.valueOf(t), + (BiFunction & Serializable) (t, x) -> t.shortValue() ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultShortBinding<>(SMALLINT, c) ); else if (type == UInteger.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Long.class, UInteger.class, - (Function & Serializable) UInteger::valueOf, - (Function & Serializable) UInteger::longValue + ScopedConverter.ofNullable(Long.class, UInteger.class, + (BiFunction & Serializable) (t, x) -> UInteger.valueOf(t), + (BiFunction & Serializable) (t, x) -> t.longValue() ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultLongBinding<>(BIGINT, c) ); else if (type == ULong.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(BigInteger.class, ULong.class, - (Function & Serializable) ULong::valueOf, - (Function & Serializable) ULong::toBigInteger + ScopedConverter.ofNullable(BigInteger.class, ULong.class, + (BiFunction & Serializable) (t, x) -> ULong.valueOf(t), + (BiFunction & Serializable) (t, x) -> t.toBigInteger() ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultBigIntegerBinding<>(DECIMAL_INTEGER, c) ); else if (type == UShort.class) return (Binding) new DelegatingBinding<>( (DataType) dataType, - Converter.ofNullable(Integer.class, UShort.class, - (Function & Serializable) UShort::valueOf, - (Function & Serializable) UShort::intValue + ScopedConverter.ofNullable(Integer.class, UShort.class, + (BiFunction & Serializable) (t, x) -> UShort.valueOf(t), + (BiFunction & Serializable) (t, x) -> t.intValue() ), - (Converter) converter, + (ScopedConverter) converter, c -> new DefaultIntegerBinding<>(INTEGER, c) ); else if (type == UUID.class) @@ -515,12 +520,12 @@ public class DefaultBinding implements Binding { theBinding = (Binding) binding; } else if (binding == null) { - theBinding = binding(dataType, (Converter) converter); + theBinding = binding(dataType, (ScopedConverter) scoped(converter)); } else { theBinding = new Binding() { - final Converter theConverter = Converters.of(binding.converter(), converter); + final ScopedConverter theConverter = Converters.of(binding.converter(), converter); @Override public Converter converter() { @@ -696,12 +701,12 @@ public class DefaultBinding implements Binding { final DataType dataType; - final Converter converter; + final ScopedConverter converter; final boolean attachable; InternalBinding(DataType dataType, Converter converter) { this.dataType = dataType; - this.converter = converter; + this.converter = ScopedConverter.scoped(converter); // [#11099] Caching this per binding seems to have a considerable performance effect. // We must be careful to short circuit instanceof Attachable checks only if we *know* @@ -711,7 +716,7 @@ public class DefaultBinding implements Binding { } @Override - public final Converter converter() { + public final ScopedConverter converter() { return converter; } @@ -935,7 +940,7 @@ public class DefaultBinding implements Binding { @Override public final void sql(BindingSQLContext ctx) throws SQLException { - T converted = converter().to(ctx.value()); + T converted = converter().to(ctx.value(), ctx.converterScope()); // Casting can be enforced or prevented switch (ctx.render().castMode()) { @@ -995,7 +1000,7 @@ public class DefaultBinding implements Binding { @Override public final void set(BindingSetStatementContext ctx) throws SQLException { - T value = converter().to(ctx.value()); + T value = converter().to(ctx.value(), ctx.converterScope()); if (!FALSE.equals(ctx.settings().isExecuteLogging())) if (log.isTraceEnabled()) @@ -1012,7 +1017,7 @@ public class DefaultBinding implements Binding { @Override public final void set(BindingSetSQLOutputContext ctx) throws SQLException { - T value = converter().to(ctx.value()); + T value = converter().to(ctx.value(), ctx.converterScope()); if (value == null) ctx.output().writeObject(null); @@ -1022,7 +1027,7 @@ public class DefaultBinding implements Binding { @Override public final void get(BindingGetResultSetContext ctx) throws SQLException { - U value = converter().from(get0(ctx)); + U value = converter().from(get0(ctx), ctx.converterScope()); if (attachable) value = attach(value, ctx.configuration()); @@ -1032,7 +1037,7 @@ public class DefaultBinding implements Binding { @Override public final void get(BindingGetStatementContext ctx) throws SQLException { - U value = converter().from(get0(ctx)); + U value = converter().from(get0(ctx), ctx.converterScope()); if (attachable) value = attach(value, ctx.configuration()); @@ -1042,7 +1047,7 @@ public class DefaultBinding implements Binding { @Override public final void get(BindingGetSQLInputContext ctx) throws SQLException { - U value = converter().from(get0(ctx)); + U value = converter().from(get0(ctx), ctx.converterScope()); if (attachable) value = attach(value, ctx.configuration()); @@ -1107,14 +1112,14 @@ public class DefaultBinding implements Binding { static final class DelegatingBinding extends InternalBinding { - private final Converter delegatingConverter; + private final ScopedConverter delegatingConverter; private final InternalBinding delegatingBinding; DelegatingBinding( DataType originalDataType, - Converter delegatingConverter, - Converter originalConverter, - Function, ? extends InternalBinding> f + ScopedConverter delegatingConverter, + ScopedConverter originalConverter, + Function, ? extends InternalBinding> f ) { super(originalDataType, originalConverter); @@ -1124,17 +1129,17 @@ public class DefaultBinding implements Binding { @Override final void sqlInline0(BindingSQLContext ctx, X value) throws SQLException { - delegatingBinding.sqlInline0(ctx, delegatingConverter.to(value)); + delegatingBinding.sqlInline0(ctx, delegatingConverter.to(value, ctx.converterScope())); } @Override final void sqlBind0(BindingSQLContext ctx, X value) throws SQLException { - delegatingBinding.sqlBind0(ctx, delegatingConverter.to(value)); + delegatingBinding.sqlBind0(ctx, delegatingConverter.to(value, ctx.converterScope())); } @Override final void set0(BindingSetStatementContext ctx, X value) throws SQLException { - delegatingBinding.set0(ctx, delegatingConverter.to(value)); + delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterScope())); } @Override @@ -1144,22 +1149,22 @@ public class DefaultBinding implements Binding { @Override final void set0(BindingSetSQLOutputContext ctx, X value) throws SQLException { - delegatingBinding.set0(ctx, delegatingConverter.to(value)); + delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterScope())); } @Override final X get0(BindingGetResultSetContext ctx) throws SQLException { - return delegatingConverter.from(delegatingBinding.get0(ctx)); + return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope()); } @Override final X get0(BindingGetStatementContext ctx) throws SQLException { - return delegatingConverter.from(delegatingBinding.get0(ctx)); + return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope()); } @Override final X get0(BindingGetSQLInputContext ctx) throws SQLException { - return delegatingConverter.from(delegatingBinding.get0(ctx)); + return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope()); } @Override @@ -3454,11 +3459,12 @@ public class DefaultBinding implements Binding { static final class DefaultInstantBinding extends InternalBinding { - private static final Converter CONVERTER = Converter.ofNullable( + @SuppressWarnings("unchecked") + private static final ScopedConverter CONVERTER = ScopedConverter.ofNullable( OffsetDateTime.class, Instant.class, - (Function & Serializable) OffsetDateTime::toInstant, - (Function & Serializable) i -> OffsetDateTime.ofInstant(i, ZoneOffset.UTC) + (BiFunction & Serializable) (t, x) -> t.toInstant(), + (BiFunction & Serializable) (i, x) -> OffsetDateTime.ofInstant(i, ZoneOffset.UTC) ); private final DefaultOffsetDateTimeBinding delegate; @@ -3476,32 +3482,32 @@ public class DefaultBinding implements Binding { @Override final void sqlInline0(BindingSQLContext ctx, Instant value) throws SQLException { - delegate.sqlInline0(ctx, CONVERTER.to(value)); + delegate.sqlInline0(ctx, CONVERTER.to(value, ctx.converterScope())); } @Override final void set0(BindingSetStatementContext ctx, Instant value) throws SQLException { - delegate.set0(ctx, CONVERTER.to(value)); + delegate.set0(ctx, CONVERTER.to(value, ctx.converterScope())); } @Override final void set0(BindingSetSQLOutputContext ctx, Instant value) throws SQLException { - delegate.set0(ctx, CONVERTER.to(value)); + delegate.set0(ctx, CONVERTER.to(value, ctx.converterScope())); } @Override final Instant get0(BindingGetResultSetContext ctx) throws SQLException { - return CONVERTER.from(delegate.get0(ctx)); + return CONVERTER.from(delegate.get0(ctx), ctx.converterScope()); } @Override final Instant get0(BindingGetStatementContext ctx) throws SQLException { - return CONVERTER.from(delegate.get0(ctx)); + return CONVERTER.from(delegate.get0(ctx), ctx.converterScope()); } @Override final Instant get0(BindingGetSQLInputContext ctx) throws SQLException { - return CONVERTER.from(delegate.get0(ctx)); + return CONVERTER.from(delegate.get0(ctx), ctx.converterScope()); } @Override @@ -3841,8 +3847,8 @@ public class DefaultBinding implements Binding { } @SuppressWarnings("unchecked") - private static final U pgFromString(Scope ctx, Field field, String string) { - Converter converter = (Converter) field.getConverter(); + private static final U pgFromString(BindingScope ctx, Field field, String string) { + ScopedConverter converter = (ScopedConverter) field.getConverter(); Class type = wrapper(converter.fromType()); if (string == null) @@ -3850,88 +3856,88 @@ public class DefaultBinding implements Binding { else if (type == Blob.class) ; // Not supported else if (type == Boolean.class) - return converter.from((T) Convert.convert(string, Boolean.class)); + return converter.from((T) Convert.convert(string, Boolean.class), ctx.converterScope()); else if (type == BigInteger.class) - return converter.from((T) new BigInteger(string)); + return converter.from((T) new BigInteger(string), ctx.converterScope()); else if (type == BigDecimal.class) - return converter.from((T) new BigDecimal(string)); + return converter.from((T) new BigDecimal(string), ctx.converterScope()); else if (type == Byte.class) - return converter.from((T) Byte.valueOf(string)); + return converter.from((T) Byte.valueOf(string), ctx.converterScope()); else if (type == byte[].class) - return converter.from((T) PostgresUtils.toBytes(string)); + return converter.from((T) PostgresUtils.toBytes(string), ctx.converterScope()); else if (type == Clob.class) ; // Not supported else if (type == Date.class) - return converter.from((T) Date.valueOf(string)); + return converter.from((T) Date.valueOf(string), ctx.converterScope()); else if (type == Double.class) - return converter.from((T) Double.valueOf(string)); + return converter.from((T) Double.valueOf(string), ctx.converterScope()); else if (type == Float.class) - return converter.from((T) Float.valueOf(string)); + return converter.from((T) Float.valueOf(string), ctx.converterScope()); else if (type == Integer.class) - return converter.from((T) Integer.valueOf(string)); + return converter.from((T) Integer.valueOf(string), ctx.converterScope()); else if (type == Long.class) - return converter.from((T) Long.valueOf(string)); + return converter.from((T) Long.valueOf(string), ctx.converterScope()); else if (type == Short.class) - return converter.from((T) Short.valueOf(string)); + return converter.from((T) Short.valueOf(string), ctx.converterScope()); else if (type == String.class) - return converter.from((T) string); + return converter.from((T) string, ctx.converterScope()); else if (type == Time.class) - return converter.from((T) Time.valueOf(string)); + return converter.from((T) Time.valueOf(string), ctx.converterScope()); else if (type == Timestamp.class) - return converter.from((T) Timestamp.valueOf(patchIso8601Timestamp(string, false))); + return converter.from((T) Timestamp.valueOf(patchIso8601Timestamp(string, false)), ctx.converterScope()); else if (type == LocalTime.class) - return converter.from((T) LocalTime.parse(string)); + return converter.from((T) LocalTime.parse(string), ctx.converterScope()); else if (type == LocalDate.class) - return converter.from((T) LocalDate.parse(string)); + return converter.from((T) LocalDate.parse(string), ctx.converterScope()); else if (type == LocalDateTime.class) - return converter.from((T) LocalDateTime.parse(patchIso8601Timestamp(string, true))); + return converter.from((T) LocalDateTime.parse(patchIso8601Timestamp(string, true)), ctx.converterScope()); else if (type == OffsetTime.class) - return converter.from((T) OffsetDateTimeParser.offsetTime(string)); + return converter.from((T) OffsetDateTimeParser.offsetTime(string), ctx.converterScope()); else if (type == OffsetDateTime.class) - return converter.from((T) OffsetDateTimeParser.offsetDateTime(string)); + return converter.from((T) OffsetDateTimeParser.offsetDateTime(string), ctx.converterScope()); else if (type == Instant.class) - return converter.from((T) OffsetDateTimeParser.offsetDateTime(string).toInstant()); + return converter.from((T) OffsetDateTimeParser.offsetDateTime(string).toInstant(), ctx.converterScope()); else if (type == JSON.class) - return converter.from((T) JSON.json(string)); + return converter.from((T) JSON.json(string), ctx.converterScope()); else if (type == JSONB.class) - return converter.from((T) JSONB.jsonb(string)); + return converter.from((T) JSONB.jsonb(string), ctx.converterScope()); else if (type == UByte.class) - return converter.from((T) UByte.valueOf(string)); + return converter.from((T) UByte.valueOf(string), ctx.converterScope()); else if (type == UShort.class) - return converter.from((T) UShort.valueOf(string)); + return converter.from((T) UShort.valueOf(string), ctx.converterScope()); else if (type == UInteger.class) - return converter.from((T) UInteger.valueOf(string)); + return converter.from((T) UInteger.valueOf(string), ctx.converterScope()); else if (type == ULong.class) - return converter.from((T) ULong.valueOf(string)); + return converter.from((T) ULong.valueOf(string), ctx.converterScope()); else if (type == UUID.class) - return converter.from((T) UUID.fromString(string)); + return converter.from((T) UUID.fromString(string), ctx.converterScope()); else if (type == XML.class) - return converter.from((T) XML.xml(string)); + return converter.from((T) XML.xml(string), ctx.converterScope()); else if (type.isArray()) - return converter.from((T) pgNewArray(ctx, field, type, string)); + return converter.from((T) pgNewArray(ctx, field, type, string), ctx.converterScope()); else if (EnumType.class.isAssignableFrom(type)) - return converter.from((T) DefaultEnumTypeBinding.getEnumType((Class) type, string)); + return converter.from((T) DefaultEnumTypeBinding.getEnumType((Class) type, string), ctx.converterScope()); else if (Result.class.isAssignableFrom(type)) if (string.startsWith("<")) - return converter.from((T) readMultisetXML(ctx, (AbstractRow) field.getDataType().getRow(), (Class) field.getDataType().getRecordType(), string)); + return converter.from((T) readMultisetXML(ctx, (AbstractRow) field.getDataType().getRow(), (Class) field.getDataType().getRecordType(), string), ctx.converterScope()); else - return converter.from((T) readMultisetJSON(ctx, (AbstractRow) field.getDataType().getRow(), (Class) field.getDataType().getRecordType(), string)); + return converter.from((T) readMultisetJSON(ctx, (AbstractRow) field.getDataType().getRow(), (Class) field.getDataType().getRecordType(), string), ctx.converterScope()); else if (Record.class.isAssignableFrom(type) // [#11812] UDTRecords/TableRecords or InternalRecords that don't have an explicit converter && (!InternalRecord.class.isAssignableFrom(type) || type == converter.fromType())) - return converter.from((T) pgNewRecord(ctx, type, (AbstractRow) field.getDataType().getRow(), string)); + return converter.from((T) pgNewRecord(ctx, type, (AbstractRow) field.getDataType().getRow(), string), ctx.converterScope()); else if (type == Object.class) - return converter.from((T) string); + return converter.from((T) string, ctx.converterScope()); // [#4964] [#6058] Recurse only if we have a meaningful converter, not the identity converter, // which would cause a StackOverflowError, here! else if (type != wrapper(converter.toType())) { - return converter.from((T) pgFromString(ctx, field("converted_field", ((ConvertedDataType) field.getDataType()).delegate()), string)); + return converter.from((T) pgFromString(ctx, field("converted_field", ((ConvertedDataType) field.getDataType()).delegate()), string), ctx.converterScope()); } throw new UnsupportedOperationException("Class " + type + " is not supported"); @@ -3949,7 +3955,7 @@ public class DefaultBinding implements Binding { * @return The converted {@link UDTRecord} */ @SuppressWarnings("unchecked") - static final Record pgNewRecord(Scope ctx, Class type, AbstractRow fields, Object object) { + static final Record pgNewRecord(BindingScope ctx, Class type, AbstractRow fields, Object object) { if (object == null) return null; @@ -3980,7 +3986,7 @@ public class DefaultBinding implements Binding { }); } - private static final void pgSetValue(Scope ctx, Record record, Field field, String value) { + private static final void pgSetValue(BindingScope ctx, Record record, Field field, String value) { record.set(field, pgFromString(ctx, field, value)); } @@ -3993,7 +3999,7 @@ public class DefaultBinding implements Binding { * @param string A String representation of an array * @return The converted array */ - private static final Object[] pgNewArray(Scope ctx, Field field, Class type, String string) { + private static final Object[] pgNewArray(BindingScope ctx, Field field, Class type, String string) { if (string == null) return null; @@ -5410,7 +5416,7 @@ public class DefaultBinding implements Binding { @Override void sqlInline0(BindingSQLContext ctx, JSONB value) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) { - bytes(ctx.configuration()).sqlInline0(ctx, bytesConverter(ctx.configuration()).to(value)); + bytes(ctx.configuration()).sqlInline0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope())); } else { super.sqlInline0(ctx, value); @@ -5431,7 +5437,7 @@ public class DefaultBinding implements Binding { @Override final void set0(BindingSetStatementContext ctx, JSONB value) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) - bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value)); + bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope())); else ctx.statement().setString(ctx.index(), value.data()); } @@ -5439,7 +5445,7 @@ public class DefaultBinding implements Binding { @Override final void set0(BindingSetSQLOutputContext ctx, JSONB value) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) - bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value)); + bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope())); else ctx.output().writeString(value.data()); } @@ -5447,7 +5453,7 @@ public class DefaultBinding implements Binding { @Override final JSONB get0(BindingGetResultSetContext ctx) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) - return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx)); + return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope()); String string = ctx.resultSet().getString(ctx.index()); return string == null ? null : JSONB.valueOf(string); @@ -5456,7 +5462,7 @@ public class DefaultBinding implements Binding { @Override final JSONB get0(BindingGetStatementContext ctx) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) - return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx)); + return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope()); String string = ctx.statement().getString(ctx.index()); return string == null ? null : JSONB.valueOf(string); @@ -5465,7 +5471,7 @@ public class DefaultBinding implements Binding { @Override final JSONB get0(BindingGetSQLInputContext ctx) throws SQLException { if (EMULATE_AS_BLOB.contains(ctx.dialect())) - return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx)); + return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope()); String string = ctx.input().readString(); return string == null ? null : JSONB.valueOf(string); @@ -5479,10 +5485,10 @@ public class DefaultBinding implements Binding { return Types.VARCHAR; } - private final Converter bytesConverter(final Configuration configuration) { - return Converter.ofNullable(byte[].class, JSONB.class, - t -> JSONB.valueOf(new String(t, configuration.charsetProvider().provide())), - u -> u.toString().getBytes(configuration.charsetProvider().provide()) + private final ScopedConverter bytesConverter(final Configuration configuration) { + return ScopedConverter.ofNullable(byte[].class, JSONB.class, + (t, x) -> JSONB.valueOf(new String(t, configuration.charsetProvider().provide())), + (u, x) -> u.toString().getBytes(configuration.charsetProvider().provide()) ); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java index b4a493f857..adabcc432a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java @@ -37,6 +37,8 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.sql.ResultSet; import org.jooq.BindingGetResultSetContext; @@ -101,7 +103,7 @@ class DefaultBindingGetResultSetContext extends AbstractExecuteScope implemen return new DefaultBindingGetResultSetContext(ctx, resultSet, index) { @Override public void value(T v) { - outer.value(converter.from(v)); + outer.value(scoped(converter).from(v, converterScope())); } }; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java index 0dab16b38f..1ae1434614 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java @@ -37,6 +37,8 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.sql.SQLInput; import org.jooq.BindingGetSQLInputContext; @@ -78,7 +80,7 @@ class DefaultBindingGetSQLInputContext extends AbstractExecuteScope implement return new DefaultBindingGetSQLInputContext(ctx, input) { @Override public void value(T v) { - outer.value(converter.from(v)); + outer.value(scoped(converter).from(v, converterScope())); } }; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java index 8d77e76416..1ce2e7c0b1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java @@ -37,11 +37,14 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.sql.CallableStatement; import org.jooq.BindingGetStatementContext; import org.jooq.Converter; import org.jooq.ExecuteContext; +import org.jooq.ScopedConverter; /** * @author Lukas Eder @@ -85,7 +88,7 @@ class DefaultBindingGetStatementContext extends AbstractExecuteScope implemen return new DefaultBindingGetStatementContext(ctx, statement, index) { @Override public void value(T v) { - outer.value(converter.from(v)); + outer.value(scoped(converter).from(v, converterScope())); } }; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java index 1d81378d3f..21bbf6a186 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java @@ -37,11 +37,14 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.util.Map; import org.jooq.BindingSQLContext; import org.jooq.Configuration; import org.jooq.Converter; +import org.jooq.ConverterScope; import org.jooq.RenderContext; /** @@ -65,6 +68,11 @@ class DefaultBindingSQLContext extends AbstractScope implements BindingSQLCon this.variable = variable; } + @Override + public final ConverterScope converterScope() { + return render.converterScope(); + } + @Override public final RenderContext render() { return render; @@ -82,7 +90,7 @@ class DefaultBindingSQLContext extends AbstractScope implements BindingSQLCon @Override public BindingSQLContext convert(Converter converter) { - return new DefaultBindingSQLContext<>(configuration, data, render, converter.to(value), variable); + return new DefaultBindingSQLContext<>(configuration, data, render, scoped(converter).to(value, converterScope()), variable); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java index 6cdc20780b..98906c2f9c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java @@ -37,6 +37,8 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.sql.SQLOutput; import org.jooq.BindingSetSQLOutputContext; @@ -70,7 +72,7 @@ class DefaultBindingSetSQLOutputContext extends AbstractExecuteScope implemen @Override public final BindingSetSQLOutputContext convert(Converter converter) { - return new DefaultBindingSetSQLOutputContext<>(ctx, output, converter.to(value)); + return new DefaultBindingSetSQLOutputContext<>(ctx, output, scoped(converter).to(value, converterScope())); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java index de8a632c2f..061ac8357e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java @@ -37,6 +37,8 @@ */ package org.jooq.impl; +import static org.jooq.ScopedConverter.scoped; + import java.sql.PreparedStatement; import org.jooq.BindingSetStatementContext; @@ -77,7 +79,7 @@ class DefaultBindingSetStatementContext extends AbstractExecuteScope implemen @Override public final BindingSetStatementContext convert(Converter converter) { - return new DefaultBindingSetStatementContext<>(ctx, statement, index, converter.to(value)); + return new DefaultBindingSetStatementContext<>(ctx, statement, index, scoped(converter).to(value, converterScope())); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConverterScope.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConverterScope.java new file mode 100644 index 0000000000..157a5c7b1b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConverterScope.java @@ -0,0 +1,57 @@ +/* + * 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 java.util.Map; + +import org.jooq.Configuration; +import org.jooq.ConverterScope; + +/** + * @author Lukas Eder + */ +final class DefaultConverterScope extends AbstractScope implements ConverterScope { + + DefaultConverterScope(Configuration configuration) { + super(configuration, new DataMap()); + } + + DefaultConverterScope(Configuration configuration, Map data) { + super(configuration, data); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 20b1501b15..7152b88d8e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -61,6 +61,7 @@ import java.util.Map; import org.jooq.Configuration; import org.jooq.ConnectionProvider; import org.jooq.Constants; +import org.jooq.ConverterScope; import org.jooq.DDLQuery; import org.jooq.DSLContext; import org.jooq.Delete; @@ -94,6 +95,7 @@ class DefaultExecuteContext implements ExecuteContext { private static final JooqLogger log = JooqLogger.getLogger(DefaultExecuteContext.class); // Persistent attributes (repeatable) + private final ConverterScope converterScope; private final Instant creationTime; private final Configuration originalConfiguration; private final Configuration derivedConfiguration; @@ -335,6 +337,7 @@ class DefaultExecuteContext implements ExecuteContext { this.data = new DataMap(); this.query = query; this.routine = routine; + this.converterScope = new DefaultConverterScope(derivedConfiguration, data); if (routine != null) { this.batch = false; @@ -366,6 +369,11 @@ class DefaultExecuteContext implements ExecuteContext { clean(); } + @Override + public final ConverterScope converterScope() { + return converterScope; + } + @Override public final Instant creationTime() { return creationTime; diff --git a/jOOQ/src/main/java/org/jooq/impl/DelegatingConverter.java b/jOOQ/src/main/java/org/jooq/impl/DelegatingConverter.java index 3ac4dc2070..1f35540467 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DelegatingConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/DelegatingConverter.java @@ -37,26 +37,37 @@ */ package org.jooq.impl; -import org.jooq.Converter; +import org.jooq.ConverterScope; +import org.jooq.ScopedConverter; /** * @author Lukas Eder */ -public class DelegatingConverter extends AbstractConverter { +public class DelegatingConverter extends AbstractScopedConverter { - private final Converter delegate; + private final ScopedConverter delegate; - public DelegatingConverter(Converter delegate) { + public DelegatingConverter(ScopedConverter delegate) { super(delegate.fromType(), delegate.toType()); this.delegate = delegate; } + @Override + public final U from(T t, ConverterScope scope) { + return delegate.from(t, scope); + } + @Override public final U from(T t) { return delegate.from(t); } + @Override + public final T to(U u, ConverterScope scope) { + return delegate.to(u, scope); + } + @Override public final T to(U u) { return delegate.to(u); diff --git a/jOOQ/src/main/java/org/jooq/impl/DomainImpl.java b/jOOQ/src/main/java/org/jooq/impl/DomainImpl.java index 392a6bfa13..439c2f0a37 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DomainImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DomainImpl.java @@ -55,6 +55,7 @@ import org.jooq.Function1; import org.jooq.Function2; import org.jooq.Name; import org.jooq.Schema; +import org.jooq.ScopedConverter; import org.jooq.QueryPart; import org.jooq.impl.QOM.UNotYetImplemented; @@ -92,7 +93,7 @@ class DomainImpl extends AbstractNamed implements Domain, UNotYetImplement } @Override - public final Converter getConverter() { + public final ScopedConverter getConverter() { return type.getConverter(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/EnumConverter.java b/jOOQ/src/main/java/org/jooq/impl/EnumConverter.java index 6d5ef4a013..19648a05e3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/EnumConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/EnumConverter.java @@ -38,18 +38,21 @@ package org.jooq.impl; import static org.jooq.impl.Convert.convert; +import static org.jooq.impl.Internal.converterScope; import static org.jooq.tools.reflect.Reflect.wrapper; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; +import org.jooq.ConverterScope; + /** * A base class for enum conversion. * * @author Lukas Eder */ -public class EnumConverter> extends AbstractConverter { +public /* non-final */ class EnumConverter> extends AbstractConverter { private final Map lookup; private final Function to; @@ -77,8 +80,8 @@ public class EnumConverter> extends AbstractConverter } @Override - public final U from(T databaseObject) { - return lookup.get(databaseObject); + public final U from(T t) { + return lookup.get(t); } /** @@ -88,11 +91,11 @@ public class EnumConverter> extends AbstractConverter * {@inheritDoc} */ @Override - public T to(U userObject) { - if (userObject == null) + public T to(U u) { + if (u == null) return null; else - return to.apply(userObject); + return to.apply(u); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java b/jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java index 66f6c838c0..b08a75e5db 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java @@ -38,13 +38,15 @@ package org.jooq.impl; import org.jooq.Converter; +import org.jooq.ConverterScope; +import org.jooq.ScopedConverter; /** * A converter that doesn't convert anything. * * @author Lukas Eder */ -public final class IdentityConverter implements Converter { +public final class IdentityConverter implements ScopedConverter { private final Class type; public IdentityConverter(Class type) { @@ -52,12 +54,12 @@ public final class IdentityConverter implements Converter { } @Override - public final T from(T t) { + public final T from(T t, ConverterScope scope) { return t; } @Override - public final T to(T t) { + public final T to(T t, ConverterScope scope) { return t; } diff --git a/jOOQ/src/main/java/org/jooq/impl/Internal.java b/jOOQ/src/main/java/org/jooq/impl/Internal.java index addd2e3f71..d099afc465 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Internal.java +++ b/jOOQ/src/main/java/org/jooq/impl/Internal.java @@ -53,6 +53,7 @@ import org.jooq.Binding; import org.jooq.Check; import org.jooq.Comment; import org.jooq.Converter; +import org.jooq.ConverterScope; import org.jooq.DDLExportConfiguration; import org.jooq.DataType; import org.jooq.Domain; @@ -628,4 +629,10 @@ public final class Internal { else return 0x7FFFFFF & object.hashCode(); } + + private static final ConverterScope CONVERTER_SCOPE = new DefaultConverterScope(CONFIG); + + public static final ConverterScope converterScope() { + return CONVERTER_SCOPE; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/JPAConverter.java b/jOOQ/src/main/java/org/jooq/impl/JPAConverter.java index bb4ef03dae..fe245f6907 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JPAConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/JPAConverter.java @@ -41,6 +41,7 @@ import java.lang.reflect.Method; import jakarta.persistence.AttributeConverter; +import org.jooq.ConverterScope; import org.jooq.exception.MappingException; import org.jooq.tools.JooqLogger; import org.jooq.tools.reflect.Reflect; @@ -58,8 +59,9 @@ import org.jooq.tools.reflect.Reflect; * * @author Lukas Eder */ -public final class JPAConverter extends AbstractConverter { - private static final JooqLogger log = JooqLogger.getLogger(JPAConverter.class); +public final class JPAConverter extends AbstractScopedConverter { + + private static final JooqLogger log = JooqLogger.getLogger(JPAConverter.class); private final AttributeConverter delegate; @@ -115,12 +117,12 @@ public final class JPAConverter extends AbstractConverter { } @Override - public final U from(T t) { + public final U from(T t, ConverterScope scope) { return delegate.convertToEntityAttribute(t); } @Override - public final T to(U u) { + public final T to(U u, ConverterScope scope) { return delegate.convertToDatabaseColumn(u); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java index 5063e49542..83ff7c481e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java @@ -37,11 +37,14 @@ */ package org.jooq.impl; +import static org.jooq.impl.Internal.converterScope; + import org.jooq.Binding; import org.jooq.Configuration; import org.jooq.Converter; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ScopedConverter; /** * @author Lukas Eder @@ -96,7 +99,7 @@ final class LegacyConvertedDataType extends DefaultDataType { // [#3200] Try to convert arbitrary objects to T else - return ((Converter) getConverter()).from(delegate.convert(object)); + return ((ScopedConverter) getConverter()).from(delegate.convert(object), converterScope()); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java index fa4a65bbbe..cd6ae0700d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java +++ b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java @@ -38,11 +38,13 @@ package org.jooq.impl; // ... +import static org.jooq.ScopedConverter.scoped; import static org.jooq.conf.ParamType.NAMED; import static org.jooq.impl.Internal.subscriber; import static org.jooq.impl.Tools.EMPTY_PARAM; import static org.jooq.impl.Tools.abstractDMLQuery; import static org.jooq.impl.Tools.abstractResultQuery; +import static org.jooq.impl.Tools.converterScope; import static org.jooq.impl.Tools.fields; import static org.jooq.impl.Tools.recordFactory; import static org.jooq.impl.Tools.translate; @@ -91,6 +93,7 @@ import org.jooq.Param; import org.jooq.Query; import org.jooq.Record; import org.jooq.SQLDialect; +import org.jooq.ScopedConverter; import org.jooq.TransactionalPublishable; import org.jooq.XML; import org.jooq.conf.Settings; @@ -1237,7 +1240,7 @@ final class R2DBC { if (converter == null) throw new DataTypeException("Cannot convert from " + o.getClass() + " to " + uType + ". Please report an issue here: https://github.com/jOOQ/jOOQ/issues/new. As a workaround, you can implement a ConverterProvider."); else - return converter.from(o); + return scoped(converter).from(o, converterScope(c)); } // --------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/SimpleExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/SimpleExecuteContext.java index cbd71fe374..b556a3d7ea 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SimpleExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/SimpleExecuteContext.java @@ -42,11 +42,11 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; -import java.time.Instant; import java.util.Map; import org.jooq.Configuration; import org.jooq.ConnectionProvider; +import org.jooq.ConverterScope; import org.jooq.ExecuteContext; import org.jooq.ExecuteType; import org.jooq.Query; @@ -67,6 +67,11 @@ final class SimpleExecuteContext extends AbstractScope implements ExecuteContext super(configuration, data); } + @Override + public final ConverterScope converterScope() { + return Tools.converterScope(configuration); + } + @Override public final Connection connection() { throw new UnsupportedOperationException("Not implemented"); diff --git a/jOOQ/src/main/java/org/jooq/impl/TimeToLocalTimeConverter.java b/jOOQ/src/main/java/org/jooq/impl/TimeToLocalTimeConverter.java index 82c0f10c28..9c4c096733 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimeToLocalTimeConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimeToLocalTimeConverter.java @@ -42,6 +42,7 @@ import java.time.LocalTime; import java.util.function.Function; import org.jooq.Converter; +import org.jooq.ConverterScope; /** * @author Lukas Eder @@ -51,19 +52,19 @@ import org.jooq.Converter; * Converter.ofNullable(Time.class, LocalTime.class, Time::toLocalTime, Time::valueOf). */ @Deprecated -public final class TimeToLocalTimeConverter extends AbstractConverter { +public final class TimeToLocalTimeConverter extends AbstractScopedConverter { public TimeToLocalTimeConverter() { super(Time.class, LocalTime.class); } @Override - public final LocalTime from(Time t) { + public final LocalTime from(Time t, ConverterScope scope) { return t == null ? null : t.toLocalTime(); } @Override - public final Time to(LocalTime u) { + public final Time to(LocalTime u, ConverterScope scope) { return u == null ? null : Time.valueOf(u); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampToJavaUtilDateConverter.java b/jOOQ/src/main/java/org/jooq/impl/TimestampToJavaUtilDateConverter.java index 6651655f35..c8173295a9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampToJavaUtilDateConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampToJavaUtilDateConverter.java @@ -39,23 +39,26 @@ package org.jooq.impl; import java.sql.Timestamp; +import org.jooq.ConverterScope; + /** * @author Lukas Eder */ -final class TimestampToJavaUtilDateConverter extends AbstractConverter { - static final TimestampToJavaUtilDateConverter INSTANCE = new TimestampToJavaUtilDateConverter(); +final class TimestampToJavaUtilDateConverter extends AbstractScopedConverter { + + static final TimestampToJavaUtilDateConverter INSTANCE = new TimestampToJavaUtilDateConverter(); private TimestampToJavaUtilDateConverter () { super(Timestamp.class, java.util.Date.class); } @Override - public final java.util.Date from(Timestamp t) { + public final java.util.Date from(Timestamp t, ConverterScope scope) { return t == null ? null : new java.util.Date(t.getTime()); } @Override - public final Timestamp to(java.util.Date u) { + public final Timestamp to(java.util.Date u, ConverterScope scope) { return u == null ? null : new Timestamp(u.getTime()); } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampToLocalDateTimeConverter.java b/jOOQ/src/main/java/org/jooq/impl/TimestampToLocalDateTimeConverter.java index c88f80685c..927d476de9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampToLocalDateTimeConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampToLocalDateTimeConverter.java @@ -42,6 +42,7 @@ import java.time.LocalDateTime; import java.util.function.Function; import org.jooq.Converter; +import org.jooq.ConverterScope; /** * @author Lukas Eder @@ -51,19 +52,19 @@ import org.jooq.Converter; * Converter.ofNullable(Timestamp.class, LocalDateTime.class, Timestamp::toLocalDateTime, Timestamp::valueOf). */ @Deprecated -public final class TimestampToLocalDateTimeConverter extends AbstractConverter { +public final class TimestampToLocalDateTimeConverter extends AbstractScopedConverter { public TimestampToLocalDateTimeConverter() { super(Timestamp.class, LocalDateTime.class); } @Override - public final LocalDateTime from(Timestamp t) { + public final LocalDateTime from(Timestamp t, ConverterScope scope) { return t == null ? null : t.toLocalDateTime(); } @Override - public final Timestamp to(LocalDateTime u) { + public final Timestamp to(LocalDateTime u, ConverterScope scope) { return u == null ? null : Timestamp.valueOf(u); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 29f5a44bc8..8d5f3a0afd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -75,6 +75,7 @@ import static org.jooq.SQLDialect.SQLITE; // ... // ... import static org.jooq.SQLDialect.YUGABYTEDB; +import static org.jooq.ScopedConverter.scoped; import static org.jooq.conf.BackslashEscaping.DEFAULT; import static org.jooq.conf.BackslashEscaping.ON; import static org.jooq.conf.ParamType.INLINED; @@ -219,7 +220,6 @@ import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; @@ -263,6 +263,7 @@ import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Converter; import org.jooq.ConverterProvider; +import org.jooq.ConverterScope; import org.jooq.Converters; import org.jooq.Cursor; import org.jooq.DSLContext; @@ -294,7 +295,6 @@ import org.jooq.Record1; import org.jooq.RecordQualifier; import org.jooq.RenderContext; import org.jooq.RenderContext.CastMode; -// ... import org.jooq.Result; import org.jooq.ResultOrRows; import org.jooq.ResultQuery; @@ -302,8 +302,8 @@ import org.jooq.Results; import org.jooq.Row; import org.jooq.SQLDialect; import org.jooq.Schema; -import org.jooq.SchemaMapping; import org.jooq.Scope; +import org.jooq.ScopedConverter; import org.jooq.Select; import org.jooq.SelectFieldOrAsterisk; import org.jooq.SortField; @@ -311,7 +311,6 @@ import org.jooq.Source; import org.jooq.Table; import org.jooq.TableField; import org.jooq.TableRecord; -// ... import org.jooq.UDT; import org.jooq.UpdatableRecord; import org.jooq.WindowSpecification; @@ -334,7 +333,6 @@ import org.jooq.exception.MappingException; import org.jooq.exception.NoDataFoundException; import org.jooq.exception.TemplatingException; import org.jooq.exception.TooManyRowsException; -import org.jooq.impl.QOM.GenerationOption; import org.jooq.impl.QOM.UEmpty; import org.jooq.impl.ResultsImpl.ResultOrRowsImpl; import org.jooq.tools.Ints; @@ -1466,7 +1464,7 @@ final class Tools { * Get a converter from a {@link ConverterProvider} or null if * no converter could be provided. */ - static final Converter converter(Configuration configuration, T instance, Class tType, Class uType) { + static final ScopedConverter converter(Configuration configuration, T instance, Class tType, Class uType) { Converter result = configuration(configuration).converterProvider().provide(tType, uType); if (result == null) @@ -1479,15 +1477,15 @@ final class Tools { if (result == null && tType == Converters.UnknownType.class) result = converter(configuration, instance, (Class) (instance == null ? Object.class : instance.getClass()), uType); - return result; + return result == null ? null : scoped(result); } /** * Get a converter from a {@link ConverterProvider} or null if * no converter could be provided. */ - static final Converter converterOrFail(Configuration configuration, T instance, Class tType, Class uType) { - Converter result = converter(configuration, instance, tType, uType); + static final ScopedConverter converterOrFail(Configuration configuration, T instance, Class tType, Class uType) { + ScopedConverter result = converter(configuration, instance, tType, uType); if (result == null) throw new DataTypeException("No Converter found for types " + tType.getName() + " and " + uType.getName()); @@ -1498,7 +1496,7 @@ final class Tools { /** * Get a converter from a {@link ConverterProvider}. */ - static final Converter converterOrFail(Attachable attachable, T instance, Class tType, Class uType) { + static final ScopedConverter converterOrFail(Attachable attachable, T instance, Class tType, Class uType) { return converterOrFail(configuration(attachable), instance, tType, uType); } @@ -7105,4 +7103,12 @@ final class Tools { return dataType; } + + static final ConverterScope converterScope(Attachable attachable) { + return new DefaultConverterScope(configuration(attachable)); + } + + static final ConverterScope converterScope(Configuration configuration) { + return new DefaultConverterScope(configuration(configuration)); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLtoJAXBConverter.java b/jOOQ/src/main/java/org/jooq/impl/XMLtoJAXBConverter.java index 3c394e3c33..92cc004542 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLtoJAXBConverter.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLtoJAXBConverter.java @@ -40,6 +40,7 @@ package org.jooq.impl; import java.io.StringReader; import java.io.StringWriter; +import org.jooq.ConverterScope; import org.jooq.XML; import jakarta.xml.bind.JAXB; @@ -56,21 +57,21 @@ public class XMLtoJAXBConverter extends AbstractConverter { } @Override - public U from(XML databaseObject) { - if (databaseObject == null) + public U from(XML t) { + if (t == null) return null; else - return JAXB.unmarshal(new StringReader(databaseObject.data()), toType()); + return JAXB.unmarshal(new StringReader(t.data()), toType()); } @Override - public XML to(U userObject) { - if (userObject == null) { + public XML to(U u) { + if (u == null) { return null; } else { StringWriter w = new StringWriter(); - JAXB.marshal(userObject, w); + JAXB.marshal(u, w); return XML.xml(w.toString()); } } diff --git a/jOOQ/src/main/java/org/jooq/tools/Convert.java b/jOOQ/src/main/java/org/jooq/tools/Convert.java index 4391429631..e8f39b69ca 100644 --- a/jOOQ/src/main/java/org/jooq/tools/Convert.java +++ b/jOOQ/src/main/java/org/jooq/tools/Convert.java @@ -40,7 +40,9 @@ package org.jooq.tools; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static org.jooq.ScopedConverter.scoped; import static org.jooq.impl.Internal.arrayType; +import static org.jooq.impl.Internal.converterScope; import static org.jooq.tools.reflect.Reflect.accessible; import static org.jooq.tools.reflect.Reflect.wrapper; import static org.jooq.types.Unsigned.ubyte; @@ -91,6 +93,7 @@ import jakarta.xml.bind.JAXB; // ... import org.jooq.Converter; import org.jooq.ConverterProvider; +import org.jooq.ConverterScope; import org.jooq.DataType; import org.jooq.EnumType; import org.jooq.Field; @@ -100,9 +103,13 @@ import org.jooq.QualifiedRecord; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; +import org.jooq.ScopedConverter; import org.jooq.XML; import org.jooq.exception.DataTypeException; +import org.jooq.impl.AbstractConverter; +import org.jooq.impl.AbstractScopedConverter; import org.jooq.impl.IdentityConverter; +import org.jooq.impl.Internal; import org.jooq.tools.jdbc.MockArray; import org.jooq.tools.jdbc.MockResultSet; import org.jooq.tools.reflect.Reflect; @@ -370,7 +377,7 @@ public final class Convert { } public static final U[] convertCollection(Collection from, Class to){ - return new ConvertAll(to).from(from); + return new ConvertAll(to).from(from, converterScope()); } /** @@ -400,10 +407,10 @@ public final class Convert { Class fromType = converter.fromType(); if (fromType == Object.class) - return converter.from((T) from); + return scoped(converter).from((T) from, converterScope()); ConvertAll convertAll = new ConvertAll<>(fromType); - return converter.from(convertAll.from(from)); + return scoped(converter).from(convertAll.from(from, converterScope()), converterScope()); } /** @@ -514,7 +521,7 @@ public final class Convert { List result = new ArrayList<>(collection.size()); for (Object o : collection) - result.add(convert(all.from(o), converter)); + result.add(convert(all.from(o, converterScope()), converter)); return result; } @@ -527,17 +534,20 @@ public final class Convert { /** * The converter to convert them all. */ - private static class ConvertAll implements Converter { + private static final class ConvertAll extends AbstractScopedConverter { private final Class toClass; + @SuppressWarnings("unchecked") ConvertAll(Class toClass) { + super(Object.class, (Class) toClass); + this.toClass = toClass; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override - public U from(Object from) { + public U from(Object from, ConverterScope scope) { if (from == null) { // [#936] If types are converted to primitives, the result must not @@ -1239,22 +1249,10 @@ public final class Convert { } @Override - public Object to(U to) { + public Object to(U to, ConverterScope scope) { return to; } - @Override - public Class fromType() { - return Object.class; - } - - @SuppressWarnings("unchecked") - @Override - public Class toType() { - return (Class) toClass; - } - - /** * Convert a long timestamp (millis) to any date type. */ diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockResultSet.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockResultSet.java index b3fbfcd351..c64a240c70 100644 --- a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockResultSet.java +++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockResultSet.java @@ -38,6 +38,8 @@ package org.jooq.tools.jdbc; import static org.jooq.SQLDialect.DEFAULT; +import static org.jooq.ScopedConverter.scoped; +import static org.jooq.impl.Internal.converterScope; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -71,8 +73,10 @@ import org.jooq.Converters; import org.jooq.Field; import org.jooq.Record; import org.jooq.Result; +import org.jooq.ScopedConverter; import org.jooq.impl.DSL; import org.jooq.impl.DefaultConfiguration; +import org.jooq.impl.Internal; import org.jooq.tools.StringUtils; /** @@ -457,7 +461,7 @@ public class MockResultSet extends JDBC41ResultSet implements ResultSet, Seriali .converterProvider() .provide(value == null ? Object.class : (Class) value.getClass(), type); - T converted = converter == null ? null : converter.from(value); + T converted = converter == null ? null : scoped(converter).from(value, converterScope()); wasNull = (converted == null); return converted; } diff --git a/jOOQ/src/main/java/org/jooq/util/postgres/PostgresUtils.java b/jOOQ/src/main/java/org/jooq/util/postgres/PostgresUtils.java index 5d8255af90..1c66b9395d 100644 --- a/jOOQ/src/main/java/org/jooq/util/postgres/PostgresUtils.java +++ b/jOOQ/src/main/java/org/jooq/util/postgres/PostgresUtils.java @@ -38,6 +38,7 @@ package org.jooq.util.postgres; import static java.lang.Integer.toOctalString; +import static org.jooq.impl.Internal.converterScope; import static org.jooq.tools.StringUtils.leftPad; import java.io.ByteArrayOutputStream; @@ -52,6 +53,7 @@ import java.util.List; import org.jooq.Converter; import org.jooq.EnumType; import org.jooq.Record; +import org.jooq.ScopedConverter; import org.jooq.exception.DataTypeException; import org.jooq.tools.StringUtils; import org.jooq.types.DayToSecond; @@ -529,7 +531,7 @@ public class PostgresUtils { String separator = ""; for (int i = 0; i < r.size(); i++) { @SuppressWarnings({ "unchecked", "rawtypes" }) - Object a = ((Converter) r.field(i).getConverter()).to(r.get(i)); + Object a = ((ScopedConverter) r.field(i).getConverter()).to(r.get(i), converterScope()); sb.append(separator); // [#753] null must not be set as a literal