package com.tesora.dve.db.mysql; /* * #%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.concurrent.CompletionHandle; import com.tesora.dve.db.CommandChannel; import com.tesora.dve.db.mysql.libmy.*; import io.netty.channel.ChannelHandlerContext; import org.apache.log4j.Logger; import com.tesora.dve.exceptions.PEException; public class MysqlStmtPrepareCommand extends MysqlCommand { static Logger logger = Logger.getLogger(MysqlStmtPrepareCommand.class); private CompletionHandle<Boolean> promise; enum ResponseState { AWAIT_HEADER, AWAIT_COL_DEF, AWAIT_COL_DEF_EOF, AWAIT_PARAM_DEF, AWAIT_PARAM_DEF_EOF, DONE }; ResponseState state = ResponseState.AWAIT_HEADER; String sqlCommand; private CommandChannel executingOnChannel; private MysqlPrepareParallelConsumer consumer; private int numCols; private int numParams; public MysqlStmtPrepareCommand(CommandChannel executingOnChannel, String sql, MysqlPrepareParallelConsumer mysqlStatementPrepareConsumer, CompletionHandle<Boolean> promise) { super(); this.executingOnChannel = executingOnChannel; this.promise = promise; this.sqlCommand = sql; this.consumer = mysqlStatementPrepareConsumer; } @Override public void active(ChannelHandlerContext ctx) { //NOOP. } //this is a prepare response, which returns a ERR, or [PREPARE_OK, N*param defs, an EOF, M*column defs, final EOF] @Override public boolean processPacket(ChannelHandlerContext ctx, MyMessage message) throws PEException { try { switch (state) { case AWAIT_HEADER: if (message instanceof MyPrepareOKResponse) { MyPrepareOKResponse prepareOK = (MyPrepareOKResponse)message; numCols = prepareOK.getNumColumns(); numParams = prepareOK.getNumParams(); // System.out.println("cols " + numCols + ", params " + numParams); if (numParams > 0) state = ResponseState.AWAIT_PARAM_DEF; else if (numCols > 0) state = ResponseState.AWAIT_COL_DEF; else state = ResponseState.DONE; consumer.header(executingOnChannel,ctx, prepareOK); if (state == ResponseState.DONE) onCompletion(); } else if (message instanceof MyErrorResponse) { MyErrorResponse error = (MyErrorResponse)message; try { consumer.errorResponse(ctx, error); promise.failure(error.asException()); } catch (Exception e) { promise.failure(e); } finally { state = ResponseState.DONE; } } else { throw new PEException("Invalid packet from mysql (expected PrepareResponse header, got " + message.getClass().getSimpleName() +")"); } break; case AWAIT_PARAM_DEF: if (--numParams== 0) state = ResponseState.AWAIT_PARAM_DEF_EOF; MyFieldPktResponse paramDef = (MyFieldPktResponse)message; if (!promise.isFulfilled()){ consumer.paramDef(ctx, paramDef); } break; case AWAIT_PARAM_DEF_EOF: MyEOFPktResponse paramEof = (MyEOFPktResponse)message; state = (numCols == 0) ? ResponseState.DONE : ResponseState.AWAIT_COL_DEF; if (!promise.isFulfilled()) { consumer.paramDefEOF(ctx, paramEof); if (state == ResponseState.DONE) onCompletion(); } break; case AWAIT_COL_DEF: if (--numCols == 0) state = ResponseState.AWAIT_COL_DEF_EOF; MyFieldPktResponse columnDef = (MyFieldPktResponse)message; if (!promise.isFulfilled()){ consumer.colDef(ctx, columnDef); } break; case AWAIT_COL_DEF_EOF: MyEOFPktResponse colEof = (MyEOFPktResponse)message; state = ResponseState.DONE; if (!promise.isFulfilled()) { consumer.colDefEOF(ctx, colEof); onCompletion(); } break; case DONE: logger.debug("Received a packet after we believe we are DONE, packet was type " + message.getClass().getSimpleName()); break; } } catch (Exception e) { promise.failure(e); } return state == ResponseState.DONE; } @Override public void packetStall(ChannelHandlerContext ctx) { } protected void onCompletion() { if (logger.isDebugEnabled()) logger.debug("Promise fulfilled on " + this); promise.success(false); state = ResponseState.DONE; } @Override public void failure(Exception e) { promise.failure(e); } @Override public String toString() { return this.getClass().getSimpleName() + "{" + promise + ", " + state + ", " + sqlCommand + "}"; } }