Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.annotations.GenIgnore;
Expand Down Expand Up @@ -145,71 +146,76 @@ public DB2ConnectOptions setPreparedStatementCacheMaxSize(int preparedStatementC
return (DB2ConnectOptions) super.setPreparedStatementCacheMaxSize(preparedStatementCacheMaxSize);
}

@Override
public DB2ConnectOptions setPreparedStatementCacheSqlFilter(Predicate<String> predicate) {
return (DB2ConnectOptions) super.setPreparedStatementCacheSqlFilter(predicate);
}

@Override
public DB2ConnectOptions setPreparedStatementCacheSqlLimit(int preparedStatementCacheSqlLimit) {
return (DB2ConnectOptions) super.setPreparedStatementCacheSqlLimit(preparedStatementCacheSqlLimit);
}

@Override
public DB2ConnectOptions setSsl(boolean ssl) {
return (DB2ConnectOptions) super.setSsl(ssl);
}

@Override
public DB2ConnectOptions setSslHandshakeTimeout(long sslHandshakeTimeout) {
return (DB2ConnectOptions) super.setSslHandshakeTimeout(sslHandshakeTimeout);
}

@Override
public DB2ConnectOptions setSslHandshakeTimeoutUnit(TimeUnit sslHandshakeTimeoutUnit) {
return (DB2ConnectOptions) super.setSslHandshakeTimeoutUnit(sslHandshakeTimeoutUnit);
}

@Override
public DB2ConnectOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) {
return (DB2ConnectOptions) super.setSslEngineOptions(sslEngineOptions);
}

@Override
public DB2ConnectOptions setJdkSslEngineOptions(JdkSSLEngineOptions sslEngineOptions) {
return (DB2ConnectOptions) super.setJdkSslEngineOptions(sslEngineOptions);
}

@Override
public DB2ConnectOptions setKeyCertOptions(KeyCertOptions options) {
return (DB2ConnectOptions) super.setKeyCertOptions(options);
}

@Override
public DB2ConnectOptions setKeyStoreOptions(JksOptions options) {
return (DB2ConnectOptions) super.setKeyStoreOptions(options);
}

@Override
public DB2ConnectOptions setOpenSslEngineOptions(OpenSSLEngineOptions sslEngineOptions) {
return (DB2ConnectOptions) super.setOpenSslEngineOptions(sslEngineOptions);
}

@Override
public DB2ConnectOptions setPemKeyCertOptions(PemKeyCertOptions options) {
return (DB2ConnectOptions) super.setPemKeyCertOptions(options);
}

@Override
public DB2ConnectOptions setPemTrustOptions(PemTrustOptions options) {
return (DB2ConnectOptions) super.setPemTrustOptions(options);
}

@Override
public DB2ConnectOptions setTrustAll(boolean trustAll) {
return (DB2ConnectOptions) super.setTrustAll(trustAll);
}

@Override
public DB2ConnectOptions setTrustOptions(TrustOptions options) {
return (DB2ConnectOptions) super.setTrustOptions(options);
}

