package org.wonderdb.query.executor;
/*******************************************************************************
* 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.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.wonderdb.block.record.manager.TableRecordManager;
import org.wonderdb.cluster.ClusterManagerFactory;
import org.wonderdb.cluster.Shard;
import org.wonderdb.expression.AndExpression;
import org.wonderdb.query.parse.DBDeleteQuery;
import org.wonderdb.query.parse.DBInsertQuery;
import org.wonderdb.query.parse.DBQuery;
import org.wonderdb.query.parse.DBUpdateQuery;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree;
import org.wonderdb.query.parser.jtree.DBSelectQueryJTree.ResultSetValue;
import org.wonderdb.query.sql.WonderDBConnection;
import org.wonderdb.query.sql.WonderDBPreparedStatement;
import org.wonderdb.server.WonderDBPropertyManager;
import org.wonderdb.thread.ThreadPoolExecutorWrapper;
import org.wonderdb.types.DBType;
import org.wonderdb.types.DoubleType;
import org.wonderdb.types.FloatType;
import org.wonderdb.types.IntType;
import org.wonderdb.types.LongType;
import org.wonderdb.types.StringType;
public class ScatterGatherQueryExecutor {
static ThreadPoolExecutorWrapper executor = new ThreadPoolExecutorWrapper(WonderDBPropertyManager.getInstance().getWriterThreadPoolCoreSize(),
WonderDBPropertyManager.getInstance().getWriterThreadPoolMaxSize(), 5,
WonderDBPropertyManager.getInstance().getWriterThreadPoolQueueSize(), "scatterGather");
DBQuery query = null;
public ScatterGatherQueryExecutor(DBQuery query) {
this.query = query;
}
public static void shutdown() {
executor.shutdown();
}
public List<List<ResultSetValue>> selectQuery() {
List<List<ResultSetValue>> resultListList = new ArrayList<List<ResultSetValue>>();
AndExpression andExp = query.getExpression();
String tableName = ((DBSelectQueryJTree) query).getFromList().get(0).getCollectionName();
List<Shard> shardIds = ClusterManagerFactory.getInstance().getClusterManager().getShards(tableName, andExp);
List<Future<List<List<ResultSetValue>>>> futures = new ArrayList<Future<List<List<ResultSetValue>>>>();
for (int i = 0; i < shardIds.size(); i++) {
Shard shard = shardIds.get(i);
Callable<List<List<ResultSetValue>>> task = null;
if (ClusterManagerFactory.getInstance().getClusterManager().isMaster(shard) || query.executeLocal()) {
// we should process
task = new LocalSelectTask(shardIds.get(i), (DBSelectQueryJTree) query);
} else {
// process with connection
task = new RemoteSelectTask((DBSelectQueryJTree) query, shard);
}
@SuppressWarnings("unchecked")
Future<List<List<ResultSetValue>>> future = (Future<List<List<ResultSetValue>>>) executor.asynchrounousExecute(task);
futures.add(future);
}
for (int i = 0; i < futures.size(); i++) {
List<List<ResultSetValue>> list;
try {
list = futures.get(i).get();
resultListList.addAll(list);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultListList;
}
public int insertQuery() {
DBInsertQuery q = (DBInsertQuery) query;
// ObjectId id = new ObjectId(ClusterManagerFactory.getInstance().getClusterManager().getMachineId(), true);
Map<Integer, DBType> m = q.getInsertMap();
// m.put("objectId", new StringType(id.toString()));
// Map<Integer, DBType> map = TableRecordManager.getInstance().convertTypes(q.getCollectionName(), m);
// Map<Integer, Column> recordMap = new HashMap<>();
// Iterator<Integer> iter = map.keySet().iterator();
// while (iter.hasNext()) {
// int key = iter.next();
// DBType dt = map.get(key);
// Column column = new Column(dt);
// recordMap.put(key, column);
// }
// TableRecord tr = new TableRecord(recordMap);
// StaticTableResultContent rc = new StaticTableResultContent(tr);
// Shard shard = ClusterManagerFactory.getInstance().getClusterManager().getShard(q.getCollectionName(), rc);
Shard shard = new Shard("");
Callable<Integer> task = null;
if (ClusterManagerFactory.getInstance().getClusterManager().isMaster(shard) || query.executeLocal()) {
// we should process
task = new LocalInsertTask(shard, m);
} else {
// process with connection
task = new RemoteInsertTask(shard);
}
@SuppressWarnings("unchecked")
Future<Integer> future = (Future<Integer>) executor.asynchrounousExecute(task);
try {
return future.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
public int deleteQuery() {
DBDeleteQuery q = (DBDeleteQuery) query;
AndExpression andExp = query.getExpression();
String tableName = q.getCollection();
List<Shard> shards = ClusterManagerFactory.getInstance().getClusterManager().getShards(tableName, andExp);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
for (int i = 0; i < shards.size(); i++) {
Shard shard = shards.get(i);
Callable<Integer> task = null;
if (ClusterManagerFactory.getInstance().getClusterManager().isMaster(shard) || query.executeLocal()) {
// we should process
task = new LocalDeleteTask(shard);
} else {
// process with connection
task = new RemoteDeleteTask(shard);
}
@SuppressWarnings("unchecked")
Future<Integer> future = (Future<Integer>) executor.asynchrounousExecute(task);
futures.add(future);
}
int count = 0;
for (int i = 0; i < futures.size(); i++) {
try {
count = count + futures.get(i).get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return count;
}
public int updateQuery() {
DBUpdateQuery q = (DBUpdateQuery) query;
List<Shard> shards = ClusterManagerFactory.getInstance().getClusterManager().getShards(q.getCollectionName(), q.getExpression());
if (shards == null || shards.size() > 1) {
throw new RuntimeException("Invalid update stmt");
}
Shard shard = shards.get(0);
Callable<Integer> task = null;
if (ClusterManagerFactory.getInstance().getClusterManager().isMaster(shard) || query.executeLocal()) {
// we should process
task = new LocalUpdateTask(q, shard);
} else {
// process with connection
task = new RemoteInsertTask(shard);
}
@SuppressWarnings("unchecked")
Future<Integer> future = (Future<Integer>) executor.asynchrounousExecute(task);
try {
return future.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
private List<List<ResultSetValue>> convert(ResultSet rs) {
List<List<ResultSetValue>> resultListList = new ArrayList<List<ResultSetValue>>();
try {
ResultSetMetaData rsMeta = rs.getMetaData();
while (rs.next()) {
List<ResultSetValue> list = new ArrayList<ResultSetValue>();
for (int i = 0; i < rsMeta.getColumnCount(); i++) {
ResultSetValue rsv = new ResultSetValue();
list.add(rsv);
int type = rsMeta.getColumnType(i);
rsv.columnName = rsMeta.getColumnName(i);
switch (type) {
case Types.VARCHAR:
String s = rs.getString(i);
if (s != null) {
rsv.value = new StringType(s);
}
break;
case Types.NUMERIC:
Long l = rs.getLong(i);
if (l != null) {
rsv.value = new LongType(l);
}
break;
case Types.DOUBLE:
Double d = rs.getDouble(i);
if (d != null) {
rsv.value = new DoubleType(d);
}
break;
case Types.INTEGER:
Integer iv = rs.getInt(i);
if (iv != null) {
rsv.value = new IntType(iv);
}
break;
case Types.FLOAT:
Float f = rs.getFloat(i);
if (f != null) {
rsv.value = new FloatType(f);
}
break;
}
}
resultListList.add(list);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return resultListList;
}
public int executeUpdate() {
return -1;
}
public class RemoteSelectTask implements Callable<List<List<ResultSetValue>>> {
Connection connection = null;
Shard shard = null;
DBSelectQueryJTree query = null;
public RemoteSelectTask(DBSelectQueryJTree query, Shard shard) {
this.shard = shard;
this.query = query;
}
@Override
public List<List<ResultSetValue>> call() throws Exception {
WonderDBConnection connection = (WonderDBConnection) ClusterManagerFactory.getInstance().getClusterManager().getMasterConnection(shard);
try {
WonderDBPreparedStatement stmt = (WonderDBPreparedStatement) connection.prepareStatement(query.getQueryString());
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
buffer.writeInt(query.getRawBuffer().capacity());
buffer.clear();
buffer.writerIndex(4);
query.getRawBuffer().clear();
query.getRawBuffer().writerIndex(query.getRawBuffer().capacity());
ChannelBuffer buf = ChannelBuffers.wrappedBuffer(buffer, query.getRawBuffer());
return convert(stmt.executeQuery(buf));
} finally {
connection.close();
}
}
}
public class LocalSelectTask implements Callable<List<List<ResultSetValue>>> {
DBSelectQueryJTree query = null;
Shard shard = null;
public LocalSelectTask(Shard shard, DBSelectQueryJTree query) {
this.query = query;
this.shard = shard;
}
@Override
public List<List<ResultSetValue>> call() throws Exception {
return query.executeLocal(shard);
}
}
public class LocalInsertTask implements Callable<Integer> {
Map<Integer, DBType> map = null;
Shard shard = null;
public LocalInsertTask(Shard shard, Map<Integer, DBType> map) {
this.map = map;
this.shard = shard;
}
@Override
public Integer call() throws Exception {
DBInsertQuery q = (DBInsertQuery) query;
return TableRecordManager.getInstance().addTableRecord(q.getCollectionName(), map, shard, false);
}
}
public class RemoteInsertTask implements Callable<Integer> {
Connection connection = null;
Shard shard = null;
public RemoteInsertTask(Shard shard) {
this.shard = shard;
}
@Override
public Integer call() throws Exception {
WonderDBConnection connection = null;
try {
connection = (WonderDBConnection) ClusterManagerFactory.getInstance().getClusterManager().getMasterConnection(shard);
WonderDBPreparedStatement stmt = (WonderDBPreparedStatement) connection.prepareStatement(query.getQueryString());
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
buffer.writeInt(query.getRawBuffer().capacity());
buffer.clear();
buffer.writerIndex(4);
query.getRawBuffer().clear();
query.getRawBuffer().writerIndex(query.getRawBuffer().capacity());
ChannelBuffer buf = ChannelBuffers.wrappedBuffer(buffer, query.getRawBuffer());
return stmt.executeUpdate(buf);
} finally {
connection.close();
}
}
}
public class LocalDeleteTask implements Callable<Integer> {
Shard shard = null;
public LocalDeleteTask(Shard shard) {
this.shard = shard;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
return ((DBDeleteQuery) query).execute(shard);
}
}
public class RemoteDeleteTask implements Callable<Integer> {
Shard shard = null;
public RemoteDeleteTask(Shard shard) {
this.shard = shard;
}
@Override
public Integer call() throws Exception {
WonderDBConnection connection = null;
try {
connection = (WonderDBConnection) ClusterManagerFactory.getInstance().getClusterManager().getMasterConnection(shard);
WonderDBPreparedStatement stmt = (WonderDBPreparedStatement) connection.prepareStatement(query.getQueryString());
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
buffer.writeInt(query.getRawBuffer().capacity());
buffer.clear();
buffer.writerIndex(4);
query.getRawBuffer().clear();
query.getRawBuffer().writerIndex(query.getRawBuffer().capacity());
ChannelBuffer buf = ChannelBuffers.wrappedBuffer(buffer, query.getRawBuffer());
return stmt.executeUpdate(buf);
} finally {
connection.close();
}
}
}
public class RemoteUpdateTask implements Callable<Integer> {
Shard shard = null;
public RemoteUpdateTask(Shard shard) {
this.shard = shard;
}
@Override
public Integer call() throws Exception {
WonderDBConnection connection = null;
try {
connection = (WonderDBConnection) ClusterManagerFactory.getInstance().getClusterManager().getMasterConnection(shard);
WonderDBPreparedStatement stmt = (WonderDBPreparedStatement) connection.prepareStatement(query.getQueryString());
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
buffer.writeInt(query.getRawBuffer().capacity());
buffer.clear();
buffer.writerIndex(4);
query.getRawBuffer().clear();
query.getRawBuffer().writerIndex(query.getRawBuffer().capacity());
ChannelBuffer buf = ChannelBuffers.wrappedBuffer(buffer, query.getRawBuffer());
return stmt.executeUpdate(buf);
} finally {
connection.close();
}
}
}
public class LocalUpdateTask implements Callable<Integer> {
Shard shard = null;
DBUpdateQuery query = null;
public LocalUpdateTask(DBUpdateQuery query, Shard shard) {
this.shard = shard;
this.query = query;
}
@Override
public Integer call() throws Exception {
return query.execute(shard);
}
}
}