/*
* Copyright 2008-2108 amoeba.meidusa.com
*
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.meidusa.amoeba.mysql.handler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.meidusa.amoeba.context.ProxyRuntimeContext;
import com.meidusa.amoeba.mysql.context.MysqlRuntimeContext;
import com.meidusa.amoeba.mysql.jdbc.MysqlDefs;
import com.meidusa.amoeba.mysql.net.MysqlClientConnection;
import com.meidusa.amoeba.mysql.net.packet.BindValue;
import com.meidusa.amoeba.mysql.net.packet.ErrorPacket;
import com.meidusa.amoeba.mysql.net.packet.ExecutePacket;
import com.meidusa.amoeba.mysql.net.packet.FieldPacket;
import com.meidusa.amoeba.mysql.net.packet.LongDataPacket;
import com.meidusa.amoeba.mysql.net.packet.MysqlPacketBuffer;
import com.meidusa.amoeba.mysql.net.packet.OkPacket;
import com.meidusa.amoeba.mysql.net.packet.QueryCommandPacket;
import com.meidusa.amoeba.mysql.net.packet.ResultSetHeaderPacket;
import com.meidusa.amoeba.mysql.net.packet.RowDataPacket;
import com.meidusa.amoeba.mysql.net.packet.result.MysqlResultSetPacket;
import com.meidusa.amoeba.net.Connection;
import com.meidusa.amoeba.net.MessageHandler;
import com.meidusa.amoeba.net.Sessionable;
import com.meidusa.amoeba.net.poolable.ObjectPool;
import com.meidusa.amoeba.parser.dbobject.Column;
import com.meidusa.amoeba.parser.statement.SelectStatement;
import com.meidusa.amoeba.parser.statement.ShowStatement;
import com.meidusa.amoeba.parser.statement.Statement;
import com.meidusa.amoeba.route.SqlBaseQueryRouter;
import com.meidusa.amoeba.route.SqlQueryObject;
/**
* handler
*
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
*/
public class MySqlCommandDispatcher implements MessageHandler {
protected static Logger logger = Logger.getLogger(MySqlCommandDispatcher.class);
private long timeout = ProxyRuntimeContext.getInstance().getRuntimeContext().getQueryTimeout() * 1000;
private static byte[] STATIC_OK_BUFFER;
static {
OkPacket ok = new OkPacket();
ok.affectedRows = 0;
ok.insertId = 0;
ok.packetId = 1;
ok.serverStatus = 2;
STATIC_OK_BUFFER = ok.toByteBuffer(null).array();
}
/**
* Ping ��COM_STMT_SEND_LONG_DATA command remove to @MysqlClientConnection #doReceiveMessage()
*/
public void handleMessage(Connection connection) {
byte[] message = null;
while((message = connection.getInQueue().getNonBlocking()) != null){
MysqlClientConnection conn = (MysqlClientConnection) connection;
QueryCommandPacket command = new QueryCommandPacket();
command.init(message, connection);
if (logger.isDebugEnabled()) {
logger.debug(command.query);
}
try {
if (MysqlPacketBuffer.isPacketType(message, QueryCommandPacket.COM_QUERY)) {
SqlBaseQueryRouter router = (SqlBaseQueryRouter)ProxyRuntimeContext.getInstance().getQueryRouter();
Statement statement = router.parseStatement(conn, command.query);
if(command.query != null && (command.query.indexOf("'$version'")>0 || command.query.indexOf("@amoebaversion")>0)){
MysqlResultSetPacket lastPacketResult = createAmoebaVersion(conn,(SelectStatement)statement,false);
lastPacketResult.wirteToConnection(conn);
return;
}
SqlQueryObject queryObject = new SqlQueryObject();
queryObject.isPrepared = false;
queryObject.sql = command.query;
ObjectPool[] pools = router.doRoute(conn, queryObject);
if (statement != null && statement instanceof SelectStatement && ((SelectStatement)statement).isQueryLastInsertId()) {
MysqlResultSetPacket lastPacketResult = createLastInsertIdPacket(conn,(SelectStatement)statement,false);
lastPacketResult.wirteToConnection(conn);
return;
}
if(statement instanceof ShowStatement){
if(pools != null && pools.length>1){
pools = new ObjectPool[]{pools[0]};
}
}
if(pools == null){
conn.postMessage(STATIC_OK_BUFFER);
return;
}
MessageHandler handler = new QueryCommandMessageHandler(conn, message,statement, pools, timeout);
if (handler instanceof Sessionable) {
Sessionable session = (Sessionable) handler;
try {
session.startSession();
} catch (Exception e) {
logger.error("start Session error:", e);
session.endSession(true);
throw e;
}
}
} else if (MysqlPacketBuffer.isPacketType(message, QueryCommandPacket.COM_STMT_PREPARE)) {
/**
* ��ȡ֮ǰprepared�������ݣ�ֱ�ӷ��ظ��ͻ��ˣ����û������Ҫ�����mysql��������
* Ȼ�������Ժ����PreparedStatmentInfo�����Ҹ��ͻ���
*/
PreparedStatmentInfo preparedInf = conn.getPreparedStatmentInfo(command.query);
if(preparedInf.getByteBuffer() != null && preparedInf.getByteBuffer().length >0){
conn.postMessage(preparedInf.getByteBuffer());
return;
}
/**
* ���������
*/
SqlBaseQueryRouter router = (SqlBaseQueryRouter)ProxyRuntimeContext.getInstance().getQueryRouter();
SqlQueryObject queryObject = new SqlQueryObject();
queryObject.isPrepared = true;
queryObject.sql = command.query;
ObjectPool[] pools = router.doRoute(conn, queryObject);
Statement statment = router.parseStatement(conn, command.query);
PreparedStatmentMessageHandler handler = new PreparedStatmentMessageHandler(conn,preparedInf,statment, message , new ObjectPool[]{pools[0]}, timeout);
if (handler instanceof Sessionable) {
Sessionable session = (Sessionable) handler;
try {
session.startSession();
} catch (Exception e) {
logger.error("start Session error:", e);
session.endSession(true);
throw e;
}
}
return;
} else if (MysqlPacketBuffer.isPacketType(message, QueryCommandPacket.COM_STMT_EXECUTE)) {
try{
long statmentId = ExecutePacket.readStatmentID(message);
PreparedStatmentInfo preparedInf = conn.getPreparedStatmentInfo(statmentId);
if (preparedInf == null) {
ErrorPacket error = new ErrorPacket();
error.errno = 1044;
error.packetId = 1;
error.sqlstate = "42000";
error.serverErrorMessage = "Unknown prepared statment id=" + statmentId;
conn.postMessage(error.toByteBuffer(connection).array());
logger.warn("Unknown prepared statment id:" + statmentId);
} else {
Statement statment = preparedInf.getStatment();
if (statment != null && statment instanceof SelectStatement && ((SelectStatement)statment).isQueryLastInsertId()) {
MysqlResultSetPacket lastPacketResult = createLastInsertIdPacket(conn,(SelectStatement)statment,true);
lastPacketResult.wirteToConnection(conn);
return;
}
Map<Integer, Object> longMap = new HashMap<Integer, Object>();
for (byte[] longdate : conn.getLongDataList()) {
LongDataPacket packet = new LongDataPacket();
packet.init(longdate, connection);
longMap.put(packet.parameterIndex, packet.data);
}
ExecutePacket executePacket = new ExecutePacket(preparedInf, longMap);
executePacket.init(message, connection);
SqlBaseQueryRouter router = (SqlBaseQueryRouter)ProxyRuntimeContext.getInstance().getQueryRouter();
SqlQueryObject queryObject = new SqlQueryObject();
queryObject.isPrepared = false;
queryObject.sql = preparedInf.getSql();
queryObject.parameters = executePacket.getParameters();
ObjectPool[] pools = router.doRoute(conn, queryObject);
PreparedStatmentExecuteMessageHandler handler = new PreparedStatmentExecuteMessageHandler(
conn,
preparedInf,statment,
message,
pools,
timeout);
handler.setExecutePacket(executePacket);
if (handler instanceof Sessionable) {
Sessionable session = (Sessionable) handler;
try {
session.startSession();
} catch (Exception e) {
logger.error("start Session error:", e);
session.endSession(true);
throw e;
}
}
}
}finally{
conn.clearLongData();
}
} else if (MysqlPacketBuffer.isPacketType(message, QueryCommandPacket.COM_INIT_DB)) {
conn.setSchema(command.query);
conn.postMessage(STATIC_OK_BUFFER);
} else if (MysqlPacketBuffer.isPacketType(message, QueryCommandPacket.COM_CHANGE_USER)){
conn.postMessage(STATIC_OK_BUFFER);
}else{
ErrorPacket error = new ErrorPacket();
error.errno = 1044;
error.packetId = 1;
error.sqlstate = "42000";
error.serverErrorMessage = "can not use this command here!!";
conn.postMessage(error.toByteBuffer(connection).array());
logger.warn("unsupport packet:" + command);
}
} catch (Exception e) {
ErrorPacket error = new ErrorPacket();
error.errno = 1044;
error.packetId = 1;
error.sqlstate = "42000";
error.serverErrorMessage = e.getMessage();
e.printStackTrace();
conn.postMessage(error.toByteBuffer(connection).array());
logger.error("messageDispate error", e);
}
}
}
private MysqlResultSetPacket createAmoebaVersion(MysqlClientConnection conn,SelectStatement statment,boolean isPrepared){
Map<String,Column> selectedMap = ((SelectStatement)statment).getSelectColumnMap();
MysqlResultSetPacket lastPacketResult = new MysqlResultSetPacket(null);
lastPacketResult.resulthead = new ResultSetHeaderPacket();
lastPacketResult.resulthead.columns = (selectedMap.size()==0?1:selectedMap.size());
if(selectedMap.size() == 0){
Column column = new Column();
column.setName("@amoebaversion");
selectedMap.put("@amoebaversion", column);
}
lastPacketResult.resulthead.extra = 1;
RowDataPacket row = new RowDataPacket(isPrepared);
row.columns = new ArrayList<Object>();
int index =0;
lastPacketResult.fieldPackets = new FieldPacket[selectedMap.size()];
for(Map.Entry<String, Column> entry : selectedMap.entrySet()){
FieldPacket field = new FieldPacket();
String alias = entry.getValue().getAlias();
if("@amoebaversion".equalsIgnoreCase(entry.getValue().getName())
|| "'$version'".equalsIgnoreCase(entry.getValue().getName())){
BindValue value = new BindValue();
value.bufferType = MysqlDefs.FIELD_TYPE_VARCHAR;
value.value = MysqlRuntimeContext.SERVER_VERSION;
value.scale = 20;
value.isSet = true;
row.columns.add(value);
field.name = (alias == null?entry.getValue().getName()+"()":alias);
}else{
BindValue value = new BindValue();
value.bufferType = MysqlDefs.FIELD_TYPE_VARCHAR;
value.scale = 20;
value.isNull = true;
row.columns.add(value);
field.name = (alias == null?entry.getValue().getName():alias);
}
field.type = (byte)MysqlDefs.FIELD_TYPE_VARCHAR;
field.catalog = "def";
field.length = 20;
lastPacketResult.fieldPackets[index] = field;
index++;
}
List<RowDataPacket> list = new ArrayList<RowDataPacket>();
list.add(row);
lastPacketResult.setRowList(list);
return lastPacketResult;
}
private MysqlResultSetPacket createLastInsertIdPacket(MysqlClientConnection conn,SelectStatement statment,boolean isPrepared){
Map<String,Column> selectedMap = ((SelectStatement)statment).getSelectColumnMap();
MysqlResultSetPacket lastPacketResult = new MysqlResultSetPacket(null);
lastPacketResult.resulthead = new ResultSetHeaderPacket();
lastPacketResult.resulthead.columns = selectedMap.size();
lastPacketResult.resulthead.extra = 1;
RowDataPacket row = new RowDataPacket(isPrepared);
row.columns = new ArrayList<Object>();
int index =0;
lastPacketResult.fieldPackets = new FieldPacket[selectedMap.size()];
for(Map.Entry<String, Column> entry : selectedMap.entrySet()){
FieldPacket field = new FieldPacket();
String alias = entry.getValue().getAlias();
if("LAST_INSERT_ID".equalsIgnoreCase(entry.getValue().getName())){
BindValue value = new BindValue();
value.bufferType = MysqlDefs.FIELD_TYPE_LONGLONG;
value.longBinding = conn.getLastInsertId();
value.scale = 20;
value.isSet = true;
row.columns.add(value);
field.name = (alias == null?entry.getValue().getName()+"()":alias);
}else if("@@IDENTITY".equalsIgnoreCase(entry.getValue().getName())){
BindValue value = new BindValue();
value.bufferType = MysqlDefs.FIELD_TYPE_LONGLONG;
value.longBinding = conn.getLastInsertId();
value.scale = 20;
value.isSet = true;
row.columns.add(value);
row.columns.add(value);
field.name = (alias == null?entry.getValue().getName():alias);
}else{
BindValue value = new BindValue();
value.bufferType = MysqlDefs.FIELD_TYPE_STRING;
value.scale = 20;
value.isNull = true;
row.columns.add(value);
field.name = (alias == null?entry.getValue().getName():alias);
}
field.type = MysqlDefs.FIELD_TYPE_LONGLONG;
field.catalog = "def";
field.length = 20;
lastPacketResult.fieldPackets[index] = field;
index++;
}
List<RowDataPacket> list = new ArrayList<RowDataPacket>();
list.add(row);
lastPacketResult.setRowList(list);
return lastPacketResult;
}
}