@Override
public DB2ConnectOptions setTrustStoreOptions(JksOptions options) {
return (DB2ConnectOptions) super.setTrustStoreOptions(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Collections;
import java.util.Map;
import java.util.function.Predicate;

import io.vertx.core.Future;
import io.vertx.core.Promise;
Expand All @@ -41,7 +42,7 @@ public class DB2ConnectionFactory implements ConnectionFactory {
private final Map<String, String> connectionAttributes;
private final boolean cachePreparedStatements;
private final int preparedStatementCacheSize;
private final int preparedStatementCacheSqlLimit;
private final Predicate<String> preparedStatementCacheSqlFilter;
private final int pipeliningLimit;

public DB2ConnectionFactory(ContextInternal context, DB2ConnectOptions options) {
Expand All @@ -58,7 +59,7 @@ public DB2ConnectionFactory(ContextInternal context, DB2ConnectOptions options)

this.cachePreparedStatements = options.getCachePreparedStatements();
this.preparedStatementCacheSize = options.getPreparedStatementCacheMaxSize();
this.preparedStatementCacheSqlLimit = options.getPreparedStatementCacheSqlLimit();
this.preparedStatementCacheSqlFilter = options.getPreparedStatementCacheSqlFilter();
this.pipeliningLimit = options.getPipeliningLimit();

this.netClient = context.owner().createNetClient(netClientOptions);
Expand All @@ -81,7 +82,7 @@ public void doConnect(Promise<Connection> promise) {
if (ar.succeeded()) {
NetSocket so = ar.result();
DB2SocketConnection conn = new DB2SocketConnection((NetSocketInternal) so, cachePreparedStatements,
preparedStatementCacheSize, preparedStatementCacheSqlLimit, pipeliningLimit, context);
preparedStatementCacheSize, preparedStatementCacheSqlFilter, pipeliningLimit, context);
conn.init();
conn.sendStartupMessage(username, password, database, connectionAttributes, promise);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.vertx.db2client.impl;

import java.util.Map;
import java.util.function.Predicate;

import io.netty.channel.ChannelPipeline;
import io.vertx.core.AsyncResult;
Expand All @@ -42,18 +43,18 @@ public class DB2SocketConnection extends SocketConnectionBase {
private Handler<Void> closeHandler;
public final ConnectionMetaData connMetadata = new ConnectionMetaData();

public DB2SocketConnection(NetSocketInternal socket,
boolean cachePreparedStatements,
public DB2SocketConnection(NetSocketInternal socket,
boolean cachePreparedStatements,
int preparedStatementCacheSize,
int preparedStatementCacheSqlLimit,
int pipeliningLimit,
Predicate<String> preparedStatementCacheSqlFilter,
int pipeliningLimit,
ContextInternal context) {
super(socket, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlLimit, pipeliningLimit, context);
super(socket, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlFilter, pipeliningLimit, context);
}

void sendStartupMessage(String username,
String password,
String database,
void sendStartupMessage(String username,
String password,
String database,
Map<String, String> properties,
Promise<Connection> completionHandler) {
InitialHandshakeCommand cmd = new InitialHandshakeCommand(this, username, password, database, properties);
Expand Down Expand Up @@ -93,7 +94,7 @@ public void handleClose(Throwable t) {
super.handleClose(t);
context().runOnContext(closeHandler);
}

@Override
public DatabaseMetadata getDatabaseMetaData() {
return connMetadata.dbMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class DB2PreparedStatement implements PreparedStatement {
final DB2ParamDesc paramDesc;
final DB2RowDesc rowDesc;
final Section section;
final boolean cacheable;

private final Map<String, QueryInstance> activeQueries = new HashMap<>(4);

Expand All @@ -52,12 +51,11 @@ public static class QueryInstance {
}
}

DB2PreparedStatement(String sql, DB2ParamDesc paramDesc, DB2RowDesc rowDesc, Section section, boolean cacheable) {
DB2PreparedStatement(String sql, DB2ParamDesc paramDesc, DB2RowDesc rowDesc, Section section) {
this.paramDesc = paramDesc;
this.rowDesc = rowDesc;
this.sql = sql;
this.section = section;
this.cacheable = cacheable;
}

@Override
Expand All @@ -80,11 +78,6 @@ public String prepare(TupleInternal values) {
return paramDesc.prepare(values);
}

@Override
public boolean cacheable() {
return cacheable;
}

QueryInstance getQueryInstance(String cursorId) {
cursorId = cursorId == null ? UUID.randomUUID().toString() : cursorId;
return activeQueries.computeIfAbsent(cursorId, c -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class PrepareStatementCodec extends CommandCodec<PreparedStatement, PrepareState
private static final Logger LOG = LoggerFactory.getLogger(PrepareStatementCodec.class);

private static enum CommandHandlerState {
INIT, HANDLING_PARAM_COLUMN_DEFINITION,
PARAM_DEFINITIONS_DECODING_COMPLETED,
INIT, HANDLING_PARAM_COLUMN_DEFINITION,
PARAM_DEFINITIONS_DECODING_COMPLETED,
HANDLING_COLUMN_COLUMN_DEFINITION,
COLUMN_DEFINITIONS_DECODING_COMPLETED
}
Expand Down Expand Up @@ -89,7 +89,7 @@ void decodePayload(ByteBuf payload, int payloadLength) {

private void handleReadyForQuery() {
completionHandler.handle(CommandResponse.success(new DB2PreparedStatement(cmd.sql(), new DB2ParamDesc(paramDesc),
new DB2RowDesc(rowDesc), section, cmd.cacheable())));
new DB2RowDesc(rowDesc), section)));
}

private void resetIntermediaryResult() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void doConnect(Promise<Connection> promise) {
fut.onComplete(ar -> {
if (ar.succeeded()) {
NetSocket so = ar.result();
MSSQLSocketConnection conn = new MSSQLSocketConnection((NetSocketInternal) so, false, 0, 0, 1, context);
MSSQLSocketConnection conn = new MSSQLSocketConnection((NetSocketInternal) so, false, 0, sql -> true, 1, context);
conn.init();
conn.sendPreLoginMessage(false, preLogin -> {
if (preLogin.succeeded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@
import io.vertx.sqlclient.spi.DatabaseMetadata;

import java.util.Map;
import java.util.function.Predicate;

class MSSQLSocketConnection extends SocketConnectionBase {

public MSSQLDatabaseMetadata dbMetaData;

MSSQLSocketConnection(NetSocketInternal socket,
boolean cachePreparedStatements,
int preparedStatementCacheSize,
int preparedStatementCacheSqlLimit,
Predicate<String> preparedStatementCacheSqlFilter,
int pipeliningLimit,
ContextInternal context) {
super(socket, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlLimit, pipeliningLimit, context);
super(socket, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlFilter, pipeliningLimit, context);
}

// command response should show what capabilities server provides
Expand All @@ -62,7 +63,7 @@ public void init() {
MSSQLCodec.initPipeLine(pipeline);
super.init();
}

@Override
public DatabaseMetadata getDatabaseMetaData() {
return dbMetaData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@
public class MSSQLPreparedStatement implements PreparedStatement {
final String sql;
final MSSQLParamDesc paramDesc;
final boolean cacheable;

public MSSQLPreparedStatement(String sql, MSSQLParamDesc paramDesc, boolean cacheable) {
public MSSQLPreparedStatement(String sql, MSSQLParamDesc paramDesc) {
this.sql = sql;
this.paramDesc = paramDesc;
this.cacheable = cacheable;
}

@Override
Expand All @@ -47,9 +45,4 @@ public String prepare(TupleInternal values) {
// return paramDesc.prepare(values);
return null;
}

@Override
public boolean cacheable() {
return cacheable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class PrepareStatementCodec extends MSSQLCommandCodec<PreparedStatement, Prepare
void encode(TdsMessageEncoder encoder) {
super.encode(encoder);
// we use sp_prepexec instead of sp_prepare + sp_exec
PreparedStatement preparedStatement = new MSSQLPreparedStatement(cmd.sql(), null, cmd.cacheable());
PreparedStatement preparedStatement = new MSSQLPreparedStatement(cmd.sql(), null);
completionHandler.handle(CommandResponse.success(preparedStatement));

}
Expand Down
6 changes: 2 additions & 4 deletions vertx-mysql-client/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -583,12 +583,10 @@ Sometimes you might meet the notorious error `Can't create more than max_prepare
You can adjust the server system variable `max_prepared_stmt_count` but it has an upper bound value so you can't get rid of the error in this way.

The best way to alleviate this is enabling prepared statement caching, so the prepared statements with the same SQL string could be reused and the client does not have to create a brand new prepared statement for every request.
The prepared statement will be automatically closed when it's evicted from the cache.
The prepared statement will be automatically closed after the statement is executed.
In this way the chances of reaching the limit could be greatly reduced though it could not be totally eliminated.

Note using `SqlClient#preparedQuery` without prepared statement caching enabled will not close the prepared statement after executing!

You can also manage the lifecycle of prepared statements manually by creating a `PreparedStatement` object via `SqlConnection#prepare` interface, or even use the https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html[SQL syntax prepared statement].
You can also manage the lifecycle of prepared statements manually by creating a `PreparedStatement` object via `SqlConnection#prepare` interface so that you can choose when to deallocate the statement handle, or even use the https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html[SQL syntax prepared statement].

=== demystifying prepared batch

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
* Connect options for configuring {@link MySQLConnection} or {@link MySQLPool}.
Expand Down Expand Up @@ -75,7 +76,7 @@ public MySQLConnectOptions(JsonObject json) {
super(json);
MySQLConnectOptionsConverter.fromJson(json, this);
}

public MySQLConnectOptions(SqlConnectOptions other) {
super(other);
if (other instanceof MySQLConnectOptions) {
Expand Down Expand Up @@ -291,6 +292,11 @@ public MySQLConnectOptions setPreparedStatementCacheMaxSize(int preparedStatemen
return (MySQLConnectOptions) super.setPreparedStatementCacheMaxSize(preparedStatementCacheMaxSize);
}

@Override
public MySQLConnectOptions setPreparedStatementCacheSqlFilter(Predicate<String> predicate) {
return (MySQLConnectOptions) super.setPreparedStatementCacheSqlFilter(predicate);
}

@Override
public MySQLConnectOptions setPreparedStatementCacheSqlLimit(int preparedStatementCacheSqlLimit) {
return (MySQLConnectOptions) super.setPreparedStatementCacheSqlLimit(preparedStatementCacheSqlLimit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;
import java.util.function.Predicate;

import static io.vertx.mysqlclient.impl.protocol.CapabilitiesFlag.*;

Expand All @@ -46,7 +47,7 @@ public class MySQLConnectionFactory implements ConnectionFactory {
private final Buffer serverRsaPublicKey;
private final boolean cachePreparedStatements;
private final int preparedStatementCacheSize;
private final int preparedStatementCacheSqlLimit;
private final Predicate<String> preparedStatementCacheSqlFilter;
private final int initialCapabilitiesFlags;

public MySQLConnectionFactory(ContextInternal context, MySQLConnectOptions options) {
Expand Down Expand Up @@ -111,7 +112,7 @@ public MySQLConnectionFactory(ContextInternal context, MySQLConnectOptions optio

this.cachePreparedStatements = options.getCachePreparedStatements();
this.preparedStatementCacheSize = options.getPreparedStatementCacheMaxSize();
this.preparedStatementCacheSqlLimit = options.getPreparedStatementCacheSqlLimit();
this.preparedStatementCacheSqlFilter = options.getPreparedStatementCacheSqlFilter();

this.netClient = context.owner().createNetClient(netClientOptions);
}
Expand All @@ -138,7 +139,7 @@ private void doConnect(Promise<Connection> promise) {
fut.onComplete(ar -> {
if (ar.succeeded()) {
NetSocket so = ar.result();
MySQLSocketConnection conn = new MySQLSocketConnection((NetSocketInternal) so, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlLimit, context);
MySQLSocketConnection conn = new MySQLSocketConnection((NetSocketInternal) so, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlFilter, context);
conn.init();
conn.sendStartupMessage(username, password, database, collation, serverRsaPublicKey, connectionAttributes, sslMode, initialCapabilitiesFlags, charsetEncoding, promise);
} else {
Expand Down
Loading