package com.tesora.dve.db.mysql.portal.protocol;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import com.tesora.dve.db.mysql.MyFieldType;
import com.tesora.dve.db.mysql.common.DBTypeBasedUtils;
import com.tesora.dve.db.mysql.common.MysqlAPIUtils;
import com.tesora.dve.db.mysql.libmy.MyNullBitmap;
import com.tesora.dve.db.mysql.libmy.MyParameter;
import com.tesora.dve.db.mysql.libmy.MyPreparedStatement;
import com.tesora.dve.exceptions.PECodingException;
import com.tesora.dve.exceptions.PEException;
import io.netty.buffer.ByteBuf;
import java.nio.ByteOrder;
import java.util.List;
public class MSPComStmtExecuteRequestMessage extends BaseMSPMessage<MSPComStmtExecuteRequestMessage.ParsedData> {
public static final MSPComStmtExecuteRequestMessage PROTOTYPE = new MSPComStmtExecuteRequestMessage();
public static final byte TYPE_IDENTIFIER = (byte) 0x17;
static class ParsedData {
long statementID;
byte flags;
long iterationCount;
int metadataOffset;
MyPreparedStatement<?> metadata;
List<Object> values;
}
protected MSPComStmtExecuteRequestMessage() {
super();
}
protected MSPComStmtExecuteRequestMessage(ByteBuf backing) {
super(backing);
}
protected MSPComStmtExecuteRequestMessage(ParsedData data) {
super(data);
}
@Override
public byte getMysqlMessageType() {
return TYPE_IDENTIFIER;
}
@Override
public MSPComStmtExecuteRequestMessage newPrototype(ByteBuf source) {
source = source.slice();
return new MSPComStmtExecuteRequestMessage(source);
}
@Override
protected ParsedData unmarshall(ByteBuf source) {
ParsedData parseValues = new ParsedData();
source.skipBytes(1);//skip the type identifier.
parseValues.statementID = source.readUnsignedInt();
parseValues.flags = source.readByte();
parseValues.iterationCount = source.readUnsignedInt();
parseValues.metadataOffset = source.readerIndex();
//we don't actually unmarshall the metadata and values here, since we don't know how many parameters there are.
parseValues.metadata = null;
parseValues.values = null;
return parseValues;
}
@Override
protected void marshall(ParsedData state, ByteBuf destination) {
ByteBuf leBuf = destination.order(ByteOrder.LITTLE_ENDIAN);
leBuf.writeByte(TYPE_IDENTIFIER);
leBuf.writeInt((int) state.statementID);
leBuf.writeByte(state.flags);
leBuf.writeInt((int)state.iterationCount);
if (state.metadata == null)
throw new IllegalStateException("Cannot build execute request, no prepare metadata provided.");
int numParams = state.metadata.getNumParams();
if (numParams > 0) {
MyNullBitmap nullBitmap = new MyNullBitmap(numParams, MyNullBitmap.BitmapType.EXECUTE_REQUEST);
int bitmapIndex = leBuf.writerIndex();
leBuf.writeZero(nullBitmap.length());
if (state.metadata.isNewParameterList()) {
leBuf.writeByte(1);
for(MyParameter param : state.metadata.getParameters()) {
leBuf.writeByte(param.getType().getByteValue());
leBuf.writeZero(1); }
} else {
leBuf.writeZero(1);
}
List<Object> params = state.values;
for (int i = 0; i < params.size(); ++i) {
if (params.get(i) != null)
DBTypeBasedUtils.getJavaTypeFunc(params.get(i).getClass()).writeObject(leBuf, params.get(i));
else
nullBitmap.setBit(i+1);
}
leBuf.setBytes(bitmapIndex, nullBitmap.getBitmapArray());
}
}
public long getStatementID() {
return readState().statementID;
}
public void readParameterMetadata(MyPreparedStatement<String> pStmt) throws PEException {
//TODO: this belongs in unmarshall, but requires knowledge about the expected number of parameters, provided by a previous prepare response.
ParsedData cachedParse = readState();
ByteBuf backingData = readBuffer();
int lengthOfMetadata = backingData.readableBytes() - cachedParse.metadataOffset;
ByteBuf in = backingData.slice(cachedParse.metadataOffset, lengthOfMetadata).order(ByteOrder.LITTLE_ENDIAN);
if (pStmt.getNumParams() > 0) {
int nullBitmapLength = (pStmt.getNumParams() + 7) / 8;
MyNullBitmap nullBitmap = new MyNullBitmap(MysqlAPIUtils.readBytes(in, nullBitmapLength),
pStmt.getNumParams(), MyNullBitmap.BitmapType.EXECUTE_REQUEST);
if (in.readByte() == 1) { // this is new params bound flag; only
// =1 on first stmt_execute
pStmt.clearParameters();
for (int i = 0; i < pStmt.getNumParams(); i++) {
pStmt.addParameter(new MyParameter(MyFieldType
.fromByte(in.readByte())));
in.skipBytes(1);
}
}
for (int paramNum = 1; paramNum <= pStmt.getNumParams(); paramNum++) {
MyParameter parameter = pStmt.getParameter(paramNum);
if (nullBitmap.getBit(paramNum)) {
parameter.setValue(null);
} else {
//TODO - may need to figure out how to get the proper flags and length into this call
parameter.setValue(DBTypeBasedUtils.getMysqlTypeFunc(parameter.getType()).readObject(in));
}
}
}
}
public static MSPComStmtExecuteRequestMessage newMessage(int statementID, MyPreparedStatement<MysqlGroupedPreparedStatementId> metadata, List<Object> values) {
if (metadata.getNumParams() > 0 && metadata.getNumParams() != values.size())
throw new PECodingException("Wrong number of parameters specified for prepared statement execution (expected"
+ metadata.getNumParams() + ", received " + values.size() + ")");
ParsedData data = new ParsedData();
data.statementID = statementID;
data.flags = 0;
data.iterationCount = 1;
data.metadata = metadata;
data.values = values;
return new MSPComStmtExecuteRequestMessage(data);
}
}