From 2802dc26072e700a0471aaebed1e58dcc6891f64 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 27 Jan 2022 10:45:49 +0100 Subject: [PATCH] [jOOQ/jOOQ#12956] Can no longer store/retrieve blob data exceeding 1M in H2 2.0 --- .../main/java/org/jooq/impl/BlobBinding.java | 9 +- .../java/org/jooq/impl/DefaultBinding.java | 137 +++++++++++++----- 2 files changed, 106 insertions(+), 40 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/BlobBinding.java b/jOOQ/src/main/java/org/jooq/impl/BlobBinding.java index 9c02a6eaf5..c96feb9272 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BlobBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/BlobBinding.java @@ -42,6 +42,7 @@ import static org.jooq.impl.DefaultExecuteContext.localTargetConnection; import static org.jooq.impl.Tools.asInt; import java.sql.Blob; +import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; @@ -88,12 +89,12 @@ public class BlobBinding implements Binding { @Override public final void set(BindingSetStatementContext ctx) throws SQLException { - ctx.statement().setBlob(ctx.index(), newBlob(ctx, ctx.value())); + ctx.statement().setBlob(ctx.index(), newBlob(ctx, ctx.value(), ctx.statement().getConnection())); } @Override public final void set(BindingSetSQLOutputContext ctx) throws SQLException { - ctx.output().writeBlob(newBlob(ctx, ctx.value())); + ctx.output().writeBlob(newBlob(ctx, ctx.value(), null)); } @Override @@ -132,7 +133,7 @@ public class BlobBinding implements Binding { } } - static final Blob newBlob(ResourceManagingScope scope, byte[] bytes) throws SQLException { + static final Blob newBlob(ResourceManagingScope scope, byte[] bytes, Connection connection) throws SQLException { Blob blob; switch (scope.dialect()) { @@ -148,7 +149,7 @@ public class BlobBinding implements Binding { default: { - blob = localConnection().createBlob(); + blob = (connection != null ? connection : localConnection()).createBlob(); break; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 0ef91efeb9..42112017d0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -1939,45 +1939,64 @@ public class DefaultBinding implements Binding { } static final class DefaultBytesBinding extends AbstractBinding { - private static final Set INLINE_AS_X_APOS = SQLDialect.supportedBy(H2, HSQLDB, MARIADB, MYSQL, SQLITE); - private static final Set REQUIRE_BYTEA_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - - - + // [#12956] Starting from H2 2.0, we can't use byte[] for BLOB anymore, if they're + // larger than 1MB + private final BlobBinding blobs; DefaultBytesBinding(DataType dataType, Converter converter) { super(dataType, converter); + + this.blobs = new BlobBinding(); } @Override final void setNull0(BindingSetStatementContext ctx) throws SQLException { + switch (ctx.family()) { - super.setNull0(ctx); + + default: + super.setNull0(ctx); + break; + } } @Override final void sqlInline0(BindingSQLContext ctx, byte[] value) { // [#1154] Binary data cannot always be inlined - if (INLINE_AS_X_APOS.contains(ctx.dialect())) - ctx.render() - .sql("X'") - .sql(convertBytesToHex(value)) - .sql('\''); - else if (ctx.dialect() == DERBY) - ctx.render() - .visit(K_CAST) - .sql("(X'") - .sql(convertBytesToHex(value)) - .sql("' ") - .visit(K_AS) - .sql(' ') - .visit(BLOB) - .sql(')'); + + switch (ctx.family()) { + + + + case H2: + case HSQLDB: + case MARIADB: + case MYSQL: + case SQLITE: + ctx.render() + .sql("X'") + .sql(convertBytesToHex(value)) + .sql('\''); + + break; + + case DERBY: + ctx.render() + .visit(K_CAST) + .sql("(X'") + .sql(convertBytesToHex(value)) + .sql("' ") + .visit(K_AS) + .sql(' ') + .visit(BLOB) + .sql(')'); + + break; @@ -2000,24 +2019,54 @@ public class DefaultBinding implements Binding { - else if (REQUIRE_BYTEA_CAST.contains(ctx.dialect())) - ctx.render() - .sql("E'") - .sql(PostgresUtils.toPGString(value)) - .sql("'::bytea"); - // This default behaviour is used in debug logging for dialects - // that do not support inlining binary data - else - ctx.render() - .sql("X'") - .sql(convertBytesToHex(value)) - .sql('\''); + + + + + + + + + + + + + + + + + + case POSTGRES: + case YUGABYTEDB: + ctx.render() + .sql("E'") + .sql(PostgresUtils.toPGString(value)) + .sql("'::bytea"); + + break; + + default: + ctx.render() + .sql("X'") + .sql(convertBytesToHex(value)) + .sql('\''); + + break; + } } @Override final void set0(BindingSetStatementContext ctx, byte[] value) throws SQLException { - ctx.statement().setBytes(ctx.index(), value); + switch (ctx.family()) { + case H2: + blobs.set(new DefaultBindingSetStatementContext<>(ctx.executeContext(), ctx.statement(), ctx.index(), value)); + break; + + default: + ctx.statement().setBytes(ctx.index(), value); + break; + } } @Override @@ -2036,12 +2085,28 @@ public class DefaultBinding implements Binding { @Override final byte[] get0(BindingGetResultSetContext ctx) throws SQLException { - return ctx.resultSet().getBytes(ctx.index()); + switch (ctx.family()) { + case H2: + DefaultBindingGetResultSetContext x = new DefaultBindingGetResultSetContext<>(ctx.executeContext(), ctx.resultSet(), ctx.index()); + blobs.get(x); + return x.value(); + + default: + return ctx.resultSet().getBytes(ctx.index()); + } } @Override final byte[] get0(BindingGetStatementContext ctx) throws SQLException { - return ctx.statement().getBytes(ctx.index()); + switch (ctx.family()) { + case H2: + DefaultBindingGetStatementContext x = new DefaultBindingGetStatementContext<>(ctx.executeContext(), ctx.statement(), ctx.index()); + blobs.get(x); + return x.value(); + + default: + return ctx.statement().getBytes(ctx.index()); + } } @Override