From c5b98e36ad96865e968ec4f1ec7dbb05db69f7b7 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 25 Mar 2021 16:36:18 +0100 Subject: [PATCH] [jOOQ/jOOQ#11700] Make sure we maintain input bind indexes The ParsingConnectionFactory currently ignores the actual bind index and uses them just for sorting the bind values. This is incorrect, in case a driver implementation support omitting bind values for a given index, e.g. when batching. --- .../jooq/impl/ParsingConnectionFactory.java | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java b/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java index 0522b2f6cc..b186b5c96d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java @@ -37,6 +37,7 @@ */ package org.jooq.impl; +import static java.util.Collections.nCopies; import static org.jooq.impl.DSL.val; import static org.jooq.impl.ParsingConnection.translate; import static org.jooq.impl.R2DBC.setParamType; @@ -44,13 +45,9 @@ import static org.jooq.impl.Tools.EMPTY_PARAM; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.TreeMap; import org.jooq.Configuration; import org.jooq.Param; -import org.jooq.conf.ParamType; -import org.jooq.conf.SettingsTools; import org.jooq.impl.DefaultRenderContext.Rendered; import org.reactivestreams.Publisher; @@ -100,22 +97,22 @@ final class ParsingConnectionFactory implements ConnectionFactory { } @Override - public void onSubscribe(Subscription s) { + public final void onSubscribe(Subscription s) { subscriber.onSubscribe(s); } @Override - public void onNext(Connection c) { + public final void onNext(Connection c) { subscriber.onNext(new ParsingR2DBCConnection(c)); } @Override - public void onError(Throwable t) { + public final void onError(Throwable t) { subscriber.onError(t); } @Override - public void onComplete() { + public final void onComplete() { subscriber.onComplete(); } } @@ -128,82 +125,82 @@ final class ParsingConnectionFactory implements ConnectionFactory { } @Override - public Publisher beginTransaction() { + public final Publisher beginTransaction() { return delegate.beginTransaction(); } @Override - public Publisher beginTransaction(TransactionDefinition definition) { + public final Publisher beginTransaction(TransactionDefinition definition) { return delegate.beginTransaction(definition); } @Override - public Publisher close() { + public final Publisher close() { return delegate.close(); } @Override - public Publisher commitTransaction() { + public final Publisher commitTransaction() { return delegate.commitTransaction(); } @Override - public Publisher createSavepoint(String name) { + public final Publisher createSavepoint(String name) { return delegate.createSavepoint(name); } @Override - public boolean isAutoCommit() { + public final boolean isAutoCommit() { return delegate.isAutoCommit(); } @Override - public ConnectionMetadata getMetadata() { + public final ConnectionMetadata getMetadata() { return delegate.getMetadata(); } @Override - public IsolationLevel getTransactionIsolationLevel() { + public final IsolationLevel getTransactionIsolationLevel() { return delegate.getTransactionIsolationLevel(); } @Override - public Publisher releaseSavepoint(String name) { + public final Publisher releaseSavepoint(String name) { return delegate.releaseSavepoint(name); } @Override - public Publisher rollbackTransaction() { + public final Publisher rollbackTransaction() { return delegate.rollbackTransaction(); } @Override - public Publisher rollbackTransactionToSavepoint(String name) { + public final Publisher rollbackTransactionToSavepoint(String name) { return delegate.rollbackTransactionToSavepoint(name); } @Override - public Publisher setAutoCommit(boolean autoCommit) { + public final Publisher setAutoCommit(boolean autoCommit) { return delegate.setAutoCommit(autoCommit); } @Override - public Publisher setTransactionIsolationLevel(IsolationLevel isolationLevel) { + public final Publisher setTransactionIsolationLevel(IsolationLevel isolationLevel) { return delegate.setTransactionIsolationLevel(isolationLevel); } @Override - public Publisher validate(ValidationDepth depth) { + public final Publisher validate(ValidationDepth depth) { return delegate.validate(depth); } @Override - public Batch createBatch() { + public final Batch createBatch() { return new ParsingR2DBCBatch(delegate.createBatch()); } @Override - public Statement createStatement(String sql) { + public final Statement createStatement(String sql) { return new ParsingR2DBCStatement(delegate, sql); } } @@ -216,70 +213,80 @@ final class ParsingConnectionFactory implements ConnectionFactory { } @Override - public Batch add(String sql) { + public final Batch add(String sql) { delegate.add(translate(configuration, sql).sql); return this; } @Override - public Publisher execute() { + public final Publisher execute() { return delegate.execute(); } } private final class ParsingR2DBCStatement implements Statement { - private final Connection delegate; - private final String input; - private final List>> params; + private final Connection delegate; + private final String input; + private final List>> params; private ParsingR2DBCStatement(Connection delegate, String input) { this.delegate = delegate; this.input = input; this.params = new ArrayList<>(); - params.add(new TreeMap<>()); + params.add(new ArrayList<>()); + } + + private final List> list(int index) { + List> list = params.get(params.size() - 1); + + int reserve = index + 1 - list.size(); + if (reserve > 0) + list.addAll(nCopies(reserve, null)); + + return list; } @Override - public Statement add() { - params.add(new TreeMap<>()); + public final Statement add() { + params.add(new ArrayList<>()); return this; } @Override - public Statement bind(int index, Object value) { - params.get(params.size() - 1).put(index, val(value)); + public final Statement bind(int index, Object value) { + list(index).set(index, val(value)); return this; } @Override - public Statement bind(String name, Object value) { + public final Statement bind(String name, Object value) { // TODO throw new UnsupportedOperationException(); } @Override - public Statement bindNull(int index, Class type) { - params.get(params.size() - 1).put(index, val(null, type)); + public final Statement bindNull(int index, Class type) { + list(index).set(index, val(null, type)); return this; } @Override - public Statement bindNull(String name, Class type) { + public final Statement bindNull(String name, Class type) { // TODO throw new UnsupportedOperationException(); } @SuppressWarnings("null") @Override - public Publisher execute() { + public final Publisher execute() { Statement statement = null; - for (Map> p : params) { + for (List> p : params) { if (statement != null) statement.add(); - Rendered rendered = translate(configuration, input, p.values().toArray(EMPTY_PARAM)); + Rendered rendered = translate(configuration, input, p.toArray(EMPTY_PARAM)); if (statement == null) statement = delegate.createStatement(rendered.sql);