/* * Copyright 2015 Liu Huanting. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package fm.liu.timo.mysql.packet; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import fm.liu.timo.config.Fields; import fm.liu.timo.mysql.BindValue; import fm.liu.timo.mysql.BindValueUtil; import fm.liu.timo.mysql.MySQLMessage; import fm.liu.timo.mysql.PreparedStatement; /** * <pre> * Bytes Name * ----- ---- * 1 code * 4 statement_id * 1 flags * 4 iteration_count * (param_count+7)/8 null_bit_map * 1 new_parameter_bound_flag (if new_params_bound == 1:) * n*2 type of parameters * n values for the parameters * -------------------------------------------------------------------------------- * code: always COM_EXECUTE * * statement_id: statement identifier * * flags: reserved for future use. In MySQL 4.0, always 0. * In MySQL 5.0: * 0: CURSOR_TYPE_NO_CURSOR * 1: CURSOR_TYPE_READ_ONLY * 2: CURSOR_TYPE_FOR_UPDATE * 4: CURSOR_TYPE_SCROLLABLE * * iteration_count: reserved for future use. Currently always 1. * * null_bit_map: A bitmap indicating parameters that are NULL. * Bits are counted from LSB, using as many bytes * as necessary ((param_count+7)/8) * i.e. if the first parameter (parameter 0) is NULL, then * the least significant bit in the first byte will be 1. * * new_parameter_bound_flag: Contains 1 if this is the first time * that "execute" has been called, or if * the parameters have been rebound. * * type: Occurs once for each parameter; * The highest significant bit of this 16-bit value * encodes the unsigned property. The other 15 bits * are reserved for the type (only 8 currently used). * This block is sent when parameters have been rebound * or when a prepared statement is executed for the * first time. * * values: for all non-NULL values, each parameters appends its value * as described in Row Data Packet: Binary (column values) * @see http://dev.mysql.com/doc/internals/en/execute-packet.html * </pre> * */ public class ExecutePacket extends MySQLPacket { public byte code; public long statementId; public byte flags; public long iterationCount; public byte[] nullBitMap; public byte newParameterBoundFlag; public BindValue[] values; protected PreparedStatement pstmt; public ExecutePacket(PreparedStatement pstmt) { this.pstmt = pstmt; this.values = new BindValue[pstmt.getParametersNumber()]; } public void read(byte[] data, String charset) throws UnsupportedEncodingException { MySQLMessage mm = new MySQLMessage(data); packetLength = mm.readUB3(); packetId = mm.read(); code = mm.read(); statementId = mm.readUB4(); flags = mm.read(); iterationCount = mm.readUB4(); // 读取NULL指示器数据 int parameterCount = values.length; nullBitMap = new byte[(parameterCount + 7) / 8]; for (int i = 0; i < nullBitMap.length; i++) { nullBitMap[i] = mm.read(); } // 当newParameterBoundFlag==1时,更新参数类型。 newParameterBoundFlag = mm.read(); if (newParameterBoundFlag == (byte) 1) { for (int i = 0; i < parameterCount; i++) { pstmt.getParametersType()[i] = mm.readUB2(); } } // 设置参数类型和读取参数值 byte[] nullBitMap = this.nullBitMap; for (int i = 0; i < parameterCount; i++) { BindValue bv = new BindValue(); bv.type = pstmt.getParametersType()[i]; if ((nullBitMap[i / 8] & (1 << (i & 7))) != 0) { bv.isNull = true; } else { BindValueUtil.read(mm, bv, charset); } values[i] = bv; } } public String getSQL() { StringBuilder sb = new StringBuilder(); String[] stmts = pstmt.getStatements(); int length = stmts.length; if (pstmt.isEndsWithQuestionMark()) { for (int i = 0; i < length; i++) { sb.append(stmts[i]).append(getValue(values[i])); } } else { for (int i = 0; i < length - 1; i++) { sb.append(stmts[i]).append(getValue(values[i])); } sb.append(stmts[length - 1]); } return sb.toString(); } private Object getValue(BindValue bv) { if (bv.isNull) { return "NULL"; } else { switch (bv.type & 0xff) { case Fields.FIELD_TYPE_BIT: return bv.value; case Fields.FIELD_TYPE_TINY: return bv.byteBinding; case Fields.FIELD_TYPE_SHORT: return bv.shortBinding; case Fields.FIELD_TYPE_LONG: return bv.intBinding; case Fields.FIELD_TYPE_LONGLONG: return bv.longBinding; case Fields.FIELD_TYPE_FLOAT: return bv.floatBinding; case Fields.FIELD_TYPE_DOUBLE: return bv.doubleBinding; case Fields.FIELD_TYPE_TIME: case Fields.FIELD_TYPE_DATE: case Fields.FIELD_TYPE_DATETIME: case Fields.FIELD_TYPE_TIMESTAMP: case Fields.FIELD_TYPE_VAR_STRING: case Fields.FIELD_TYPE_STRING: case Fields.FIELD_TYPE_VARCHAR: case Fields.FIELD_TYPE_DECIMAL: case Fields.FIELD_TYPE_NEW_DECIMAL: if (bv.value == null) { return "NULL"; } return "'" + bv.value + "'"; default: throw new IllegalArgumentException( "bindValue error,unsupported type:" + bv.type); } } } @Override public int calcPacketSize() { throw new RuntimeException("calcPacketSize for ExecutePacket not implement!"); } @Override protected String getPacketInfo() { return "MySQL Execute Packet"; } @Override protected void writeBody(ByteBuffer buffer) { throw new RuntimeException("writeBody for ExecutePacket not implement!"); } @Override protected void readBody(MySQLMessage mm) { throw new RuntimeException("readBody for ExecutePacket not implement!"); } }