package com.tesora.dve.db.mysql.libmy;
/*
* #%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 io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import java.nio.ByteOrder;
import java.sql.SQLException;
import java.util.Map;
import org.apache.log4j.Logger;
import com.google.common.collect.ImmutableMap;
import com.tesora.dve.errmap.FormattedErrorInfo;
import com.tesora.dve.exceptions.PESQLStateException;
import com.tesora.dve.db.mysql.common.MysqlAPIUtils;
import com.tesora.dve.exceptions.PEException;
public class MyErrorResponse extends MyResponseMessage {
static final Logger logger = Logger.getLogger(MyErrorResponse.class);
public static final byte ERRORPKT_FIELD_COUNT = ((byte) 0xff);
private static final Map<String, PESQLStateException> SQLSTATE_TRANSFORM_TABLE = ImmutableMap.<String, PESQLStateException>builder()
.put("XA102", new PESQLStateException(1213, "40001", "Deadlock found when trying to get lock; try restarting transaction"))
.build();
private int errorNumber = 0;
private String sqlState = "00000";
private String errorMsg = "Unknown Error";
public MyErrorResponse() {
setOK(false);
}
public MyErrorResponse(FormattedErrorInfo fei) {
this();
this.errorNumber = fei.getErrNo();
this.sqlState = fei.getSQLState();
this.errorMsg = fei.getErrorMessage();
}
public MyErrorResponse(Exception e) {
this();
setException(e);
setSQLErrorInformation(doTransforms(extractRootCause(e)));
}
@Override
public boolean isErrorResponse() {
return true;
}
private Throwable extractRootCause(Exception e) {
if (e instanceof PESQLStateException) {
return e;
} else if (e instanceof PEException) {
return getLastException();
}
return e;
}
private static Throwable doTransforms(Throwable t) {
if (t instanceof PESQLStateException) {
String key = ((PESQLStateException) t).getSqlState();
if (SQLSTATE_TRANSFORM_TABLE.containsKey(key)) {
return SQLSTATE_TRANSFORM_TABLE.get(key);
}
} else if (t instanceof SQLException) {
return doTransforms(new PESQLStateException((SQLException) t));
}
return t;
}
private void setSQLErrorInformation(Throwable t) {
if (t instanceof PESQLStateException) {
PESQLStateException sqle = (PESQLStateException) t;
setSQLErrorInformation(sqle.getErrorNumber(), sqle.getSqlState(), sqle.getErrorMsg());
} else {
setSQLErrorInformation(99, "99999", extractExceptionMessage(t));
}
}
private void setSQLErrorInformation(int errorNumber, String sqlState, String errorMsg) {
setErrorMsg(errorMsg);
if (sqlState != null)
setSqlState(sqlState);
setErrorNumber(errorNumber);
}
public static String extractExceptionMessage(Throwable use) {
return use.getClass().getSimpleName() + ": " + ((use.getMessage() != null) ? use.getMessage() : use.toString());
}
public Throwable getLastException() {
Throwable lastEx = exception;
for (Throwable root = exception; (root = root.getCause()) != null;) {
lastEx = root;
}
return lastEx;
}
@Override
public void marshallMessage(ByteBuf cb) {
cb.writeByte(ERRORPKT_FIELD_COUNT);
cb.writeShort((short) errorNumber);
cb.writeBytes("#".getBytes());
cb.writeBytes(sqlState.substring(0, 5).getBytes());
cb.writeBytes(errorMsg.getBytes());
}
@Override
public void unmarshallMessage(ByteBuf cb) {
cb.skipBytes(1); // error header
errorNumber = cb.readUnsignedShort();
cb.skipBytes(1);
sqlState = MysqlAPIUtils.readBytesAsString(cb, 5, CharsetUtil.UTF_8);
errorMsg = MysqlAPIUtils.readBytesAsString(cb, CharsetUtil.UTF_8);
}
public PEException asException() {
return new PESQLStateException(errorNumber, sqlState, errorMsg);
}
public int getErrorNumber() {
return errorNumber;
}
public void setErrorNumber(int errorNumber) {
this.errorNumber = errorNumber;
}
public String getSqlState() {
return sqlState;
}
public void setSqlState(String sqlState) {
this.sqlState = sqlState;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
if ( errorMsg != null )
this.errorMsg = errorMsg;
}
@Override
public MyMessageType getMessageType() {
return MyMessageType.ERROR_RESPONSE;
}
@Override
public String toString() {
return "Mysql Error: errorNumber=" + errorNumber + " sqlState=" + sqlState + " errorMsg=" + errorMsg;
}
public static void throwException(ByteBuf cb) throws PESQLStateException {
throw asException(cb);
}
public static PESQLStateException asException(ByteBuf in) {
ByteBuf cb = in.order(ByteOrder.LITTLE_ENDIAN);
cb.skipBytes(1); // error header
int errorNumber = cb.readUnsignedShort();
cb.skipBytes(1); // sqlState header
String sqlState = MysqlAPIUtils.readBytesAsString(cb, 5, CharsetUtil.UTF_8);
String errorMsg = MysqlAPIUtils.readBytesAsString(cb, CharsetUtil.UTF_8);
return new PESQLStateException(errorNumber, sqlState, errorMsg);
}
}