/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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. */ package org.apache.geode.cache.query.internal; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.geode.cache.Region; import org.apache.geode.cache.query.AmbiguousNameException; import org.apache.geode.cache.query.FunctionDomainException; import org.apache.geode.cache.query.IndexType; import org.apache.geode.cache.query.NameResolutionException; import org.apache.geode.cache.query.QueryInvocationTargetException; import org.apache.geode.cache.query.QueryService; import org.apache.geode.cache.query.SelectResults; import org.apache.geode.cache.query.Struct; import org.apache.geode.cache.query.TypeMismatchException; import org.apache.geode.cache.query.internal.index.IndexData; import org.apache.geode.cache.query.internal.index.IndexProtocol; import org.apache.geode.cache.query.internal.index.IndexUtils; import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes; import org.apache.geode.cache.query.internal.types.StructTypeImpl; import org.apache.geode.cache.query.internal.types.TypeUtils; import org.apache.geode.cache.query.types.ObjectType; import org.apache.geode.cache.query.types.StructType; import org.apache.geode.internal.offheap.annotations.Retained; import org.apache.geode.pdx.PdxInstance; import org.apache.geode.pdx.internal.PdxString; /** * Comparison value: <, >, <=, >=, <>, = * */ public class CompiledComparison extends AbstractCompiledValue implements Negatable, OQLLexerTokenTypes, Indexable { // persistent inst vars public final CompiledValue _left; public 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.