[jOOQ/jOOQ#11512] ParsingConnection should infer bind variable type from
PreparedStatement calls This includes: - [jOOQ/jOOQ#11623] Refactor a few internal checked exception throwing functional interfaces
This commit is contained in:
parent
e1082e9e1c
commit
984d308b07
@ -330,11 +330,6 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface BooleanConsumer {
|
||||
void accept(boolean b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final C data(Object key, Object value, Consumer<? super C> consumer) {
|
||||
return toggle(
|
||||
|
||||
@ -83,6 +83,7 @@ import org.jooq.conf.StatementType;
|
||||
import org.jooq.exception.ControlFlowSignal;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.exception.DetachedException;
|
||||
import org.jooq.impl.DefaultRenderContext.Rendered;
|
||||
import org.jooq.tools.Ints;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
|
||||
@ -488,23 +489,6 @@ abstract class AbstractQuery<R extends Record> extends AbstractFetchable<R> impl
|
||||
return true;
|
||||
}
|
||||
|
||||
static class Rendered {
|
||||
String sql;
|
||||
QueryPartList<Param<?>> bindValues;
|
||||
int skipUpdateCounts;
|
||||
|
||||
Rendered(String sql, QueryPartList<Param<?>> bindValues, int skipUpdateCounts) {
|
||||
this.sql = sql;
|
||||
this.bindValues = bindValues;
|
||||
this.skipUpdateCounts = skipUpdateCounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
|
||||
private final Rendered getSQL0(ExecuteContext ctx) {
|
||||
Rendered result;
|
||||
DefaultRenderContext render;
|
||||
|
||||
@ -336,7 +336,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
else if (Tools.nonReplacingEmbeddable(field))
|
||||
return (T) Tools
|
||||
.newRecord(fetched, ((EmbeddableTableField<?, ?>) field).recordType)
|
||||
.operate(new TransferRecordState<Record>(embeddedFields(field)));
|
||||
.operate(new TransferRecordState<>(embeddedFields(field)));
|
||||
else
|
||||
throw Tools.indexFail(fields, field);
|
||||
}
|
||||
@ -850,7 +850,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
return Tools.newRecord(fetched, type, fields, configuration()).operate(new TransferRecordState<>(null));
|
||||
}
|
||||
|
||||
private class TransferRecordState<R extends Record> implements RecordOperation<R, MappingException> {
|
||||
private class TransferRecordState<R extends Record> implements ThrowingFunction<R, R, MappingException> {
|
||||
|
||||
private final Field<?>[] targetFields;
|
||||
|
||||
@ -859,7 +859,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
}
|
||||
|
||||
@Override
|
||||
public R operate(R target) throws MappingException {
|
||||
public R apply(R target) throws MappingException {
|
||||
AbstractRecord source = AbstractRecord.this;
|
||||
|
||||
try {
|
||||
|
||||
@ -1453,7 +1453,7 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private class CursorRecordInitialiser implements RecordOperation<AbstractRecord, SQLException> {
|
||||
private class CursorRecordInitialiser implements ThrowingFunction<AbstractRecord, AbstractRecord, SQLException> {
|
||||
|
||||
private final AbstractRow initialiserFields;
|
||||
private int offset;
|
||||
@ -1469,7 +1469,7 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractRecord operate(AbstractRecord record) throws SQLException {
|
||||
public AbstractRecord apply(AbstractRecord record) throws SQLException {
|
||||
ctx.record(record);
|
||||
listener.recordStart(ctx);
|
||||
int size = initialiserFields.size();
|
||||
@ -1528,7 +1528,7 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
|
||||
}
|
||||
|
||||
if (nested != null) {
|
||||
value = (T) Tools.newRecord(true, recordType, nested, ((DefaultExecuteContext) ctx).originalConfiguration())
|
||||
value = (T) Tools.newRecord(true, (Class<AbstractRecord>) recordType, nested, ((DefaultExecuteContext) ctx).originalConfiguration())
|
||||
.operate(new CursorRecordInitialiser(nested, offset + index));
|
||||
|
||||
offset += nested.size() - 1;
|
||||
|
||||
@ -962,4 +962,21 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
|
||||
log.debug("Re-render query", "Forcing bind variable inlining as " + configuration().dialect() + " does not support " + peekIndex() + " bind variables (or more) in a single query");
|
||||
}
|
||||
}
|
||||
|
||||
static class Rendered {
|
||||
String sql;
|
||||
QueryPartList<Param<?>> bindValues;
|
||||
int skipUpdateCounts;
|
||||
|
||||
Rendered(String sql, QueryPartList<Param<?>> bindValues, int skipUpdateCounts) {
|
||||
this.sql = sql;
|
||||
this.bindValues = bindValues;
|
||||
this.skipUpdateCounts = skipUpdateCounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ final class MetaImpl extends AbstractMeta {
|
||||
this.inverseSchemaCatalog = INVERSE_SCHEMA_CATALOG.contains(dialect());
|
||||
}
|
||||
|
||||
final <R> R catalogSchema(Catalog catalog, Schema schema, ThrowingBiFunction<String, String, R> function) throws SQLException {
|
||||
final <R> R catalogSchema(Catalog catalog, Schema schema, ThrowingBiFunction<String, String, R, SQLException> function) throws SQLException {
|
||||
return catalogSchema(
|
||||
catalog != null ? catalog.getName() : null,
|
||||
schema != null ? schema.getName() : null,
|
||||
@ -165,7 +165,7 @@ final class MetaImpl extends AbstractMeta {
|
||||
);
|
||||
}
|
||||
|
||||
final <R> R catalogSchema(String catalog, String schema, ThrowingBiFunction<String, String, R> function) throws SQLException {
|
||||
final <R> R catalogSchema(String catalog, String schema, ThrowingBiFunction<String, String, R, SQLException> function) throws SQLException {
|
||||
String c = defaultIfEmpty(catalog, null);
|
||||
String s = defaultIfEmpty(schema, null);
|
||||
|
||||
@ -176,22 +176,12 @@ final class MetaImpl extends AbstractMeta {
|
||||
return function.apply(c, s);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingBiFunction<T1, T2, R> {
|
||||
R apply(T1 t1, T2 t2) throws SQLException;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface MetaFunction {
|
||||
Result<Record> run(DatabaseMetaData meta) throws SQLException;
|
||||
}
|
||||
|
||||
private final Result<Record> meta(MetaFunction consumer) {
|
||||
private final Result<Record> meta(ThrowingFunction<DatabaseMetaData, Result<Record>, SQLException> function) {
|
||||
if (databaseMetaData == null)
|
||||
return dsl().connectionResult(connection -> consumer.run(connection.getMetaData()));
|
||||
return dsl().connectionResult(connection -> function.apply(connection.getMetaData()));
|
||||
|
||||
try {
|
||||
return consumer.run(databaseMetaData);
|
||||
return function.apply(databaseMetaData);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new DataAccessException("Error while running MetaFunction", e);
|
||||
|
||||
@ -44,7 +44,9 @@ import java.sql.Statement;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.Parser;
|
||||
import org.jooq.impl.DefaultRenderContext.Rendered;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.jdbc.DefaultConnection;
|
||||
|
||||
@ -55,9 +57,9 @@ final class ParsingConnection extends DefaultConnection {
|
||||
|
||||
private static final JooqLogger log = JooqLogger.getLogger(ParsingConnection.class);
|
||||
|
||||
private final Configuration configuration;
|
||||
private final DSLContext ctx;
|
||||
private final Parser parser;
|
||||
final Configuration configuration;
|
||||
final DSLContext ctx;
|
||||
final Parser parser;
|
||||
|
||||
ParsingConnection(Configuration configuration) {
|
||||
super(configuration.connectionProvider().acquire());
|
||||
@ -67,10 +69,15 @@ final class ParsingConnection extends DefaultConnection {
|
||||
this.parser = ctx.parser();
|
||||
}
|
||||
|
||||
final String translate(String sql) {
|
||||
final Rendered translate(String sql, Param<?>... bindValues) {
|
||||
log.debug("Translating from", sql);
|
||||
String result = ctx.render(parser.parseQuery(sql));
|
||||
log.debug("Translating to", result);
|
||||
DefaultRenderContext render = (DefaultRenderContext) ctx.renderContext();
|
||||
Rendered result = new Rendered(
|
||||
render.visit(parser.parseQuery(sql, (Object[]) bindValues)).render(),
|
||||
render.bindValues(),
|
||||
render.skipUpdateCounts()
|
||||
);
|
||||
log.debug("Translating to", result.sql);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -89,49 +96,61 @@ final class ParsingConnection extends DefaultConnection {
|
||||
return new ParsingStatement(this, getDelegate().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
|
||||
}
|
||||
|
||||
private final ThrowingFunction<Param<?>[], PreparedStatement, SQLException> prepareAndBind(
|
||||
String sql,
|
||||
ThrowingFunction<String, PreparedStatement, SQLException> f
|
||||
) {
|
||||
return p -> {
|
||||
Rendered rendered = translate(sql, p);
|
||||
PreparedStatement s = f.apply(rendered.sql);
|
||||
new DefaultBindContext(configuration, s).visit(rendered.bindValues);
|
||||
return s;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql)));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql), resultSetType, resultSetConcurrency));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s, resultSetType, resultSetConcurrency)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql), resultSetType, resultSetConcurrency, resultSetHoldability));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s, resultSetType, resultSetConcurrency, resultSetHoldability)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql), autoGeneratedKeys));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s, autoGeneratedKeys)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql), columnIndexes));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s, columnIndexes)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareStatement(translate(sql), columnNames));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareStatement(s, columnNames)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CallableStatement prepareCall(String sql) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareCall(translate(sql)));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareCall(s)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareCall(s, resultSetType, resultSetConcurrency)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return new ParsingStatement(this, getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
|
||||
return new ParsingStatement(this, prepareAndBind(sql, s -> getDelegate().prepareCall(s, resultSetType, resultSetConcurrency, resultSetHoldability)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -92,7 +92,7 @@ final class RecordDelegate<R extends Record> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final <E extends Exception> R operate(RecordOperation<? super R, E> operation) throws E {
|
||||
final <E extends Exception> R operate(ThrowingFunction<R, R, E> operation) throws E {
|
||||
R record = recordSupplier.get();
|
||||
|
||||
// [#3300] Records that were fetched from the database
|
||||
@ -142,7 +142,7 @@ final class RecordDelegate<R extends Record> {
|
||||
|
||||
if (operation != null) {
|
||||
try {
|
||||
operation.operate(record);
|
||||
operation.apply(record);
|
||||
}
|
||||
|
||||
// [#2770][#3036] Exceptions must not propagate before listeners receive "end" events
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordListener;
|
||||
|
||||
/**
|
||||
* An operation callback for {@link Record} objects, abstracting
|
||||
* {@link RecordListener} lifecycle handling.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface RecordOperation<R extends Record, E extends Exception> {
|
||||
|
||||
/**
|
||||
* Callback method to initialise a record.
|
||||
*/
|
||||
R operate(R record) throws E;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user