blob: d7d382d33d9b644accdc5b1fb6b64d3ce96c4af8 [file] [log] [blame]
/*=========================================================================
* Copyright Copyright (c) 2000-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.
* $Id: CompiledComparison.java,v 1.1 2005/01/27 06:26:33 vaibhav Exp $
*=========================================================================
*/
package com.gemstone.gemfire.cache.query.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
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.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.Struct;
import com.gemstone.gemfire.cache.query.TypeMismatchException;
import com.gemstone.gemfire.cache.query.internal.index.IndexData;
import com.gemstone.gemfire.cache.query.internal.index.IndexProtocol;
import com.gemstone.gemfire.cache.query.internal.index.IndexUtils;
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.offheap.annotations.Retained;
import com.gemstone.gemfire.pdx.PdxInstance;
import com.gemstone.gemfire.pdx.internal.PdxString;
/**
* Comparison value: <, >, <=, >=, <>, =
*
* @author ericz
* @author asif
*/
public class CompiledComparison extends AbstractCompiledValue implements
Negatable, OQLLexerTokenTypes, Indexable {
// persistent inst vars
private final CompiledValue _left;
private final CompiledValue _right;
private int _operator;
// List groupRuntimeItrs = null;
// List definitions = null;
CompiledComparison(CompiledValue left, CompiledValue right, int op) {
// invariant:
// operator must be one of <,>,<=,>=,=,<>
Support.Assert(op == TOK_LT || op == TOK_LE || op == TOK_GT || op == TOK_GE
|| op == TOK_EQ || op == TOK_NE, String.valueOf(op));
_left = left;
_right = right;
_operator = op;
}
/* ******** CompiledValue Methods **************** */
@Override
public List getChildren() {
List list = new ArrayList();
list.add(_left);
list.add(_right);
return list;
}
public int getType() {
return COMPARISON;
}
public Object evaluate(ExecutionContext context)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException {
Object left = _left.evaluate(context);
Object right = _right.evaluate(context);
if (context.isCqQueryContext() && left instanceof Region.Entry) {
left = ((Region.Entry) left).getValue();
}
if (context.isCqQueryContext() && right instanceof Region.Entry) {
right = ((Region.Entry) right).getValue();
}
if(left == null || right == null){
return TypeUtils.compare(left,right,_operator);
}
// if read-serialized is not set, deserialize the pdx instance for comparison
// only if it is of the same class as that of the object being compared
if(!context.getCache().getPdxReadSerialized()) {
if(left instanceof PdxInstance && !(right instanceof PdxInstance) && ((PdxInstance)left).getClassName().equals(right.getClass().getName())) {
left = ((PdxInstance)left).getObject();
} else if(right instanceof PdxInstance && !(left instanceof PdxInstance) && ((PdxInstance)right).getClassName().equals(left.getClass().getName())) {
right = ((PdxInstance)right).getObject();
}
}
if (left instanceof PdxString) {
if (right instanceof String) {
switch (_right.getType()) {
case LITERAL:
right = ((CompiledLiteral) _right).getSavedPdxString();
break;
case QUERY_PARAM:
right = ((CompiledBindArgument) _right).getSavedPdxString(context);
break;
case FUNCTION:
case PATH:
right = new PdxString((String) right);
}
}
} else if (right instanceof PdxString) {
switch (_left.getType()) {
case LITERAL:
left = ((CompiledLiteral) _left).getSavedPdxString();
break;
case QUERY_PARAM:
left = ((CompiledBindArgument) _left).getSavedPdxString(context);
break;
case FUNCTION:
case PATH:
left = new PdxString((String) left);
}
}
return TypeUtils.compare(left,right,_operator);
}
/**
* Asif : Evaluates as a filter taking advantage of indexes if appropriate.
* This function has a meaningful implementation only in CompiledComparison &
* CompiledUndefined . It is unsupported in other classes. The additional
* parameters which it takes are a boolean which is used to indicate whether
* the index result set needs to be expanded to the top level or not. The
* second is a CompiledValue representing the operands which are only iter
* evaluatable. The CompiledValue passed will be null except if a
* GroupJunction has only one filter evaluatable condition & rest are iter
* operands. In such cases , the iter operands will be evaluated while
* expanding/cutting down the index resultset
*
* @return SelectResults
*/
@Override
public SelectResults filterEvaluate(ExecutionContext context,
SelectResults intermediateResults, boolean completeExpansionNeeded,
@Retained CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, boolean isIntersection, boolean conditioningNeeded, boolean evaluateProjection)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException {
// see if we're dependent on the current iterator
// if not let super handle it
// RuntimeIterator itr = context.getCurrentIterator();
// Support.Assert(itr != null);
if (!isDependentOnCurrentScope(context))
return super.filterEvaluate(context, intermediateResults);
IndexInfo[] idxInfo = getIndexInfo(context);
Support
.Assert(
idxInfo != null,
"a comparison that is dependent, not indexed, and filter evaluated is not possible");
if (idxInfo.length == 1) {
return singleBaseCollectionFilterEvaluate(context, intermediateResults,
completeExpansionNeeded, iterOperands, idxInfo[0], indpndntItrs, isIntersection,conditioningNeeded, evaluateProjection);
}
else {
Support
.Assert(
idxInfo.length == 2,
"A Composite CompiledComparison which is filter evaluatable needs to have two indexes");
return doubleBaseCollectionFilterEvaluate(context, intermediateResults,
completeExpansionNeeded, iterOperands, idxInfo, indpndntItrs);
}
}
/**
* evaluate as a filter, producing an intermediate result set. This may
* require iteration if there is no index available. Asif :The booelan true
* implies that CompiledComparsion when existing on its own always requires a
* Completeexpansion to top level iterators. This flag can get toggled to
* false only from inside a GroupJunction
*
* @param intermediateResults if this parameter is provided, and we have to
* iterate, then iterate over this result set instead of the entire
* base collection.
*/
@Override
public SelectResults filterEvaluate(ExecutionContext context,
SelectResults intermediateResults) throws FunctionDomainException,
TypeMismatchException, NameResolutionException,
QueryInvocationTargetException {
// Asif : This function can be invoked only if the where clause contains
// a single condition which is CompiledComparison.
// If a CompiledComparison exists inside a GroupJunction, then it will
// always
// call the overloaded filterEvalauate with the RuntimeIterator passed
// as not null.
// Thus if the RuntimeIterator array passed is null then it is
// guaranteed
// that the condition was a isolatory condition ( directly in where
// clause)
// and the final iterators to which we need to expand to is all the
// iterators
// of the scope
RuntimeIterator indpndntItr = null;
List currentScopeIndpndntItrs = context.getAllIndependentIteratorsOfCurrentScope();
Set rntmItrs = QueryUtils.getCurrentScopeUltimateRuntimeIteratorsIfAny(this,context);
if(rntmItrs.size() ==1 && currentScopeIndpndntItrs.size() ==1 ) {
indpndntItr = (RuntimeIterator)rntmItrs.iterator().next();
}
return filterEvaluate(context, intermediateResults, true/*
* Complete
* Expansion needed
*/, null, indpndntItr !=null?new RuntimeIterator[]{indpndntItr}:null/*
* Asif :It is safe to pass null as the independent iterator
* to which the condition belongs is required only if boolean
* complete expansion turns out to be false, which can happen
* only in case of CompiledComparison/CompiledUndefined
* called from roupJunction or CompositeGroupJunction
*/, true,this.isConditioningNeededForIndex(indpndntItr, context, true),true /*evaluate projection attribute */);
}
/*
* Asif : This function should never get invoked as now if a CompiledJunction
* or GroupJunction contains a single filterable CompiledComparison it should
* directly call filterEvaluate rather than auxFilterEvalutae. Overriding this
* function just for ensuring that auxFilterEvaluate is not being called by
* mistake.
*/
@Override
public SelectResults auxFilterEvaluate(ExecutionContext context,
SelectResults intermediateResults) throws FunctionDomainException,
TypeMismatchException, NameResolutionException,
QueryInvocationTargetException {
Support
.assertionFailed(" This auxFilterEvaluate of CompiledComparison should never have got invoked.");
return null;
}
public void negate() {
_operator = inverseOperator(_operator);
}
// Invariant: the receiver is dependent on the current iterator.
@Override
protected PlanInfo protGetPlanInfo(ExecutionContext context)
throws TypeMismatchException, AmbiguousNameException, NameResolutionException {
PlanInfo result = new PlanInfo();
IndexInfo[] indexInfo = getIndexInfo(context);
if (indexInfo == null) return result;
for (int i = 0; i < indexInfo.length; ++i) {
result.indexes.add(indexInfo[i]._index);
}
result.evalAsFilter = true;
String preferredCondn = (String)context.cacheGet(PREF_INDEX_COND);
if(preferredCondn != null) {
//This means that the system is having only one independent iterator so equi join is ruled out.
// thus the first index is guaranteed to be on the condition which may match our preferred index
if(indexInfo[0]._index.getCanonicalizedIndexedExpression().equals(preferredCondn) &&
indexInfo[0]._index.getType() != IndexType.PRIMARY_KEY) {
result.isPreferred = true;
}
}
return result;
}
@Override
public Set computeDependencies(ExecutionContext context)
throws TypeMismatchException, AmbiguousNameException, NameResolutionException {
context.addDependencies(this, _left.computeDependencies(context));
return context.addDependencies(this, _right.computeDependencies(context));
}
int reflectOnOperator(CompiledValue key) {
int operator = _operator;
if (key == _left)
operator = reflectOperator(operator);
return operator;
}
public boolean isRangeEvaluatable() {
if(this._left instanceof MapIndexable || this._right instanceof MapIndexable ) {
return false;
}
return true;
}
public int getSizeEstimate(ExecutionContext context)
throws FunctionDomainException, TypeMismatchException,
NameResolutionException, QueryInvocationTargetException
{
IndexInfo[] idxInfo = getIndexInfo(context);
if (idxInfo == null) {
// Asif: This implies it is an independent condition. So evaluate it first
// in filter operand
return 0;
}
assert idxInfo.length == 1;
Object key = idxInfo[0].evaluateIndexKey(context);
if (key != null && key.equals(QueryService.UNDEFINED)) {
return 0;
}
if (context instanceof QueryExecutionContext) {
QueryExecutionContext qcontext = (QueryExecutionContext) context;
if (qcontext.isHinted(idxInfo[0]._index.getName())) {
return qcontext.getHintSize(idxInfo[0]._index.getName());
}
}
// if the key is the LEFT operand, then reflect the operator
// before the index lookup
int op = reflectOnOperator(idxInfo[0]._key());
return idxInfo[0]._index.getSizeEstimate(key, op, idxInfo[0]._matchLevel);
}
/** **************** PRIVATE METHODS ************************** */
/**
* evaluate as a filter, involving a single iterator. Use an index if
* possible.
*/
// Invariant: the receiver is dependent on the current iterator.
private SelectResults singleBaseCollectionFilterEvaluate(
ExecutionContext context, SelectResults intermediateResults,
final boolean completeExpansionNeeded, @Retained CompiledValue iterOperands,
IndexInfo indexInfo, RuntimeIterator[] indpndntItr, boolean isIntersection, boolean conditioningNeeded, boolean evaluateProj)
throws TypeMismatchException, AmbiguousNameException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException
{
ObjectType resultType = indexInfo._index.getResultSetType();
int indexFieldsSize = -1;
SelectResults set = null;
boolean createEmptySet = false;
// Direct comparison with UNDEFINED will always return empty set
Object key = indexInfo.evaluateIndexKey(context);
createEmptySet = (key != null && key.equals(QueryService.UNDEFINED)) ;
if (resultType instanceof StructType) {
indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length;
}
else {
indexFieldsSize = 1;
}
// if the key is the LEFT operand, then reflect the operator
// before the index lookup
int op = reflectOnOperator(indexInfo._key());
// actual index lookup
QueryObserver observer = QueryObserverHolder.getInstance();
List projAttrib = null;
/*
* Asif : First obtain the match level of index resultset. If the match
* level happens to be zero , this implies that we just have to change the
* StructType ( again if only the Index resultset is a StructBag). If the
* match level is zero & expand to to top level flag is true & iff the total
* no. of iterators in current scope is greater than the no. of fields in
* StructBag , then only we need to do any expansion.
*
*/
try {
if( !createEmptySet) {
observer.beforeIndexLookup(indexInfo._index, op, key);
context.cachePut(CompiledValue.INDEX_INFO, indexInfo);
}
// //////////////////////////////////////////////////////////
// Asif:Create an instance of IndexConditionHelper to see , if the match
// level is zero & expansion is to Group level & that no reshuffling is
// needed.
// If this holds true , then we will try to evaluate iter operand while
// collecting the results itself. Pass the iter operand as null so that
// we get the right idea. Also right now we will assume that only single
// iterator cases will be candidates for this oprtmization.
// dependent iterators will come later.
boolean useLinkedDataStructure = false;
boolean nullValuesAtStart = true;
Boolean orderByClause = (Boolean)context.cacheGet(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX);
if(orderByClause != null && orderByClause.booleanValue()) {
List orderByAttrs = (List)context.cacheGet(CompiledValue.ORDERBY_ATTRIB);
useLinkedDataStructure =orderByAttrs.size()==1;
nullValuesAtStart = !((CompiledSortCriterion)orderByAttrs.get(0)).getCriterion();
}
// ////////////////////////////////////////////////////////////////
if (!conditioningNeeded) {
ObjectType projResultType = evaluateProj ? (ObjectType)context
.cacheGet(RESULT_TYPE) : null;
if (projResultType != null) {
resultType = projResultType;
projAttrib = (List)context.cacheGet(PROJ_ATTRIB);
context.cachePut(RESULT_TYPE, Boolean.TRUE);
}
if (isIntersection) {
if (resultType instanceof StructType) {
context.getCache().getLogger().fine(
"StructType resultType.class="
+ resultType.getClass().getName());
if(useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType)
: new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart);
}else {
set = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType) ;
}
indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length;
}
else {
context.getCache().getLogger().fine(
"non-StructType resultType.class="
+ resultType.getClass().getName());
if (useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedResultSet(resultType) :
new SortedResultsBag(resultType, nullValuesAtStart) ;
} else {
set = QueryUtils.createResultCollection(context, resultType) ;
}
indexFieldsSize = 1;
}
}
else {
if (intermediateResults != null && context.getQuery() != null &&
((DefaultQuery)context.getQuery()).getSelect().isDistinct()) {
set = intermediateResults;
intermediateResults = null;
}
else {
if (resultType instanceof StructType) {
context.getCache().getLogger().fine(
"StructType resultType.class="
+ resultType.getClass().getName());
if(useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType)
: new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart);
}else {
set = QueryUtils.createStructCollection(context,(StructTypeImpl)resultType) ;
}
indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length;
}
else {
context.getCache().getLogger().fine(
"non-StructType resultType.class="
+ resultType.getClass().getName());
if (useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedResultSet(resultType) :
new SortedResultsBag(resultType, nullValuesAtStart );
} else {
set = QueryUtils.createResultCollection(context, resultType) ;
}
indexFieldsSize = 1;
}
}
}
if(!createEmptySet) {
indexInfo._index.query(key, op, set, iterOperands, indpndntItr != null? indpndntItr[0]:null,
context, projAttrib, intermediateResults,
isIntersection);
}
}
else {
if (resultType instanceof StructType) {
context.getCache().getLogger().fine(
"StructType resultType.class=" + resultType.getClass().getName());
if (useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType)
: new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart);
} else {
set = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType) ;
}
indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length;
}
else {
context.getCache().getLogger().fine(
"non-StructType resultType.class="
+ resultType.getClass().getName());
if (useLinkedDataStructure) {
set = context.isDistinct() ? new LinkedResultSet(resultType) :
new SortedResultsBag(resultType, nullValuesAtStart);
} else {
set = QueryUtils.createResultCollection(context, resultType) ;
}
indexFieldsSize = 1;
}
if(!createEmptySet) {
indexInfo._index.query(key, op, set, context); // tow rows qualify from index
}
// look up. rahul
}
}
finally {
if(!createEmptySet) {
observer.afterIndexLookup(set);
}
}
if (conditioningNeeded) {
return QueryUtils.getconditionedIndexResults(set, indexInfo, context,
indexFieldsSize, completeExpansionNeeded, iterOperands, indpndntItr);
}
else {
return set;
}
}
/**
* evaluate as a filter, involving a two independent iterators. Use an index
* if possible on both. And merge theresults obtained.
*/
// Invariant: the receiver is dependent on the current iterator.
private SelectResults doubleBaseCollectionFilterEvaluate(
ExecutionContext context, SelectResults intermediateResults,
boolean completeExpansionNeeded, CompiledValue iterOperands,
IndexInfo[] indxInfo, RuntimeIterator[] indpdntItrs)
throws TypeMismatchException, AmbiguousNameException,
FunctionDomainException, NameResolutionException,
QueryInvocationTargetException {
//Asif : First we need to collect the results of the two indexes
//We will be calling a function of Index & passing the other Index as a
// parameter.
//We will be getting List object. Each element of the List will contain a
// two dimesional
//Object array. The first row will contain the result objects of the first
// Index
//& secondrow will contain that of second index. The object contained in
// each of the
// one dimensional array can be either genuine result object or StructImpl
// object.
QueryObserver observer = QueryObserverHolder.getInstance();
context.cachePut(CompiledValue.INDEX_INFO, indxInfo);
/*
* Asif : If the independent Group of iterators passed is not null or the
* independent Group of iterators passed is null & complete expansion flag
* is true & the intermediate result set is empty or null, in such cases ,
* we have to definitely use indexes on both conditions & go for complete
* expansion or CompsoiteGroupJunction expansion as be the case. ( ie
* retaining the old logic of expanding the index result to top or
* compositeGroupJunction level)
*
* The condition in which we sure are to use indexes on both LHS & RHS & the
* the resultset expanded to CGJ or top level are 1) A stand alone
* filterable composite condition : In such cases the independent group of
* itr is passed as null , the intermediate resultset is empty & the
* complete expansion flag is true. This indicates that we have to expand to
* top level. 2) Multiple filterable composite condition in OR junction: In
* such cases the independent grp of its passed is not null but the
* intermediate resultset is null . This means that indexes on both LHS &
* RHS to be used & expanded to top or CGJ level ( same as above)
*
* The case which is different from above is AND condition evaluation In
* such cases, till the last but one filerable condition we pass complete
* expn flag as false & group of indpndt itrs as null. Only last filterabel
* condn is passed genuine value of expn flag & the not null grp of
* independent itrs . The intermedaite set is not null & not empty for such
* cases which wil help us distinguish the case from OR junction
*
* IndependentGroup Iterators being null or not null is not a definitive &
* exclusive * criteria for knowing whether the CC result needs to be
* expanded to Top or CGJ level as for independent grp not null will happen
* in OR junction implying expn to CGJ level & ignoring intermediate
* resultset as well as in evalaution of last filterable condition of AND
* junction implying usage of intermediate resultset for cartesian.
*
* Similarly grp of indpendent itrs can be null for stand alone condition
* implying expn to top level & ignoring intermediate set & also for
* evaluating all the conditions except last in AND junction( which means
* using intermediate resultset for cartesian)
*
*/
if ((intermediateResults == null || intermediateResults.isEmpty())
&& (indpdntItrs != null || completeExpansionNeeded)) {
/*
* Asif : First obtain the match level of index resultset. If the match
* level happens to be zero , this implies that we just have to change the
* StructType ( again if only the Index resultset is a StructBag). If the
* match level is zero & expand to to top level flag is true & iff the
* total no. of iterators in current scope is greater than the no. of
* fields in StructBag , then only we need to do any expansion.
*
*/
Support
.Assert(this._operator == TOK_EQ,
"A relationship index is not usable for any condition other than equality");
List data = null;
try {
observer.beforeIndexLookup(indxInfo[0]._index, this._operator, null);
observer.beforeIndexLookup(indxInfo[1]._index, this._operator, null);
if (context.getBucketList() != null) {
data = QueryUtils.queryEquijoinConditionBucketIndexes(indxInfo, context);
} else {
data = indxInfo[0]._index.queryEquijoinCondition(indxInfo[1]._index, context);
}
}
finally {
observer.afterIndexLookup(data);
}
return QueryUtils
.getconditionedRelationshipIndexResultsExpandedToTopOrCGJLevel(data,
indxInfo, context, completeExpansionNeeded, iterOperands,
indpdntItrs);
}
else {
// Asif . We are in this block , this itself guarantees that this
// conditioned is being called
// from AND junction evalauation of CompositeGroupJunction . The nature of
// resultset obtained from here
// will depend upon the intermediate Resultset passed etc.
return QueryUtils
.getRelationshipIndexResultsMergedWithIntermediateResults(
intermediateResults, indxInfo, context, completeExpansionNeeded,
iterOperands, indpdntItrs);
}
}
// !!!:ezoerner:20081031 get rid of this method once we are compiling at java 1.5+
// @see Class#getSimpleName
public static String getSimpleClassName(Class cls) {
return cls.getName().substring(cls.getPackage().getName().length() + 1);
}
// Asif: If the size of aray is two this implies that it is
// a relation ship index & so the key field will be null in both the indexes
// as key is not a meaningful entity. The 0th element will refer to LHS
// operand
// and 1th element will refer to RHS operannd
public IndexInfo[] getIndexInfo(ExecutionContext context)
throws TypeMismatchException, AmbiguousNameException, NameResolutionException {
IndexInfo[] indexInfo = privGetIndexInfo(context);
if (indexInfo != null ) {
if(indexInfo == NO_INDEXES_IDENTIFIER) {
return null;
}else {
return indexInfo;
}
}
if (!IndexUtils.indexesEnabled) return null;
// get the path and index key to try
PathAndKey pAndK = getPathAndKey(context);
IndexInfo newIndexInfo[] = null;
if (pAndK == null) {
IndexData[] indexData = QueryUtils.getRelationshipIndexIfAny(_left,
_right, context, this._operator);// findOnlyFunctionalIndex.
if (indexData != null) {
newIndexInfo = new IndexInfo[2];
for (int i = 0; i < 2; ++i) {
newIndexInfo[i] = new IndexInfo(null, i == 0 ? _left : _right,
indexData[i].getIndex(), indexData[i].getMatchLevel(),
indexData[i].getMapping(), i == 0 ? this._operator : reflectOperator(this._operator));
}
}
}
else {
CompiledValue path = pAndK._path;
CompiledValue indexKey = pAndK._key;
IndexData indexData = null;
// CompiledLike should not use HashIndex and PrimarKey Index.
if (this instanceof CompiledLike) {
indexData = QueryUtils.getAvailableIndexIfAny(path, context,
OQLLexerTokenTypes.LITERAL_like);
} else {
indexData = QueryUtils.getAvailableIndexIfAny(path, context,
this._operator);
}
IndexProtocol index = null;
if (indexData != null) {
index = indexData.getIndex();
}
if (index != null && index.isValid()) {
newIndexInfo = new IndexInfo[1];
newIndexInfo[0] = new IndexInfo(indexKey, path, index, indexData
.getMatchLevel(), indexData.getMapping(), reflectOnOperator(indexKey));
}
}
if(newIndexInfo != null ) {
privSetIndexInfo(newIndexInfo, context);
}else {
privSetIndexInfo(NO_INDEXES_IDENTIFIER, context);
}
return newIndexInfo;
}
CompiledValue getKey(ExecutionContext context) throws AmbiguousNameException, TypeMismatchException {
return getPathAndKey(context)._key;
}
/**
* get the path to see if there's an index for, and also determine which
* CompiledValue is the key while we're at it
*/
private PathAndKey getPathAndKey(ExecutionContext context)
throws TypeMismatchException, AmbiguousNameException {
// RuntimeIterator lIter = context.findRuntimeIterator(_left);
// RuntimeIterator rIter = context.findRuntimeIterator(_right);
boolean isLeftDependent = context.isDependentOnCurrentScope(_left);
boolean isRightDependent = context.isDependentOnCurrentScope(_right);
if ((isLeftDependent == false) == (isRightDependent == false)) return null;
CompiledValue indexKey;
CompiledValue path;
if (isLeftDependent == false) {
path = _right;
indexKey = _left;
}
else {
path = _left;
indexKey = _right;
}
if (indexKey.isDependentOnCurrentScope(context)) return null; // this check
// seems to be
// redunant.
return new PathAndKey(path, indexKey);
}
// _indexInfo is a transient field
// if this is just faulted in then can be null
private IndexInfo[] privGetIndexInfo(ExecutionContext context) {
return (IndexInfo[]) context.cacheGet(this);
}
private void privSetIndexInfo(IndexInfo[] indexInfo, ExecutionContext context) {
context.cachePut(this, indexInfo);
}
/* Inner classes for passing stuff around */
static class PathAndKey {
CompiledValue _path;
CompiledValue _key;
PathAndKey(CompiledValue path, CompiledValue indexKey) {
_path = path;
_key = indexKey;
}
}
@Override
public boolean isProjectionEvaluationAPossibility(ExecutionContext context)
{
return true;
}
@Override
public boolean isLimitApplicableAtIndexLevel(ExecutionContext context) {
return true;
}
@Override
public boolean isOrderByApplicableAtIndexLevel(ExecutionContext context,
String canonicalizedOrderByClause) throws FunctionDomainException,
TypeMismatchException, NameResolutionException,
QueryInvocationTargetException {
if (this.getPlanInfo(context).evalAsFilter) {
PlanInfo pi = this.getPlanInfo(context);
if (pi.indexes.size() == 1) {
IndexProtocol ip = (IndexProtocol)pi.indexes.get(0);
if (ip.getCanonicalizedIndexedExpression().equals(
canonicalizedOrderByClause) && ip.getType() != IndexType.PRIMARY_KEY && pi.isPreferred) {
return true;
}
}
}
return false;
}
public boolean isConditioningNeededForIndex(RuntimeIterator independentIter,
ExecutionContext context, boolean completeExpnsNeeded) throws AmbiguousNameException, TypeMismatchException, NameResolutionException {
IndexConditioningHelper ich = null;
IndexInfo[] idxInfo = getIndexInfo(context);
int indexFieldsSize = -1;
boolean conditioningNeeded = true;
if(idxInfo == null || idxInfo.length > 1) {
return conditioningNeeded;
}
//assert idxInfo.length == 1;
ObjectType indexRsltType = idxInfo[0]._index.getResultSetType();
if (indexRsltType instanceof StructType) {
indexFieldsSize = ((StructTypeImpl)indexRsltType).getFieldNames().length;
}
else {
indexFieldsSize = 1;
}
if (independentIter != null && indexFieldsSize == 1) {
ich = new IndexConditioningHelper(idxInfo[0], context, indexFieldsSize,
completeExpnsNeeded, null, independentIter);
}
return ich == null || ich.shufflingNeeded;
}
public int getOperator() {
return this._operator;
}
public boolean isBetterFilter(Filter comparedTo, ExecutionContext context, final int thisSize) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException
{
//If the current filter is equality & comparedTo filter is also equality based , then
// return the one with lower size estimate is better
boolean isThisBetter = true;
int thatSize = comparedTo.getSizeEstimate(context);
int thatOperator = comparedTo.getOperator() ;
//Go with the lowest cost when hint is used.
if (context instanceof QueryExecutionContext && ((QueryExecutionContext)context).hasHints()) {
return thisSize <= thatSize;
}
//There may be some hard rules that give unoptimal selections based on these switch cases.
if(this._operator == TOK_EQ || this._operator == TOK_NE || this._operator == TOK_NE_ALT) {
switch(thatOperator) {
case TOK_EQ:
case TOK_NE:
case TOK_NE_ALT:
isThisBetter = thisSize <= thatSize;
break;
case LITERAL_and:
// This is is possible only in case of RangeJunction.
if(this._operator == TOK_NE || this._operator == TOK_NE_ALT ) {
//Asif: Give preference to range as I am assuming that range will fetch less data
// as compared to NOT EQUALs
isThisBetter = false;
}
break;
case TOK_LE:
case TOK_LT:
case TOK_GE:
case TOK_GT:
//Give preference to this rather than that as this is more deterministic
break;
default :
throw new IllegalArgumentException("The operator type ="+ thatOperator + " is unknown");
}
}else {
//This is a inequality. If that is true the priority goes to equality & Not Equality & Range
switch(thatOperator) {
case TOK_EQ:
case TOK_NE:
case TOK_NE_ALT:
case LITERAL_and:
//Asif: Give preference to range as I am assuming that raneg will fetch less data
// as compared to NOT EQUALs
isThisBetter = false;
break;
case TOK_LE:
case TOK_LT:
case TOK_GE:
case TOK_GT:
isThisBetter = thisSize <= thatSize;
break;
default :
throw new IllegalArgumentException("The operator type ="+ thatOperator + " is unknown");
}
}
return isThisBetter;
}
}
// IndexInfo was removed from here to its own file.