package org.wonderdb.server;
/*******************************************************************************
* Copyright 2013 Vilas Athavale
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.wonderdb.collection.exceptions.UniqueKeyViolationException;
import org.wonderdb.exception.InvalidCollectionNameException;
import org.wonderdb.exception.InvalidIndexException;
import org.wonderdb.exceptions.FileAlreadyExists;
import org.wonderdb.exceptions.StorageFileCreationException;
import org.wonderdb.parser.jtree.QueryParser;
import org.wonderdb.query.executor.ScatterGatherQueryExecutor;
import org.wonderdb.query.parse.CreateIndexQuery;
import org.wonderdb.query.parse.DBCreateStorageQuery;
import org.wonderdb.query.parse.DBCreateTableQuery;
import org.wonderdb.query.parse.DBDeleteQuery;
import org.wonderdb.query.parse.DBInsertQuery;
import org.wonderdb.query.parse.DBQuery;
import org.wonderdb.query.parse.DBShowPlanQuery;
import org.wonderdb.query.parse.DBUpdateQuery;
import org.wonderdb.query.parse.ShowIndexQuery;
import org.wonderdb.query.parse.ShowSchemaQuery;
import org.wonderdb.query.parse.ShowStoragesQuery;
import org.wonderdb.query.parse.ShowTableQuery;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree.ResultSetColumn;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree.ResultSetValue;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree.VirtualResultSetColumn;
import org.wonderdb.query.plan.FullTableScan;
import org.wonderdb.query.plan.IndexRangeScan;
import org.wonderdb.query.plan.QueryPlan;
import org.wonderdb.schema.CollectionMetadata;
import org.wonderdb.schema.SchemaMetadata;
import org.wonderdb.serialize.Serializer;
import org.wonderdb.serialize.SerializerManager;
import org.wonderdb.types.ColumnNameMeta;
import org.wonderdb.types.StringType;
public class WonderDBShardServerHandler extends SimpleChannelHandler {
public static final int SERVER_HANDLER = 0;
public static final int SHARD_HANDLER = 1;
int handlerType = SHARD_HANDLER;
public WonderDBShardServerHandler(int type) {
this.handlerType = type;
}
public int getHandlerType() {
return handlerType;
}
@SuppressWarnings("unused")
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer buf = (ChannelBuffer) e.getMessage();
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(buf);
buffer.clear();
buffer.writerIndex(buffer.capacity());
int queryLen = buffer.readInt();
byte[] bytes = new byte[queryLen];
buffer.readBytes(bytes);
String s = new String(bytes);
List<Object> bindParamList = new ArrayList<Object>();
int bindParamCount = buffer.readInt();
for (int i = 0; i < bindParamCount; i++) {
byte b = buffer.readByte();
if (b == 0) {
bindParamList.add(new StringType(null));
continue;
}
int size = buffer.readInt();
bytes = new byte[size];
buffer.readBytes(bytes);
int type = buffer.readInt();
Object dt = null;
String s1 = new String(bytes);
switch (type) {
case Types.INTEGER:
dt = Integer.parseInt(s1);
break;
case Types.FLOAT:
dt = Float.parseFloat(s1);
break;
case Types.DOUBLE:
dt = Double.parseDouble(s1);
break;
case Types.VARCHAR:
dt = s1;
break;
case Types.NUMERIC:
dt = Long.parseLong(s1);
break;
default:
break;
}
bindParamList.add(dt);
}
int version = buffer.readInt();
ChannelBuffer outBuf = ChannelBuffers.dynamicBuffer();
Channel channel = e.getChannel();
try {
if ("shutdown".equals(s)) {
synchronized (WonderDBServer.lock) {
WonderDBServer.shutdown = true;
WonderDBServer.lock.notifyAll();
outBuf.clear();
outBuf.writeInt(8);
outBuf.writeByte((byte) 1);
outBuf.writeInt(0);
outBuf.writeInt(0);
}
} else {
String query = s;
QueryParser parser = QueryParser.getInstance();
DBQuery q = parser.parse(query, bindParamList, handlerType, buffer);
if (q instanceof DBShowPlanQuery) {
// handleValidate(outBuf, channel);
handleExplainPlan((DBShowPlanQuery) q, outBuf, channel);
} else if (q instanceof ShowStoragesQuery) {
handleShowStoragesQuery((ShowStoragesQuery) q, channel);
} else {
if (q instanceof DBSelectQueryJTree) {
handleSelectQuery((DBSelectQueryJTree) q, outBuf, channel);
} else if (q instanceof DBInsertQuery) {
handleInsertQuery((DBInsertQuery) q, outBuf);
} else if (q instanceof DBUpdateQuery) {
handleUpdateQuery((DBUpdateQuery) q, outBuf);
} else if (q instanceof DBDeleteQuery) {
handleDeleteQuery((DBDeleteQuery) q, outBuf);
} else if (q instanceof CreateIndexQuery) {
handleCreateIndexQuery((CreateIndexQuery) q, outBuf);
} else if (q instanceof DBCreateTableQuery) {
handleCreateTableQuery((DBCreateTableQuery) q, outBuf);
} else if (q instanceof ShowTableQuery) {
handleShowTableQuery((ShowTableQuery) q, channel);
} else if (q instanceof ShowIndexQuery) {
handleShowIndexQuery((ShowIndexQuery) q, channel);
}else if (q instanceof ShowSchemaQuery) {
handleShowSchemaQuery((ShowSchemaQuery) q, channel);
} else if (q instanceof DBCreateStorageQuery) {
handleCreateStorageQuery((DBCreateStorageQuery) q, outBuf);
}
// } else if (q instanceof CreateShardQuery) {
// handleCreateShardQuery((CreateShardQuery) q, outBuf);
// } else if (q instanceof CreateReplicaSetQuery) {
// handleCreateReplicaSetQuery((CreateReplicaSetQuery) q, outBuf);
// } else if (q instanceof AddToReplicaSetQuery) {
// handleAddToReplicaSetQuery((AddToReplicaSetQuery) q, outBuf);
// }
}
}
} catch (Throwable t) {
outBuf.writeInt(0);
outBuf.writeByte(1);
t.printStackTrace();
// t.printStackTrace();
// String s1 = "Exception :" + t.getMessage();
// outBuf.writeInt(s1.getBytes().length);
// outBuf.writeBytes(s1.getBytes());
}
channel.write(outBuf);
outBuf.clear();
}
// private void handleValidate(ChannelBuffer buffer, Channel channel) {
// Map<String, CollectionMetadata> map = SchemaMetadata.getInstance().getCollections();
// Iterator<String> iter = map.keySet().iterator();
// IndexKeyType previkt = null;
// Set<BlockPtr> pinnedBlocks = new HashSet<BlockPtr>();
// while (iter.hasNext()) {
// String colName = iter.next();
// int schemaId = SchemaMetadata.getInstance().getCollectionMetadata(colName).getSchemaId();
// List<IndexMetadata> list = SchemaMetadata.getInstance().getIndexes(colName);
// for (int i = 0; i < list.size(); i++) {
// IndexMetadata idxMeta = list.get(i);
// ResultIterator rs = idxMeta.getIndexTree().getHead(false, pinnedBlocks);
// Block currentBlock = rs.getCurrentBlock();
// try {
// while (rs.hasNext()) {
// if (rs.getCurrentBlock() != currentBlock) {
// CacheEntryPinner.getInstance().unpin(currentBlock.getPtr(), pinnedBlocks);
// currentBlock = rs.getCurrentBlock();
// }
// IndexResultContent content = (IndexResultContent) rs.next();
// RecordId recId = content.get();
// Set<ColumnType> colList = idxMeta.getIndex().getColumnNameList();
// Iterator<ColumnType> colTypeIter = colList.iterator();
// CacheEntryPinner.getInstance().pin(recId.getPtr(), pinnedBlocks);
// RecordBlock lockedBlock = (RecordBlock) PrimaryCacheHandlerFactory.getInstance().getCacheHandler().get(recId.getPtr());
//
// TableRecord tr = (TableRecord) CacheObjectMgr.getInstance().getRecord(lockedBlock, recId, null, schemaId, null);
// List<DBType> vals = new ArrayList<DBType>();
// while (colTypeIter.hasNext()) {
// ColumnType ct = colTypeIter.next();
// DBType idxVal = content.getValue(ct, null);
// DBType colVal = tr.getColumns().get(ct);
// if (idxVal.compareTo(colVal) != 0) {
// System.out.println("mismatch");
// }
// vals.add(idxVal);
// }
//
// IndexKeyType ikt = new IndexKeyType(vals, recId);
// if (previkt != null) {
// if (previkt.compareTo(ikt) > 0) {
// System.out.println("bad");
// }
// }
// previkt = ikt;
// CacheEntryPinner.getInstance().unpin(pinnedBlocks, pinnedBlocks);
// }
// } finally {
// rs.unlock();
// }
// CacheEntryPinner.getInstance().unpin(pinnedBlocks, pinnedBlocks);
// }
// }
// }
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
WonderDBServer.allServerChannels.add(e.getChannel());
}
private void handleSelectQuery(DBSelectQueryJTree q, ChannelBuffer buffer, Channel channel) {
List<List<ResultSetValue>> list = q.execute();
serialize(q, list, buffer, channel);
}
private void serialize(DBSelectQueryJTree q, List<List<ResultSetValue>> list, ChannelBuffer buffer, Channel channel) {
List<ResultSetColumn> rscList = q.resultsetColumns;
ChannelBuffer metaBuffer = ChannelBuffers.dynamicBuffer();
metaBuffer.writerIndex(5);
metaBuffer.writeInt(rscList.size());
int colCount = 0;
for (int i = 0; i < rscList.size(); i++) {
ResultSetColumn rsc = rscList.get(i);
CollectionMetadata colMeta = SchemaMetadata.getInstance().getCollectionMetadata(rsc.ca.getCollectionName());
String colName = "";
int colType = -1;
if (rsc instanceof VirtualResultSetColumn) {
colName = ((VirtualResultSetColumn) rsc).columnAlias;
colType = ((VirtualResultSetColumn) rsc).function.getColumnType();
} else {
colName = colName + rsc.resultColumnName;
colType = colMeta.getColumnType(colName);
}
int sqlType = Serializer.getInstance().getSQLType(colType);
colCount++;
metaBuffer.writeInt(colName.getBytes().length);
metaBuffer.writeBytes(colName.getBytes());
metaBuffer.writeInt(sqlType);
}
int posn = metaBuffer.writerIndex();
metaBuffer.writerIndex(5);
metaBuffer.writeInt(colCount);
metaBuffer.writerIndex(0);
metaBuffer.writeInt(posn-5);
metaBuffer.writeByte(0);
metaBuffer.writerIndex(posn);
channel.write(metaBuffer);
for (int x = 0; x < list.size(); x++) {
List<ResultSetValue> valueList = list.get(x);
ChannelBuffer tmpBuffer = ChannelBuffers.dynamicBuffer();
tmpBuffer.writerIndex(5);
for (int i = 0; i < rscList.size(); i++) {
ResultSetColumn rsc = rscList.get(i);
// int schemaId = SchemaMetadata.getInstance().getCollectionMetadata(rsc.ca.getCollectionName()).getSchemaId();
// if (schemaId < 3) {
// continue;
// }
int colType = -1;
if (rsc instanceof VirtualResultSetColumn) {
colType = ((VirtualResultSetColumn) rsc).function.getColumnType();
} else {
int colId = rsc.columnId;
CollectionMetadata colMeta = SchemaMetadata.getInstance().getCollectionMetadata(rsc.ca.getCollectionName());
ColumnNameMeta cnm = colMeta.getCollectionColumn(colId);
colType = cnm.getColumnType();
}
ResultSetValue rsv = valueList.get(i);
// if (ser instanceof BlockPtrSerializer) {
// continue;
// }
if (rsv.value != null) {
tmpBuffer.writeByte(0);
SerializerManager.getInstance().getSerializer(colType).toBytes(rsv.value, tmpBuffer, null);
} else {
tmpBuffer.writeByte(1);
}
}
// tmpBuffer.clear();
int p = tmpBuffer.readableBytes();
tmpBuffer.resetWriterIndex();
tmpBuffer.writeInt(p-5);
// if (x+1 == list.size()) {
// tmpBuffer.writeByte(1);
// } else {
tmpBuffer.writeByte(0);
// }
tmpBuffer.writerIndex(p);
tmpBuffer.resetReaderIndex();
channel.write(tmpBuffer);
// buffer.clear();
}
buffer.clear();
buffer.writeInt(4);
buffer.writeByte((byte) 1);
buffer.writeInt(list.size());
// channel.write(buffer);
// buffer.clear();
}
private void handleInsertQuery(DBInsertQuery q, ChannelBuffer buffer) {
int count = 0;
String message = "";
int errCode = 0;
try {
count = q.execute();
} catch (InvalidCollectionNameException e) {
message = "Collection: " + q.getCollectionName() + " already exists";
errCode = 1;
} catch (UniqueKeyViolationException e1) {
message = "Unique key violation...";
errCode = 2;
}
// String s = count + " record(s) inserted\n";
// buffer.writeInt(s.getBytes().length);
// buffer.writeByte((byte) 1);
// buffer.writeBytes(s.getBytes());
// String s = count + " record(s) inserted\n";
buffer.clear();
buffer.writeInt(8 + message.getBytes().length);
buffer.writeByte((byte) 1);
buffer.writeInt(count);
buffer.writeInt(errCode);
buffer.writeBytes(message.getBytes());
}
private void handleCreateStorageQuery(DBCreateStorageQuery q, ChannelBuffer buffer) {
String message = null;
int errCode = 0;
try {
q.execute();
message = q.getFileName() + " storage " + q.getFileName() + " created ";
} catch (StorageFileCreationException e) {
errCode = 5;
message = "Cannot create storage: " + q.getFileName();
} catch (FileAlreadyExists e1) {
message = q.getFileName() + " storage " + q.getFileName() + " already exisits ...";
errCode = 1;
}
buffer.clear();
buffer.writeInt(8 + message.getBytes().length);
buffer.writeByte((byte) 1);
buffer.writeInt(1);
buffer.writeInt(errCode);
buffer.writeBytes(message.getBytes());
}
// private void handleCreateShardQuery(CreateShardQuery q, ChannelBuffer buffer) {
// q.execute();
// String message = "shard created ...";
// buffer.clear();
// buffer.writeInt(8 + message.getBytes().length);
// buffer.writeByte((byte) 1);
// buffer.writeInt(1);
// buffer.writeInt(0);
// buffer.writeBytes(message.getBytes());
// }
//
// private void handleCreateReplicaSetQuery(CreateReplicaSetQuery q, ChannelBuffer buffer) {
// q.execute();
// String message = "shard created ...";
// buffer.clear();
// buffer.writeInt(8 + message.getBytes().length);
// buffer.writeByte((byte) 1);
// buffer.writeInt(1);
// buffer.writeInt(0);
// buffer.writeBytes(message.getBytes());
// }
//
// private void handleAddToReplicaSetQuery(AddToReplicaSetQuery q, ChannelBuffer buffer) {
// q.execute();
// String message = "shard created ...";
// buffer.clear();
// buffer.writeInt(8 + message.getBytes().length);
// buffer.writeByte((byte) 1);
// buffer.writeInt(1);
// buffer.writeInt(0);
// buffer.writeBytes(message.getBytes());
// }
//
private void handleUpdateQuery(DBUpdateQuery q, ChannelBuffer buffer) {
int count = 0;
String message = "";
int errCode = 0;
try {
ScatterGatherQueryExecutor sgqe = new ScatterGatherQueryExecutor(q);
count = sgqe.updateQuery();
} catch (UniqueKeyViolationException e1) {
message = "Unique key violation...";
errCode = 2;
}
// String s = count + " records updated\n";
buffer.clear();
buffer.writeInt(8+message.getBytes().length);
buffer.writeByte((byte) 1);
buffer.writeInt(count);
buffer.writeInt(errCode);
buffer.writeBytes(message.getBytes());
}
private void handleDeleteQuery(DBDeleteQuery q, ChannelBuffer buffer) {
int count = 0;
ScatterGatherQueryExecutor sgqe = new ScatterGatherQueryExecutor(q);
count = sgqe.deleteQuery();
buffer.clear();
buffer.writeInt(8);
buffer.writeByte((byte) 1);
buffer.writeInt(count);
buffer.writeInt(0);
}
private void handleCreateIndexQuery(CreateIndexQuery q, ChannelBuffer buffer) {
int errCode = 0;
String message = "";
try {
q.execute();
message = "Index " + q.getIndexName() + " created";
} catch (InvalidCollectionNameException e) {
errCode = 1;
message = "Table " + q.getCollectionName() + " already exisits ";
} catch (InvalidIndexException e1) {
errCode = 2;
message = "Index " + q.getIndexName() + " already exists";
}
buffer.writeInt(8 + message.getBytes().length);
buffer.writeByte((byte) 1);
buffer.writeInt(1);
buffer.writeInt(errCode);
buffer.writeBytes(message.getBytes());
}
private void handleCreateTableQuery(DBCreateTableQuery q, ChannelBuffer buffer) {
String message = "";
int errCode = 0;
try {
q.execute();
message = "Table " + q.getCollectionName() + " created";
} catch (InvalidCollectionNameException e) {
message = "Collection: " + q.getCollectionName() + " already exists";
errCode = 1;
}
buffer.writeInt(8 + message.getBytes().length);
buffer.writeByte((byte) 1);
buffer.writeInt(1);
buffer.writeInt(errCode);
buffer.writeBytes(message.getBytes());
}
private void handleExplainPlan(DBShowPlanQuery query, ChannelBuffer buffer, Channel channel) {
List<QueryPlan> list = query.execute();
StringBuilder builder = new StringBuilder();
for (QueryPlan plan : list) {
if (plan instanceof FullTableScan) {
builder.append("Full Collection Scan on ")
.append(plan.getCollectionAlias().getAlias());
String alias = plan.getCollectionAlias().getAlias();
if (alias != null && alias.length() > 0) {
builder.append(".");
}
builder.append(plan.getCollectionAlias().getCollectionName()).append('\n');
}
if (plan instanceof IndexRangeScan) {
builder.append("Index Scan on ").append(((IndexRangeScan) plan).getIndex().getIndexName()).append('\n');
}
}
serializeExplainPlan(builder.toString(), channel);
}
private void serializeExplainPlan(String plan, Channel channel) {
ChannelBuffer metaBuffer = ChannelBuffers.dynamicBuffer();
metaBuffer.writerIndex(5);
metaBuffer.writeInt(1);
String colName = "";
metaBuffer.writeInt(colName.getBytes().length);
metaBuffer.writeBytes(colName.getBytes());
metaBuffer.writeInt(Types.VARCHAR);
int posn = metaBuffer.writerIndex();
metaBuffer.writerIndex(0);
metaBuffer.writeInt(posn-5);
metaBuffer.writeByte(0);
metaBuffer.writerIndex(posn);
channel.write(metaBuffer);
ChannelBuffer tmpBuffer = ChannelBuffers.dynamicBuffer();
tmpBuffer.writerIndex(5);
tmpBuffer.writeByte(0);
// Serializer.getInstance().serialize(SerializerManager.STRING, new StringType(plan), tmpBuffer, null);
tmpBuffer.writeInt(plan.length());
tmpBuffer.writeBytes(plan.getBytes());
int p = tmpBuffer.readableBytes();
tmpBuffer.resetWriterIndex();
tmpBuffer.writeInt(p-5);
tmpBuffer.writeByte(0);
tmpBuffer.writerIndex(p);
tmpBuffer.resetReaderIndex();
channel.write(tmpBuffer);
tmpBuffer.clear();
tmpBuffer.writeInt(4);
tmpBuffer.writeByte((byte) 1);
tmpBuffer.writeInt(1);
channel.write(tmpBuffer);
}
private void handleShowTableQuery(ShowTableQuery q, Channel channel) {
String s = q.execute();
serializeExplainPlan(s, channel);
}
private void handleShowStoragesQuery(ShowStoragesQuery q, Channel channel) {
String s = q.execute();
serializeExplainPlan(s, channel);
}
private void handleShowIndexQuery(ShowIndexQuery q, Channel channel) {
String s = q.execute();
serializeExplainPlan(s, channel);
}
private void handleShowSchemaQuery(ShowSchemaQuery q, Channel channel) {
String s = q.execute();
serializeExplainPlan(s, channel);
}
}