| /*========================================================================= |
| * 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: CompiledJunction.java,v 1.2 2005/02/01 17:19:20 vaibhav Exp $ |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.cache.query.internal; |
| |
| import java.util.*; |
| |
| import com.gemstone.gemfire.InternalGemFireError; |
| import com.gemstone.gemfire.cache.EntryDestroyedException; |
| import com.gemstone.gemfire.cache.query.*; |
| import com.gemstone.gemfire.cache.query.internal.index.IndexProtocol; |
| import com.gemstone.gemfire.cache.query.internal.parse.OQLLexerTokenTypes; |
| import com.gemstone.gemfire.cache.query.internal.types.StructTypeImpl; |
| import com.gemstone.gemfire.cache.query.types.ObjectType; |
| import com.gemstone.gemfire.internal.Assert; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| |
| /** |
| * Conjunctions and Disjunctions (LITERAL_and LITERAL_or) As a part of feature |
| * development to ensure index usage for multiple regions & index usage in equi |
| * join conditions across the regions , a CompiledJunction's organized operands |
| * method creates internal structures like GroupJunction, AllGroupJunction & |
| * CompositeGroupJunction. The method createJunction is where the decision of |
| * creating an AllGroupJunction ( wrapping multiple GroupJunctions and |
| * CompositeGroupJunctions) or a single GroupJunction or a singe |
| * CompositeGroupJunction is taken. |
| * |
| * CompiledJunction -----> on organization of Operands may result in any |
| * of the following cases |
| * |
| * 1) CompiledJunction |
| * | |
| * | |
| * -------------------------------- |
| * | | |
| * CompiledJunction GroupJunction (Filter evaluable) |
| * (Filter evaluable) |
| * |
| * 2) CompiledJunction |
| * | |
| * | |
| * -------------------------------- |
| * | | |
| * CompiledJunction RangeJunction (Filter evaluable same index operands. May contain iter operands belonging to the same Group) |
| * (Filter evaluable) |
| * |
| * |
| * 3) CompiledJunction |
| * | |
| * | |
| * -------------------------------- |
| * | | |
| * CompiledJunction GroupJunction (Filter evaluable) |
| * (Filter evaluable) | |
| * | |
| * ---------------------------------------------- |
| * | | |
| * CompiledComparison RangeJunction |
| * (at least one filter evaluable compiled |
| * Comparison using an index shared by no other |
| * condition) |
| * |
| * |
| * 5) CompiledJunction |
| * | |
| * | |
| * -------------------------------- |
| * | | |
| * CompiledJunction GroupJunction (Filter evaluable) |
| * (Filter evaluable) | |
| * | |
| * --------------------------------------- |
| * | | | |
| * RangeJunction RangeJunction CompiledComparison |
| * (zero or more Filter evaluable or iter |
| * evaluable ) |
| * |
| * 6) CompiledJunction |
| * | |
| * ---------------------------------- |
| * | | |
| * CompiledJunction AllGroupJunction |
| * (filter evaluable) | |
| * ------------------------------------------------ |
| * | | | |
| * GroupJunction GroupJunction CompositeGroupJunction |
| * | |
| * --------------------------------------- |
| * | | | |
| * GroupJunction equi join conditions GroupJunction |
| * |
| * |
| * |
| * @version $Revision: 1.2 $ |
| * @author ericz |
| * @author Asif |
| */ |
| public class CompiledJunction extends AbstractCompiledValue implements |
| Negatable { |
| |
| /** left operand */ |
| private final CompiledValue[] _operands; |
| private int _operator = 0; |
| private List unevaluatedFilterOperands = null; |
| |
| |
| CompiledJunction(CompiledValue[] operands, int operator) { |
| // invariant: operator must be LITERAL_and or LITERAL_or |
| // invariant: at least two operands |
| if (!((operator == LITERAL_or || |
| operator == LITERAL_and) && operands.length >= 2)) { |
| throw new InternalGemFireError( |
| "operator=" + operator + "operands.length =" + operands.length); } |
| _operator = operator; |
| _operands = operands; |
| } |
| |
| @Override |
| public List getChildren() { |
| return Arrays.asList(this._operands); |
| } |
| |
| public int getType() { |
| return JUNCTION; |
| } |
| |
| public Object evaluate(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| Object r = _operands[0].evaluate(context); // UNDEFINED, null, or a Boolean |
| // if it's true, and op is or then return true immediately |
| // if it's false and the op is and then return false immediately |
| if (r instanceof Boolean) |
| if (((Boolean) r).booleanValue() && _operator == LITERAL_or) |
| return r; |
| else if (!((Boolean) r).booleanValue() && _operator == LITERAL_and) |
| return r; |
| if (r == null || r == QueryService.UNDEFINED) |
| r = QueryService.UNDEFINED; // keep going to see if we hit a |
| // short-circuiting truth value |
| else if (!(r instanceof Boolean)) |
| throw new TypeMismatchException(LocalizedStrings.CompiledJunction_LITERAL_ANDLITERAL_OR_OPERANDS_MUST_BE_OF_TYPE_BOOLEAN_NOT_TYPE_0.toLocalizedString(r.getClass().getName())); |
| for (int i = 1; i < _operands.length; i++) { |
| Object ri = null; |
| try { |
| ri = _operands[i].evaluate(context); // UNDEFINED, null, or |
| } catch (EntryDestroyedException ede) { |
| continue; |
| } |
| // Boolean |
| if (ri instanceof Boolean) |
| if (((Boolean) ri).booleanValue() && _operator == LITERAL_or) |
| return ri; |
| else if (!((Boolean) ri).booleanValue() && _operator == LITERAL_and) |
| return ri; |
| if (ri == null || ri == QueryService.UNDEFINED |
| || r == QueryService.UNDEFINED) { |
| r = QueryService.UNDEFINED; |
| continue; // keep going to see if we hit a short-circuiting truth value |
| } |
| else if (!(ri instanceof Boolean)) |
| throw new TypeMismatchException(LocalizedStrings.CompiledJunction_LITERAL_ANDLITERAL_OR_OPERANDS_MUST_BE_OF_TYPE_BOOLEAN_NOT_TYPE_0.toLocalizedString(ri.getClass().getName())); |
| // now do the actual and/or |
| if (_operator == LITERAL_and) |
| r = Boolean.valueOf(((Boolean) r).booleanValue() |
| && ((Boolean) ri).booleanValue()); |
| else |
| // LITERAL_or |
| r = Boolean.valueOf(((Boolean) r).booleanValue() |
| || ((Boolean) ri).booleanValue()); |
| } |
| return r; |
| } |
| |
| @Override |
| public Set computeDependencies(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, NameResolutionException { |
| for (int i = 0; i < _operands.length; i++) { |
| context.addDependencies(this, this._operands[i] |
| .computeDependencies(context)); |
| } |
| return context.getDependencySet(this, true); |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException |
| { |
| OrganizedOperands newOperands = organizeOperands(context); |
| SelectResults result = intermediateResults; |
| Support.Assert(newOperands.filterOperand != null); |
| // evaluate directly on the operand |
| result = newOperands.isSingleFilter ? (newOperands.filterOperand) |
| .filterEvaluate(context, result) : (newOperands.filterOperand) |
| .auxFilterEvaluate(context, result); |
| if (!newOperands.isSingleFilter) { |
| List unevaluatedOps = ((CompiledJunction)newOperands.filterOperand).unevaluatedFilterOperands; |
| if (unevaluatedOps != null) { |
| if (newOperands.iterateOperand == null) { |
| if (unevaluatedOps.size() == 1) { |
| newOperands.iterateOperand = (CompiledValue)unevaluatedOps.get(0); |
| } |
| else { |
| int len = unevaluatedOps.size(); |
| CompiledValue[] iterOps = new CompiledValue[len]; |
| for (int i = 0; i < len; i++) { |
| iterOps[i] = (CompiledValue)unevaluatedOps.get(i); |
| } |
| newOperands.iterateOperand = new CompiledJunction(iterOps, |
| getOperator()); |
| } |
| } |
| else { |
| // You cannot have two CompiledJunctions a the same level |
| if (newOperands.iterateOperand instanceof CompiledJunction) { |
| CompiledJunction temp = (CompiledJunction)newOperands.iterateOperand; |
| List prevOps = temp.getOperands(); |
| unevaluatedOps.addAll(prevOps); |
| } |
| else { |
| unevaluatedOps.add(newOperands.iterateOperand); |
| } |
| int totalLen = unevaluatedOps.size(); |
| CompiledValue[] combinedOps = new CompiledValue[totalLen]; |
| Iterator itr = unevaluatedOps.iterator(); |
| int j = 0; |
| while (itr.hasNext()) { |
| combinedOps[j++] = (CompiledValue)itr.next(); |
| } |
| newOperands.iterateOperand = new CompiledJunction(combinedOps, |
| getOperator()); |
| } |
| } |
| } |
| if (newOperands.iterateOperand != null) |
| // call private method here to evaluate |
| result = auxIterateEvaluate(newOperands.iterateOperand, context, result); |
| return result; |
| } |
| |
| private List getCondtionsSortedOnIncreasingEstimatedIndexResultSize( |
| ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| // The checks invoked before this function have ensured that all the |
| // operands are of type ComparisonQueryInfo and of the form 'var = constant'. |
| // Also need for sorting will not arise if there are only two operands |
| int len = this._operands.length; |
| List sortedList = new ArrayList(len); |
| for (int i = 0; i < len; ++i) { |
| Filter toSort = (Filter) this._operands[i]; |
| int indxRsltToSort = toSort.getSizeEstimate(context); |
| int sortedListLen = sortedList.size(); |
| int j = 0; |
| for (; j < sortedListLen; ++j) { |
| Filter currSorted = (Filter) sortedList.get(j); |
| if (currSorted.getSizeEstimate(context) > indxRsltToSort) { |
| break; |
| } |
| } |
| sortedList.add(j, toSort); |
| } |
| return sortedList; |
| } |
| |
| /** |
| * invariant: all operands are known to be evaluated as a filter no operand |
| * organization is necessary |
| */ |
| @Override |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| // evaluate the result set from the indexed values |
| // using the intermediate results so far (passed in) |
| // put results into new intermediate results |
| List sortedConditionsList = this.getCondtionsSortedOnIncreasingEstimatedIndexResultSize(context); |
| |
| // Sort the operands in increasing order of resultset size |
| Iterator sortedConditionsItr = sortedConditionsList.iterator(); |
| while (sortedConditionsItr.hasNext()) { |
| // Asif:TODO The intermediate ResultSet should be passed as null when invoking |
| // filterEvaluate. Just because filterEvaluate is being called, itself |
| // guarantees that there will be at least on auxFilterEvalaute call. |
| // The filterEvalaute will return after invoking auxIterEvaluate. |
| // The auxIterEvaluate cannot get invoked for an OR junction. |
| // The approach of calculation of resultset should be Bubble UP ( i.e |
| // bottom to Top of the tree. This implies that for filterEvaluate, we |
| // should not be passing IntermediateResultSet. |
| // Each invocation of filterEvaluate will be complete in itself with the |
| // recursion being ended by evaluating auxIterEvaluate if any. The passing |
| // of IntermediateResult in filterEvalaute causes AND junction evaluation |
| // to be corrupted , if the intermediateResultset contains some value. |
| SelectResults filterResults = ((Filter) sortedConditionsItr.next()).filterEvaluate(context, null); |
| if (_operator == LITERAL_and) { |
| if (filterResults != null && filterResults.isEmpty()) { |
| return filterResults; |
| } |
| else if (filterResults != null) { |
| intermediateResults = (intermediateResults == null) ? filterResults |
| : QueryUtils.intersection(intermediateResults, filterResults, context); |
| |
| sortedConditionsItr.remove(); |
| |
| if (intermediateResults.size() <= indexThresholdSize) { |
| // Abort further intersection , the remaining filter operands will be |
| // transferred for |
| // iter evaluation |
| break; |
| } |
| } |
| } |
| else { |
| //Asif : In case of OR clause, the filterEvaluate cannot return a |
| // null value ,as a null value implies Cartesian of Iterators |
| // in current scope which can happen only if the operand of OR |
| // clause evaluates to true (& which we are not allowing). |
| // Though if the operand evaluates to false,it is permissible case |
| // for filterEvaluation. |
| Assert.assertTrue(filterResults != null); |
| intermediateResults = intermediateResults == null ? filterResults |
| : QueryUtils.union(intermediateResults, filterResults, context); |
| } |
| } |
| if (_operator == LITERAL_and && !sortedConditionsList.isEmpty()) { |
| this.unevaluatedFilterOperands = sortedConditionsList; |
| } |
| return intermediateResults; |
| } |
| |
| /** invariant: the operand is known to be evaluated by iteration */ |
| SelectResults auxIterateEvaluate(CompiledValue operand, |
| ExecutionContext context, SelectResults intermediateResults) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // Asif: This can be a valid value if we have an AND condition like ID=1 AND |
| // true = true. In such cases , if an index is not available on ID still we |
| // have at least one expression which is independent of current scope. In |
| // such cases a value of null means ignore the null ( because it means all |
| // the results ) thus a the result-set of current auxIterate which will be |
| // the subset of total result-set , will be the right data. |
| |
| // auxIterEvalaute can be called only from an AND junction. |
| |
| // This also implies that there will be at least one operand which will be |
| // evaluated using auxFilterEvaluate.Thus the result-set given to this |
| // function can be empty ( obtained using auxFilterEvaluate ,meaning no need |
| // to do iterations below ) but it can never be null. As null itself |
| // signifies that the junction cannot be evaluated as a filter. |
| if (intermediateResults == null) |
| throw new RuntimeException(LocalizedStrings.CompiledJunction_INTERMEDIATERESULTS_CAN_NOT_BE_NULL.toLocalizedString()); |
| if (intermediateResults.isEmpty()) // short circuit |
| return intermediateResults; |
| List currentIters = context.getCurrentIterators(); |
| RuntimeIterator rIters[] = new RuntimeIterator[currentIters.size()]; |
| currentIters.toArray(rIters); |
| ObjectType elementType = intermediateResults.getCollectionType() |
| .getElementType(); |
| SelectResults resultSet ; |
| if(elementType.isStructType()) { |
| resultSet = QueryUtils.createStructCollection(context, (StructTypeImpl) elementType) ; |
| }else { |
| resultSet = QueryUtils.createResultCollection(context, elementType); |
| } |
| |
| |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.startIteration(intermediateResults, operand); |
| Iterator iResultsIter = intermediateResults.iterator(); |
| while (iResultsIter.hasNext()) { |
| Object tuple = iResultsIter.next(); |
| if (tuple instanceof Struct) { |
| Object values[] = ((Struct) tuple).getFieldValues(); |
| for (int i = 0; i < values.length; i++) { |
| rIters[i].setCurrent(values[i]); |
| } |
| } |
| else { |
| rIters[0].setCurrent(tuple); |
| } |
| Object result = null; |
| try { |
| result = operand.evaluate(context); |
| } |
| finally { |
| observer.afterIterationEvaluation(result); |
| } |
| if (result instanceof Boolean) { |
| if (((Boolean) result).booleanValue()) resultSet.add(tuple); |
| } |
| else if (result != null && result != QueryService.UNDEFINED) |
| throw new TypeMismatchException(LocalizedStrings.CompiledJunction_ANDOR_OPERANDS_MUST_BE_OF_TYPE_BOOLEAN_NOT_TYPE_0.toLocalizedString(result.getClass().getName())); |
| } |
| } |
| finally { |
| observer.endIteration(resultSet); |
| } |
| return resultSet; |
| } |
| |
| /** |
| * invariant: all operands are known to be evaluated as a filter no operand |
| * organization is necessary |
| */ |
| public void negate() { |
| _operator = inverseOperator(_operator); |
| for (int i = 0; i < _operands.length; i++) { |
| if (_operands[i] instanceof Negatable) |
| ((Negatable) _operands[i]).negate(); |
| else |
| _operands[i] = new CompiledNegation(_operands[i]); |
| } |
| } |
| |
| @Override |
| protected PlanInfo protGetPlanInfo(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| Support.Assert(_operator == LITERAL_or || _operator == LITERAL_and); |
| PlanInfo resultPlanInfo = new PlanInfo(); |
| // set default evalAsFilter depending on operator |
| boolean isOr = (_operator == LITERAL_or); |
| resultPlanInfo.evalAsFilter = isOr; |
| // collect indexes |
| // for LITERAL_and operator, if any say yes to filter, |
| // then change default evalAsFilter from false to true |
| // of LITERAL_or operator, if any say no to filter, change to false |
| for (int i = 0; i < _operands.length; i++) { |
| PlanInfo opPlanInfo = _operands[i].getPlanInfo(context); |
| resultPlanInfo.indexes.addAll(opPlanInfo.indexes); |
| if (!isOr && opPlanInfo.evalAsFilter) { |
| resultPlanInfo.evalAsFilter = true; |
| } |
| else if (isOr && !opPlanInfo.evalAsFilter) { |
| resultPlanInfo.evalAsFilter = false; |
| } |
| } |
| return resultPlanInfo; |
| } |
| |
| /* Package methods */ |
| public int getOperator() { |
| return _operator; |
| } |
| |
| List getOperands() { |
| // return unmodifiable copy |
| return Collections.unmodifiableList(Arrays.asList(_operands)); |
| } |
| |
| |
| /** |
| * TODO: Should composite operands be part of iterator operands of |
| * CompiledJunction or should it be part of AllGroupJunction Write a unit Test |
| * for this function. |
| * |
| * Asif: The iterators which can be considered part of GroupJunction are |
| * those which are exclusively dependent only on the independent |
| * iterator of the group. Any iterator which is ultimately dependent |
| * on more than one independent iterators cannot be assumed to be part of |
| * the GroupJunction, even if one of independent iterator belongs to a |
| * different scope. |
| * |
| * @param context |
| * @return New combination of AbstractCompiledValue(s) in form of CompiledJunction. |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException |
| * @throws NameResolutionException |
| * @throws QueryInvocationTargetException |
| */ |
| OrganizedOperands organizeOperands(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // get the list of operands to evaluate, and evaluate operands that can use |
| // indexes first. |
| List evalOperands = new ArrayList(_operands.length); |
| int indexCount = 0; |
| // TODO: Check if we can defer the creation of this array list only |
| // if there exists an eval operand |
| List compositeIterOperands = new ArrayList(_operands.length); |
| // Asif: This Map will contain as key the composite filter operand & as |
| // value , the set containing independent RuntimeIterators ( which will |
| // necessarily be two ) |
| Map compositeFilterOpsMap = new LinkedHashMap(); |
| Map iterToOperands = new HashMap(); |
| CompiledValue operand = null; |
| boolean isJunctionNeeded = false; |
| |
| for (int i = 0; i < _operands.length; i++) { |
| // Asif : If we are inside this function this itself indicates |
| // that there exists at least on operand which can be evaluated |
| // as an auxFilterEvaluate. If any operand even if its flag of |
| // evalAsFilter happens to be false, but if it is independently |
| // evaluable, we should attach it with the auxFilterevaluable |
| // operands irrespective of its value ( either true or false.) |
| // This is because if it is true or false, it can always be paired |
| // up with other filter operands for AND junction. But for |
| // OR junction it can get paired with the filterOperands only |
| // if it is false. But we do not have to worry about whether |
| // the junction is AND or OR because, if the independent operand's |
| // value is true & was under an OR junction , it would not have |
| // been filterEvaluated. Instead it would have gone for auxIterEvaluate. |
| // We are here itself implies, that any independent operand can be |
| // either true or false for an AND junction but always false for an |
| // OR Junction. |
| operand = this._operands[i]; |
| if (!operand.isDependentOnCurrentScope(context)) { |
| indexCount++; |
| // Asif Ensure that independent operands are always at the start |
| evalOperands.add(0, operand); |
| } |
| else if (operand instanceof CompiledJunction) { |
| if (operand.getPlanInfo(context).evalAsFilter) { |
| // Asif Ensure that independent operands are always at the start |
| evalOperands.add(indexCount++, operand); |
| } |
| else { |
| evalOperands.add(operand); |
| } |
| } |
| else { |
| // If the operand happens to be a Like predicate , this is the point |
| // where we can expand as now the structures created are all for the current |
| // execution. We expand only if the current junction is AND because if its |
| // OR we cannot bring Like at level of OR , because like itself is an AND |
| // Also cannot expand for NOT LIKE because CCs generated by CompiledLike with AND |
| // will be converted to OR by negation |
| CompiledValue expandedOperands[] = null; |
| |
| if (operand.getType() == LIKE |
| && this._operator == OQLLexerTokenTypes.LITERAL_and |
| && ((CompiledLike) operand).getOperator() != OQLLexerTokenTypes.TOK_NE) { |
| expandedOperands = ((CompiledLike) operand) |
| .getExpandedOperandsWithIndexInfoSetIfAny(context); |
| } else { |
| expandedOperands = new CompiledValue[] { operand }; |
| } |
| |
| for (CompiledValue expndOperand : expandedOperands) { |
| isJunctionNeeded = isJunctionNeeded |
| || expndOperand.getPlanInfo(context).evalAsFilter; |
| Set set = QueryUtils.getCurrentScopeUltimateRuntimeIteratorsIfAny( |
| expndOperand, context); |
| if (set.size() != 1) { |
| // Asif: A set of size not equal to 1 ( which can mean anything more |
| // than 1, will mean a composite condition. For a Composite |
| // Condition which is filter evaluable that means necessarily that |
| // RHS is dependent on one independent iterator & LHS on the other. |
| if ((expndOperand.getPlanInfo(context).evalAsFilter)) { |
| Support.Assert(set.size() == 2, |
| " The no of independent iterators should be equal to 2"); |
| compositeFilterOpsMap.put(expndOperand, set); |
| } |
| else { |
| compositeIterOperands.add(expndOperand); |
| } |
| } |
| else { |
| Support |
| .Assert(set.size() == 1, |
| "The size has to be 1 & cannot be zero as that would mean it is independent"); |
| RuntimeIterator rIter = (RuntimeIterator)set.iterator().next(); |
| List operandsList = (List)iterToOperands.get(rIter); |
| if (operandsList == null) { |
| operandsList = new ArrayList(); |
| iterToOperands.put(rIter, operandsList); |
| } |
| operandsList.add(expndOperand); |
| } |
| } |
| } |
| } |
| /* |
| * Asif : If there exists a SingleGroupJunction & no other Filter operand , |
| * then all remaining eval operands ( even if they are CompiledJunction which |
| * are not evaluable as Filter) & composite operands should become part of |
| * GroupJunction. If there exists only SingleGroupJunction & at least one |
| * Filter operand, then all conditions of GroupJunction which are not filter |
| * evaluable should become iter operand of CompiledJunction. If there |
| * exists an AllGroupJunction & no Filter operand, then all remaining iter |
| * operands of CompiledJunction along with Composite operands should be part |
| * of AllGroupJunction. If there exists at least one Filter operand , all |
| * remaining operands of AllGroupJunction ( including those GroupJunction not |
| * filter evaluable should become part of eval operand of |
| * CompiledJunction) The index count will give the idea of number of filter |
| * operands |
| */ |
| if (isJunctionNeeded) { |
| // There exists at least one condition which must have an index available. |
| Filter junction = createJunction(compositeIterOperands, |
| compositeFilterOpsMap, iterToOperands, context, indexCount, |
| evalOperands); |
| // Asif Ensure that independent operands are always at the start |
| evalOperands.add(indexCount++, junction); |
| } |
| else { |
| // if(compositeIterOperands != null) { |
| if (!compositeIterOperands.isEmpty()) { |
| evalOperands.addAll(compositeIterOperands); |
| } |
| Iterator itr = iterToOperands.values().iterator(); |
| while (itr.hasNext()) { |
| evalOperands.addAll((List) itr.next()); |
| } |
| } |
| OrganizedOperands result = new OrganizedOperands(); |
| // Group the operands into those that use indexes and those that don't, |
| // so we can evaluate all the operands that don't use indexes together in |
| // one iteration first get filter operands |
| Filter filterOperands = null; |
| // there must be at least one filter operand or else we wouldn't be here |
| Support.Assert(indexCount > 0); |
| if (indexCount == 1) { |
| filterOperands = (Filter) evalOperands.get(0); |
| result.isSingleFilter = true; |
| } |
| else { |
| CompiledValue[] newOperands = new CompiledValue[indexCount]; |
| for (int i = 0; i < indexCount; i++) |
| newOperands[i] = (CompiledValue) evalOperands.get(i); |
| filterOperands = new CompiledJunction(newOperands, _operator); |
| } |
| // get iterating operands |
| // INVARIANT: the number of iterating operands when the _operator is |
| // LITERAL_or must be zero. This is an invariant on filter evaluation |
| CompiledValue iterateOperands = null; |
| //int numIterating = _operands.length - indexCount; |
| int numIterating = evalOperands.size() - indexCount; |
| Support.Assert(_operator == LITERAL_and || numIterating == 0); |
| if (numIterating > 0) { |
| if (numIterating == 1) |
| iterateOperands = (CompiledValue) evalOperands.get(indexCount); |
| else { |
| CompiledValue[] newOperands = new CompiledValue[numIterating]; |
| for (int i = 0; i < numIterating; i++) |
| newOperands[i] = (CompiledValue) evalOperands.get(i + indexCount); |
| iterateOperands = new CompiledJunction(newOperands, _operator); |
| } |
| } |
| result.filterOperand = filterOperands; |
| result.iterateOperand = iterateOperands; |
| return result; |
| } |
| |
| /** |
| * Creates a GroupJunction or a RangeJunction based on the operands passed. |
| * The operands are either Filter Operands belonging to single independent |
| * RuntimeIterator or are iter evaluable on the RuntimeIterator[] passed as |
| * parameter. If all the filter evaluable operands use a single Index , than |
| * a RangeJunction is created with iter operand as part of RangeJunction. If |
| * there exists operands using multiple indexes, then a GroupJunction is |
| * created. In that case , the operands using the same index are grouped in a |
| * RangeJunction & that Range Junction becomes part of the GroupJunction. A |
| * GroupJunction may contain one or more RangeJunction |
| * |
| * @param needsCompacting |
| * boolean indicating if there is a possibility of |
| * RangeJunction or not. If needsCompacting is false , then a |
| * GroupJunction will be formed, without any RangeJunction |
| * @param grpIndpndntItr |
| * Array of RuntimeIterator which represents the independent |
| * iterator for the Group. for all cases , except a |
| * CompositeGroupJunction containing a single GroupJunction , |
| * will have a single RuntimeIterator in this array. |
| * @param completeExpnsn |
| * boolean indicating whether the Group or Range Junction needs |
| * to be expanded to the query from clause level or the group |
| * level |
| * @param cv |
| * Array of CompiledValue containing operands belonging to a |
| * group |
| * @param sameIndexOperands |
| * Map object containing Index as the key & as a value an |
| * Integer object or a List. If it contains an Integer as value |
| * against Index, it implies that there is only one operand |
| * which uses that type of index & hence cannot form a |
| * RangeJunction. If it contains a List then the Lis contains |
| * the operands which use same index & thus can form a |
| * RangeJunction |
| * @return Object of GroupJunction or RangeJunction as the case may be |
| */ |
| private AbstractGroupOrRangeJunction createGroupJunctionOrRangeJunction( |
| boolean needsCompacting, RuntimeIterator[] grpIndpndntItr, |
| boolean completeExpnsn, CompiledValue[] cv, Map sameIndexOperands) { |
| AbstractGroupOrRangeJunction junction; |
| if (needsCompacting) { |
| Iterator itr = sameIndexOperands.values().iterator(); |
| if (sameIndexOperands.size() == 1) { |
| // This means that there exists only one type of Index for the whole |
| // Group. Thus overall we should just create a RangeJunction. It also |
| // means that the rest of the operands are the Iter Operands ( they may |
| // contain those operands which are not index evaluable but evaluate to |
| // true/false ( not dependent on current scope) |
| List sameIndexOps = (List)itr.next(); |
| Iterator sameIndexOpsItr = sameIndexOps.iterator(); |
| for (int i = 0; i < cv.length; ++i) { |
| if (cv[i] == null) { |
| cv[i] = (CompiledValue)sameIndexOpsItr.next(); |
| } |
| } |
| junction = new RangeJunction(this._operator, grpIndpndntItr, |
| completeExpnsn, cv); |
| } |
| else { |
| int numRangeJunctions = 0; |
| CompiledValue[] rangeJunctions = new CompiledValue[sameIndexOperands |
| .size()]; |
| int nullifiedFields = 0; |
| while (itr.hasNext()) { |
| Object listOrPosition = itr.next(); |
| if (listOrPosition instanceof List) { |
| List ops = (List)listOrPosition; |
| nullifiedFields += ops.size(); |
| CompiledValue operands[] = (CompiledValue[])ops |
| .toArray(new CompiledValue[ops.size()]); |
| rangeJunctions[numRangeJunctions++] = new RangeJunction( |
| this._operator, grpIndpndntItr, completeExpnsn, operands); |
| } |
| } |
| int totalOperands = cv.length - nullifiedFields + numRangeJunctions; |
| CompiledValue[] allOperands = new CompiledValue[totalOperands]; |
| // Fill the Non RangeJunction operands first |
| int k = 0; |
| for (int i = 0; i < cv.length; ++i) { |
| CompiledValue tempCV = cv[i]; |
| if (tempCV != null) { |
| allOperands[k++] = tempCV; |
| } |
| } |
| for (int i = 0; i < numRangeJunctions; ++i) { |
| allOperands[k++] = rangeJunctions[i]; |
| } |
| junction = new GroupJunction(this._operator, grpIndpndntItr, |
| completeExpnsn, allOperands); |
| } |
| } |
| else { |
| junction = new GroupJunction(this._operator, grpIndpndntItr, |
| completeExpnsn, cv); |
| } |
| return junction; |
| } |
| |
| private boolean sortSameIndexOperandsForGroupJunction(CompiledValue cv[], |
| List operandsList, Map sameIndexOperands, ExecutionContext context) |
| throws AmbiguousNameException, TypeMismatchException, |
| NameResolutionException, FunctionDomainException, |
| QueryInvocationTargetException { |
| int size = operandsList.size(); |
| CompiledValue tempOp = null; |
| boolean needsCompacting = false; |
| for (int i = 0; i < size; ++i) { |
| tempOp = (CompiledValue)operandsList.get(i); |
| IndexInfo[] indx = null; |
| Object listOrPosition = null; |
| boolean evalAsFilter = tempOp.getPlanInfo(context).evalAsFilter; |
| //TODO:Do not club Like predicate in an existing range |
| if (evalAsFilter ) { |
| indx = ((Indexable)tempOp).getIndexInfo(context); |
| Assert.assertTrue(indx.length == 1, |
| "There should have been just one index for the condition"); |
| listOrPosition = sameIndexOperands.get(indx[0]._index); |
| } |
| if (listOrPosition != null) { |
| if (listOrPosition instanceof Integer) { |
| int position = ((Integer)listOrPosition).intValue(); |
| List operands = new ArrayList(size); |
| operands.add(cv[position]); |
| operands.add(tempOp); |
| cv[position] = null; |
| sameIndexOperands.put(indx[0]._index, operands); |
| needsCompacting = true; |
| } |
| else { |
| List operands = (List)listOrPosition; |
| operands.add(tempOp); |
| } |
| } |
| else { |
| cv[i] = tempOp; |
| // TODO: Enable only for AND junction for now |
| if (evalAsFilter && this._operator == OQLLexerTokenTypes.LITERAL_and) { |
| sameIndexOperands.put(indx[0]._index, Integer.valueOf(i)); |
| } |
| } |
| } |
| return needsCompacting; |
| } |
| //This is called only if the CompiledJunction was either independent or filter evaluable. |
| public int getSizeEstimate(ExecutionContext context)throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| if( this.isDependentOnCurrentScope(context)) { |
| return Integer.MAX_VALUE; |
| }else { |
| return 0; |
| } |
| } |
| |
| // TODO: Optmize this function further in terms of creation of Arrays & |
| // Lists |
| private Filter createJunction(List compositeIterOperands, |
| Map compositeFilterOpsMap, Map iterToOperands, ExecutionContext context, |
| int indexCount, List evalOperands) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| Support.Assert(!(iterToOperands.isEmpty() && compositeFilterOpsMap |
| .isEmpty()), |
| " There should not have been any need to create a Junction"); |
| CompiledValue junction = null; |
| int size; |
| /*---------- Create only a GroupJunction */ |
| if (iterToOperands.size() == 1 && compositeFilterOpsMap.isEmpty()) { |
| // Asif :Create only a GroupJunction. The composite conditions can be |
| // evaluated as iter operands inside GroupJunction. |
| Map.Entry entry = (Map.Entry) iterToOperands.entrySet().iterator().next(); |
| List operandsList = (List) entry.getValue(); |
| // Asif: If there exists no other filter operand, transfer all the |
| // remaining iter operands to the Group Junction. Else transfer all the |
| // remaining operands to eval operands of CompiledJunctio. This means that |
| // we are following the logic that as far as possible minimize the |
| // result-set used for iter evaluation of conditions without index. |
| // This way we also save on extra iterations for eval conditions if they |
| // existed both in GroupJunction as well as CompiledJunction. The only |
| // exception in this logic is the iter evaluable conditions belonging to a |
| // GroupJunction. |
| // If a condition is dependent on a Single Group & is not filter evaluable |
| // we do not push it out to iter operands of AllGroupJunction or Compiled |
| // Junction , but make it part of GroupJunction itself. This helps bcoz we |
| // are sure that the condition will be completely evaluable by the |
| // iterators of that group itself. |
| if (indexCount == 0) { |
| operandsList.addAll(evalOperands); |
| evalOperands.clear(); |
| if (!compositeIterOperands.isEmpty()) { |
| operandsList.addAll(compositeIterOperands); |
| } |
| } |
| else { |
| if (!compositeIterOperands.isEmpty()) { |
| evalOperands.addAll(compositeIterOperands); |
| } |
| Iterator itr = operandsList.iterator(); |
| while (itr.hasNext()) { |
| CompiledValue cv = (CompiledValue) itr.next(); |
| // Asif : Those conditions not filter evaluable are transferred to |
| // eval operand of CompiledJunction. |
| if (!cv.getPlanInfo(context).evalAsFilter) { |
| itr.remove(); |
| evalOperands.add(cv); |
| } |
| } |
| } |
| size = operandsList.size(); |
| Map sameIndexOperands = new HashMap(size); |
| CompiledValue cv[] = new CompiledValue[size]; |
| //Enable only for AND junction |
| boolean needsCompacting = sortSameIndexOperandsForGroupJunction(cv,operandsList, sameIndexOperands, context); |
| junction = createGroupJunctionOrRangeJunction(needsCompacting, new RuntimeIterator[] { (RuntimeIterator)entry.getKey()}, true /* need a Complete expansion */, cv, sameIndexOperands); |
| } |
| /* |
| * Asif : An AllGroupJunction or a CompositeGroupJunction can get created. |
| * An AllGroupJunction will get created , if there exists a need to create |
| * more than one CompositeGroupJunction AND/OR more than one GroupJunctions ( |
| * not part of CompositeGroupJunction) If suppose the where clause contained |
| * two sets of conditions each dependent exclusively on a separate |
| * independent group ( implying two independent groups) then we should |
| * create an AllGroupJunction iff the two Groups are both filter |
| * evaluable. If an AllGroupJunction contains only one filter evaluable |
| * Group & no CompositeGroupJunction then we should just create a |
| * singleGroupJunction with complete expansion as true |
| */ |
| else { |
| // Asif : First create the required amount of CompositeGroupJunctions |
| // A map which keeps track of no. of independent iterators pointing to the |
| // same CompositeGroupJunction. The integer count will keep track of |
| // number of different CompositeGroupJunctions which got created. |
| Map tempMap = new HashMap(); |
| Set entries = compositeFilterOpsMap.entrySet(); |
| Set cgjs = new HashSet(compositeFilterOpsMap.size()); |
| Iterator entryItr = entries.iterator(); |
| while (entryItr.hasNext()) { |
| Map.Entry entry = (Map.Entry) entryItr.next(); |
| CompiledValue op = (CompiledValue) entry.getKey(); |
| Set indpndtItrSet = (Set) entry.getValue(); |
| Iterator itr = indpndtItrSet.iterator(); |
| CompositeGroupJunction cgj = null; |
| RuntimeIterator[] absentItrs = new RuntimeIterator[2]; |
| int k = 0; |
| while (itr.hasNext()) { |
| RuntimeIterator ritr = (RuntimeIterator) itr.next(); |
| CompositeGroupJunction temp = (CompositeGroupJunction) tempMap |
| .get(ritr); |
| if (temp == null) { |
| absentItrs[k++] = ritr; |
| } |
| if (cgj == null && temp != null) { |
| cgj = temp; |
| cgj.addFilterableCompositeCondition(op); |
| } |
| else if (cgj != null && temp != null && cgj != temp) { |
| // Asif A peculiar case wherein we have some thing like four regions |
| // with relation ship conditions like r1 = r2 and r3 = r4 and r4 = |
| // r1. In such situations we should just have a single |
| // CompositeJunction containing r1 r2 r3 and r4 We will merge temp |
| // into CompositeGroupJunction & change all the values in tempMap |
| // which contained temp object to CompositeGroupJunction. |
| // Also we will remove the entry of temp from CompositeGroupJunctions. |
| cgj.mergeFilterableCCsAndIndependentItrs(temp); |
| cgjs.remove(temp); |
| Iterator entriesItr = tempMap.entrySet().iterator(); |
| Map.Entry tempEntry = null; |
| while (entriesItr.hasNext()) { |
| tempEntry = (Map.Entry) entriesItr.next(); |
| if (tempEntry.getValue() == temp) { |
| tempEntry.setValue(cgj); |
| } |
| } |
| } |
| } |
| if (cgj == null) { |
| // Asif Create a new CompositeGroupJunction |
| cgj = new CompositeGroupJunction(this._operator, op /* CompositeFilterOperand */); |
| cgjs.add(cgj); |
| } |
| for (int i = 0; i < k; ++i) { |
| tempMap.put(absentItrs[i], cgj); |
| cgj.addIndependentIterators(absentItrs[i]); |
| } |
| } |
| // Asif :If no of cgj is zero definitely there will be an AllGroupJunction |
| // If no. of cgj is more than 1 , then also there will be an AllGroupJunction. |
| // But if no. of cgj is excactly one & all the GroupJunctions can be |
| // accomodated within the CGJ , then we will have CompsiteGroupJunction. |
| // Also , those composite conditions which are not filter evaluable will |
| // be added to CompositeGroupJunction as iter operands only if we don't |
| // have any additional filter & CompositeGroupJunction is the only |
| // junction which will get created. If there exists any other filter then |
| // it will be part of CompiledJunction's iter operand. Basically we will |
| // not distinguish between various CompositeConditions which are not |
| // filter evaluable & will not try to place it in various |
| // ComspoiteGroupJunctions to which they belong. |
| List gjs = new ArrayList(iterToOperands.size()); |
| Iterator itr = iterToOperands.entrySet().iterator(); |
| Map.Entry entry; |
| while (itr.hasNext()) { |
| entry = (Map.Entry)itr.next(); |
| List operandsList = (List)entry.getValue(); |
| CompiledValue cv[] = new CompiledValue[size = operandsList.size()]; |
| int j = 0; |
| // Asif:An individual GroupJunction is Filter evaluable if at least one |
| // operand is filter evaluable. |
| boolean evalAsFilter = false; |
| boolean needsCompacting = false; |
| CompiledValue tempOp = null; |
| Map sameIndexOperands = new HashMap(size); |
| |
| for (; j < size; ++j) { |
| tempOp = (CompiledValue)operandsList.get(j); |
| boolean isFilterevaluable = tempOp.getPlanInfo(context).evalAsFilter; |
| evalAsFilter = evalAsFilter || isFilterevaluable; |
| IndexInfo[] indx = null; |
| Object listOrPosition = null; |
| if (isFilterevaluable) { |
| indx = ((Indexable)tempOp).getIndexInfo(context); |
| Assert.assertTrue(indx.length == 1, |
| "There should have been just one index for the condition"); |
| listOrPosition = sameIndexOperands.get(indx[0]._index); |
| } |
| if (listOrPosition != null) { |
| if (listOrPosition instanceof Integer) { |
| int position = ((Integer)listOrPosition).intValue(); |
| List operands = new ArrayList(size); |
| operands.add(cv[position]); |
| operands.add(tempOp); |
| cv[position] = null; |
| sameIndexOperands.put(indx[0]._index, operands); |
| needsCompacting = true; |
| } |
| else { |
| List operands = (List)listOrPosition; |
| operands.add(tempOp); |
| } |
| } |
| else { |
| cv[j] = tempOp; |
| if (isFilterevaluable |
| && this._operator == OQLLexerTokenTypes.LITERAL_and) { |
| sameIndexOperands.put(indx[0]._index, Integer.valueOf(j)); |
| } |
| } |
| } |
| if (!evalAsFilter) { |
| // Asif : If there exists at least one Filter then those GroupJunction |
| // which are not Filter evaluable should become part of eval operands |
| // of CompiledJunction else add the Group operands which is not filter |
| // evaluable to the Composite iterator oprands |
| List toAddTo = null; |
| if (indexCount > 0) { |
| toAddTo = evalOperands; |
| } |
| else { |
| toAddTo = compositeIterOperands; |
| } |
| for (int i = 0; i < size; ++i) { |
| toAddTo.add(cv[i]); |
| } |
| } |
| else { |
| RuntimeIterator grpIndpndtItr = (RuntimeIterator)entry.getKey(); |
| AbstractGroupOrRangeJunction gj = createGroupJunctionOrRangeJunction( |
| needsCompacting, new RuntimeIterator[] { (RuntimeIterator)entry |
| .getKey() }, false/* Expand only to Group Level */, cv, |
| sameIndexOperands); |
| CompositeGroupJunction cgj = null; |
| if ((cgj = (CompositeGroupJunction)tempMap.get(grpIndpndtItr)) != null) { |
| cgj.addGroupOrRangeJunction(gj); |
| } |
| else { |
| gjs.add(gj); |
| } |
| } |
| } |
| if (indexCount == 0) { |
| // Asif : all the eval operands of CompiledJunction should be part of |
| // AllGroupJunction |
| compositeIterOperands.addAll(evalOperands); |
| evalOperands.clear(); |
| } |
| else { |
| // Asif : Add the composite operands to the eval operands so that they |
| // are evaluated as part of CompiledJunction. |
| if (!compositeIterOperands.isEmpty()) { |
| evalOperands.addAll(compositeIterOperands); |
| } |
| } |
| // Asif : An AllGroupJunction will be created iff either the List |
| // containing GroupJunctions is not empty ot there exists multiple |
| // CompositeGroupJunctions Else a CompositeGroupJunction will be created |
| // Convert the List of Indpendent Runtime Iterators of |
| // CompositeGroupJunction into Array of Indpendent RuntimeIterators |
| Iterator cgjItr = cgjs.iterator(); |
| while (cgjItr.hasNext()) { |
| ((CompositeGroupJunction) cgjItr.next()).setArrayOfIndependentItrs(); |
| } |
| int cgjSize = cgjs.size(); |
| if (gjs.isEmpty() && cgjSize == 1) { |
| CompositeGroupJunction compGrpJnc = (CompositeGroupJunction) cgjs |
| .iterator().next(); |
| compGrpJnc.addIterOperands(compositeIterOperands); |
| compGrpJnc.setCompleteExpansionOn(); |
| junction = compGrpJnc; |
| } |
| else if (gjs.size() == 1 && cgjSize == 0) { |
| // Asif: There exists only a single Filterable GroupJunction & no |
| // CompositeGroupJunction so we should just create a GroupJunction |
| // instead of an AllGroupJunction . ( In this way the remaining iter |
| // operands will get evaluated during the filter evaluation of |
| // GroupJunction) For such GroupJunction complete expansion needs to be |
| // true & the list of composite iter operands ( which at this point |
| // contains the iter operands, should be added to the operands of |
| // GroupJunction where they will get orgainzed as iter operands during |
| // the call of organizeOperands in GroupJunction |
| AbstractGroupOrRangeJunction gjTemp = (AbstractGroupOrRangeJunction) gjs.get(0); |
| compositeIterOperands.addAll(gjTemp.getOperands()); |
| CompiledValue newOps[] = new CompiledValue[compositeIterOperands.size()]; |
| compositeIterOperands.toArray(newOps); |
| // Asif : If gjTemp is a RangeJunction, we will get an instance of |
| // RangeJunction else we will get an instance of GroupJunction. |
| junction = gjTemp.createNewOfSameType(this._operator,gjTemp.getIndependentIteratorForGroup(), true/* The expansion needs to be complete */, newOps); |
| |
| } |
| else { |
| gjs.addAll(cgjs); |
| junction = new AllGroupJunction(gjs, this._operator, compositeIterOperands); |
| } |
| } |
| return (Filter) junction; |
| } |
| |
| // Asif : This function provides package visbility for testing the |
| // output of organizeOperands function. |
| OrganizedOperands testOrganizedOperands(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| return organizeOperands(context); |
| } |
| |
| @Override |
| public boolean isProjectionEvaluationAPossibility(ExecutionContext context) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException |
| { |
| for (int i=0; i<this._operands.length; ++i) { |
| // LIKE gives rise to a JUNCTION in CompiledLike whether wildcard is present or not |
| if ((this._operands[i].getType() == JUNCTION || this._operands[i] |
| .getType() == LIKE) |
| && this._operands[i].getPlanInfo(context).evalAsFilter) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* |
| * This method checks the limit applicability on index intermediate results for |
| * junctions and optimizes the limit on index intermediate results ONLY if ONE index |
| * is used for whole query and all conditions in where clause use that index. Look at |
| * the call hierarchy of this function. There are two cases: |
| * |
| * Literal_OR: A junction with OR will contain operands which are CompiledComparison or |
| * CompiledJunction (or subjunction). we recursively check if all of those use the same index |
| * and if ANY one of those comparisons or subjunctions does not use the index, it retruns false. |
| * |
| * Literal_AND: If we get combination of comparisons and subjunctions then limit is NOT |
| * applicable on index results. Like, "where ID != 10 AND (ID < 5 OR ID > 10) LIMIT 5". |
| * If we get only comparisons ONLY then if all comparisons use the same index then limit |
| * is applicable and this returns true. Like, "where ID != 10 AND (ID < 5 AND ID > 10) LIMIT 5". |
| * |
| */ |
| @Override |
| public boolean isLimitApplicableAtIndexLevel(ExecutionContext context) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| if(this._operator == LITERAL_or) { |
| //There is a slight inefficiency in the sense that if the subjunction ( say AND) cannot apply limit, |
| // then limit would not be applied at remaining conditions of OR. But since we have single flag |
| //governing the behaviour of applying limit at index level, we cannot make it true for specific clauses |
| for (int i=0; i<this._operands.length; ++i) { |
| if( !this._operands[i].getPlanInfo(context).evalAsFilter || ((Filter)this._operands[i]).isLimitApplicableAtIndexLevel(context)) { |
| return false; |
| } |
| } |
| return true; |
| }else { |
| //For limit to be applicable on a AND junction, there should be only one type of index used and rest iter evaluable |
| //But since the selection of which index to use happens in GroupJunction created at runtimke, we do not have that |
| //information yet, and at this point there would be multiple indexes. Ideally we should invoke this function in GroupJunction, |
| //in case we want to support multi index usage again at some point. Till then since it is hard coded to use 1 index |
| // we can for the time being return true if there exists atleast one indexable condition |
| boolean foundIndex = false; |
| for (int i = 0; i < this._operands.length; ++i) { |
| if( this._operands[i].getPlanInfo(context).evalAsFilter && this._operands[i].getType() == JUNCTION) { |
| return false; |
| } else if (this._operands[i].getPlanInfo(context).evalAsFilter) { |
| foundIndex = true; |
| } |
| } |
| return foundIndex; |
| } |
| } |
| |
| @Override |
| public boolean isOrderByApplicableAtIndexLevel(ExecutionContext context, String canonicalizedOrderByClause) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| if(this._operator == LITERAL_and) { |
| //Set<IndexProtocol> usedIndex = new HashSet<IndexProtocol>(); |
| boolean foundRightIndex = false; |
| for (int i=0; i<this._operands.length; ++i) { |
| PlanInfo pi =this._operands[i].getPlanInfo(context); |
| if( pi.evalAsFilter && this._operands[i].getType() == JUNCTION) { |
| return false; |
| }else if(pi.evalAsFilter) { |
| if(!foundRightIndex) { |
| IndexProtocol ip = (IndexProtocol)this._operands[i].getPlanInfo(context).indexes.get(0); |
| if(ip.getCanonicalizedIndexedExpression().equals(canonicalizedOrderByClause) && pi.isPreferred ) { |
| foundRightIndex = true; |
| } |
| } |
| //usedIndex.addAll(this._operands[i].getPlanInfo(context).indexes); |
| } |
| } |
| return foundRightIndex; |
| // if(usedIndex.size() == 1 && usedIndex.iterator().next().getCanonicalizedIndexedExpression().equals(canonicalizedOrderByClause)) { |
| // return true; |
| // } |
| } |
| return false; |
| } |
| |
| /* |
| * class CGJData { RuntimeIterator [] indpndItrs = null; List iterOperands = |
| * null; List groupJunctions = null; CompiledValue [] |
| * compositeCompiledComparisons = null; |
| * |
| * CGJData(RuntimeIterator [] indpndItrs, List iterOperands,List |
| * groupJunctions, CompiledValue [] compositeCompiledComparisons) { |
| * this.indpndItrs = indpndItrs; this.iterOperands = iterOperands; |
| * this.groupJunctions = groupJunctions; this.compositeCompiledComparisons = |
| * compositeCompiledComparisons; } } |
| */ |
| } |