blob: 7278bd8343d59b267b4a601c10883d0088d5f2c7 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2021 MariaDB Corporation Ab
package org.mariadb.jdbc.client.impl;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.mariadb.jdbc.Configuration;
import org.mariadb.jdbc.HostAddress;
import org.mariadb.jdbc.client.Completion;
import org.mariadb.jdbc.client.context.RedoContext;
import org.mariadb.jdbc.export.MaxAllowedPacketException;
import org.mariadb.jdbc.export.Prepare;
import org.mariadb.jdbc.message.ClientMessage;
import org.mariadb.jdbc.message.client.*;
import org.mariadb.jdbc.message.server.PrepareResultPacket;
import org.mariadb.jdbc.util.log.Logger;
import org.mariadb.jdbc.util.log.Loggers;
/** Replay client wrapper */
public class ReplayClient extends StandardClient {
private static final Logger logger = Loggers.getLogger(ReplayClient.class);
/**
* Constructor
*
* @param conf configuration
* @param hostAddress host
* @param lock thread lock object
* @param skipPostCommands must skip connection post commands
* @throws SQLException if connection fails
*/
public ReplayClient(
Configuration conf, HostAddress hostAddress, ReentrantLock lock, boolean skipPostCommands)
throws SQLException {
super(conf, hostAddress, lock, skipPostCommands);
}
@Override
public int sendQuery(ClientMessage message) throws SQLException {
checkNotClosed();
try {
if (message instanceof RedoableClientMessage)
((RedoableClientMessage) message).ensureReplayable(context);
return message.encode(writer, context);
} catch (IOException ioException) {
if (ioException instanceof MaxAllowedPacketException) {
if (((MaxAllowedPacketException) ioException).isMustReconnect()) {
destroySocket();
throw exceptionFactory
.withSql(message.description())
.create(
"Packet too big for current server max_allowed_packet value",
"08000",
ioException);
}
throw exceptionFactory
.withSql(message.description())
.create(
"Packet too big for current server max_allowed_packet value", "HZ000", ioException);
}
destroySocket();
throw exceptionFactory
.withSql(message.description())
.create("Socket error", "08000", ioException);
}
}
@Override
public List<Completion> executePipeline(
ClientMessage[] messages,
org.mariadb.jdbc.Statement stmt,
int fetchSize,
long maxRows,
int resultSetConcurrency,
int resultSetType,
boolean closeOnCompletion,
boolean canRedo)
throws SQLException {
List<Completion> res =
super.executePipeline(
messages,
stmt,
fetchSize,
maxRows,
resultSetConcurrency,
resultSetType,
closeOnCompletion,
canRedo);
((RedoContext) context).saveRedo(messages);
return res;
}
@Override
public List<Completion> execute(
ClientMessage message,
org.mariadb.jdbc.Statement stmt,
int fetchSize,
long maxRows,
int resultSetConcurrency,
int resultSetType,
boolean closeOnCompletion,
boolean canRedo)
throws SQLException {
List<Completion> completions =
super.execute(
message,
stmt,
fetchSize,
maxRows,
resultSetConcurrency,
resultSetType,
closeOnCompletion,
canRedo);
((RedoContext) context).saveRedo(message);
return completions;
}
/**
* Replay transaction, re-prepare server command if needed
*
* @param transactionSaver transaction cache
* @throws SQLException if any error occurs
*/
public void transactionReplay(TransactionSaver transactionSaver) throws SQLException {
RedoableClientMessage[] buffers = transactionSaver.getBuffers();
try {
// replay all but last
Prepare prepare;
for (int i = 0; i < transactionSaver.getIdx(); i++) {
RedoableClientMessage querySaver = buffers[i];
int responseNo;
if (querySaver instanceof RedoableWithPrepareClientMessage) {
// command is a prepare statement query
// redo on new connection need to re-prepare query
// and substitute statement id
RedoableWithPrepareClientMessage redoable =
((RedoableWithPrepareClientMessage) querySaver);
String cmd = redoable.getCommand();
prepare = context.getPrepareCache().get(cmd, redoable.prep());
if (prepare == null) {
PreparePacket preparePacket = new PreparePacket(cmd);
sendQuery(preparePacket);
prepare = (PrepareResultPacket) readPacket(preparePacket);
logger.info("replayed command after failover: " + preparePacket.description());
}
responseNo = querySaver.reEncode(writer, context, prepare);
} else {
responseNo = querySaver.reEncode(writer, context, null);
}
logger.info("replayed command after failover: " + querySaver.description());
for (int j = 0; j < responseNo; j++) {
readResponse(querySaver);
}
}
} catch (IOException e) {
throw context
.getExceptionFactory()
.create("Socket error during transaction replay", "08000", e);
}
}
}