diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java index bb61ba8b11..f0b7bc9622 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java @@ -103,16 +103,16 @@ import org.jooq.impl.Tools.BooleanDataKey; * @author Lukas Eder */ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImplemented { - private static final Set CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); + static final Set CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - final Table table; - final Map, Field> empty; + final Table table; + final Map, Field> empty; // Depending on whether embeddable types are allowed, this data structure // needs to be flattened with duplicates removed, prior to consumption // [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms - final Map, List>> values; - int rows; - int nextRow = -1; + final Map, List>> values; + int rows; + int nextRow = -1; FieldMapsForInsert(Table table) { this.table = table; diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index b1ba763927..b762827f99 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -59,6 +59,7 @@ import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; // ... +// ... import static org.jooq.SQLDialect.SQLITE; // ... // ... diff --git a/jOOQ/src/main/java/org/jooq/impl/Values.java b/jOOQ/src/main/java/org/jooq/impl/Values.java index 6e16674146..77707d7c6c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Values.java +++ b/jOOQ/src/main/java/org/jooq/impl/Values.java @@ -53,6 +53,7 @@ import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; // ... // ... +import static org.jooq.SQLDialect.POSTGRES; // ... // ... // ... @@ -60,6 +61,7 @@ import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.DSL.name; +import static org.jooq.impl.DSL.row; import static org.jooq.impl.Keywords.K_MULTISET; import static org.jooq.impl.Keywords.K_ROW; import static org.jooq.impl.Keywords.K_STRUCT; @@ -69,7 +71,9 @@ import static org.jooq.impl.Keywords.K_VALUES; import static org.jooq.impl.QueryPartListView.wrap; import static org.jooq.impl.SubqueryCharacteristics.DERIVED_TABLE; import static org.jooq.impl.Tools.EMPTY_ROW; +import static org.jooq.impl.Tools.anyMatch; import static org.jooq.impl.Tools.isEmpty; +import static org.jooq.impl.Tools.isVal; import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.visitSubquery; @@ -101,9 +105,10 @@ implements QOM.Values { - static final Set NO_SUPPORT_VALUES = SQLDialect.supportedUntil(FIREBIRD, MARIADB); - static final Set REQUIRE_ROWTYPE_CAST = SQLDialect.supportedBy(FIREBIRD); - static final Set NO_SUPPORT_PARENTHESES = SQLDialect.supportedBy(); + static final Set NO_SUPPORT_VALUES = SQLDialect.supportedUntil(FIREBIRD, MARIADB); + static final Set REQUIRE_ROWTYPE_CAST = SQLDialect.supportedBy(FIREBIRD); + static final Set REQUIRE_ROWTYPE_CAST_ON_NULLS = SQLDialect.supportedBy(POSTGRES); + static final Set NO_SUPPORT_PARENTHESES = SQLDialect.supportedBy(); private final Row[] rows; private transient DataType[] types; @@ -198,6 +203,27 @@ implements return result; } + private final Row castNullLiteralToRowType(Context ctx, Row row) { + if (anyMatch(row.fields(), f -> rendersNullLiteral(ctx, f))) { + Field[] result = new Field[row.size()]; + + for (int i = 0; i < result.length; i++) + if (rendersNullLiteral(ctx, row.field(i)) && rowType()[i].getType() != Object.class) + result[i] = row.field(i).cast(rowType()[i]); + else + result[i] = row.field(i); + + return row(result); + } + else + return row; + } + + private final boolean rendersNullLiteral(Context ctx, Field field) { + return isVal(field) && ((Val) field).getValue() == null && ((Val) field).isInline(ctx) + || field instanceof NullCondition; + } + @Override public final void accept(Context ctx) { @@ -266,7 +292,11 @@ implements - ctx.visit(rows[i]); + // [#11015] NULL literals of known type should be cast in PostgreSQL in the first row + if (i == 0 && ctx.family() == POSTGRES) + ctx.visit(castNullLiteralToRowType(ctx, rows[i])); + else + ctx.visit(rows[i]); } if (rows.length > 1)