package com.tesora.dve.mysqlapi.repl.messages;
/*
* #%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.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.tesora.dve.db.mysql.common.MysqlAPIUtils;
import com.tesora.dve.exceptions.PEException;
public class MyStatusVariables {
public enum MyQueryEventCode {
Q_FLAGS2_CODE((byte) 0x00),
Q_SQL_MODE_CODE((byte) 0x01),
Q_CATALOG_CODE((byte) 0x02),
Q_AUTO_INCREMENT((byte) 0x03),
Q_CHARSET_CODE((byte) 0x04),
Q_TIME_ZONE_CODE((byte) 0x05),
Q_CATALOG_NZ_CODE((byte) 0x06),
Q_LC_TIME_NAMES_CODE((byte) 0x07),
Q_CHARSET_DATABASE_CODE((byte) 0x08),
Q_TABLE_MAP_FOR_UPDATE_CODE((byte) 0x09),
Q_MASTER_DATA_WRITTEN_CODE((byte) 0x0A),
Q_INVOKER((byte) 0x0B),
Q_UPDATED_DB_NAMES((byte) 0x0C),
Q_MICROSECONDS((byte) 0x0D),
Q_HRNOW((byte)0x80)
;
private final byte code;
MyQueryEventCode(byte b) {
code = b;
}
public static MyQueryEventCode fromByte(byte b) {
for (MyQueryEventCode mt : values()) {
if (mt.code == b) {
return mt;
}
}
return null;
}
public byte getByteValue() {
return code;
}
}
Set<BaseQueryEvent> suppliedEventCodes = new LinkedHashSet<BaseQueryEvent>();
public MyStatusVariables() {
// TODO Auto-generated constructor stub
}
public Set<BaseQueryEvent> getSuppliedEventCodes() {
return suppliedEventCodes;
}
public void setSuppliedEventCodes(Set<BaseQueryEvent> suppliedEventCodes) {
this.suppliedEventCodes = suppliedEventCodes;
}
public void parseStatusVariables(ByteBuf cb, int svLen) throws PEException {
if (svLen > 0) {
ByteBuf statsVarsBuf = cb.readBytes(svLen);
while (statsVarsBuf.isReadable()) {
byte code = statsVarsBuf.readByte();
MyQueryEventCode mqec = MyQueryEventCode.fromByte(code);
if (mqec == null) {
throw new PEException("Replication could not decode query event code: '" + code + "' (0x" + Integer.toHexString(code) + ")");
}
switch (mqec) {
case Q_FLAGS2_CODE:
int flags = statsVarsBuf.readInt();
suppliedEventCodes.add(new QueryFlags2Event(flags));
break;
case Q_SQL_MODE_CODE:
long sqlMode = statsVarsBuf.readLong();
suppliedEventCodes.add(new QuerySQLModeEvent(sqlMode));
break;
case Q_CATALOG_CODE:
{
byte len = statsVarsBuf.readByte();
String catalog = MysqlAPIUtils.readBytesAsString(statsVarsBuf, len, CharsetUtil.UTF_8);
statsVarsBuf.readByte(); // null terminated byte
suppliedEventCodes.add(new QueryCatalogEvent(catalog));
break;
}
case Q_AUTO_INCREMENT:
int autoIncrementIncrement = statsVarsBuf.readUnsignedShort();
int autoIncrementOffset = statsVarsBuf.readUnsignedShort();
suppliedEventCodes.add(new QueryAutoIncrementEvent(autoIncrementIncrement, autoIncrementOffset));
break;
case Q_CHARSET_CODE:
int charSetClient = statsVarsBuf.readUnsignedShort();
int collationConnection = statsVarsBuf.readUnsignedShort();
int collationServer = statsVarsBuf.readUnsignedShort();
suppliedEventCodes.add(new QueryCharSetCodeEvent(charSetClient, collationConnection, collationServer));
break;
case Q_TIME_ZONE_CODE:
{
byte len = statsVarsBuf.readByte();
String timeZone = MysqlAPIUtils.readBytesAsString(statsVarsBuf, len, CharsetUtil.UTF_8);
suppliedEventCodes.add(new QueryTimeZoneCodeEvent(timeZone));
break;
}
case Q_CATALOG_NZ_CODE:
{
byte catalogLen = statsVarsBuf.readByte();
String catalog = MysqlAPIUtils.readBytesAsString(statsVarsBuf, catalogLen, CharsetUtil.UTF_8);
suppliedEventCodes.add(new QueryCatalogNZEvent(catalog));
break;
}
case Q_LC_TIME_NAMES_CODE:
short monthDayNames = statsVarsBuf.readShort();
suppliedEventCodes.add(new QueryTimeNamesEvent(monthDayNames));
break;
case Q_CHARSET_DATABASE_CODE:
short collationDatabase = statsVarsBuf.readShort();
suppliedEventCodes.add(new QueryCollationDatabaseEvent(collationDatabase));
break;
case Q_TABLE_MAP_FOR_UPDATE_CODE:
long tableMapForUpdate = statsVarsBuf.readLong();
suppliedEventCodes.add(new QueryTableMapEvent(tableMapForUpdate));
break;
case Q_MASTER_DATA_WRITTEN_CODE:
int originalLength = statsVarsBuf.readInt();
suppliedEventCodes.add(new QueryMasterDataWrittenEvent(originalLength));
break;
case Q_INVOKER:
int userLen = statsVarsBuf.readByte();
String user = MysqlAPIUtils.readBytesAsString(statsVarsBuf, userLen, CharsetUtil.UTF_8);
int hostLen = statsVarsBuf.readByte();
String host = MysqlAPIUtils.readBytesAsString(statsVarsBuf, hostLen, CharsetUtil.UTF_8);
suppliedEventCodes.add(new QueryInvokerEvent(user, host));
break;
case Q_UPDATED_DB_NAMES:
List<String> dbNames = new ArrayList<String>();
int numDbs = statsVarsBuf.readByte();
if (numDbs > 0) {
for(int i=0; i<numDbs; i++) {
dbNames.add(statsVarsBuf.readSlice(statsVarsBuf.bytesBefore((byte)0)).toString(
CharsetUtil.UTF_8));
statsVarsBuf.readByte(); //read null byte
}
}
suppliedEventCodes.add(new QueryUpdatedDBNamesEvent(dbNames));
break;
case Q_MICROSECONDS:
int microseconds = statsVarsBuf.readMedium();
suppliedEventCodes.add(new QueryMicrosecondsEvent(microseconds));
break;
case Q_HRNOW:
//TODO: this was apparently added for MariaDB, but I can't find a lot of info on it. skip for now.
suppliedEventCodes.add(new QueryMicrosecondsEvent(statsVarsBuf.readUnsignedMedium()));
break;
default :
throw new PEException("Replication encountered an unknown query event code: '" + code + "' (0x" + Integer.toHexString(code) + ")");
}
}
}
}
public void writeStatusVariables(ByteBuf cb) {
for(BaseQueryEvent qe : getSuppliedEventCodes()) {
MyQueryEventCode code = qe.getCode();
switch(code) {
case Q_FLAGS2_CODE:
cb.writeByte(code.getByteValue());
cb.writeInt(((QueryFlags2Event)qe).getFlags());
break;
case Q_SQL_MODE_CODE:
cb.writeByte(code.getByteValue());
cb.writeLong(((QuerySQLModeEvent)qe).getSqlMode());
break;
case Q_CATALOG_CODE:
cb.writeByte(code.getByteValue());
cb.writeByte(((QueryCatalogEvent)qe).getCatalog().length());
cb.writeBytes(((QueryCatalogEvent)qe).getCatalog().getBytes());
cb.writeByte(0); //for trailing 0
break;
case Q_AUTO_INCREMENT:
cb.writeByte(code.getByteValue());
cb.writeShort(((QueryAutoIncrementEvent)qe).getAutoIncrementIncrement());
cb.writeShort(((QueryAutoIncrementEvent)qe).getAutoIncrementOffset());
break;
case Q_CHARSET_CODE:
cb.writeByte(code.getByteValue());
cb.writeShort(((QueryCharSetCodeEvent)qe).getCharSetClient());
cb.writeShort(((QueryCharSetCodeEvent)qe).getCollationConnection());
cb.writeShort(((QueryCharSetCodeEvent)qe).getCollationServer());
break;
case Q_TIME_ZONE_CODE:
cb.writeByte(code.getByteValue());
cb.writeByte(((QueryTimeZoneCodeEvent)qe).getTimeZone().length());
cb.writeBytes(((QueryTimeZoneCodeEvent)qe).getTimeZone().getBytes());
break;
case Q_CATALOG_NZ_CODE:
cb.writeByte(code.getByteValue());
cb.writeByte(((QueryCatalogNZEvent)qe).getCatalog().length());
cb.writeBytes(((QueryCatalogNZEvent)qe).getCatalog().getBytes());
break;
case Q_LC_TIME_NAMES_CODE:
cb.writeByte(code.getByteValue());
cb.writeShort(((QueryTimeNamesEvent)qe).getMonthDayNames());
break;
case Q_CHARSET_DATABASE_CODE:
cb.writeByte(code.getByteValue());
cb.writeShort(((QueryCollationDatabaseEvent)qe).getCollationDatabase());
break;
case Q_TABLE_MAP_FOR_UPDATE_CODE:
cb.writeByte(code.getByteValue());
cb.writeLong(((QueryTableMapEvent)qe).getTableMapForUpdate());
break;
case Q_MASTER_DATA_WRITTEN_CODE:
cb.writeByte(code.getByteValue());
cb.writeInt(((QueryMasterDataWrittenEvent)qe).getOriginalLength());
break;
case Q_INVOKER:
cb.writeByte(code.getByteValue());
cb.writeByte(((QueryInvokerEvent)qe).getUser().length());
cb.writeBytes(((QueryInvokerEvent)qe).getUser().getBytes());
cb.writeByte(((QueryInvokerEvent)qe).getHost().length());
cb.writeBytes(((QueryInvokerEvent)qe).getHost().getBytes());
break;
case Q_UPDATED_DB_NAMES:
cb.writeByte(code.getByteValue());
List<String> dbs = ((QueryUpdatedDBNamesEvent)qe).getDbNames();
cb.writeByte(dbs == null ? 0 : dbs.size());
if (dbs.size() > 0) {
for(String db : dbs) {
cb.writeByte(db.length());
cb.writeBytes(db.getBytes(CharsetUtil.UTF_8));
cb.writeByte(0); //for trailing 0
}
}
break;
case Q_MICROSECONDS:
cb.writeByte(code.getByteValue());
cb.writeMedium(((QueryMicrosecondsEvent)qe).getMicroseconds());
break;
case Q_HRNOW:
cb.writeMedium(((QueryHRNowEvent)qe).threeBytes);
break;
default :
break;
}
}
}
public abstract class BaseQueryEvent {
MyQueryEventCode code;
public BaseQueryEvent() {
}
public BaseQueryEvent(MyQueryEventCode code) {
this.code = code;
}
public abstract void outputConsole();
public MyQueryEventCode getCode() {
return code;
}
public void setCode(MyQueryEventCode code) {
this.code = code;
}
}
public class QueryFlags2Event extends BaseQueryEvent {
final int OPTION_AUTO_IS_NULL = (1 << 14);
final int OPTION_NO_FOREIGN_KEY_CHECKS = (1 << 26);
final int OPTION_RELAXED_UNIQUE_CHECKS = (1 << 27);
final int OPTION_NOT_AUTOCOMMIT = (1 << 19);
int flags = 0;
Boolean optionAutoIsNull = null;
Boolean optionNoForeignKeyChecks = null;
Boolean optionRelaxedUniqueChecks = null;
Boolean optionNotAutoCommit = null;
public QueryFlags2Event(int flags) {
super(MyQueryEventCode.Q_FLAGS2_CODE);
this.flags = flags;
optionAutoIsNull = (flags | OPTION_AUTO_IS_NULL) == 1;
optionNoForeignKeyChecks = (flags | OPTION_NO_FOREIGN_KEY_CHECKS) == 1;
optionRelaxedUniqueChecks = (flags | OPTION_RELAXED_UNIQUE_CHECKS) == 1;
optionNotAutoCommit = (flags | OPTION_NOT_AUTOCOMMIT) == 1;
}
@Override
public void outputConsole() {
System.out.println("\t\tQueryFlag2");
System.out.println("\t\t\tAutoIsNull: " + optionAutoIsNull);
System.out.println("\t\t\tNoForeignKeyChecks: " + optionNoForeignKeyChecks);
System.out.println("\t\t\tRelaxedUniqueChecks: " + optionRelaxedUniqueChecks);
System.out.println("\t\t\tNotAutoCommit: " + optionNotAutoCommit);
}
public int getFlags() {
return flags;
}
public void setFlags(int flags) {
this.flags = flags;
}
public boolean isOptionAutoIsNull() {
return optionAutoIsNull;
}
public void setOptionAutoIsNull(boolean optionAutoIsNull) {
this.optionAutoIsNull = optionAutoIsNull;
}
public boolean isOptionNoForeignKeyChecks() {
return optionNoForeignKeyChecks;
}
public void setOptionNoForeignKeyChecks(boolean optionNoForeignKeyChecks) {
this.optionNoForeignKeyChecks = optionNoForeignKeyChecks;
}
public boolean isOptionRelaxedUniqueChecks() {
return optionRelaxedUniqueChecks;
}
public void setOptionRelaxedUniqueChecks(boolean optionRelaxedUniqueChecks) {
this.optionRelaxedUniqueChecks = optionRelaxedUniqueChecks;
}
public boolean isOptionNotAutoCommit() {
return optionNotAutoCommit;
}
public void setOptionNotAutoCommit(boolean optionNotAutoCommit) {
this.optionNotAutoCommit = optionNotAutoCommit;
}
}
public class QuerySQLModeEvent extends BaseQueryEvent {
long sqlMode;
public QuerySQLModeEvent(long sqlMode) {
super(MyQueryEventCode.Q_SQL_MODE_CODE);
this.sqlMode = sqlMode;
}
@Override
public void outputConsole() {
System.out.println("\t\tSQLMode");
System.out.println("\t\t\tvalue: " + sqlMode);
}
public long getSqlMode() {
return sqlMode;
}
public void setSqlMode(long sqlMode) {
this.sqlMode = sqlMode;
}
}
public class QueryCatalogEvent extends BaseQueryEvent {
String cat;
public QueryCatalogEvent(String cat) {
super(MyQueryEventCode.Q_CATALOG_CODE);
this.cat = cat;
}
@Override
public void outputConsole() {
System.out.println("\t\tCatalog");
System.out.println("\t\t\tCatalog: " + cat);
}
public String getCatalog() {
return cat;
}
public void setCatalog(String cat) {
this.cat = cat;
}
}
public class QueryAutoIncrementEvent extends BaseQueryEvent {
int autoIncrementIncrement;
int autoIncrementOffset;
public QueryAutoIncrementEvent(int autoIncrementIncrement, int autoIncrementOffset) {
super(MyQueryEventCode.Q_AUTO_INCREMENT);
this.autoIncrementIncrement = autoIncrementIncrement;
this.autoIncrementOffset = autoIncrementOffset;
}
@Override
public void outputConsole() {
System.out.println("\t\tAutoIncrement");
System.out.println("\t\t\tIncrement: " + autoIncrementIncrement);
System.out.println("\t\t\tOffset: " + autoIncrementOffset);
}
public int getAutoIncrementIncrement() {
return autoIncrementIncrement;
}
public void setAutoIncrementIncrement(int autoIncrementIncrement) {
this.autoIncrementIncrement = autoIncrementIncrement;
}
public int getAutoIncrementOffset() {
return autoIncrementOffset;
}
public void setAutoIncrementOffset(int autoIncrementOffset) {
this.autoIncrementOffset = autoIncrementOffset;
}
}
public class QueryCharSetCodeEvent extends BaseQueryEvent {
int charSetClient;
int collationConnection;
int collationServer;
public QueryCharSetCodeEvent(int charSetClient, int collationConnection, int collationServer) {
super(MyQueryEventCode.Q_CHARSET_CODE);
this.charSetClient = charSetClient;
this.collationConnection = collationConnection;
this.collationServer = collationServer;
}
@Override
public void outputConsole() {
System.out.println("\t\tCharSet Code");
System.out.println("\t\t\tClient Charset: " + charSetClient);
System.out.println("\t\t\tConnection Collation: " + collationConnection);
System.out.println("\t\t\tServer Collation: " + collationServer);
}
public int getCharSetClient() {
return charSetClient;
}
public void setCharSetClient(int charSetClient) {
this.charSetClient = charSetClient;
}
public int getCollationConnection() {
return collationConnection;
}
public void setCollationConnection(int collationConnection) {
this.collationConnection = collationConnection;
}
public int getCollationServer() {
return collationServer;
}
public void setCollationServer(int collationServer) {
this.collationServer = collationServer;
}
}
public class QueryTimeZoneCodeEvent extends BaseQueryEvent {
String timeZone;
public QueryTimeZoneCodeEvent(String timeZone) {
super(MyQueryEventCode.Q_TIME_ZONE_CODE);
this.timeZone = timeZone;
}
@Override
public void outputConsole() {
System.out.println("\t\tTime Zone");
System.out.println("\t\t\tTime Zone: " + timeZone);
}
public String getTimeZone() {
return timeZone;
}
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
}
public class QueryCatalogNZEvent extends BaseQueryEvent {
String catalog;
public QueryCatalogNZEvent(String catalog) {
super(MyQueryEventCode.Q_CATALOG_NZ_CODE);
this.catalog = catalog;
}
@Override
public void outputConsole() {
System.out.println("\t\tCatalog");
System.out.println("\t\t\tName: " + catalog);
}
public String getCatalog() {
return catalog;
}
public void setCatalog(String catalog) {
this.catalog = catalog;
}
}
public class QueryTimeNamesEvent extends BaseQueryEvent {
short monthDayNames;
public QueryTimeNamesEvent(short monthDayNames) {
super(MyQueryEventCode.Q_LC_TIME_NAMES_CODE);
this.monthDayNames = monthDayNames;
}
@Override
public void outputConsole() {
System.out.println("\t\tTime Name Map");
System.out.println("\t\t\tMonth/Day: " + monthDayNames);
}
public short getMonthDayNames() {
return monthDayNames;
}
public void setMonthDayNames(short monthDayNames) {
this.monthDayNames = monthDayNames;
}
}
public class QueryCollationDatabaseEvent extends BaseQueryEvent {
short collationDatabase;
public QueryCollationDatabaseEvent(short collationDatabase) {
super(MyQueryEventCode.Q_CHARSET_DATABASE_CODE);
this.collationDatabase = collationDatabase;
}
@Override
public void outputConsole() {
System.out.println("\t\tDB Collation");
System.out.println("\t\t\tCollation Code: " + collationDatabase);
}
public short getCollationDatabase() {
return collationDatabase;
}
public void setCollationDatabase(short collationDatabase) {
this.collationDatabase = collationDatabase;
}
}
class QueryTableMapEvent extends BaseQueryEvent {
long tableMapForUpdate;
public QueryTableMapEvent(long tableMapForUpdate) {
super(MyQueryEventCode.Q_TABLE_MAP_FOR_UPDATE_CODE);
this.tableMapForUpdate = tableMapForUpdate;
}
@Override
public void outputConsole() {
System.out.println("\t\tTable Map for Update");
System.out.println("\t\t\tCode: " + tableMapForUpdate);
}
public long getTableMapForUpdate() {
return tableMapForUpdate;
}
public void setTableMapForUpdate(long tableMapForUpdate) {
this.tableMapForUpdate = tableMapForUpdate;
}
}
class QueryMasterDataWrittenEvent extends BaseQueryEvent {
int originalLength;
public QueryMasterDataWrittenEvent(int originalLength) {
super(MyQueryEventCode.Q_MASTER_DATA_WRITTEN_CODE);
this.originalLength = originalLength;
}
@Override
public void outputConsole() {
System.out.println("\t\tMaster Data Written");
System.out.println("\t\t\tOriginal Length: " + originalLength);
}
public int getOriginalLength() {
return originalLength;
}
public void setOriginalLength(int originalLength) {
this.originalLength = originalLength;
}
}
class QueryInvokerEvent extends BaseQueryEvent {
String user;
String host;
public QueryInvokerEvent(String user, String host) {
super(MyQueryEventCode.Q_INVOKER);
this.user = user;
this.host = host;
}
@Override
public void outputConsole() {
System.out.println("\t\tInvoker");
System.out.println("\t\t\tUser: " + user);
System.out.println("\t\t\tHost: " + host);
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}
class QueryUpdatedDBNamesEvent extends BaseQueryEvent {
List<String> dbNames;
public List<String> getDbNames() {
return dbNames;
}
@Override
public void outputConsole() {
System.out.println("\t\tUpdated Databases");
for(String db : dbNames) {
System.out.println("\t\t\tDatabase: " + db);
}
}
public void setDbNames(List<String> dbNames) {
this.dbNames = dbNames;
}
public QueryUpdatedDBNamesEvent(List<String> dbNames) {
this.dbNames = dbNames;
}
}
class QueryMicrosecondsEvent extends BaseQueryEvent {
int microseconds;
public QueryMicrosecondsEvent(int microseconds) {
this.microseconds = microseconds;
}
@Override
public void outputConsole() {
System.out.println("\t\tMicroseconds");
System.out.println("\t\t\tMicroseconds: " + microseconds);
}
public int getMicroseconds() {
return microseconds;
}
public void setMicroseconds(int microseconds) {
this.microseconds = microseconds;
}
}
class QueryHRNowEvent extends BaseQueryEvent {
int threeBytes;
QueryHRNowEvent(int threeBytes) {
this.threeBytes = threeBytes;
}
@Override
public void outputConsole() {
System.out.println("\t\tHRNOW");
System.out.println("\t\t\tHRNOW: " + threeBytes);
}
}
}