[jOOQ/jOOQ#14694] INSERT statement does not apply types to bind values in VALUES clause when using valuesOfRows()

This commit is contained in:
Lukas Eder 2023-02-24 18:01:55 +01:00
parent 849d4ec9e2
commit 4c63a20d2a
3 changed files with 50 additions and 13 deletions

View File

@ -29976,7 +29976,7 @@ public class DSL {
@NotNull
@Support
public static <T> Param<T> param(DataType<T> type) {
return new Val<>(null, type);
return new Val<>(null, type, false);
}
/**
@ -30038,7 +30038,7 @@ public class DSL {
@NotNull
@Support
public static <T> Param<T> param(String name, DataType<T> 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 <T> Param<T> 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 <T> Param<T> 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 <T> Param<T> val(Object value, DataType<T> type) {
return val0(value, type, false);
}
private static <T> Param<T> val0(Object value, DataType<T> 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);
}
/**

View File

@ -2030,8 +2030,19 @@ final class Tools {
@SuppressWarnings("unchecked")
private static final <T> Field<T> field(Object value, Supplier<Field<T>> 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<T>) p;
}
// Fields can be mixed with constant values
if (value instanceof Field<?>)
else if (value instanceof Field<?>)
return (Field<T>) value;
// [#6362] [#8220] Single-column selects can be considered fields, too
@ -3317,7 +3328,7 @@ final class Tools {
else {
@SuppressWarnings("unchecked")
Class<Object> type = (Class<Object>) (substitute != null ? substitute.getClass() : Object.class);
result.add(new Val<>(substitute, DSL.getDataType(type)));
result.add(new Val<>(substitute, DSL.getDataType(type), true));
}
}

View File

@ -96,12 +96,29 @@ final class Val<T> extends AbstractParam<T> implements UEmpty {
private static final JooqLogger log = JooqLogger.getLogger(Val.class);
private static final ConcurrentHashMap<Class<?>, Object> legacyWarnings = new ConcurrentHashMap<>();
Val(T value, DataType<T> type) {
/**
* [#14694] Whether the data type was inferred as opposed to provided
* explicitly.
* <p>
* 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 <code>INSERT</code> 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<T> type, boolean inferredDataType) {
super(value, type(value, type));
this.inferredDataType = inferredDataType;
}
Val(T value, DataType<T> type, String paramName) {
Val(T value, DataType<T> type, boolean inferredDataType, String paramName) {
super(value, type(value, type), paramName);
this.inferredDataType = inferredDataType;
}
private static final <T> DataType<T> type(T value, DataType<T> type) {
@ -116,7 +133,7 @@ final class Val<T> extends AbstractParam<T> implements UEmpty {
* [#10438] Convert this bind value to a new type.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
final <U> Field<U> convertTo(DataType<U> type) {
final <U> Param<U> convertTo(DataType<U> 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<T> extends AbstractParam<T> implements UEmpty {
}
final Val<T> copy(Object newValue) {
Val<T> w = new Val<>(getDataType().convert(newValue), getDataType(), getParamName());
Val<T> w = new Val<>(getDataType().convert(newValue), getDataType(), inferredDataType, getParamName());
w.setInline0(isInline());
return w;
}
final <U> Val<U> convertTo0(DataType<U> type) {
Val<U> w = new Val<>(type.convert(getValue()), type, getParamName());
Val<U> w = new Val<>(type.convert(getValue()), type, inferredDataType, getParamName());
w.setInline0(isInline());
return w;
}
@ -358,7 +375,7 @@ final class Val<T> extends AbstractParam<T> implements UEmpty {
@Override
public final Param<T> $inline(boolean inline) {
Val<T> w = new Val<>(value, getDataType(), getParamName());
Val<T> w = new Val<>(value, getDataType(), inferredDataType, getParamName());
w.setInline0(inline);
return w;
}