| /*========================================================================= |
| * 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. |