package io.mycat.server.packet;
import io.mycat.net.BufferArray;
import io.mycat.net.Connection;
import io.mycat.net.NetSystem;
import io.mycat.server.Fields;
import io.mycat.server.packet.util.BufferUtil;
import io.mycat.util.ByteUtil;
import io.mycat.util.DateUtil;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* ProtocolBinary::ResultsetRow:
* row of a binary resultset (COM_STMT_EXECUTE)
* Payload
* 1 packet header [00]
* string[$len] NULL-bitmap, length: (column_count + 7 + 2) / 8
* string[$len] values
*
* A Binary Protocol Resultset Row is made up of the NULL bitmap
* containing as many bits as we have columns in the resultset + 2
* and the values for columns that are not NULL in the Binary Protocol Value format.
*
* @see http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html#packet-ProtocolBinary::ResultsetRow
* @see http://dev.mysql.com/doc/internals/en/binary-protocol-value.html
* @author CrazyPig
*
*/
public class BinaryRowDataPacket extends MySQLPacket {
public int fieldCount;
public List<byte[]> fieldValues;
public byte packetHeader = (byte) 0;
public byte[] nullBitMap;
public List<FieldPacket> fieldPackets;
public BinaryRowDataPacket() {}
/**
* 从RowDataPacket转换成BinaryRowDataPacket
* @param fieldPackets 字段包集合
* @param rowDataPk 文本协议行数据包
*/
public void read(List<FieldPacket> fieldPackets, RowDataPacket rowDataPk) {
this.fieldPackets = fieldPackets;
this.fieldCount = rowDataPk.fieldCount;
this.fieldValues = new ArrayList<byte[]>(fieldCount);
nullBitMap = new byte[(fieldCount + 7 + 2) / 8];
List<byte[]> _fieldValues = rowDataPk.fieldValues;
for (int i = 0; i < fieldCount; i++) {
byte[] fv = _fieldValues.get(i);
FieldPacket fieldPk = fieldPackets.get(i);
if (fv == null) { // 字段值为null,根据协议规定存储nullBitMap
int bitMapPos = (i + 2) / 8;
int bitPos = (i + 2) % 8;
nullBitMap[bitMapPos] |= (byte) (1 << bitPos);
this.fieldValues.add(fv);
} else {
// 从RowDataPacket的fieldValue的数据转化成BinaryRowDataPacket的fieldValue数据
int fieldType = fieldPk.type;
switch (fieldType) {
case Fields.FIELD_TYPE_STRING:
case Fields.FIELD_TYPE_VARCHAR:
case Fields.FIELD_TYPE_VAR_STRING:
case Fields.FIELD_TYPE_ENUM:
case Fields.FIELD_TYPE_SET:
case Fields.FIELD_TYPE_LONG_BLOB:
case Fields.FIELD_TYPE_MEDIUM_BLOB:
case Fields.FIELD_TYPE_BLOB:
case Fields.FIELD_TYPE_TINY_BLOB:
case Fields.FIELD_TYPE_GEOMETRY:
case Fields.FIELD_TYPE_BIT:
case Fields.FIELD_TYPE_DECIMAL:
case Fields.FIELD_TYPE_NEW_DECIMAL:
// Fields
// value (lenenc_str) -- string
// Example
// 03 66 6f 6f -- string = "foo"
this.fieldValues.add(_fieldValues.get(i));
break;
case Fields.FIELD_TYPE_LONGLONG:
// Fields
// value (8) -- integer
// Example
// 01 00 00 00 00 00 00 00 -- int64 = 1
long longVar = ByteUtil.getLong(_fieldValues.get(i));
this.fieldValues.add(ByteUtil.getBytes(longVar));
break;
case Fields.FIELD_TYPE_LONG:
case Fields.FIELD_TYPE_INT24:
// Fields
// value (4) -- integer
// Example
// 01 00 00 00 -- int32 = 1
int intVar = ByteUtil.getInt(_fieldValues.get(i));
this.fieldValues.add(ByteUtil.getBytes(intVar));
break;
case Fields.FIELD_TYPE_SHORT:
case Fields.FIELD_TYPE_YEAR:
// Fields
// value (2) -- integer
// Example
// 01 00 -- int16 = 1
short shortVar = ByteUtil.getShort(_fieldValues.get(i));
this.fieldValues.add(ByteUtil.getBytes(shortVar));
break;
case Fields.FIELD_TYPE_TINY:
// Fields
// value (1) -- integer
// Example
// 01 -- int8 = 1
int tinyVar = ByteUtil.getInt(_fieldValues.get(i));
byte[] bytes = new byte[1];
bytes[0] = new Integer(tinyVar).byteValue();
this.fieldValues.add(bytes);
break;
case Fields.FIELD_TYPE_DOUBLE:
// Fields
// value (string.fix_len) -- (len=8) double
// Example
// 66 66 66 66 66 66 24 40 -- double = 10.2
double doubleVar = ByteUtil.getDouble(_fieldValues.get(i));
this.fieldValues.add(ByteUtil.getBytes(doubleVar));
break;
case Fields.FIELD_TYPE_FLOAT:
// Fields
// value (string.fix_len) -- (len=4) float
// Example
// 33 33 23 41 -- float = 10.2
float floatVar = ByteUtil.getFloat(_fieldValues.get(i));
this.fieldValues.add(ByteUtil.getBytes(floatVar));
break;
case Fields.FIELD_TYPE_DATE:
try {
Date dateVar = DateUtil.parseDate(
ByteUtil.getDate(_fieldValues.get(i)),
DateUtil.DATE_PATTERN_ONLY_DATE);
this.fieldValues.add(ByteUtil.getBytes(dateVar, false));
} catch (ParseException e) {
e.printStackTrace();
}
break;
case Fields.FIELD_TYPE_DATETIME:
case Fields.FIELD_TYPE_TIMESTAMP:
String dateStr = ByteUtil.getDate(_fieldValues.get(i));
Date dateTimeVar = null;
try {
if (dateStr.indexOf(".") > 0) {
dateTimeVar = DateUtil.parseDate(dateStr,
DateUtil.DATE_PATTERN_FULL);
this.fieldValues.add(ByteUtil.getBytes(dateTimeVar,
false));
} else {
dateTimeVar = DateUtil.parseDate(dateStr,
DateUtil.DEFAULT_DATE_PATTERN);
this.fieldValues.add(ByteUtil.getBytes(dateTimeVar,
false));
}
} catch (ParseException e) {
e.printStackTrace();
}
break;
case Fields.FIELD_TYPE_TIME:
String timeStr = ByteUtil.getTime(_fieldValues.get(i));
Date timeVar = null;
try {
if (timeStr.indexOf(".") > 0) {
timeVar = DateUtil.parseDate(timeStr,
DateUtil.TIME_PATTERN_FULL);
this.fieldValues.add(ByteUtil.getBytes(timeVar,
true));
} else {
timeVar = DateUtil.parseDate(timeStr,
DateUtil.DEFAULT_TIME_PATTERN);
this.fieldValues.add(ByteUtil.getBytes(timeVar,
true));
}
} catch (ParseException e) {
e.printStackTrace();
}
break;
}
}
}
}
@Override
public void write(BufferArray bufferArray) {
int size = calcPacketSize();
ByteBuffer bb = bufferArray.checkWriteBuffer(packetHeaderSize
+ size);
BufferUtil.writeUB3(bb, calcPacketSize());
bb.put(packetId);
bb.put(packetHeader); // packet header [00]
bb.put(nullBitMap); // NULL-Bitmap
for(int i = 0; i < fieldCount; i++) { // values
byte[] fv = fieldValues.get(i);
if(fv != null) {
bb = bufferArray.checkWriteBuffer(BufferUtil.getLength(fv.length));
FieldPacket fieldPk = this.fieldPackets.get(i);
int fieldType = fieldPk.type;
switch(fieldType) {
case Fields.FIELD_TYPE_STRING:
case Fields.FIELD_TYPE_VARCHAR:
case Fields.FIELD_TYPE_VAR_STRING:
case Fields.FIELD_TYPE_ENUM:
case Fields.FIELD_TYPE_SET:
case Fields.FIELD_TYPE_LONG_BLOB:
case Fields.FIELD_TYPE_MEDIUM_BLOB:
case Fields.FIELD_TYPE_BLOB:
case Fields.FIELD_TYPE_TINY_BLOB:
case Fields.FIELD_TYPE_GEOMETRY:
case Fields.FIELD_TYPE_BIT:
case Fields.FIELD_TYPE_DECIMAL:
case Fields.FIELD_TYPE_NEW_DECIMAL:
// 长度编码的字符串需要一个字节来存储长度(0表示空字符串)
BufferUtil.writeLength(bb, fv.length);
break;
default:
break;
}
if(fv.length > 0) {
bufferArray.write(fv);
}
}
}
}
public void write(Connection conn) {
int size = calcPacketSize();
int totalSize = size + packetHeaderSize;
if(totalSize <= NetSystem.getInstance().getBufferPool().getChunkSize()) {
ByteBuffer bb = NetSystem.getInstance().getBufferPool()
.allocate();
BufferUtil.writeUB3(bb, calcPacketSize());
bb.put(packetId);
bb.put(packetHeader); // packet header [00]
bb.put(nullBitMap); // NULL-Bitmap
for(int i = 0; i < fieldCount; i++) { // values
byte[] fv = fieldValues.get(i);
if(fv != null) {
FieldPacket fieldPk = this.fieldPackets.get(i);
int fieldType = fieldPk.type;
switch(fieldType) {
case Fields.FIELD_TYPE_STRING:
case Fields.FIELD_TYPE_VARCHAR:
case Fields.FIELD_TYPE_VAR_STRING:
case Fields.FIELD_TYPE_ENUM:
case Fields.FIELD_TYPE_SET:
case Fields.FIELD_TYPE_LONG_BLOB:
case Fields.FIELD_TYPE_MEDIUM_BLOB:
case Fields.FIELD_TYPE_BLOB:
case Fields.FIELD_TYPE_TINY_BLOB:
case Fields.FIELD_TYPE_GEOMETRY:
case Fields.FIELD_TYPE_BIT:
case Fields.FIELD_TYPE_DECIMAL:
case Fields.FIELD_TYPE_NEW_DECIMAL:
// 长度编码的字符串需要一个字节来存储长度(0表示空字符串)
BufferUtil.writeLength(bb, fv.length);
break;
default:
break;
}
if(fv.length > 0) {
bb.put(fv);
}
}
}
conn.write(bb);
} else {
BufferArray bufArray = NetSystem.getInstance().getBufferPool()
.allocateArray();
write(bufArray);
conn.write(bufArray);
}
}
@Override
public int calcPacketSize() {
int size = 0;
size = size + 1 + nullBitMap.length;
for(int i = 0, n = fieldValues.size(); i < n; i++) {
byte[] value = fieldValues.get(i);
if(value != null) {
FieldPacket fieldPk = this.fieldPackets.get(i);
int fieldType = fieldPk.type;
switch(fieldType) {
case Fields.FIELD_TYPE_STRING:
case Fields.FIELD_TYPE_VARCHAR:
case Fields.FIELD_TYPE_VAR_STRING:
case Fields.FIELD_TYPE_ENUM:
case Fields.FIELD_TYPE_SET:
case Fields.FIELD_TYPE_LONG_BLOB:
case Fields.FIELD_TYPE_MEDIUM_BLOB:
case Fields.FIELD_TYPE_BLOB:
case Fields.FIELD_TYPE_TINY_BLOB:
case Fields.FIELD_TYPE_GEOMETRY:
case Fields.FIELD_TYPE_BIT:
case Fields.FIELD_TYPE_DECIMAL:
case Fields.FIELD_TYPE_NEW_DECIMAL:
// 长度编码的字符串需要一个字节来存储长度
if(value.length != 0) {
size = size + 1 + value.length;
} else {
size = size + 1; // 处理空字符串,只计算长度1个字节
}
break;
default:
size = size + value.length;
break;
}
}
}
return size;
}
@Override
protected String getPacketInfo() {
return "MySQL Binary RowData Packet";
}
}