diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 46dd9055b1..92eb874460 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -29976,7 +29976,7 @@ public class DSL { @NotNull @Support public static Param param(DataType type) { - return new Val<>(null, type); + return new Val<>(null, type, false); } /** @@ -30038,7 +30038,7 @@ public class DSL { @NotNull @Support public static Param param(String name, DataType type) { - return new Val<>(null, type, name); + return new Val<>(null, type, false, name); } /** @@ -30073,7 +30073,7 @@ public class DSL { @NotNull @Support public static Param param(String name, T value) { - return new Val<>(value, Tools.field(value).getDataType(), name); + return new Val<>(value, Tools.field(value).getDataType(), true, name); } /** @@ -31603,7 +31603,7 @@ public class DSL { @Support public static Param val(T value) { Class type = value == null ? Object.class : value.getClass(); - return val(value, getDataType0(type)); + return val0(value, getDataType0(type), true); } /** @@ -32064,6 +32064,10 @@ public class DSL { @NotNull @Support public static Param val(Object value, DataType type) { + return val0(value, type, false); + } + + private static Param val0(Object value, DataType type, boolean inferredDataType) { // Advanced data types have dedicated constant types if (value instanceof QualifiedRecord r) @@ -32079,9 +32083,14 @@ public class DSL { + + // [#14694] value can be a Param contained in a Row + else if (value instanceof Val p) + return p.convertTo(type); + // The default behaviour T converted = type.convert(value); - return new Val<>(converted, mostSpecific(converted, type)); + return new Val<>(converted, mostSpecific(converted, type), inferredDataType); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 56a6af94d2..9b4e039a53 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -2030,8 +2030,19 @@ final class Tools { @SuppressWarnings("unchecked") private static final Field field(Object value, Supplier> defaultValue) { + // [#14694] Inferred data types may have to be refined lazily, here. + // For example, when wrapping row(1, 2), then the integers may + // still require a converter to be applied to them, when the + // row is passed to the INSERT's valuesOfRows() method. + if (value instanceof Val p) { + if (p.inferredDataType) + return defaultValue.get(); + else + return (Field) p; + } + // Fields can be mixed with constant values - if (value instanceof Field) + else if (value instanceof Field) return (Field) value; // [#6362] [#8220] Single-column selects can be considered fields, too @@ -3317,7 +3328,7 @@ final class Tools { else { @SuppressWarnings("unchecked") Class type = (Class) (substitute != null ? substitute.getClass() : Object.class); - result.add(new Val<>(substitute, DSL.getDataType(type))); + result.add(new Val<>(substitute, DSL.getDataType(type), true)); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java index 414a66eccc..9e70a2bbbd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Val.java +++ b/jOOQ/src/main/java/org/jooq/impl/Val.java @@ -96,12 +96,29 @@ final class Val extends AbstractParam implements UEmpty { private static final JooqLogger log = JooqLogger.getLogger(Val.class); private static final ConcurrentHashMap, Object> legacyWarnings = new ConcurrentHashMap<>(); - Val(T value, DataType type) { + /** + * [#14694] Whether the data type was inferred as opposed to provided + * explicitly. + *

+ * Numerous features depend on an inferred data type being overriden lazily + * once the information is available, e.g. when passing around a row(1, 2) + * to an INSERT statement, the initial type information should + * be overridden once the row is copied to the statement. It is different + * from when users provide type information explicitly, such as row(val(1, + * INTEGER), val(2, INTEGER)). + */ + final boolean inferredDataType; + + Val(T value, DataType type, boolean inferredDataType) { super(value, type(value, type)); + + this.inferredDataType = inferredDataType; } - Val(T value, DataType type, String paramName) { + Val(T value, DataType type, boolean inferredDataType, String paramName) { super(value, type(value, type), paramName); + + this.inferredDataType = inferredDataType; } private static final DataType type(T value, DataType type) { @@ -116,7 +133,7 @@ final class Val extends AbstractParam implements UEmpty { * [#10438] Convert this bind value to a new type. */ @SuppressWarnings({ "rawtypes", "unchecked" }) - final Field convertTo(DataType type) { + final Param convertTo(DataType type) { // [#10438] A user defined data type could was not provided explicitly, // when wrapping a bind value in DSL::val or DSL::inline @@ -146,13 +163,13 @@ final class Val extends AbstractParam implements UEmpty { } final Val copy(Object newValue) { - Val w = new Val<>(getDataType().convert(newValue), getDataType(), getParamName()); + Val w = new Val<>(getDataType().convert(newValue), getDataType(), inferredDataType, getParamName()); w.setInline0(isInline()); return w; } final Val convertTo0(DataType type) { - Val w = new Val<>(type.convert(getValue()), type, getParamName()); + Val w = new Val<>(type.convert(getValue()), type, inferredDataType, getParamName()); w.setInline0(isInline()); return w; } @@ -358,7 +375,7 @@ final class Val extends AbstractParam implements UEmpty { @Override public final Param $inline(boolean inline) { - Val w = new Val<>(value, getDataType(), getParamName()); + Val w = new Val<>(value, getDataType(), inferredDataType, getParamName()); w.setInline0(inline); return w; }