package com.tesora.dve.worker; /* * #%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.libmy.*; import com.tesora.dve.db.DBResultConsumer; import com.tesora.dve.db.MysqlQueryResultConsumer; import com.tesora.dve.exceptions.PECodingException; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.resultset.ColumnInfo; import io.netty.channel.ChannelHandlerContext; public abstract class MysqlParallelResultConsumer extends DBResultConsumer implements MysqlQueryResultConsumer { protected enum ResponseState { AWAIT_FIELD_COUNT, AWAIT_FIELD, AWAIT_ROW, DONE } RowCountAdjuster rowAdjuster = null; ResponseState state = ResponseState.AWAIT_FIELD_COUNT; int senderCount = 0; int field = 0; int senderComplete = 0; MyOKResponse okPacket = null; MyEOFPktResponse eofPacket = null; long numRowsAffected = 0; long numRowsAffectedAccum = 0; short statusFlags = 0; short warnings = 0; long lastInsertId = 0; String infoString; private boolean hasResults = false; boolean successful = false; public MysqlParallelResultConsumer() { super(); } @Override public void active(ChannelHandlerContext ctx) { //NOOP. } public boolean emptyResultSet(MyOKResponse ok) { synchronized (this) { if (senderCount == 0) throw new PECodingException("ResultConsumer fired, but no senderCount set"); getOKPacket(); okPacket = ok; numRowsAffectedAccum += okPacket.getAffectedRows(); statusFlags |= okPacket.getServerStatus(); warnings += okPacket.getWarningCount(); infoString = okPacket.getMessage(); lastInsertId = okPacket.getInsertId(); // System.out.println("emptyResultSet: " + numRowsAffected + ": " + // infoString); if (++senderComplete == senderCount && state != ResponseState.DONE) { if (rowAdjuster != null) numRowsAffectedAccum = rowAdjuster.adjust(numRowsAffectedAccum, senderCount); numRowsAffected += numRowsAffectedAccum; consumeEmptyResultSet(ok); successful = true; state = ResponseState.DONE; } return okPacket.getAffectedRows() > 0; } } protected MyOKResponse getOKPacket() { if (okPacket == null) okPacket = new MyOKResponse(); return okPacket; } @Override public void rollback() { numRowsAffected = 0; } public abstract void consumeEmptyResultSet(MyOKResponse ok); @Override public void error(MyErrorResponse err) throws PEException { synchronized (this) { if (state != ResponseState.DONE) { state = ResponseState.DONE; consumeError(err); } } } public abstract void consumeError(MyErrorResponse errorResp) throws PEException; @Override public void fieldCount(MyColumnCount colCount) { synchronized (this) { hasResults = true; if (state == ResponseState.AWAIT_FIELD_COUNT) { consumeFieldCount(colCount); state = ResponseState.AWAIT_FIELD; } } } public abstract void consumeFieldCount(MyColumnCount colCount); @Override public void field(int fieldIndex, MyFieldPktResponse columnDef, ColumnInfo columnProjection) throws PEException { synchronized (this) { if (state == ResponseState.AWAIT_FIELD && this.field == fieldIndex) { consumeField(fieldIndex, columnDef, columnProjection); ++this.field; } } } public abstract void consumeField(int field, MyFieldPktResponse columnDef, ColumnInfo columnProjection) throws PEException; @Override public void fieldEOF(MyMessage rawMessage) { synchronized (this) { if (state == ResponseState.AWAIT_FIELD) { consumeFieldEOF(rawMessage); state = ResponseState.AWAIT_ROW; } } } public abstract void consumeFieldEOF(MyMessage someMessage); @Override public void rowEOF(MyEOFPktResponse wholePacket) throws PEException { synchronized (this) { if (senderCount == 0) throw new PECodingException("ResultConsumer fired, but no senderCount set"); if (++senderComplete == senderCount && state != ResponseState.DONE) { consumeRowEOF(); state = ResponseState.DONE; } } } public abstract void consumeRowEOF() throws PEException; MyEOFPktResponse getEOFPacket() { if (eofPacket == null) { eofPacket = new MyEOFPktResponse(); } return eofPacket; } public abstract void consumeRowText(MyTextResultRow textRow) throws PEException; public abstract void consumeRowBinary(MyBinaryResultRow binRow) throws PEException; @Override public void rowText(MyTextResultRow textRow) throws PEException { //TODO: having the same sync block in rowText() and rowBinary() is less than ideal. synchronized (this) { if (state != ResponseState.DONE) { if (state != ResponseState.AWAIT_ROW) throw new PECodingException("Processing row out of sequence"); consumeRowText(textRow); } } } @Override public void rowBinary(MyBinaryResultRow binRow) throws PEException { //TODO: having the same sync block in rowText() and rowBinary() is less than ideal. synchronized (this) { if (state != ResponseState.DONE) { if (state != ResponseState.AWAIT_ROW) throw new PECodingException("Processing row out of sequence"); consumeRowBinary(binRow); } } } @Override public void rowFlush() throws PEException { //ignored. } public long getNumRowsAffected() { return numRowsAffected; } public short getStatusFlags() { return statusFlags; } public short getWarnings() { return warnings; } public String getInfoString() { return infoString; } public long getLastInsertId() { return lastInsertId; } @Override public void setSenderCount(int senderCount) { this.senderCount = senderCount; senderComplete = 0; numRowsAffectedAccum = 0; state = ResponseState.AWAIT_FIELD_COUNT; } @Override public boolean hasResults() { return hasResults; } public void setHasResults() { hasResults = true; } @Override public long getUpdateCount() { return numRowsAffected; } @Override public void setNumRowsAffected(long numRowsAffected) { this.numRowsAffected = numRowsAffected; } @Override public void setRowAdjuster(RowCountAdjuster rowAdjuster) { this.rowAdjuster = rowAdjuster; } @Override public boolean isSuccessful() { return successful; } }