blob: cceeb8a8fea26b54377fa54cd0cc8b34a2d1fb0b [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.cache.query.internal.index;
import com.gemstone.gemfire.internal.cache.CachedDeserializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheException;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.query.AmbiguousNameException;
import com.gemstone.gemfire.cache.query.FunctionDomainException;
import com.gemstone.gemfire.cache.query.IndexStatistics;
import com.gemstone.gemfire.cache.query.IndexType;
import com.gemstone.gemfire.cache.query.NameResolutionException;
import com.gemstone.gemfire.cache.query.QueryInvocationTargetException;
import com.gemstone.gemfire.cache.query.QueryService;
import com.gemstone.gemfire.cache.query.SelectResults;
import com.gemstone.gemfire.cache.query.TypeMismatchException;
import com.gemstone.gemfire.cache.query.internal.CompiledBindArgument;
import com.gemstone.gemfire.cache.query.internal.CompiledIteratorDef;
import com.gemstone.gemfire.cache.query.internal.CompiledLiteral;
import com.gemstone.gemfire.cache.query.internal.CompiledPath;
import com.gemstone.gemfire.cache.query.internal.CompiledSortCriterion;
import com.gemstone.gemfire.cache.query.internal.CompiledValue;
import com.gemstone.gemfire.cache.query.internal.CqEntry;
import com.gemstone.gemfire.cache.query.internal.ExecutionContext;
import com.gemstone.gemfire.cache.query.internal.IndexInfo;
import com.gemstone.gemfire.cache.query.internal.QRegion;
import com.gemstone.gemfire.cache.query.internal.QueryMonitor;
import com.gemstone.gemfire.cache.query.internal.QueryObserver;
import com.gemstone.gemfire.cache.query.internal.QueryObserverHolder;
import com.gemstone.gemfire.cache.query.internal.QueryUtils;
import com.gemstone.gemfire.cache.query.internal.RuntimeIterator;
import com.gemstone.gemfire.cache.query.internal.StructImpl;
import com.gemstone.gemfire.cache.query.internal.Support;
import com.gemstone.gemfire.cache.query.internal.index.IndexManager.TestHook;
import com.gemstone.gemfire.cache.query.internal.index.IndexStore.IndexStoreEntry;
import com.gemstone.gemfire.cache.query.internal.index.MemoryIndexStore.MemoryIndexStoreEntry;
import com.gemstone.gemfire.cache.query.internal.parse.OQLLexerTokenTypes;
import com.gemstone.gemfire.cache.query.internal.types.StructTypeImpl;
import com.gemstone.gemfire.cache.query.internal.types.TypeUtils;
import com.gemstone.gemfire.cache.query.types.ObjectType;
import com.gemstone.gemfire.cache.query.types.StructType;
import com.gemstone.gemfire.internal.cache.LocalRegion;
import com.gemstone.gemfire.internal.cache.RegionEntry;
import com.gemstone.gemfire.internal.cache.RegionEntryContext;
import com.gemstone.gemfire.internal.cache.VMThinRegionEntryHeap;
import com.gemstone.gemfire.internal.cache.persistence.query.CloseableIterator;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.pdx.internal.PdxString;
//@todo Extend to support the keys or entries of a region.
/**
* A CompactRangeIndex is a range index that has simple data structures to
* minimize its footprint, at the expense of doing extra work at index
* maintenance. It is selected as the index implementation when the indexed
* expression is a path expression and the from clause has only one iterator.
* This implies there is only one value in the index for each region entry.
*
* This index does not support the storage of projection attributes.
*
* Currently this implementation only supports an index on a region path.
*
* @author Eric Zoerner
* @since 6.0
*/
public class CompactRangeIndex extends AbstractIndex {
private static TestHook testHook;
protected ThreadLocal<OldKeyValuePair> oldKeyValue;
private IndexStore indexStore;
public CompactRangeIndex(String indexName, Region region, String fromClause,
String indexedExpression, String projectionAttributes,
String origFromClause, String origIndexExpr, String[] definitions,
IndexStatistics stats) {
super(indexName, region, fromClause, indexedExpression,
projectionAttributes, origFromClause, origIndexExpr, definitions, stats);
if (IndexManager.IS_TEST_LDM) {
indexStore = new MapIndexStore(((LocalRegion)region).getIndexMap(indexName, indexedExpression, origFromClause), region);
}
else {
indexStore = new MemoryIndexStore(region, internalIndexStats);
}
}
public IndexStore getIndexStorage() {
return indexStore;
}
/**
* Get the index type
*
* @return the type of index
*/
public IndexType getType() {
return IndexType.FUNCTIONAL;
}
@Override
protected boolean isCompactRangeIndex() {
return true;
}
@Override
public void initializeIndex(boolean loadEntries) throws IMQException {
long startTime = System.nanoTime();
this.evaluator.initializeIndex(loadEntries);
this.internalIndexStats.incNumUpdates(((IMQEvaluator) this.evaluator)
.getTotalEntriesUpdated());
long endTime = System.nanoTime();
this.internalIndexStats.incUpdateTime(endTime - startTime);
}
void addMapping(RegionEntry entry) throws IMQException {
this.evaluator.evaluate(entry, true);
this.internalIndexStats.incNumUpdates();
}
/**
* @param opCode
* one of OTHER_OP, BEFORE_UPDATE_OP, AFTER_UPDATE_OP.
*/
void removeMapping(RegionEntry entry, int opCode) throws IMQException {
if (opCode == BEFORE_UPDATE_OP) {
// Either take key from reverse map OR evaluate it using IMQEvaluator.
if (!IndexManager.isObjectModificationInplace()) {
// It will always contain 1 element only, for this thread.
if (oldKeyValue == null) {
oldKeyValue = new ThreadLocal<OldKeyValuePair>();
}
oldKeyValue.set(new OldKeyValuePair());
this.evaluator.evaluate(entry, false);
}
} else {
// Need to reset the thread-local map as many puts and destroys might
// happen in same thread.
if (oldKeyValue != null) {
oldKeyValue.remove();
}
this.evaluator.evaluate(entry, false);
this.internalIndexStats.incNumUpdates();
}
}
void removeMapping(Object key, RegionEntry entry) throws IMQException {
indexStore.removeMapping(key, entry);
}
public boolean clear() {
return indexStore.clear();
}
public List queryEquijoinCondition(IndexProtocol indx,
ExecutionContext context) throws TypeMismatchException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException {
// get a read lock when doing a lookup
long start = updateIndexUseStats();
((AbstractIndex) indx).updateIndexUseStats();
List data = new ArrayList();
CloseableIterator<IndexStoreEntry> outer = null;
Iterator inner = null;
try {
// We will iterate over each of the index Map to obtain the keys
outer = indexStore.iterator(null);
if (indx instanceof CompactRangeIndex) {
inner = ((CompactRangeIndex) indx).getIndexStorage().iterator(null);
} else {
inner = ((RangeIndex) indx).getValueToEntriesMap().entrySet()
.iterator();
}
IndexStoreEntry outerEntry = null;
Object innerEntry = null;
Object outerKey = null;
Object innerKey = null;
// boolean incrementOuter = true;
boolean incrementInner = true;
outer: while (outer.hasNext()) {
// if (incrementOuter) {
outerEntry = outer.next();
// }
outerKey = outerEntry.getDeserializedKey();
inner: while (!incrementInner || inner.hasNext()) {
if (incrementInner) {
innerEntry = inner.next();
if (innerEntry instanceof IndexStoreEntry) {
innerKey = ((IndexStoreEntry) innerEntry).getDeserializedKey();
} else {
innerKey = ((Map.Entry) innerEntry).getKey();
}
}
int compare = ((Comparable) outerKey).compareTo(innerKey);
if (compare == 0) {
Object innerValue = null;
CloseableIterator<IndexStoreEntry> iter = null;
try {
if (innerEntry instanceof IndexStoreEntry) {
innerValue = ((CompactRangeIndex) indx).getIndexStorage().get(
outerKey);
} else {
innerValue = ((Map.Entry) innerEntry).getValue();
}
iter = indexStore.get(outerKey);
populateListForEquiJoin(data, iter, innerValue, context, innerKey);
} finally {
if (iter != null) {
iter.close();
}
if (innerValue != null && innerValue instanceof CloseableIterator) {
((CloseableIterator<IndexStoreEntry>) innerValue).close();
}
}
incrementInner = true;
continue outer;
} else if (compare < 0) {
// Asif :The outer key is smaller than the inner key. That means
// that we need
// to increment the outer loop without moving inner loop.
// incrementOuter = true;
incrementInner = false;
continue outer;
} else {
// Asif : The outer key is greater than inner key , so increment the
// inner loop without changing outer
incrementInner = true;
}
}
break;
}
return data;
} finally {
((AbstractIndex) indx).updateIndexUseEndStats(start);
updateIndexUseEndStats(start);
if (outer != null) {
outer.close();
}
if (inner != null && indx instanceof CompactRangeIndex) {
((CloseableIterator<IndexStoreEntry>) inner).close();
}
}
}
/**
* This evaluates the left and right side of a EQUI-JOIN where condition for
* which this Index was used. Like, if condition is "p.ID = e.ID",
* {@link IndexInfo} will contain Left as p.ID, Right as e.ID and operator as
* TOK_EQ. This method will evaluate p.ID OR e.ID based on if it is inner or
* outer RegionEntry, and verify the p.ID = e.ID.
*
* This method is called only for Memory indexstore
*
* @param entry
* @param context
* @param indexInfo
* @param keyVal
* @return true if entry value and index value are consistent.
* @throws FunctionDomainException
* @throws TypeMismatchException
* @throws NameResolutionException
* @throws QueryInvocationTargetException
*/
protected boolean verifyInnerAndOuterEntryValues(IndexStoreEntry entry,
ExecutionContext context, IndexInfo indexInfo, Object keyVal)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException {
// Verify index key in value only for memory index store
CompactRangeIndex index = (CompactRangeIndex) indexInfo._getIndex();
RuntimeIterator runtimeItr = index.getRuntimeIteratorForThisIndex(context, indexInfo);
if (runtimeItr != null) {
runtimeItr.setCurrent(((MemoryIndexStoreEntry) entry)
.getDeserializedValue());
}
return evaluateEntry(indexInfo, context, keyVal);
}
public int getSizeEstimate(Object key, int operator, int matchLevel)
throws TypeMismatchException {
// Get approx size;
int size = 0;
if (key == null) {
key = IndexManager.NULL;
}
long start = updateIndexUseStats(false);
try {
switch (operator) {
case OQLLexerTokenTypes.TOK_EQ: {
key = TypeUtils.indexKeyFor(key);
key = getPdxStringForIndexedPdxKeys(key);
size = indexStore.size(key);
break;
}
case OQLLexerTokenTypes.TOK_NE_ALT:
case OQLLexerTokenTypes.TOK_NE:
size = this.region.size();
key = TypeUtils.indexKeyFor(key);
key = getPdxStringForIndexedPdxKeys(key);
size -= indexStore.size(key);
break;
case OQLLexerTokenTypes.TOK_LE:
case OQLLexerTokenTypes.TOK_LT:
if (matchLevel <= 0 && (key instanceof Number)) {
int totalSize = indexStore.size();
if (CompactRangeIndex.testHook != null) {
CompactRangeIndex.testHook.hook(1);
}
if (totalSize > 1) {
Number keyAsNum = (Number) key;
int x = 0;
IndexStoreEntry firstEntry = null;
CloseableIterator<IndexStoreEntry> iter1 = null;
CloseableIterator<IndexStoreEntry> iter2 = null;
try {
iter1 = indexStore.iterator(null);
if (iter1.hasNext()) {
firstEntry = iter1.next();
IndexStoreEntry lastEntry = null;
iter2 = indexStore.descendingIterator(null);
if (iter2.hasNext()) {
lastEntry = iter2.next();
}
if (firstEntry != null && lastEntry != null) {
Number first = (Number) firstEntry.getDeserializedKey();
Number last = (Number) lastEntry.getDeserializedKey();
if (first.doubleValue() != last.doubleValue()) {
// Shobhit: Now without ReadLoack on index we can end up with 0
// in denominator if the numbers are floating-point and
// truncated with conversion to long, and the first and last
// truncate to the same long, so safest calculation is to
// convert to doubles.
x = (int) (((keyAsNum.doubleValue() - first.doubleValue()) * totalSize) / (last
.doubleValue() - first.doubleValue()));
}
}
if (x < 0) {
x = 0;
}
size = x;
}
} finally {
if (iter1 != null) {
iter1.close();
}
if (iter2 != null) {
iter1.close();
}
}
} else {
// not attempting to differentiate between LT & LE
size = indexStore.size(key) > 0 ? 1 : 0;
}
} else {
size = Integer.MAX_VALUE;
}
break;
case OQLLexerTokenTypes.TOK_GE:
case OQLLexerTokenTypes.TOK_GT:
if (matchLevel <= 0 && (key instanceof Number)) {
int totalSize = indexStore.size();
if (CompactRangeIndex.testHook != null) {
CompactRangeIndex.testHook.hook(2);
}
if (totalSize > 1) {
Number keyAsNum = (Number) key;
int x = 0;
IndexStoreEntry firstEntry = null;
CloseableIterator<IndexStoreEntry> iter1 = null;
CloseableIterator<IndexStoreEntry> iter2 = null;
try {
iter1 = indexStore.iterator(null);
if (iter1.hasNext()) {
firstEntry = iter1.next();
}
IndexStoreEntry lastEntry = null;
iter2 = indexStore.descendingIterator(null);
if (iter2.hasNext()) {
lastEntry = iter2.next();
}
if (firstEntry != null && lastEntry != null) {
Number first = (Number) firstEntry.getDeserializedKey();
Number last = (Number) lastEntry.getDeserializedKey();
if (first.doubleValue() != last.doubleValue()) {
// Shobhit: Now without ReadLoack on index we can end up with 0
// in denominator if the numbers are floating-point and
// truncated with conversion to long, and the first and last
// truncate to the same long, so safest calculation is to
// convert to doubles.
x = (int) (((last.doubleValue() - keyAsNum.doubleValue()) * totalSize) / (last
.doubleValue() - first.doubleValue()));
}
}
if (x < 0) {
x = 0;
}
size = x;
} finally {
if (iter1 != null) {
iter1.close();
}
}
} else {
// not attempting to differentiate between GT & GE
size = indexStore.size(key) > 0 ? 1 : 0;
}
} else {
size = Integer.MAX_VALUE;
}
break;
}
} finally {
updateIndexUseEndStats(start, false);
}
return size;
}
/** Method called while appropriate lock held on index */
private void lockedQueryPrivate(Object key, int operator, Collection results,
CompiledValue iterOps, RuntimeIterator runtimeItr,
ExecutionContext context, Set keysToRemove, List projAttrib,
SelectResults intermediateResults, boolean isIntersection)
throws TypeMismatchException, FunctionDomainException,
NameResolutionException, QueryInvocationTargetException {
if (keysToRemove == null) {
keysToRemove = new HashSet(0);
}
int limit = -1;
Boolean applyLimit = (Boolean) context
.cacheGet(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX);
if (applyLimit != null && applyLimit.booleanValue()) {
limit = ((Integer) context.cacheGet(CompiledValue.RESULT_LIMIT))
.intValue();
}
Boolean orderByClause = (Boolean) context
.cacheGet(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX);
boolean applyOrderBy = false;
boolean asc = true;
List orderByAttrs = null;
boolean multiColOrderBy = false;
if (orderByClause != null && orderByClause.booleanValue()) {
orderByAttrs = (List) context.cacheGet(CompiledValue.ORDERBY_ATTRIB);
CompiledSortCriterion csc = (CompiledSortCriterion) orderByAttrs.get(0);
asc = !csc.getCriterion();
applyOrderBy = true;
multiColOrderBy = orderByAttrs.size() > 1;
}
if (isEmpty()) {
return;
}
key = getPdxStringForIndexedPdxKeys(key);
evaluate(key, operator, results, iterOps, runtimeItr, context,
keysToRemove, projAttrib, intermediateResults, isIntersection, limit,
applyOrderBy, orderByAttrs);
}
/** Method called while appropriate lock held on index */
void lockedQuery(Object lowerBoundKey, int lowerBoundOperator,
Object upperBoundKey, int upperBoundOperator, Collection results,
Set keysToRemove, ExecutionContext context) throws TypeMismatchException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException {
lowerBoundKey = TypeUtils.indexKeyFor(lowerBoundKey);
upperBoundKey = TypeUtils.indexKeyFor(upperBoundKey);
boolean lowerBoundInclusive = lowerBoundOperator == OQLLexerTokenTypes.TOK_GE;
boolean upperBoundInclusive = upperBoundOperator == OQLLexerTokenTypes.TOK_LE;
// LowerBound Key inclusive , Upper bound key exclusive
int limit = -1;
Boolean applyLimit = (Boolean) context
.cacheGet(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX);
if (applyLimit != null && applyLimit.booleanValue()) {
limit = ((Integer) context.cacheGet(CompiledValue.RESULT_LIMIT))
.intValue();
}
Boolean orderByClause = (Boolean) context
.cacheGet(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX);
List orderByAttrs = null;
boolean asc = true;
boolean multiColOrderBy = false;
if (orderByClause != null && orderByClause.booleanValue()) {
orderByAttrs = (List) context.cacheGet(CompiledValue.ORDERBY_ATTRIB);
CompiledSortCriterion csc = (CompiledSortCriterion) orderByAttrs.get(0);
asc = !csc.getCriterion();
multiColOrderBy = orderByAttrs.size() > 1;
}
// return if the index map is still empty at this stage
if (isEmpty()) {
return;
}
lowerBoundKey = getPdxStringForIndexedPdxKeys(lowerBoundKey);
upperBoundKey = getPdxStringForIndexedPdxKeys(upperBoundKey);
if (keysToRemove == null) {
keysToRemove = new HashSet();
}
CloseableIterator<IndexStoreEntry> iterator = null;
try {
if (asc) {
iterator = indexStore.iterator(lowerBoundKey, lowerBoundInclusive,
upperBoundKey, upperBoundInclusive, keysToRemove);
}
else {
iterator = indexStore.descendingIterator(lowerBoundKey,
lowerBoundInclusive, upperBoundKey, upperBoundInclusive,
keysToRemove);
}
addToResultsFromEntries(lowerBoundKey, upperBoundKey, lowerBoundOperator, upperBoundOperator, iterator, results, null, null, context, null,
null, true, multiColOrderBy ? -1 : limit);
} finally {
if (iterator != null) {
iterator.close();
}
}
}
private void evaluate(Object key, int operator, Collection results,
CompiledValue iterOps, RuntimeIterator runtimeItr,
ExecutionContext context, Set keysToRemove, List projAttrib,
SelectResults intermediateResults, boolean isIntersection, int limit,
boolean applyOrderBy, List orderByAttribs) throws TypeMismatchException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException {
boolean multiColOrderBy = false;
if (keysToRemove == null) {
keysToRemove = new HashSet(0);
}
key = TypeUtils.indexKeyFor(key);
if (key == null) {
key = IndexManager.NULL;
}
boolean asc = true;
if (applyOrderBy) {
CompiledSortCriterion csc = (CompiledSortCriterion) orderByAttribs.get(0);
asc = !csc.getCriterion();
multiColOrderBy = orderByAttribs.size() > 1;
}
CloseableIterator<IndexStoreEntry> iterator = null;
try {
switch (operator) {
case OQLLexerTokenTypes.TOK_EQ:
assert keysToRemove.isEmpty();
iterator = indexStore.get(key);
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
break;
case OQLLexerTokenTypes.TOK_LT: {
if (asc) {
iterator = indexStore.iterator(null, true, key, false, keysToRemove);
} else {
iterator = indexStore.descendingIterator(null, true, key, false,
keysToRemove);
}
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
}
break;
case OQLLexerTokenTypes.TOK_LE: {
if (asc) {
iterator = indexStore.iterator(null, true, key, true, keysToRemove);
} else {
iterator = indexStore.descendingIterator(null, true, key, true,
keysToRemove);
}
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
}
break;
case OQLLexerTokenTypes.TOK_GT: {
if (asc) {
iterator = indexStore.iterator(key, false, keysToRemove);
} else {
iterator = indexStore.descendingIterator(key, false, keysToRemove);
}
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
}
break;
case OQLLexerTokenTypes.TOK_GE: {
if (asc) {
iterator = indexStore.iterator(key, true, keysToRemove);
} else {
iterator = indexStore.descendingIterator(key, true, keysToRemove);
}
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
}
break;
case OQLLexerTokenTypes.TOK_NE_ALT:
case OQLLexerTokenTypes.TOK_NE: {
keysToRemove.add(key);
if (asc) {
iterator = indexStore.iterator(keysToRemove);
} else {
iterator = indexStore.descendingIterator(keysToRemove);
}
addToResultsFromEntries(key, operator, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
//If the key is not null, then add the nulls to the results as this is a not equals query
if (!IndexManager.NULL.equals(key)) {
//we pass in the operator TOK_EQ because we want to add results where the key is equal to NULL
addToResultsFromEntries(IndexManager.NULL, OQLLexerTokenTypes.TOK_EQ, indexStore.get(IndexManager.NULL), results,
iterOps, runtimeItr, context, projAttrib, intermediateResults,
isIntersection, multiColOrderBy ? -1 : limit);
}
//If the key is not undefined, then add the undefineds to the results as this is a not equals query
if (!QueryService.UNDEFINED.equals(key)) {
//we pass in the operator TOK_EQ because we want to add results where the key is equal to UNDEFINED
addToResultsFromEntries(QueryService.UNDEFINED, OQLLexerTokenTypes.TOK_EQ, indexStore.get(QueryService.UNDEFINED),
results, iterOps, runtimeItr, context, projAttrib,
intermediateResults, isIntersection, multiColOrderBy ? -1 : limit);
}
}
break;
default:
throw new AssertionError("Operator = " + operator);
} // end switch
} catch (ClassCastException ex) {
if (operator == OQLLexerTokenTypes.TOK_EQ) { // result is empty set
return;
} else if (operator == OQLLexerTokenTypes.TOK_NE
|| operator == OQLLexerTokenTypes.TOK_NE_ALT) { // put all in result
keysToRemove.add(key);
try {
if (asc) {
iterator = indexStore.iterator(keysToRemove);
}
else {
iterator = indexStore.descendingIterator(keysToRemove);
}
addToResultsFromEntries(key, OQLLexerTokenTypes.TOK_NE, iterator, results, iterOps, runtimeItr,
context, projAttrib, intermediateResults, isIntersection,
multiColOrderBy ? -1 : limit);
} finally {
if (iterator != null) {
iterator.close();
}
}
} else { // otherwise throw exception
throw new TypeMismatchException("", ex);
}
} finally {
if (iterator != null) {
iterator.close();
}
}
}
@Override
void instantiateEvaluator(IndexCreationHelper ich) {
this.evaluator = new IMQEvaluator(ich);
}
//Only used by CompactMapRangeIndex. This is due to the way the index initialization happens
//first we use the IMQEvaluator for CompactMapRangeIndex
//Each index in CMRI is a CRI that has the CRI.IMQ and not AbstractIndex.IMQ
//So instead we create create the evaluator
//because we are not doing index init as usual (each value is just put directly?)
//we must set the result type to match
void instantiateEvaluator(IndexCreationHelper ich, ObjectType objectType) {
instantiateEvaluator(ich);
((IMQEvaluator)this.evaluator).indexResultSetType = objectType;
}
public ObjectType getResultSetType() {
return this.evaluator.getIndexResultSetType();
}
/*
*
* @param lowerBoundKey the index key to match on
* @param lowerBoundOperator the operator to use to determine a match
*/
private void addToResultsFromEntries(Object lowerBoundKey,
int lowerBoundOperator,
CloseableIterator<IndexStoreEntry> entriesIter, Collection result,
CompiledValue iterOps, RuntimeIterator runtimeItr,
ExecutionContext context, List projAttrib,
SelectResults intermediateResults, boolean isIntersection, int limit)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException {
addToResultsFromEntries(lowerBoundKey, null, lowerBoundOperator, -1
,entriesIter, result, iterOps, runtimeItr, context, projAttrib
,intermediateResults, isIntersection, limit);
}
/*
*
* @param lowerBoundKey the index key to match on for a lower bound on a ranged query, otherwise the key to match on
* @param upperBoundKey the index key to match on for an upper bound on a ranged query, otherwise null
* @param lowerBoundOperator the operator to use to determine a match against the lower bound
* @param upperBoundOperator the operator to use to determine a match against the upper bound
*/
private void addToResultsFromEntries(Object lowerBoundKey, Object upperBoundKey,
int lowerBoundOperator, int upperBoundOperator,
CloseableIterator<IndexStoreEntry> entriesIter, Collection result,
CompiledValue iterOps, RuntimeIterator runtimeItr,
ExecutionContext context, List projAttrib,
SelectResults intermediateResults, boolean isIntersection, int limit)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException {
QueryObserver observer = QueryObserverHolder.getInstance();
boolean limitApplied = false;
if (entriesIter == null || (limitApplied =verifyLimit(result, limit, context))) {
if(limitApplied) {
if(observer != null) {
observer.limitAppliedAtIndexLevel(this, limit, result);
}
}
return;
}
Set seenKey = null;
if (IndexManager.IS_TEST_EXPANSION) {
seenKey = new HashSet();
}
while (entriesIter.hasNext()) {
try {
if (IndexManager.testHook != null) {
if (this.region.getCache().getLogger().fineEnabled()) {
this.region
.getCache()
.getLogger()
.fine(
"IndexManager TestHook is set in addToResultsFromEntries.");
}
IndexManager.testHook.hook(11);
}
IndexStoreEntry indexEntry = null;
try {
indexEntry = entriesIter.next();
} catch (NoSuchElementException ex) {
// We are done with all the elements in array.
// Continue from while.
continue;
}
Object value = indexEntry.getDeserializedValue();
if (IndexManager.IS_TEST_EXPANSION) {
Object rk = indexEntry.getDeserializedRegionKey();
if (seenKey.contains(rk)) {
continue;
}
seenKey.add(rk);
// Some code that we might be able to use to optimize skipping the
// expansion of a value if no expansion is needed
// if
// (((CompactRangeIndex.IMQEvaluator)evaluator).getInitContext().getCurrentIterators().size()
// == 1) {
// boolean structType = (evaluator.getIndexResultSetType() instanceof
// StructType);
// if (!structType) {
// boolean ok = true;
// if (indexEntry.isUpdateInProgress()) {
// IndexInfo indexInfo =
// (IndexInfo)context.cacheGet(CompiledValue.INDEX_INFO);
// if (runtimeItr == null) {
// runtimeItr = getRuntimeIteratorForThisIndex(context, indexInfo);
// }
// runtimeItr.setCurrent(value);
// // Verify index key in region entry value.
// ok = evaluateEntry((IndexInfo) indexInfo, context, null);
// }
// if (runtimeItr != null) {
// runtimeItr.setCurrent(value);
// }
// if (ok && runtimeItr != null && iterOps != null) {
// ok = QueryUtils.applyCondition(iterOps, context);
// }
// if (ok) {
// if (context != null && context.isCqQueryContext()) {
// result.add(new CqEntry(indexEntry.getDeserializedRegionKey(),
// value));
// } else {
// applyProjection(projAttrib, context, result, value,
// intermediateResults, isIntersection);
// }
// if (verifyLimit(result, limit, context)) {
// observer.limitAppliedAtIndexLevel(this, limit, result);
// return;
// }
// }
// continue;
// }
// }
//
List expandedResults = expandValue(context, lowerBoundKey,
upperBoundKey, lowerBoundOperator, upperBoundOperator, value);
Iterator iterator = ((Collection) expandedResults).iterator();
while (iterator.hasNext()) {
value = iterator.next();
if (value != null) {
boolean ok = true;
// We should not need to call the commented out code if expansion
// is occuring as we already reevaluate the index key per value
// if (indexEntry.isUpdateInProgress()) {
// IndexInfo indexInfo =
// (IndexInfo)context.cacheGet(CompiledValue.INDEX_INFO);
// if (runtimeItr == null) {
// runtimeItr = getRuntimeIteratorForThisIndex(context,
// indexInfo);
// }
// runtimeItr.setCurrent(value);
// // Verify index key in region entry value.
// ok = evaluateEntry((IndexInfo) indexInfo, context, null);
// }
if (runtimeItr != null) {
runtimeItr.setCurrent(value);
}
if (ok && runtimeItr != null && iterOps != null) {
ok = QueryUtils.applyCondition(iterOps, context);
}
if (ok) {
if (context != null && context.isCqQueryContext()) {
result.add(new CqEntry(indexEntry.getDeserializedRegionKey(),
value));
} else {
applyProjection(projAttrib, context, result, value,
intermediateResults, isIntersection);
}
if (verifyLimit(result, limit, context)) {
observer.limitAppliedAtIndexLevel(this, limit, result);
return;
}
}
}
}
} else {
if (value != null) {
boolean ok = true;
if (indexEntry.isUpdateInProgress()) {
IndexInfo indexInfo = (IndexInfo) context
.cacheGet(CompiledValue.INDEX_INFO);
if (runtimeItr == null) {
runtimeItr = getRuntimeIteratorForThisIndex(context, indexInfo);
if (runtimeItr == null) {
// could not match index with iterator
throw new QueryInvocationTargetException(
"Query alias's must be used consistently");
}
}
runtimeItr.setCurrent(value);
// Verify index key in region entry value.
ok = evaluateEntry((IndexInfo) indexInfo, context, null);
}
if (runtimeItr != null) {
runtimeItr.setCurrent(value);
}
if (ok && runtimeItr != null && iterOps != null) {
ok = QueryUtils.applyCondition(iterOps, context);
}
if (ok) {
if (context != null && context.isCqQueryContext()) {
result.add(new CqEntry(indexEntry.getDeserializedRegionKey(),
value));
} else {
applyProjection(projAttrib, context, result, value,
intermediateResults, isIntersection);
}
if (verifyLimit(result, limit, context)) {
observer.limitAppliedAtIndexLevel(this, limit, result);
return;
}
}
}
}
} catch (ClassCastException e) {
}
}
}
public List expandValue(ExecutionContext context, Object lowerBoundKey, Object upperBoundKey, int lowerBoundOperator, int upperBoundOperator, Object value) {
try {
List expandedResults = new ArrayList();
this.evaluator.expansion(expandedResults, lowerBoundKey, upperBoundKey, lowerBoundOperator, upperBoundOperator, value);
return expandedResults;
}
catch (IMQException e) {
e.printStackTrace();
throw new CacheException(e){};
}
}
/**
* This evaluates the left and right side of a where condition for which this
* Index was used. Like, if condition is "ID > 1", {@link IndexInfo} will
* contain Left as ID, Right as '1' and operator as TOK_GT. This method will
* evaluate ID from region entry value and verify the ID > 1.
*
* Note: IndexInfo is created for each query separately based on the condition
* being evaluated using the Index.
*
* @param indexInfo
* @param context
* @param keyVal
* @return true if RegionEntry value satisfies the where condition (contained
* in IndexInfo).
* @throws FunctionDomainException
* @throws TypeMismatchException
* @throws NameResolutionException
* @throws QueryInvocationTargetException
*/
protected boolean evaluateEntry(IndexInfo indexInfo,
ExecutionContext context, Object keyVal) throws FunctionDomainException,
TypeMismatchException, NameResolutionException,
QueryInvocationTargetException {
CompiledValue path = ((IndexInfo) indexInfo)._path();
Object left = path.evaluate(context);
CompiledValue key = ((IndexInfo) indexInfo)._key();
Object right = null;
// For CompiledUndefined indexInfo has null key.
if (keyVal == null && key == null) {
if (left == QueryService.UNDEFINED) {
return true;
} else {
return false;
}
}
if (key != null) {
right = key.evaluate(context);
} else {
right = keyVal;
}
int operator = indexInfo._operator();
if (left == null && right == null) {
return Boolean.TRUE;
} else {
if (left instanceof PdxString) {
if (right instanceof String) {
switch (key.getType()) {
case CompiledValue.LITERAL:
right = ((CompiledLiteral) key).getSavedPdxString();
break;
case OQLLexerTokenTypes.QUERY_PARAM:
right = ((CompiledBindArgument) key).getSavedPdxString(context);
break;
case CompiledValue.FUNCTION:
case CompiledValue.PATH:
right = new PdxString((String) right);
}
}
}
Object result = TypeUtils.compare(left, right, operator);
// result is Undefined if either left or right is Undefined or
// either of them is null and operator is other than == or !=
if (result == QueryService.UNDEFINED){
// Undefined is added to results for != conditions only
if (operator != OQLLexerTokenTypes.TOK_NE
|| operator != OQLLexerTokenTypes.TOK_NE_ALT) {
return Boolean.TRUE;
} else{
return Boolean.FALSE;
}
} else {
return ((Boolean) result).booleanValue();
}
}
}
void recreateIndexData() throws IMQException {
indexStore.clear();
int numKeys = (int) this.internalIndexStats.getNumberOfKeys();
if(numKeys > 0){
this.internalIndexStats.incNumKeys(-numKeys);
}
int numValues = (int) this.internalIndexStats.getNumberOfValues();
if(numValues > 0) {
this.internalIndexStats.incNumValues(-numValues);
}
int updates = (int) this.internalIndexStats.getNumUpdates();
if(updates > 0) {
this.internalIndexStats.incNumUpdates(updates);
}
this.initializeIndex(true);
}
public String dump() {
return this.indexStore.printAll();
}
protected InternalIndexStatistics createStats(String indexName) {
return new RangeIndexStatistics(indexName);
}
class RangeIndexStatistics extends InternalIndexStatistics {
private IndexStats vsdStats;
public RangeIndexStatistics(String indexName) {
this.vsdStats = new IndexStats(getRegion().getCache()
.getDistributedSystem(), indexName);
}
/**
* Return the total number of times this index has been updated
*/
public long getNumUpdates() {
return this.vsdStats.getNumUpdates();
}
public void incNumValues(int delta) {
this.vsdStats.incNumValues(delta);
}
public void incNumUpdates() {
this.vsdStats.incNumUpdates();
}
public void incNumUpdates(int delta) {
this.vsdStats.incNumUpdates(delta);
}
public void updateNumKeys(long numKeys) {
this.vsdStats.updateNumKeys(numKeys);
}
public void incNumKeys(long numKeys) {
this.vsdStats.incNumKeys(numKeys);
}
public void incUpdateTime(long delta) {
this.vsdStats.incUpdateTime(delta);
}
public void incUpdatesInProgress(int delta) {
this.vsdStats.incUpdatesInProgress(delta);
}
public void incNumUses() {
this.vsdStats.incNumUses();
}
public void incUseTime(long delta) {
this.vsdStats.incUseTime(delta);
}
public void incUsesInProgress(int delta) {
this.vsdStats.incUsesInProgress(delta);
}
public void incReadLockCount(int delta) {
this.vsdStats.incReadLockCount(delta);
}
/**
* Returns the total amount of time (in nanoseconds) spent updating this
* index.
*/
public long getTotalUpdateTime() {
return this.vsdStats.getTotalUpdateTime();
}
/**
* Returns the total number of times this index has been accessed by a
* query.
*/
public long getTotalUses() {
return this.vsdStats.getTotalUses();
}
/**
* Returns the number of keys in this index.
*/
public long getNumberOfKeys() {
return this.vsdStats.getNumberOfKeys();
}
/**
* Returns the number of values in this index.
*/
public long getNumberOfValues() {
return this.vsdStats.getNumberOfValues();
}
/**
* Return the number of values for the specified key in this index.
*/
public long getNumberOfValues(Object key) {
return indexStore.size(key);
}
/**
* Return the number of read locks taken on this index
*/
public int getReadLockCount() {
return this.vsdStats.getReadLockCount();
}
public void close() {
this.vsdStats.close();
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("No Keys = ").append(getNumberOfKeys()).append("\n");
sb.append("No Values = ").append(getNumberOfValues()).append("\n");
sb.append("No Uses = ").append(getTotalUses()).append("\n");
sb.append("No Updates = ").append(getNumUpdates()).append("\n");
sb.append("Total Update time = ").append(getTotalUpdateTime())
.append("\n");
return sb.toString();
}
}
/**
*
* @author vaibhav
* @author Asif
*/
class IMQEvaluator implements IndexedExpressionEvaluator {
private Cache cache;
private List fromIterators = null;
private CompiledValue indexedExpr = null;
final private String[] canonicalIterNames;
private ObjectType indexResultSetType = null;
private Region rgn = null;
private Map dependencyGraph = null;
/*
* Asif : The boolean if true indicates that the 0th iterator is on entries
* . If the 0th iterator is on collection of Region.Entry objects, then the
* RegionEntry object used in Index data objects is obtained directly from
* its corresponding Region.Entry object. However if the 0th iterator is not
* on entries then the boolean is false. In this case the additional
* projection attribute gives us the original value of the iterator while
* the Region.Entry object is obtained from 0th iterator. It is possible to
* have index being created on a Region Entry itself , instead of a Region.
* A Map operator( Compiled Index Operator) used with Region enables, us to
* create such indexes. In such case the 0th iterator, even if it represents
* a collection of Objects which are not Region.Entry objects, still the
* boolean remains true, as the Entry object can be easily obtained from the
* 0th iterator. In this case, the additional projection attribute s not
* null as it is used to evaluate the Entry object from the 0th iterator.
*/
private boolean isFirstItrOnEntry = false;
// Asif: List of modified iterators, not null only when the boolean
// isFirstItrOnEntry is false.
private List indexInitIterators = null;
// Asif : The additional Projection attribute representing the value of the
// original 0th iterator. If the isFirstItrOnEntry is false, then it is not
// null. However if the isFirstItrOnEntry is true and this attribute is not
// null, this indicates that the 0th iterator is derived using an individual
// entry thru Map operator on the Region.
private CompiledValue additionalProj = null;
// Asif : This is not null iff the boolean isFirstItrOnEntry is false.
private CompiledValue modifiedIndexExpr = null;
private ObjectType addnlProjType = null;
private int initEntriesUpdated = 0;
private boolean hasInitOccuredOnce = false;
private boolean hasIndxUpdateOccuredOnce = false;
private ExecutionContext initContext = null;
private int iteratorSize = -1;
/** Creates a new instance of IMQEvaluator */
IMQEvaluator(IndexCreationHelper helper) {
this.cache = helper.getCache();
this.fromIterators = helper.getIterators();
this.indexedExpr = helper.getCompiledIndexedExpression();
this.canonicalIterNames = ((FunctionalIndexCreationHelper) helper).canonicalizedIteratorNames;
this.rgn = helper.getRegion();
// Asif : The modified iterators for optmizing Index cxreation
isFirstItrOnEntry = ((FunctionalIndexCreationHelper) helper).isFirstIteratorRegionEntry;
additionalProj = ((FunctionalIndexCreationHelper) helper).additionalProj;
Object params1[] = { new QRegion(rgn, false) };
initContext = new ExecutionContext(params1, cache);
if (isFirstItrOnEntry) {
this.indexInitIterators = this.fromIterators;
} else {
this.indexInitIterators = ((FunctionalIndexCreationHelper) helper).indexInitIterators;
modifiedIndexExpr = ((FunctionalIndexCreationHelper) helper).modifiedIndexExpr;
addnlProjType = ((FunctionalIndexCreationHelper) helper).addnlProjType;
}
this.iteratorSize = this.indexInitIterators.size();
if (this.additionalProj instanceof CompiledPath) {
String tailId = ((CompiledPath) this.additionalProj).getTailID();
if (tailId.equals("key")) {
// index on keys
indexOnRegionKeys = true;
indexStore.setIndexOnRegionKeys(true);
} else if (!isFirstItrOnEntry) {
// its not entries, its on value.
indexOnValues = true;
indexStore.setIndexOnValues(true);
}
}
}
public String getIndexedExpression() {
return CompactRangeIndex.this.getCanonicalizedIndexedExpression();
}
public String getProjectionAttributes() {
return CompactRangeIndex.this.getCanonicalizedProjectionAttributes();
}
public String getFromClause() {
return CompactRangeIndex.this.getCanonicalizedFromClause();
}
public void expansion(List expandedResults, Object lowerBoundKey, Object upperBoundKey, int lowerBoundOperator, int upperBoundOperator, Object value) throws IMQException {
try {
ExecutionContext expansionContext = createExecutionContext(value);
List iterators = expansionContext.getCurrentIterators();
RuntimeIterator iter = (RuntimeIterator)iterators.get(0);
iter.setCurrent(value);
//first iter level is region entries, we can ignore as we already broke it down in the index
doNestedExpansion(1, expansionContext, expandedResults, lowerBoundKey, upperBoundKey, lowerBoundOperator, upperBoundOperator, value);
}
catch (Exception e) {
throw new IMQException(e){};
}
}
private void doNestedExpansion(int level, ExecutionContext expansionContext, List expandedResults, Object lowerBoundKey, Object upperBoundKey, int lowerBoundOperator, int upperBoundOperator, Object value) throws TypeMismatchException,
AmbiguousNameException, FunctionDomainException,
NameResolutionException, QueryInvocationTargetException, IMQException {
List iterList = expansionContext.getCurrentIterators();
int iteratorSize = iterList.size();
if (level == iteratorSize) {
expand(expansionContext, expandedResults, lowerBoundKey, upperBoundKey, lowerBoundOperator, upperBoundOperator, value);
}
else {
RuntimeIterator rIter = (RuntimeIterator)iterList.get(level);
Collection c = rIter.evaluateCollection(expansionContext);
if (c == null)
return;
Iterator cIter = c.iterator();
while (cIter.hasNext()) {
rIter.setCurrent(cIter.next());
doNestedExpansion(level + 1, expansionContext,expandedResults, lowerBoundKey, upperBoundKey, lowerBoundOperator, upperBoundOperator, value);
}
}
}
/**
*
* @param expansionContext
* @param expandedResults
* @param lowerBoundKey
* @param upperBoundKey if null, we do not do an upperbound check (may need to change this if we ever use null in a range query)
* @param lowerBoundOperator
* @param upperBoundOperator
* @param value
* @throws IMQException
*/
public void expand(ExecutionContext expansionContext, List expandedResults, Object lowerBoundKey, Object upperBoundKey, int lowerBoundOperator, int upperBoundOperator, Object value) throws IMQException {
try {
RuntimeIterator runtimeItr = getRuntimeIteratorForThisIndex(expansionContext);
if (runtimeItr != null) {
runtimeItr.setCurrent(value);
}
Object tupleIndexKey = indexedExpr.evaluate(expansionContext);
tupleIndexKey = getPdxStringForIndexedPdxKeys(tupleIndexKey);
Object compResult;
//Check upper bound
if (upperBoundKey != null) {
compResult = TypeUtils.compare(tupleIndexKey, upperBoundKey, upperBoundOperator);
if (compResult instanceof Boolean) {
Boolean ok = (Boolean) compResult;
if (!ok.booleanValue()) {
return;
}
}
}
if (tupleIndexKey instanceof Map) {
if (lowerBoundOperator == OQLLexerTokenTypes.TOK_EQ) {
if (!((Map) tupleIndexKey).containsKey(lowerBoundKey)) {
return;
}
}
else if (lowerBoundOperator == OQLLexerTokenTypes.TOK_NE) {
if (((Map) tupleIndexKey).containsKey(lowerBoundKey)) {
return;
}
}
}
else {
//Check lower bound
compResult = TypeUtils.compare(tupleIndexKey, lowerBoundKey, lowerBoundOperator);
if (compResult instanceof Boolean) {
Boolean ok = (Boolean) compResult;
if (!ok.booleanValue()) {
return;
}
}
}
List currentRuntimeIters = expansionContext.getCurrentIterators();
int iteratorSize = currentRuntimeIters.size();
Object indxResultSet = null;
//if the resultSetType is of structType, we need to create tuples
//this is due to the way the resultsSets are being created
boolean structType = (indexResultSetType instanceof StructType);
if (iteratorSize == 1 && !structType) {
RuntimeIterator iter = (RuntimeIterator)currentRuntimeIters.get(0);
iter.setCurrent(value);
indxResultSet = iter.evaluate(expansionContext);
indxResultSet = value;
}
else {
Object tuple[] = new Object[iteratorSize];
tuple[0] = value;
if (iteratorSize > 1) {
for (int i = 1; i < iteratorSize; i++) {
RuntimeIterator iter = (RuntimeIterator)currentRuntimeIters.get(i);
tuple[i] = iter.evaluate(expansionContext);
}
Support
.Assert(
this.indexResultSetType instanceof StructTypeImpl,
"The Index ResultType should have been an instance of StructTypeImpl rather than ObjectTypeImpl. The indxeResultType is "
+ this.indexResultSetType);
}
indxResultSet = new StructImpl(
(StructTypeImpl)this.indexResultSetType, tuple);
}
expandedResults.add(indxResultSet);
}
catch (Exception e) {
throw new IMQException(e);
}
}
private ExecutionContext createExecutionContext(Object value) {
DummyQRegion dQRegion = new DummyQRegion(rgn);
dQRegion.setEntry(VMThinRegionEntryHeap.getEntryFactory().createEntry((RegionEntryContext)rgn, 0, value));
Object params[] = { dQRegion };
ExecutionContext context = new ExecutionContext(params, this.cache);
context.newScope(IndexCreationHelper.INDEX_QUERY_SCOPE_ID);
try {
if (this.dependencyGraph != null) {
context.setDependencyGraph(dependencyGraph);
}
for (int i = 0; i < this.iteratorSize; i++) {
CompiledIteratorDef iterDef = (CompiledIteratorDef) fromIterators
.get(i);
if (this.dependencyGraph == null) {
iterDef.computeDependencies(context);
}
RuntimeIterator rIter = iterDef.getRuntimeIterator(context);
context.addToIndependentRuntimeItrMapForIndexCreation(iterDef);
context.bindIterator(rIter);
}
// Save the dependency graph for future updates.
if (dependencyGraph == null) {
dependencyGraph = context.getDependencyGraph();
}
Support
.Assert(
this.indexResultSetType != null,
"IMQEvaluator::evaluate:The StrcutType should have been initialized during index creation");
} catch (Exception e) {
e.printStackTrace(System.out);
throw new Error("Unable to reevaluate, this should not happen");
} finally {
}
return context;
}
/**
* @param add
* true if adding to index, false if removing
*/
public void evaluate(RegionEntry target, boolean add) throws IMQException {
assert !target.isInvalid() : "value in RegionEntry should not be INVALID";
DummyQRegion dQRegion = new DummyQRegion(rgn);
dQRegion.setEntry(target);
Object params[] = { dQRegion };
ExecutionContext context = new ExecutionContext(params, this.cache);
context.newScope(IndexCreationHelper.INDEX_QUERY_SCOPE_ID);
try {
if (this.dependencyGraph != null) {
context.setDependencyGraph(dependencyGraph);
}
for (int i = 0; i < this.iteratorSize; i++) {
CompiledIteratorDef iterDef = (CompiledIteratorDef) fromIterators
.get(i);
// We are re-using the same ExecutionContext on every evaluate -- this
// is not how ExecutionContext was intended to be used.
// Asif: Compute the dependency only once. The call to methods of this
// class are thread safe as for update lock on Index is taken .
if (this.dependencyGraph == null) {
iterDef.computeDependencies(context);
}
RuntimeIterator rIter = iterDef.getRuntimeIterator(context);
context.addToIndependentRuntimeItrMapForIndexCreation(iterDef);
context.bindIterator(rIter);
}
// Save the dependency graph for future updates.
if (dependencyGraph == null) {
dependencyGraph = context.getDependencyGraph();
}
Support
.Assert(
this.indexResultSetType != null,
"IMQEvaluator::evaluate:The StrcutType should have been initialized during index creation");
doNestedIterations(0, add, context);
} catch (IMQException imqe) {
throw imqe;
} catch (Exception e) {
throw new IMQException(e);
} finally {
context.popScope();
}
}
/**
* Asif : This function is used for creating Index data at the start
*
*/
public void initializeIndex(boolean loadEntries) throws IMQException {
this.initEntriesUpdated = 0;
try {
// Asif: Since an index initialization can happen multiple times
// for a given region, due to clear operation, we are using harcoded
// scope ID of 1 , as otherwise if obtained from ExecutionContext
// object, it will get incremented on very index initialization
this.initContext.newScope(1);
for (int i = 0; i < this.iteratorSize; i++) {
CompiledIteratorDef iterDef = (CompiledIteratorDef) this.indexInitIterators
.get(i);
RuntimeIterator rIter = null;
if (!this.hasInitOccuredOnce) {
iterDef.computeDependencies(this.initContext);
rIter = iterDef.getRuntimeIterator(this.initContext);
this.initContext
.addToIndependentRuntimeItrMapForIndexCreation(iterDef);
}
if (rIter == null) {
rIter = iterDef.getRuntimeIterator(this.initContext);
}
this.initContext.bindIterator(rIter);
}
this.hasInitOccuredOnce = true;
if (this.indexResultSetType == null) {
this.indexResultSetType = createIndexResultSetType();
}
if(loadEntries) {
doNestedIterationsForIndexInit(0,
this.initContext.getCurrentIterators());
}
} catch (IMQException imqe) {
throw imqe;
} catch (Exception e) {
throw new IMQException(e);
}
finally {
this.initContext.popScope();
}
}
private void doNestedIterationsForIndexInit(int level, List runtimeIterators)
throws TypeMismatchException, AmbiguousNameException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException, IMQException {
if (level == 1) {
++this.initEntriesUpdated;
}
if (level == this.iteratorSize) {
applyProjectionForIndexInit(runtimeIterators);
} else {
RuntimeIterator rIter = (RuntimeIterator) runtimeIterators.get(level);
Collection c = rIter.evaluateCollection(this.initContext);
if (c == null)
return;
Iterator cIter = c.iterator();
while (cIter.hasNext()) {
rIter.setCurrent(cIter.next());
doNestedIterationsForIndexInit(level + 1, runtimeIterators);
}
}
}
/*
* Asif : This function is used to obtain Index data at the time of index
* creation. Each element of the List is an Object Array of size 3. The 0th
* element of Object Array stores the value of Index Expression. The 1st
* element of ObjectArray contains the RegionEntry object ( If the booelan
* isFirstItrOnEntry is false, then the 0th iterator will give us the
* Region.Entry object which can be used to obtain the underlying
* RegionEntry object. If the boolean is true & additional projection
* attribute is not null, then the Region.Entry object can be obtained by
* evaluating the additional projection attribute. If the boolean
* isFirstItrOnEntry is tru e& additional projection attribute is null, then
* teh 0th iterator itself will evaluate to Region.Entry Object.
*
* The 2nd element of Object Array contains the Struct object ( tuple)
* created. If the boolean isFirstItrOnEntry is false, then the first
* attribute of the Struct object is obtained by evaluating the additional
* projection attribute.
*/
private void applyProjectionForIndexInit(List currentRuntimeIters)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException, IMQException {
if (QueryMonitor.isLowMemory()) {
throw new IMQException(
LocalizedStrings.IndexCreationMsg_CANCELED_DUE_TO_LOW_MEMORY
.toLocalizedString());
}
Object indexKey = this.isFirstItrOnEntry ? this.indexedExpr
.evaluate(this.initContext) : modifiedIndexExpr
.evaluate(this.initContext);
if (indexKey == null) {
indexKey = IndexManager.NULL;
}
// if the first key is PdxString set the flag so that rest of the keys
// would be converted to PdxString
if (!isIndexedPdxKeysFlagSet) {
setPdxStringFlag(indexKey);
}
indexKey = getPdxStringForIndexedPdxKeys(indexKey);
LocalRegion.NonTXEntry temp = null;
if (this.isFirstItrOnEntry && this.additionalProj != null) {
temp = (LocalRegion.NonTXEntry) additionalProj
.evaluate(this.initContext);
} else {
temp = (LocalRegion.NonTXEntry) (((RuntimeIterator) currentRuntimeIters
.get(0)).evaluate(this.initContext));
}
RegionEntry re = temp.getRegionEntry();
indexStore.addMapping(indexKey, re);
}
/**
* @param add
* true if adding to index, false if removing
* @param context
*/
private void doNestedIterations(int level, boolean add,
ExecutionContext context) throws TypeMismatchException,
AmbiguousNameException, FunctionDomainException,
NameResolutionException, QueryInvocationTargetException, IMQException {
List iterList = context.getCurrentIterators();
if (level == this.iteratorSize) {
applyProjection(add, context);
} else {
RuntimeIterator rIter = (RuntimeIterator) iterList.get(level);
// System.out.println("Level = "+level+" Iter = "+rIter.getDef());
Collection c = rIter.evaluateCollection(context);
if (c == null)
return;
Iterator cIter = c.iterator();
while (cIter.hasNext()) {
rIter.setCurrent(cIter.next());
doNestedIterations(level + 1, add, context);
}
}
}
/**
* @param add
* true if adding, false if removing from index
* @param context
*/
private void applyProjection(boolean add, ExecutionContext context)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException, IMQException {
Object indexKey = indexedExpr.evaluate(context);
if (indexKey == null) {
indexKey = IndexManager.NULL;
}
// if the first key is PdxString set the flag so that rest of the keys
// would be converted to PdxString
if (!isIndexedPdxKeysFlagSet) {
setPdxStringFlag(indexKey);
}
indexKey = getPdxStringForIndexedPdxKeys(indexKey);
RegionEntry entry = ((DummyQRegion) context.getBindArgument(1))
.getEntry();
// Get thread local reverse map if available.
OldKeyValuePair oldKeyValuePair = null;
if (oldKeyValue != null) {
oldKeyValuePair = oldKeyValue.get();
}
if (add) {
Object oldKey = null;
Object oldValue = null;
// Get Old keys to be removed.
if (oldKeyValuePair != null) {
oldKey = oldKeyValuePair.getOldKey();
oldValue = oldKeyValuePair.getOldValue();
}
// Add new index entries
// A null oldKey means this is a create
// oldKey would be a NullToken in case of update
if (oldKey == null) {
indexStore.addMapping(indexKey, entry);
} else {
// Add new key and remove old
indexStore.updateMapping(indexKey, oldKey, entry, oldValue);
// reset the thread local as the update is done
if (oldKeyValue != null) {
oldKeyValue.remove();
}
}
} else { // remove from forward and reverse maps
// We will cleanup the index entry later.
if (oldKeyValuePair != null) {
oldKeyValuePair.setOldKeyValuePair(indexKey, entry);
} else {
indexStore.removeMapping(indexKey, entry);
}
}
}
// TODO :Asif: Test this function .
// The struct type calculation is modified if the
// 0th iterator is modified to make it dependent on Entry
private ObjectType createIndexResultSetType() {
List currentIterators = this.initContext.getCurrentIterators();
int len = currentIterators.size();
ObjectType type = null;
// String fieldNames[] = new String[len];
ObjectType fieldTypes[] = new ObjectType[len];
int start = this.isFirstItrOnEntry ? 0 : 1;
for (; start < len; start++) {
RuntimeIterator iter = (RuntimeIterator) currentIterators.get(start);
// fieldNames[start] = iter.getInternalId();
fieldTypes[start] = iter.getElementType();
}
if (!this.isFirstItrOnEntry) {
// fieldNames[0] = "iter1";
fieldTypes[0] = addnlProjType;
}
type = (len == 1) ? fieldTypes[0] : new StructTypeImpl(
this.canonicalIterNames, fieldTypes);
return type;
}
int getTotalEntriesUpdated() {
return this.initEntriesUpdated;
}
public ObjectType getIndexResultSetType() {
return this.indexResultSetType;
}
public List getAllDependentIterators() {
return fromIterators;
}
}
void lockedQuery(Object key, int operator, Collection results,
CompiledValue iterOps, RuntimeIterator indpndntItr,
ExecutionContext context, List projAttrib,
SelectResults intermediateResults, boolean isIntersection)
throws TypeMismatchException, FunctionDomainException,
NameResolutionException, QueryInvocationTargetException {
this.lockedQueryPrivate(key, operator, results, iterOps, indpndntItr,
context, null, projAttrib, intermediateResults, isIntersection);
}
void lockedQuery(Object key, int operator, Collection results,
Set keysToRemove, ExecutionContext context) throws TypeMismatchException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException {
this.lockedQueryPrivate(key, operator, results, null, null, context,
keysToRemove, null, null, true);
}
@Override
void addMapping(Object key, Object value, RegionEntry entry)
throws IMQException
{
//Only called from CompactMapRangeIndex
indexStore.addMapping(key, entry);
}
public static void setTestHook(TestHook hook) {
testHook = hook;
}
@Override
void saveMapping(Object key, Object value, RegionEntry entry)
throws IMQException {
// TODO Auto-generated method stub
}
public boolean isEmpty() {
return indexStore.size() == 0 ? true : false;
}
@Override
public Map getValueToEntriesMap() {
throw new UnsupportedOperationException(
"valuesToEntriesMap should not be accessed directly");
}
public void addSavedMappings(RegionEntry entry) {
}
private class OldKeyValuePair {
private Object oldKey;
private Object oldValue;
public void setOldKeyValuePair(Object oldKey, RegionEntry entry){
this.oldKey = oldKey;
//We obtain the object currently in vm, we are using this old value
//only to detect if in place modifications have occurred
//if the object is not in memory, obviously an in place modification could
//not have occured
this.oldValue = indexStore.getTargetObjectInVM(entry);
}
public Object getOldValue(){
return oldValue;
}
public Object getOldKey(){
return oldKey;
}
}
}