blob: 29755916510b7d9d260e37d2a13281274e69c1c3 [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.message.client;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import org.mariadb.jdbc.ServerPreparedStatement;
import org.mariadb.jdbc.client.Context;
import org.mariadb.jdbc.client.socket.Writer;
import org.mariadb.jdbc.client.util.Parameter;
import org.mariadb.jdbc.client.util.Parameters;
import org.mariadb.jdbc.export.Prepare;
import org.mariadb.jdbc.message.ClientMessage;
import org.mariadb.jdbc.message.server.PrepareResultPacket;
import org.mariadb.jdbc.plugin.codec.ByteArrayCodec;
/**
* Execute command (COM_STMT_EXECUTE) See https://mariadb.com/kb/en/com_stmt_execute/ for
* documentation
*/
public final class ExecutePacket implements RedoableWithPrepareClientMessage {
private Parameters parameters;
private final String command;
private final ServerPreparedStatement prep;
private Prepare prepareResult;
private InputStream localInfileInputStream;
/**
* Constructor
*
* @param prepareResult prepare result
* @param parameters parameter
* @param command sql command
* @param prep prepared statement
* @param localInfileInputStream local infile input stream
*/
public ExecutePacket(
Prepare prepareResult,
Parameters parameters,
String command,
ServerPreparedStatement prep,
InputStream localInfileInputStream) {
this.parameters = parameters;
this.prepareResult = prepareResult;
this.command = command;
this.prep = prep;
this.localInfileInputStream = localInfileInputStream;
}
public void saveParameters() {
this.parameters = this.parameters.clone();
}
@Override
public void ensureReplayable(Context context) throws IOException, SQLException {
int parameterCount = parameters.size();
for (int i = 0; i < parameterCount; i++) {
Parameter p = parameters.get(i);
if (!p.isNull() && p.canEncodeLongData()) {
this.parameters.set(
i, new org.mariadb.jdbc.codec.Parameter<>(ByteArrayCodec.INSTANCE, p.encodeData()));
}
}
}
public int encode(Writer writer, Context context, Prepare newPrepareResult)
throws IOException, SQLException {
int statementId =
(newPrepareResult != null && newPrepareResult.getStatementId() != -1)
? newPrepareResult.getStatementId()
: (this.prepareResult != null ? this.prepareResult.getStatementId() : -1);
int parameterCount = parameters.size();
// send long data value in separate packet
for (int i = 0; i < parameterCount; i++) {
Parameter p = parameters.get(i);
if (!p.isNull() && p.canEncodeLongData()) {
new LongDataPacket(statementId, p, i).encode(writer, context);
}
}
writer.initPacket();
writer.writeByte(0x17);
writer.writeInt(statementId);
writer.writeByte(0x00); // NO CURSOR
writer.writeInt(1); // Iteration pos
if (parameterCount > 0) {
// create null bitmap and reserve place in writer
int nullCount = (parameterCount + 7) / 8;
byte[] nullBitsBuffer = new byte[nullCount];
int initialPos = writer.pos();
writer.pos(initialPos + nullCount);
// Send Parameter type flag
writer.writeByte(0x01);
// Store types of parameters in first package that is sent to the server.
for (int i = 0; i < parameterCount; i++) {
Parameter p = parameters.get(i);
writer.writeByte(p.getBinaryEncodeType());
writer.writeByte(0);
if (p.isNull()) {
nullBitsBuffer[i / 8] |= (1 << (i % 8));
}
}
// write nullBitsBuffer in reserved place
writer.writeBytesAtPos(nullBitsBuffer, initialPos);
// send not null parameter, not long data
for (int i = 0; i < parameterCount; i++) {
Parameter p = parameters.get(i);
if (!p.isNull() && !p.canEncodeLongData()) {
p.encodeBinary(writer);
}
}
}
writer.flush();
return 1;
}
public boolean canSkipMeta() {
return true;
}
public int batchUpdateLength() {
return 1;
}
public String getCommand() {
return command;
}
public InputStream getLocalInfileInputStream() {
return localInfileInputStream;
}
public ServerPreparedStatement prep() {
return prep;
}
public boolean binaryProtocol() {
return true;
}
public String description() {
return "EXECUTE " + command;
}
public boolean validateLocalFileName(String fileName, Context context) {
return ClientMessage.validateLocalFileName(command, parameters, fileName, context);
}
public void setPrepareResult(PrepareResultPacket prepareResult) {
this.prepareResult = prepareResult;
}
}