package org.wonderdb.query.plan;
/*******************************************************************************
* 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.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.wonderdb.block.Block;
import org.wonderdb.block.BlockManager;
import org.wonderdb.block.IndexCompareIndexQuery;
import org.wonderdb.cache.impl.CacheEntryPinner;
import org.wonderdb.cluster.Shard;
import org.wonderdb.collection.IndexResultContent;
import org.wonderdb.core.collection.BTree;
import org.wonderdb.core.collection.ResultIterator;
import org.wonderdb.core.collection.impl.BTreeIteratorImpl;
import org.wonderdb.core.collection.impl.BaseResultIteratorImpl;
import org.wonderdb.expression.BasicExpression;
import org.wonderdb.expression.Expression;
import org.wonderdb.expression.Operand;
import org.wonderdb.expression.VariableOperand;
import org.wonderdb.query.parse.CollectionAlias;
import org.wonderdb.query.parse.StaticOperand;
import org.wonderdb.schema.SchemaMetadata;
import org.wonderdb.types.DBType;
import org.wonderdb.types.ExtendedColumn;
import org.wonderdb.types.IndexKeyType;
import org.wonderdb.types.IndexNameMeta;
import org.wonderdb.types.RecordId;
import org.wonderdb.types.TypeMetadata;
import org.wonderdb.types.record.IndexRecord;
import org.wonderdb.types.record.Record;
import org.wonderdb.types.record.TableRecord;
public class IndexRangeScan implements QueryPlan {
IndexNameMeta idx;
CollectionAlias collectionAlias;
List<BasicExpression> expList = null;
Map<CollectionAlias, Integer> executionOrder = new HashMap<CollectionAlias, Integer>();
BTree tree = null;
boolean continueOnMiss = false;
Set<CollectionAlias> dependentCollections = null;
IndexRangeScanIterator iter = null;
Set<Object> pinnedBlocks = null;
TypeMetadata meta = null;
public CollectionAlias getCollectionAlias() {
return collectionAlias;
}
public void setDependentCollections(Set<CollectionAlias> s) {
dependentCollections = s;
}
public IndexRangeScan(CollectionAlias ca, IndexNameMeta idx, Shard shard, List<BasicExpression> list, List<CollectionAlias> executionOrder, Set<Object> pinnedBlocks) {
this.idx = idx;
collectionAlias = ca;
expList = list;
tree = SchemaMetadata.getInstance().getIndex(idx.getIndexName()).getIndexTree(shard);
this.pinnedBlocks = pinnedBlocks;
meta = SchemaMetadata.getInstance().getIndexMetadata(idx);
}
public IndexNameMeta getIndex() {
return idx;
}
public Iterator<TableRecord> recordIterator(DataContext dataContext) {
throw new RuntimeException("Method not supported");
}
public ResultIterator iterator(DataContext dataContext, Shard shard, List<Integer> selectColumns, boolean writeLock) {
Shard indexShard = new Shard(idx.getIndexName());
BTree tree = idx.getIndexTree(indexShard);
IndexRangeScanIterator iter = null;
if (expList.size() != 0) {
iter = new IndexRangeScanIterator(idx, indexShard, collectionAlias, expList, dataContext, writeLock, pinnedBlocks);
} else {
continueOnMiss = true;
iter = new IndexRangeScanIterator(tree.getHead(writeLock, pinnedBlocks), shard, selectColumns, pinnedBlocks, idx.getIndexName());
}
this.iter = iter;
return iter;
}
public Block getCurrentBlock() {
if (iter == null) {
return null;
}
return iter.getCurrentBlock();
}
public Block getCurrentRecordBlock() {
if (iter == null) {
return null;
}
return iter.getCurrentRecordBlock();
}
public boolean continueOnMiss() {
return continueOnMiss;
}
public class IndexRangeScanIterator implements ResultIterator {
BaseResultIteratorImpl iter;
DataContext context = null;
boolean hasNext = true;
IndexNameMeta idx;
CollectionAlias collectionAlias = null;
List<Integer> selectColumns = null;
Shard shard = null;
String schemaObjectName = null;
IndexKeyType min = null;
IndexKeyType max = null;
boolean includeMin = false;
boolean includeMax = false;
Record currentRecord = null;
public IndexRangeScanIterator(ResultIterator iter, Shard shard, List<Integer> selectColumns, Set<Object> pinnedBlocks, String schemaObjectName) {
this.iter = (BaseResultIteratorImpl) iter;
this.selectColumns = selectColumns;
this.shard = shard;
this.schemaObjectName = schemaObjectName;
}
public IndexRangeScanIterator(IndexNameMeta idx, Shard shard, CollectionAlias ca, List<BasicExpression> expList,
DataContext context, boolean writeLock, Set<Object> pinnedBlocks) {
this.context = context;
this.idx = idx;
this.collectionAlias = ca;
List<DBType> list = new ArrayList<DBType>(idx.getColumnIdList().size());
for (int i = 0; i < idx.getColumnIdList().size(); i++) {
list.add(null);
}
min = new IndexKeyType(list, null);
list = new ArrayList<DBType>(list);
max = new IndexKeyType(list, null);
buildRange();
iter = (BTreeIteratorImpl) getIterator(shard, expList, context, writeLock);
}
private void buildRange() {
for (int i = 0; i < expList.size(); i++) {
BasicExpression exp = expList.get(i);
Operand left = exp.getLeftOperand();
Operand right = exp.getRightOperand();
DBType value = null;
if (left instanceof VariableOperand) {
VariableOperand vo = (VariableOperand) left;
if (((VariableOperand) left).getCollectionAlias().equals(collectionAlias)) {
if (idx.getColumnIdList().contains(vo.getColumnId())) {
if (right instanceof StaticOperand) {
value = right.getValue((TableRecord) null, null);
} else if (right instanceof VariableOperand){
if (idx.getColumnIdList().contains(((VariableOperand) right).getColumnId())) {
value = context.getValue(((VariableOperand) right).getCollectionAlias(), ((VariableOperand) right).getColumnId(), null);
}
}
updateMinMax((VariableOperand) left, value, exp.getOperator());
}
}
} else if (left instanceof StaticOperand) {
value = left.getValue((TableRecord) null, null);
if (right instanceof VariableOperand) {
VariableOperand vo = (VariableOperand) right;
if (vo.getCollectionAlias().equals(collectionAlias)) {
if (idx.getColumnIdList().contains(vo.getColumnId())) {
updateMinMax((VariableOperand) right, value, invertOp(exp.getOperator()));
}
}
}
}
}
}
private int invertOp(int op) {
switch(op) {
case Expression.EQ:
case Expression.LIKE:
return op;
case Expression.LE:
return Expression.GE;
case Expression.LT:
return Expression.GT;
case Expression.GE:
return Expression.LE;
case Expression.GT:
return Expression.LT;
}
return op;
}
private void updateMinMax(VariableOperand left, DBType value, int op) {
int posn = getPosn(left.getColumnId());
switch (op) {
case Expression.EQ:
includeMin = true;
includeMax = true;
DBType dt1 = min.getValue().get(posn);
if (dt1 == null || dt1.compareTo(value) > 0) {
min.getValue().set(posn, value);
}
dt1 = max.getValue().get(posn);
if (dt1 == null || dt1.compareTo(value) < 0) {
max.getValue().set(posn, value);
}
break;
case Expression.GE:
includeMin = true;
case Expression.GT:
dt1 = min.getValue().get(posn);
if (dt1 == null || dt1.compareTo(value) > 0) {
min.getValue().set(posn, value);
}
break;
case Expression.LE:
includeMax = true;
case Expression.LT:
dt1 = max.getValue().get(posn);
if (dt1 == null || dt1.compareTo(value) < 0) {
max.getValue().set(posn, value);
}
break;
}
}
private int getPosn(int colId) {
for (int i = 0; i < idx.getColumnIdList().size(); i++) {
if (colId == idx.getColumnIdList().get(i)) {
return i;
}
}
return -1;
}
@Override
public void insert (Record c) {
throw new RuntimeException("Method not supported");
}
@Override
public Record next() {
IndexRecord key = (IndexRecord) currentRecord;
if (key == null) {
return null;
}
DBType column = key.getColumn();
if (column == null) {
return null;
}
if (column instanceof ExtendedColumn) {
column = (IndexKeyType) ((ExtendedColumn) column).getValue(meta);
}
return new IndexResultContent((IndexKeyType) column, meta, pinnedBlocks);
}
public Block getCurrentBlock() {
return iter.getCurrentBlock();
}
public Block getCurrentRecordBlock() {
IndexRecord record = (IndexRecord) iter.next();
IndexKeyType key = (IndexKeyType) record.getColumn();
if (key == null) {
return null;
}
// BlockEntryPosition bep = key.getBlockEntryPosn();
RecordId recordId = key.getRecordId();
CacheEntryPinner.getInstance().pin(recordId.getPtr(), pinnedBlocks);
Block b = (Block) BlockManager.getInstance().getBlock(recordId.getPtr(), meta, pinnedBlocks);
return b;
}
public boolean hasNext() {
boolean val = iter.hasNext();
if (!val) {
return val;
}
IndexRecord record = (IndexRecord) iter.next();
currentRecord = record;
DBType column = record.getColumn();
if (column instanceof ExtendedColumn) {
column = (IndexKeyType) ((ExtendedColumn) column).getValue(meta);
}
IndexKeyType ikt = (IndexKeyType) (column instanceof ExtendedColumn ? ((ExtendedColumn) column).getValue(meta) : column);
for (int i= 0; i < min.getValue().size(); i++) {
DBType minValue = min.getValue().get(i);
DBType maxValue = max.getValue().get(i);
DBType value = ikt.getValue().get(i);
if ((minValue == null || value.compareTo(minValue) >= 0) &&
((maxValue == null || (value.compareTo(maxValue) >= 0 && includeMax)) || (value.compareTo(maxValue) < 0))) {
continue;
} else {
return false;
}
}
return true;
}
public boolean isAnyBlockEmpty() {
throw new RuntimeException("Method not supported");
}
public void remove() {
iter.remove();
}
public void lock(Block b) {
iter.lock(b);
}
public void unlock() {
iter.unlock();
}
private ResultIterator getIterator(Shard shard, List<BasicExpression> expList, DataContext context, boolean writeLock) {
Shard indexShard = new Shard(idx.getIndexName());
BTree tree = SchemaMetadata.getInstance().getIndex(idx.getIndexName()).getIndexTree(indexShard);
// ExpressionFilterIndexQuery query = new ExpressionFilterIndexQuery(expList, context, idx, collectionAlias);
TypeMetadata meta = SchemaMetadata.getInstance().getIndexMetadata(idx);
IndexCompareIndexQuery query = new IndexCompareIndexQuery(min, includeMin, meta, pinnedBlocks);
return tree.find(query, writeLock, pinnedBlocks);
}
@Override
public void unlock(boolean shouldUnpin) {
unlock();
}
@Override
public Set<Object> getPinnedSet() {
return iter.getPinnedSet();
}
@Override
public TypeMetadata getTypeMetadata() {
return iter.getTypeMetadata();
}
@Override
public Record peek() {
return null;
}
// @Override
// public String getSchemaObjectName() {
// return schemaObjectName;
// }
}
}