| /*========================================================================= |
| * Copyright (c) 2005-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * more patents listed at http://www.pivotal.io/patents. |
| *======================================================================== |
| */ |
| package com.gemstone.gemfire.cache.query.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Set; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import com.gemstone.gemfire.cache.query.AmbiguousNameException; |
| import com.gemstone.gemfire.cache.query.FunctionDomainException; |
| import com.gemstone.gemfire.cache.query.Index; |
| 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.AbstractIndex; |
| 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.index.PartitionedIndex; |
| 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.CollectionType; |
| import com.gemstone.gemfire.cache.query.types.ObjectType; |
| import com.gemstone.gemfire.cache.query.types.StructType; |
| import com.gemstone.gemfire.internal.Assert; |
| import com.gemstone.gemfire.internal.cache.BucketRegion; |
| import com.gemstone.gemfire.internal.cache.CachePerfStats; |
| import com.gemstone.gemfire.internal.cache.PartitionedRegion; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.internal.logging.LogService; |
| import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage; |
| |
| /** |
| * |
| * @author Eric Zoerner |
| * @author Asif |
| */ |
| public class QueryUtils { |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * Return a SelectResults that is the intersection of c1 and c2. May or may |
| * not return a modified c1 or c2. |
| */ |
| public static SelectResults intersection(SelectResults c1, |
| SelectResults c2, |
| ExecutionContext contextOrNull) { |
| QueryObserverHolder.getInstance().invokedQueryUtilsIntersection(c1,c2); |
| assertCompatible(c1, c2); |
| if (c1.isEmpty()) { return c1; } |
| if (c2.isEmpty()) { return c2; } |
| // iterate on the smallest one |
| if (c1.size() < c2.size()) { |
| return sizeSortedIntersection(c1, c2, contextOrNull); |
| } |
| else { |
| return sizeSortedIntersection(c2, c1, contextOrNull); |
| } |
| } |
| |
| /** |
| * Return a SelectResults that is the union of c1 and c2. May or may not |
| * return a modified c1 or c2. |
| */ |
| public static SelectResults union(SelectResults c1, |
| SelectResults c2, |
| ExecutionContext contextOrNull) { |
| QueryObserverHolder.getInstance().invokedQueryUtilsUnion(c1,c2); |
| assertCompatible(c1, c2); |
| // iterate on the smallest one |
| if (c1.size() < c2.size()) { |
| return sizeSortedUnion(c1, c2, contextOrNull); |
| } |
| else { |
| return sizeSortedUnion(c2, c1, contextOrNull); |
| } |
| } |
| |
| public static void assertCompatible(SelectResults sr1, SelectResults sr2) { |
| Assert.assertTrue(sr1.getCollectionType().getElementType().equals( |
| sr2.getCollectionType().getElementType())); |
| } |
| |
| |
| public static SelectResults createResultCollection(ExecutionContext context, ObjectType elementType) { |
| return context.isDistinct() ? new ResultsSet(elementType) : |
| new ResultsBag(elementType, context.getCachePerfStats()); |
| } |
| |
| public static SelectResults createStructCollection(ExecutionContext context, StructType elementType) { |
| return context.isDistinct() ? new StructSet(elementType) : |
| new StructBag(elementType, context.getCachePerfStats()) ; |
| } |
| |
| public static SelectResults createResultCollection(boolean distinct, ObjectType elementType, |
| ExecutionContext context) { |
| return distinct ? new ResultsSet(elementType) : |
| new ResultsBag(elementType, context.getCachePerfStats()); |
| } |
| |
| public static SelectResults createStructCollection(boolean distinct, StructType elementType, |
| ExecutionContext context) { |
| return distinct ? new StructSet(elementType) : |
| new StructBag(elementType, context.getCachePerfStats()) ; |
| } |
| |
| |
| /** |
| * Returns an appropriate, empty <code>SelectResults</code> |
| * @param objectType The <code>ObjectType</code> of the query results |
| * @return an appropriate, empty <code>SelectResults</code> |
| */ |
| public static SelectResults getEmptySelectResults(ObjectType objectType, |
| CachePerfStats statsOrNull) { |
| SelectResults emptyResults = null; |
| if (objectType instanceof StructType) { |
| emptyResults = new StructBag((StructTypeImpl) objectType, statsOrNull); |
| } else { |
| emptyResults = new ResultsBag(objectType, statsOrNull); |
| } |
| return emptyResults; |
| } |
| |
| /** |
| * Returns an appropriate, empty <code>SelectResults</code> |
| * @param collectionType The <code>CollectionType</code> of the query results |
| * @return an appropriate, empty <code>SelectResults</code> |
| */ |
| public static SelectResults getEmptySelectResults(CollectionType collectionType, |
| CachePerfStats statsOrNull) { |
| SelectResults emptyResults = null; |
| if (collectionType.isOrdered()) { |
| // The collectionType is ordered. |
| // The 'order by' clause was used in the query. |
| // Wrap an ArrayList with a ResultsCollectionWrapper |
| emptyResults = new ResultsCollectionWrapper(collectionType.getElementType(), new ArrayList()); |
| } |
| else if (!collectionType.allowsDuplicates()) { |
| // The collectionType does not allow duplicates. |
| // The distinct keyword was used in the query. |
| // Wrap a HashSet with a ResultsCollectionWrapper |
| emptyResults = new ResultsCollectionWrapper(collectionType.getElementType(), new HashSet()); |
| } |
| else { |
| // Use ObjectType to determine the correct SelectResults implementation |
| emptyResults = getEmptySelectResults(collectionType.getElementType(), |
| statsOrNull); |
| } |
| return emptyResults; |
| } |
| |
| // convenience method |
| private static boolean isBag(SelectResults coln) { |
| return coln.getCollectionType().allowsDuplicates(); |
| } |
| |
| /** collections are passed in from smallest to largest */ |
| // consider: taking intersection of two bags, only retain |
| // the number of occurrences in the intersection equal to the |
| // minimum number between the two bags |
| private static SelectResults sizeSortedIntersection(SelectResults small, |
| SelectResults large, |
| ExecutionContext contextOrNull) { |
| // if one is a set and one is a bag, |
| // then treat the set like a bag (and return a bag) |
| boolean smallModifiable = small.isModifiable() && (isBag(small) || !isBag(large)); |
| boolean largeModifiable = large.isModifiable() && (isBag(large) || !isBag(small)); |
| if (smallModifiable) { |
| try { |
| for (Iterator itr = small.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| int count = large.occurrences(element); |
| if (small.occurrences(element) > count) { // bag intersection: only retain smaller number of dups |
| itr.remove(); |
| } |
| } |
| return small; |
| } |
| catch (UnsupportedOperationException e1) { |
| // didn't succeed because small is actually unmodifiable |
| } |
| } |
| if (largeModifiable) { |
| try { |
| for (Iterator itr = large.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| int count = small.occurrences(element); |
| if (large.occurrences(element) > count) { // bag intersection: only retain smaller number of dups |
| itr.remove(); |
| } |
| } |
| return large; |
| } |
| catch (UnsupportedOperationException e2) { |
| // didn't succeed because large is actually unmodifiable |
| } |
| } |
| |
| SelectResults rs ; |
| if(contextOrNull != null) { |
| rs = contextOrNull.isDistinct() ? new ResultsSet(small) :new ResultsBag(small, |
| contextOrNull.getCachePerfStats()); |
| }else { |
| rs =new ResultsBag(small,null); |
| } |
| |
| for (Iterator itr = rs.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| int count = large.occurrences(element); |
| if (rs.occurrences(element) > count) { // bag intersection: only retain smaller number of dups |
| itr.remove(); |
| } |
| } |
| return rs; |
| } |
| |
| /** collections are passed in from smallest to largest */ |
| // assume we're dealing with bags and/or sets here, number of occurrences in the |
| // union should be the sum of the occurrences in the two bags |
| //Asif: Is this Ok? There may be tuples which are actually common to both set so |
| // union in such cases should not increase count. right.? |
| |
| private static SelectResults sizeSortedUnion(SelectResults small, |
| SelectResults large, |
| ExecutionContext contextOrNull) { |
| // if one is a set and one is a bag, |
| // then treat the set like a bag (and return a bag) |
| boolean smallModifiable = small.isModifiable() && (isBag(small) || !isBag(large)); |
| boolean largeModifiable = large.isModifiable() && (isBag(large) || !isBag(small)); |
| if (largeModifiable) { |
| try { |
| for (Iterator itr = small.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| int count = small.occurrences(element); |
| if (large.occurrences(element) < count) { |
| large.add(element); |
| } |
| } |
| return large; |
| } |
| catch (UnsupportedOperationException e1) { |
| // didn't succeed because large is actually unmodifiable |
| } |
| } |
| if (smallModifiable) { |
| try { |
| for (Iterator itr = large.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| int count = large.occurrences(element); |
| if (small.occurrences(element) < count) { |
| small.add(element); |
| } |
| } |
| return small; |
| } |
| catch (UnsupportedOperationException e2) { |
| // didn't succeed because small is actually unmodifiable |
| } |
| } |
| SelectResults rs; |
| if(contextOrNull != null) { |
| rs = contextOrNull.isDistinct() ? new ResultsSet(large) :new ResultsBag(large, |
| contextOrNull.getCachePerfStats()); |
| }else { |
| rs =new ResultsBag(large, null); |
| } |
| |
| for (Iterator itr = small.iterator(); itr.hasNext(); ) { |
| Object element = itr.next(); |
| rs.add(element); |
| } |
| return rs; |
| } |
| |
| /** |
| * This function returns a list of runtime iterators in current scope which |
| * are exclusively dependent on given independent RuntimeIterators. The order |
| * of dependent iterators in the List is based on the order of independent |
| * Iterators present in the array . For each group the first iterator is its |
| * independent iterator |
| * |
| * @param indpndntItrs array of independent RuntimeIterators |
| * @param context |
| */ |
| public static List getDependentItrChainForIndpndntItrs( |
| RuntimeIterator[] indpndntItrs, ExecutionContext context) { |
| List ret = new ArrayList(); |
| for (int k = 0; k < indpndntItrs.length; ++k) { |
| ret.addAll(context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(indpndntItrs[k])); |
| } |
| return ret; |
| } |
| |
| |
| /** |
| * Asif : This util function does a cartesian of the array of SelectResults |
| * object , expanding the resultant set to the number of iterators passed in |
| * expansionList. The position of the iterator fields in the final result is |
| * governed by the order of RuntimeIterators present in the finalList. If any |
| * condition needs to be evaluated during cartesian , it can be passed as |
| * operand |
| * |
| * @param results Array of SelectResults object which are to be cartesianed |
| * @param itrsForResultFields A two dimensional array of RuntimeIterator. Each |
| * row of this two dimensional RuntimeIterator array , maps to a |
| * SelectResults object in the results array. Thus the 0th row of two |
| * dimensional RuntimeIterator array will map to the 0th element of |
| * the SelectResults array. The order of RuntimeIterator in a row |
| * will map to the fields in the SelectResults object. The 0th |
| * RuntimeIterator will map to the 0th field of the corresponding |
| * SelectResults object. The number of rows in the two |
| * dimensional array of RuntimeIterator should be equal to the size |
| * of array of SelectResults object passed and the number of |
| * RuntimeIterators in each row should be equal to the number of |
| * fields in the SelectResults object . The SelectResults object |
| * itself may be a ResultBag object or a StructBag object. |
| * |
| * @param expansionList List containing RunimeIterators to which the final |
| * Results should be expanded to. |
| * @param finalList List containing RuntimeIterators which define the number |
| * of fields to be present in the resultant SelectResults and their |
| * relative positions. The Runtime Iterators present in the List |
| * should be either available in the expansion List or should be |
| * present in each row of the two dimensional RuntimeIterator array. |
| * |
| * @param context ExecutionContext object |
| * @param operand The CompiledValue which needs to be iter evaluated during |
| * cartesian. Only those tuples will be selected in the final Result |
| * for which oerand evaluates to true. |
| * @return SelectResults object representing the final result of the cartesian |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException |
| * @throws NameResolutionException |
| * @throws QueryInvocationTargetException |
| * |
| */ |
| public static SelectResults cartesian(SelectResults[] results, |
| RuntimeIterator[][] itrsForResultFields, List expansionList, |
| List finalList, ExecutionContext context, CompiledValue operand) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| SelectResults returnSet = null; |
| if (finalList.size() == 1) { |
| ObjectType type = ((RuntimeIterator) finalList.iterator().next()) |
| .getElementType(); |
| if (type instanceof StructType) { |
| returnSet = QueryUtils.createStructCollection(context,(StructTypeImpl) type) ; |
| } |
| else { |
| return QueryUtils.createResultCollection(context, type); |
| } |
| } |
| else { |
| StructType structType = createStructTypeForRuntimeIterators(finalList); |
| returnSet = QueryUtils.createStructCollection(context, structType); |
| } |
| ListIterator expnItr = expansionList.listIterator(); |
| // RuntimeIterator levelExpnItr = |
| // expnItr.hasNext()?(RuntimeIterator)expnItr.next():null; |
| doNestedIterations(0, returnSet, results, itrsForResultFields, finalList, |
| expnItr, (results.length + expansionList.size()), context, operand); |
| return returnSet; |
| } |
| |
| //TODO:Asif :Optimize the function further in terms of reducing the |
| // parameters passed in the function, if possible |
| private static void doNestedIterations(int level, SelectResults returnSet, |
| SelectResults[] results, RuntimeIterator[][] itrsForResultFields, |
| List finalItrs, ListIterator expansionItrs, int finalLevel, |
| ExecutionContext context, CompiledValue operand) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| if (level == finalLevel) { |
| //end recusrion |
| boolean select = true; |
| if (operand != null) { |
| select = applyCondition(operand, context); |
| } |
| Iterator itr = finalItrs.iterator(); |
| int len = finalItrs.size(); |
| if (len > 1) { |
| Object values[] = new Object[len]; |
| int j = 0; |
| while (itr.hasNext()) { |
| values[j++] = ((RuntimeIterator) itr.next()).evaluate(context); |
| } |
| if (select) { |
| |
| ((StructFields) returnSet).addFieldValues(values); |
| |
| } |
| } |
| else { |
| if (select) |
| returnSet.add(((RuntimeIterator) itr.next()).evaluate(context)); |
| } |
| } |
| else if (level < results.length) { |
| SelectResults individualResultSet = results[level]; |
| RuntimeIterator[] itrsForFields = itrsForResultFields[level]; |
| int len = itrsForFields.length; |
| Iterator itr = individualResultSet.iterator(); |
| while (itr.hasNext()) { |
| // Check if query execution on this thread is canceled. |
| QueryMonitor.isQueryExecutionCanceled(); |
| Object value = itr.next(); |
| if (len == 1) { |
| // Asif : this means we have a ResultSet |
| itrsForFields[0].setCurrent(value); |
| } |
| else { |
| Struct struct = (Struct) value; |
| Object fieldValues[] = struct.getFieldValues(); |
| int size = fieldValues.length; |
| for (int i = 0; i < size; ++i) { |
| itrsForFields[i].setCurrent(fieldValues[i]); |
| } |
| } |
| doNestedIterations(level + 1, returnSet, results, itrsForResultFields, |
| finalItrs, expansionItrs, finalLevel, context, operand); |
| } |
| } |
| else { |
| RuntimeIterator currLevel = (RuntimeIterator) expansionItrs.next(); |
| SelectResults c = currLevel.evaluateCollection(context); |
| if (c == null) { |
| //TODO:Asif Check this out |
| expansionItrs.previous(); |
| return; |
| } |
| Iterator cIter = c.iterator(); |
| while (cIter.hasNext()) { |
| currLevel.setCurrent(cIter.next()); |
| doNestedIterations(level + 1, returnSet, results, itrsForResultFields, |
| finalItrs, expansionItrs, finalLevel, context, operand); |
| } |
| expansionItrs.previous(); |
| } |
| } |
| |
| public static boolean applyCondition(CompiledValue operand, |
| ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| if(operand == null) return true; |
| Object result = operand.evaluate(context); |
| if (result instanceof Boolean) { |
| return ((Boolean) result).booleanValue(); |
| } |
| else if (result != null && result != QueryService.UNDEFINED) { |
| throw new TypeMismatchException(LocalizedStrings.QueryUtils_ANDOR_OPERANDS_MUST_BE_OF_TYPE_BOOLEAN_NOT_TYPE_0.toLocalizedString(result.getClass().getName())); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| //TODO:Asif This function is used to do cartesian of index resultset while |
| // expanding/cutting down index resultset |
| // with the intermediate resultset |
| //TODO :Asif :Explain the parameters& Unit test it |
| private static void mergeRelationshipIndexResultsWithIntermediateResults( |
| SelectResults returnSet, SelectResults[] intermediateResults /* |
| * Asif This |
| * should be |
| * a single |
| * element |
| * array |
| */, RuntimeIterator[][] itrsForIntermediateResults /* |
| * Asif This should be |
| * a two dimensional |
| * array but with only |
| * one row |
| */, Object[][] indexResults, RuntimeIterator[][] indexFieldToItrsMapping, |
| ListIterator expansionListItr, List finalItrs, ExecutionContext context, |
| List[] checkList, CompiledValue iterOps, |
| IndexCutDownExpansionHelper icdeh[], int level, int maxExpnCartesianDepth) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // Object[] checkFields = null; |
| // int len = indexFieldToItrsMapping[level].length; |
| // RuntimeIterator rItr = null; |
| int resultSize = indexResults[level].length; |
| //TODO ASif : Since this is constant for a given merge call, pass it as a |
| // parameter to |
| //the function rather than calling it every time |
| for (int j = 0; j < resultSize; ++j) { |
| if (setIndexFieldValuesInRespectiveIterators(indexResults[level][j], |
| indexFieldToItrsMapping[level], icdeh[level])) { |
| if (level == (indexResults.length - 1)) { |
| //Asif :Set the values in the Intermedaite Resultset |
| doNestedIterations(0, returnSet, intermediateResults, |
| itrsForIntermediateResults, finalItrs, expansionListItr, |
| maxExpnCartesianDepth, context, iterOps); |
| } |
| else { |
| mergeRelationshipIndexResultsWithIntermediateResults(returnSet, |
| intermediateResults, itrsForIntermediateResults, indexResults, |
| indexFieldToItrsMapping, expansionListItr, finalItrs, context, |
| checkList, iterOps, icdeh, level + 1, maxExpnCartesianDepth); |
| if (icdeh[level + 1].cutDownNeeded) { |
| icdeh[level + 1].checkSet.clear(); |
| } |
| } |
| } |
| } |
| } |
| |
| //TODO:Asif : Test the function & write expnanation of the parameters |
| private static void mergeAndExpandCutDownRelationshipIndexResults( |
| Object[][] values, SelectResults result, |
| RuntimeIterator[][] indexFieldToItrsMapping, |
| ListIterator expansionListIterator, List finalItrs, |
| ExecutionContext context, List[] checkList, CompiledValue iterOps, |
| IndexCutDownExpansionHelper icdeh[], int level) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // int len = indexFieldToItrsMapping[level].length; |
| // RuntimeIterator rItr = null; |
| int resultSize = values[level].length; |
| int limit = getLimitValue(context); |
| //stops recursion if limit has already been met |
| if (limit != -1 && result.size() >= limit) { |
| return; |
| } |
| for (int j = 0; j < resultSize; ++j) { |
| // Check if query execution on this thread is canceled. |
| QueryMonitor.isQueryExecutionCanceled(); |
| |
| if (setIndexFieldValuesInRespectiveIterators(values[level][j], |
| indexFieldToItrsMapping[level], icdeh[level])) { |
| if (level == (values.length - 1)) { |
| doNestedIterationsForIndex(expansionListIterator.hasNext(), result, |
| finalItrs, expansionListIterator, context, iterOps, limit); |
| if (limit != -1 && result.size() >= limit) { |
| break; |
| } |
| } |
| else { |
| mergeAndExpandCutDownRelationshipIndexResults(values, result, |
| indexFieldToItrsMapping, expansionListIterator, finalItrs, |
| context, checkList, iterOps, icdeh, level + 1); |
| if (icdeh[level + 1].cutDownNeeded) { |
| icdeh[level + 1].checkSet.clear(); |
| } |
| } |
| } |
| } |
| } |
| |
| //TODO:Asif : Explain the function & write test cases. A boolean false means |
| // , |
| // by pass i.e the set value to be ignored |
| |
| //End result if we have not already expanded is that we have created a new struct and added to a set to prevent future expansions of the same object |
| //It also advances the current object for the iterator. |
| private static boolean setIndexFieldValuesInRespectiveIterators(Object value, |
| RuntimeIterator[] indexFieldToItrsMapping, |
| IndexCutDownExpansionHelper icdeh) { |
| Object[] checkFields = null; |
| boolean select = true; |
| int len = indexFieldToItrsMapping.length; |
| RuntimeIterator rItr = null; |
| if (len == 1) { |
| //Asif : this means we have a ResultSet |
| Support |
| .Assert( |
| !icdeh.cutDownNeeded, |
| "If the index fields to iter mapping is of of size 1 then cut down cannot occur"); |
| indexFieldToItrsMapping[0].setCurrent(value); |
| } |
| else { |
| Struct struct = (Struct) value; |
| Object fieldValues[] = struct.getFieldValues(); |
| int size = fieldValues.length; |
| if (icdeh.cutDownNeeded) checkFields = new Object[icdeh.checkSize]; |
| // Object values[] = new Object[numItersInResultSet]; |
| int j = 0; |
| for (int i = 0; i < size; i++) { |
| rItr = indexFieldToItrsMapping[i]; |
| if (rItr != null) { |
| rItr.setCurrent(fieldValues[i]); |
| if (icdeh.cutDownNeeded) { |
| checkFields[j++] = fieldValues[i]; |
| } |
| } |
| } |
| if (icdeh.cutDownNeeded) { |
| Object temp = null; |
| if (icdeh.checkSize == 1) { |
| temp = checkFields[0]; |
| } |
| else { |
| temp = new StructImpl((StructTypeImpl) icdeh.checkType, checkFields); |
| } |
| if (icdeh.checkSet.contains(temp)) { |
| select = false; |
| } |
| else { |
| icdeh.checkSet.add(temp); |
| } |
| } |
| } |
| return select; |
| } |
| |
| //creates the returned set and then calls other methods to do actual work |
| private static SelectResults cutDownAndExpandIndexResults( |
| SelectResults result, RuntimeIterator[] indexFieldToItrsMapping, |
| List expansionList, List finalItrs, ExecutionContext context, |
| List checkList, CompiledValue iterOps) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| SelectResults returnSet = null; |
| 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 (finalItrs.size() == 1) { |
| ObjectType resultType = ((RuntimeIterator) finalItrs.iterator().next()).getElementType(); |
| if (useLinkedDataStructure) { |
| returnSet = context.isDistinct() ? new LinkedResultSet(resultType): |
| new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| returnSet =QueryUtils.createResultCollection(context, resultType); |
| } |
| |
| } |
| else { |
| StructTypeImpl resultType = (StructTypeImpl) createStructTypeForRuntimeIterators(finalItrs); |
| if(useLinkedDataStructure) { |
| returnSet = context.isDistinct() ? new LinkedStructSet(resultType) |
| : new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart); |
| }else { |
| returnSet = QueryUtils.createStructCollection(context, resultType); |
| } |
| |
| } |
| cutDownAndExpandIndexResults(returnSet, result, indexFieldToItrsMapping, |
| expansionList, finalItrs, context, checkList, iterOps); |
| return returnSet; |
| } |
| |
| //TODO:Asif Explain the parameters passed |
| private static void cutDownAndExpandIndexResults(SelectResults returnSet, |
| SelectResults result, RuntimeIterator[] indexFieldToItrsMapping, |
| List expansionList, List finalItrs, ExecutionContext context, |
| List checkList, CompiledValue iterOps) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| // Object[] checkFields = null; |
| IndexCutDownExpansionHelper icdeh = |
| new IndexCutDownExpansionHelper(checkList, |
| context); |
| int len = indexFieldToItrsMapping.length; |
| // don't call instanceof ResultsBag here, since a StructBag is a subtype of ResultsBag |
| if (result.getClass() == ResultsBag.class) { |
| Support.Assert(len == 1, "The array size of iterators should be 1 here, but got " |
| + len); |
| } |
| Iterator itr = result.iterator(); |
| ListIterator expansionListIterator = expansionList.listIterator(); |
| int limit = getLimitValue(context); |
| |
| while (itr.hasNext()) { |
| Object value = itr.next(); |
| if (setIndexFieldValuesInRespectiveIterators(value, |
| indexFieldToItrsMapping, icdeh)) { //does that mean we don't get dupes even if they exist in the index? |
| // DO NESTED LOOPING |
| |
| doNestedIterationsForIndex(expansionListIterator.hasNext(), returnSet, |
| finalItrs, expansionListIterator, context, iterOps, limit); |
| if (limit != -1 && returnSet.size() >= limit) { |
| break; |
| } |
| } |
| } |
| } |
| |
| //returns the limit value from the context. This was set in CompiledSelect evaluate |
| //We do not apply limit if we have an order by attribute at this time |
| //it may be possible but we need better understanding of when ordering is taking place |
| //If it's at the index level, we may be able to apply limits at this point |
| //however a lot of the code in this class is fragile/unreadable/hard to maintain |
| private static int getLimitValue(ExecutionContext context) { |
| int limit = -1; |
| if (context.cacheGet(CompiledValue.ORDERBY_ATTRIB) == null) { |
| limit = ((Integer) context.cacheGet(CompiledValue.RESULT_LIMIT)) != null?((Integer) context.cacheGet(CompiledValue.RESULT_LIMIT)).intValue(): -1; |
| } |
| return limit; |
| } |
| |
| //Add comments |
| private static void doNestedIterationsForIndex(boolean continueRecursion, |
| SelectResults resultSet, List finalItrs, ListIterator expansionItrs, |
| ExecutionContext context, CompiledValue iterOps, int limit) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| |
| if (!continueRecursion) { |
| //end recusrion |
| Iterator itr = finalItrs.iterator(); |
| int len = finalItrs.size(); |
| boolean select = true; |
| if (iterOps != null) { |
| select = applyCondition(iterOps, context); |
| } |
| if (len > 1) { |
| boolean isOrdered = resultSet.getCollectionType().isOrdered(); |
| StructTypeImpl elementType = (StructTypeImpl)resultSet.getCollectionType().getElementType(); |
| //TODO:Asif Optimize the LinkedStructSet implementation so that |
| // Object[] can be added rather than Struct |
| |
| Object values[] = new Object[len]; |
| int j = 0; |
| //creates tuple |
| while (itr.hasNext()) { |
| // Check if query execution on this thread is canceled. |
| QueryMonitor.isQueryExecutionCanceled(); |
| |
| values[j++] = ((RuntimeIterator) itr.next()).evaluate(context); |
| } |
| if (select) { |
| if(isOrdered) { |
| //((LinkedStructSet) resultSet).add(new StructImpl(elementType, values)); |
| // Can be LinkedStructSet or SortedResultsBag ( containing underlying LinkedHashMap) |
| resultSet.add(new StructImpl(elementType, values)); |
| }else { |
| |
| ((StructFields) resultSet).addFieldValues(values); |
| |
| } |
| } |
| } |
| else { |
| if (select) |
| resultSet.add(((RuntimeIterator) itr.next()).evaluate(context)); |
| } |
| } |
| else { |
| RuntimeIterator currentLevel = (RuntimeIterator) expansionItrs.next(); |
| SelectResults c = currentLevel.evaluateCollection(context); |
| // RuntimeIterator next = expansionItrs.hasNext() ? |
| // (RuntimeIterator)expansionItrs.next() : null; |
| if (c == null) { |
| //TODO:Asif Check this out |
| expansionItrs.previous(); |
| return; |
| } |
| Iterator cIter = c.iterator(); |
| while (cIter.hasNext()) { |
| // Check if query execution on this thread is canceled. |
| QueryMonitor.isQueryExecutionCanceled(); |
| |
| currentLevel.setCurrent(cIter.next()); |
| doNestedIterationsForIndex(expansionItrs.hasNext(), resultSet, |
| finalItrs, expansionItrs, context, iterOps, limit); |
| if (limit != -1 && resultSet.size() >= limit) { |
| break; |
| } |
| } |
| expansionItrs.previous(); |
| } |
| } |
| |
| /** |
| * Ketan/Asif : This function will evaluate the starting CompiledValue for a |
| * given CompliedValue. The value returned will always be either the original |
| * CompiledValue, or a CompiledID, or a CompiledRegion, or a |
| * CompiledBindArgument, or a CompiledOperation. The ExecutionContext passed can |
| * be null. If it is null, then for a CompiledOperation , if supposed to get |
| * resolved implicitly, will have its receiver as null. This is because in normal |
| * cases , a CompiledID marks the termination, but in case of |
| * CompiledOperation this is not the case |
| * |
| * @param expr CompiledValue object |
| * @return CompiledValue |
| */ |
| public static CompiledValue obtainTheBottomMostCompiledValue( |
| CompiledValue expr) { |
| boolean toContinue = true; |
| int exprType = expr.getType(); |
| while (toContinue) { |
| switch (exprType) { |
| case OQLLexerTokenTypes.RegionPath: |
| toContinue = false; |
| break; |
| case OQLLexerTokenTypes.METHOD_INV: |
| CompiledOperation operation = (CompiledOperation) expr; |
| expr = operation.getReceiver(null/* |
| * pass the ExecutionContext as null, |
| * thus never implicitly resolving to |
| * RuntimeIterator |
| */); |
| if (expr == null) { |
| expr = operation; |
| toContinue = false; |
| } |
| break; |
| case CompiledValue.PATH: |
| expr = ((CompiledPath) expr).getReceiver(); |
| break; |
| case OQLLexerTokenTypes.TOK_LBRACK: |
| expr = ((CompiledIndexOperation) expr).getReceiver(); |
| break; |
| default: |
| toContinue = false; |
| break; |
| } |
| if (toContinue) exprType = expr.getType(); |
| } |
| return expr; |
| } |
| |
| /** |
| * Asif : This function creates a StructType using Internal IDs of the |
| * iterators as the field names for the StructType. It should be invoked iff |
| * the iterators size is greater than 1 |
| * |
| * @param runTimeIterators List of RuntimeIterator objects |
| * @return StructType object |
| * |
| */ |
| public static StructType createStructTypeForRuntimeIterators( |
| List runTimeIterators) { |
| Support |
| .Assert(runTimeIterators.size() > 1, |
| "The number of Iterators passed should be greater than 1 to create a structSet"); |
| int len = runTimeIterators.size(); |
| String fieldNames[] = new String[len]; |
| String[] indexAlternativeFieldNames = new String[len]; |
| ObjectType fieldTypes[] = new ObjectType[len]; |
| //Asif : use an Iterator as the chances are that we will be sending |
| //LinkedList rather than ArrayList |
| Iterator itr = runTimeIterators.iterator(); |
| int i = 0; |
| while (itr.hasNext()) { |
| RuntimeIterator iter = (RuntimeIterator) itr.next(); |
| fieldNames[i] = iter.getInternalId(); |
| indexAlternativeFieldNames[i] = iter.getIndexInternalID(); |
| fieldTypes[i++] = iter.getElementType(); |
| } |
| StructTypeImpl type = new StructTypeImpl(fieldNames, indexAlternativeFieldNames, fieldTypes); |
| return type; |
| } |
| |
| /** |
| * Asif :This function returns the ultimate independent RuntimeIterators of |
| * current scope on which the CompiledValue passed is dependent upon. This |
| * does not return the RuntimeIterators on which it may be dependent but are |
| * not part of the current scope. If no such RuntimeIterator exists it returns |
| * empty set. |
| * |
| * @param compiledValue CompiledValue Object |
| * @param context ExecutionContextobject |
| * @return Set containing the ultimate independent RuntimeIterators of current |
| * scope |
| * |
| */ |
| public static Set getCurrentScopeUltimateRuntimeIteratorsIfAny( |
| CompiledValue compiledValue, ExecutionContext context) { |
| HashSet set = new HashSet(); |
| context.computeUtlimateDependencies(compiledValue, set); |
| //if (set.size() != 1) return null; |
| Iterator iter = set.iterator(); |
| while (iter.hasNext()) { |
| RuntimeIterator rIter = (RuntimeIterator) iter.next(); |
| if (rIter.getScopeID() != context.currentScope().getScopeID()/*context.getScopeCount()*/) iter.remove(); |
| } |
| return set; |
| } |
| |
| /** |
| * Asif :Returns the pair of RangeIndexes available for a composite condition ( |
| * equi join across the region). It will either return two indexes or will |
| * return null. * |
| * |
| * @param lhs One of the operands of the equi-join condition |
| * @param rhs The other operand of the equi-join condition |
| * @param context ExecutionContext object |
| * @param operator The operator which necesarily has to be an equality ( ' = ' ) |
| * @return An array of IndexData object with 0th IndexData for the lhs operand & |
| * 1th object for rhs operad |
| * @throws AmbiguousNameException |
| * @throws TypeMismatchException |
| * |
| */ |
| static IndexData[] getRelationshipIndexIfAny(CompiledValue lhs, |
| CompiledValue rhs, ExecutionContext context, int operator) |
| throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| if (operator != OQLLexerTokenTypes.TOK_EQ) return null;// Operator must be |
| // '=' |
| IndexData lhsIndxData = QueryUtils.getAvailableIndexIfAny(lhs, context, |
| false /* Do not use PrimaryKey Index */);// findOnlyFunctionalIndex. |
| if (lhsIndxData == null) return null; |
| IndexData rhsIndxData = QueryUtils.getAvailableIndexIfAny(rhs, context, |
| false /* Do not use PrimaryKey Index */);// findOnlyFunctionalIndex. |
| if (rhsIndxData == null) { |
| // release the lock held on lhsIndex as it will not be used |
| Index index = lhsIndxData.getIndex(); |
| Index prIndex = ((AbstractIndex) index).getPRIndex(); |
| if (prIndex != null) { |
| ((PartitionedIndex) prIndex).releaseIndexReadLockForRemove(); |
| } else { |
| ((AbstractIndex) index).releaseIndexReadLockForRemove(); |
| } |
| return null; |
| } |
| Index lhsIndx = lhsIndxData.getIndex(); |
| Index rhsIndx = rhsIndxData.getIndex(); |
| if (((IndexProtocol) lhsIndx).isValid() |
| && ((IndexProtocol) rhsIndx).isValid()) { |
| return new IndexData[] {lhsIndxData, rhsIndxData}; |
| } |
| return null; |
| } |
| |
| /** |
| * Asif : Gets an Index available for the condition |
| * |
| * @param cv Condition on which index needs to be obtained |
| * @param context ExecutionContext object |
| * @param operator int argument identifying the type of operator |
| * @return IndexData object |
| * @throws AmbiguousNameException |
| * @throws TypeMismatchException |
| * |
| */ |
| static IndexData getAvailableIndexIfAny(CompiledValue cv, |
| ExecutionContext context, int operator) throws AmbiguousNameException, |
| TypeMismatchException, NameResolutionException { |
| // If operator is = or != then first search for PRIMARY_KEY Index |
| boolean usePrimaryIndex = (operator == OQLLexerTokenTypes.TOK_EQ || operator == OQLLexerTokenTypes.TOK_NE); |
| return getAvailableIndexIfAny(cv, context, usePrimaryIndex); |
| } |
| |
| //TODO:Asif : Provide description of the function. |
| private static IndexData getAvailableIndexIfAny(CompiledValue cv, |
| ExecutionContext context, boolean usePrimaryIndex) |
| throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| Set set = new HashSet(); |
| context.computeUtlimateDependencies(cv, set); |
| if (set.size() != 1) return null; |
| RuntimeIterator rIter = (RuntimeIterator) set.iterator().next(); |
| String regionPath = null; |
| // An Index is not available if the ultimate independent RuntimeIterator is |
| // of different scope or if the underlying |
| // collection is not a Region |
| if (rIter.getScopeID() != context.currentScope().getScopeID() /*context.getScopeCount()*/ |
| || (regionPath = context |
| .getRegionPathForIndependentRuntimeIterator(rIter)) == null) { |
| return null; |
| } |
| // Asif : The independent iterator is added as the first element |
| List groupRuntimeItrs = context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(rIter); |
| String[] definitions = new String[groupRuntimeItrs.size()]; |
| Iterator iterator = groupRuntimeItrs.iterator(); |
| int i = 0; |
| while (iterator.hasNext()) { |
| RuntimeIterator rIterator = (RuntimeIterator) iterator.next(); |
| definitions[i++] = rIterator.getDefinition(); |
| |
| } |
| |
| // StringBuffer sb = new StringBuffer(); |
| // cv.generateCanonicalizedExpression(sb, context); |
| IndexData indexData = IndexUtils.findIndex(regionPath, definitions, cv, |
| "*", context.getCache(), usePrimaryIndex, context); |
| if (indexData != null) { |
| if (logger.isDebugEnabled() ) { |
| logger.debug("Indexed expression for indexed data : {} for region : {}", indexData.getIndex().getCanonicalizedIndexedExpression(), regionPath); |
| } |
| } |
| |
| return indexData; |
| } |
| |
| /** |
| * Asif : Conditions the raw index result obtained on a non composite |
| * condition ( i.e a condition with a format of variable = constant . A |
| * constant may be either a CompiledLiteral or an expression which is |
| * completely dependent on iterators other than the current scope. The |
| * variable is a path expression which is completely dependent on iterators |
| * belonging only to a single region ( i.e iterators belonging to a Group of |
| * iterators only dependent on a single indpendent iterator for the region). |
| * The raw index result is appropriately expanded / cutdown with evaluation of |
| * iter operand if any , StructType/ObjectType appropriately set, Shuffling of |
| * the fields appropriately done, such that the final result is compatible, in |
| * terms of the position and names of the fields of SelectResults( StructBag) , |
| * with the Iterators of the query from clause ( if complete expansion flag is |
| * true) or the chain of iterators identified by the indpendent iterator for |
| * the group. |
| * |
| * @param indexResults The raw index results which may be a ResultBag object |
| * or an StructBag object |
| * @param indexInfo IndexInfo object containing data such as match level & the |
| * mapping of the position of Runtime Iterators of the group to the |
| * position of the corresponding field in the index result ( |
| * StructBag) |
| * @param context ExecutionContext object |
| * @param indexFieldsSize The number of fields contained in the raw index |
| * resultset |
| * @param completeExpansion The boolean indicating whether the index resultset |
| * needs to be expanded to the query from clause level ( i.e top |
| * level) |
| * @param iterOperands The CompiledValue representing the iter operand which |
| * needs to be evaluated during conditioning of index resultset |
| * @param grpIndpndntItr An Array of Independent Iterators representing their |
| * respective groups. The conditioned Index Resultset will be created |
| * as per the chain of dependent iterators for each group. |
| * @return SelectResults object representing the conditioned Results |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException |
| * @throws NameResolutionException |
| * @throws QueryInvocationTargetException |
| * |
| */ |
| static SelectResults getconditionedIndexResults(SelectResults indexResults, |
| IndexInfo indexInfo, ExecutionContext context, int indexFieldsSize, |
| boolean completeExpansion, CompiledValue iterOperands, |
| RuntimeIterator[] grpIndpndntItr) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| if (!completeExpansion && grpIndpndntItr != null |
| && grpIndpndntItr.length > 1) { |
| //Asif : If for a Single Base Collection Index usage we are having |
| // independent |
| // group of iterators with size greater than 1 , that implies it is being |
| // invoked |
| // from CompositeGroupJunction with a single GroupJunction present in it. |
| //If it so happens that Complete expansion is false, this implies that we |
| // need to expand |
| //it to CompositeGroupJunction level. We will esnure in |
| // CompositeGroupJunction that |
| // we pass an array of independent iterators of size > 1 only if complete |
| // expansion is false |
| IndexConditioningHelper ich = new IndexConditioningHelper(indexInfo, |
| context, indexFieldsSize, completeExpansion, iterOperands, null); |
| // We will definitely need shuffling as a CompositeGroupJunction itself |
| // indicates |
| // that there will be at least one independent group to which we need to |
| // expand to |
| ich.finalList = getDependentItrChainForIndpndntItrs(grpIndpndntItr, |
| context); |
| //Add the iterators of remaining independent grp to the expansion list |
| List newExpList = new ArrayList(); |
| int len = grpIndpndntItr.length; |
| RuntimeIterator tempItr = null; |
| for (int i = 0; i < len; ++i) { |
| tempItr = grpIndpndntItr[i]; |
| if (tempItr != ich.indpndntItr) { |
| newExpList.addAll(context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(tempItr)); |
| } |
| } |
| newExpList.addAll(ich.expansionList); |
| ich.expansionList = newExpList; |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeCutDownAndExpansionOfSingleIndexResult(indexInfo._index, |
| indexResults); |
| indexResults = QueryUtils.cutDownAndExpandIndexResults(indexResults, |
| ich.indexFieldToItrsMapping, ich.expansionList, ich.finalList, |
| context, ich.checkList, iterOperands); |
| } |
| finally { |
| observer.afterCutDownAndExpansionOfSingleIndexResult(indexResults); |
| } |
| } |
| else { |
| IndexConditioningHelper ich = new IndexConditioningHelper(indexInfo, |
| context, indexFieldsSize, completeExpansion, iterOperands, |
| grpIndpndntItr != null ? grpIndpndntItr[0] : null); |
| if (ich.shufflingNeeded) { |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeCutDownAndExpansionOfSingleIndexResult( |
| indexInfo._index, indexResults); |
| indexResults = QueryUtils.cutDownAndExpandIndexResults(indexResults, |
| ich.indexFieldToItrsMapping, ich.expansionList, ich.finalList, |
| context, ich.checkList, iterOperands); |
| } |
| finally { |
| observer.afterCutDownAndExpansionOfSingleIndexResult(indexResults); |
| } |
| } |
| else if (indexInfo.mapping.length > 1) { |
| indexResults.setElementType(ich.structType); |
| } |
| } |
| return indexResults; |
| } |
| |
| /** |
| * Asif :This function is used to evaluate a filter evaluatable |
| * CompositeCondition(ie Range Indexes available on both LHS & RHS |
| * operands).This function is invoked from AND junction evaluation of |
| * CompositeGroupJunction. It expands the intermediate resultset passed , to |
| * the level of groups determined by the LHS & RHS operand, using the range |
| * indexes. It is possible that the group of iterators for an operand of |
| * condition already exists in the intermediate resultset passed. In such |
| * situation, the intermediate resultset is iterated & the operand ( whose group |
| * of iterators are available in the intermediate resultset ) is evaluated. |
| * For each such evaluated value , the other operand's Range Index is queried & |
| * the Range Index's results are appropriately expanded & cut down & a final |
| * tuple obtained( which includes the previously existing fields of |
| * intermediate resultset). The array of independent iterators passed from the |
| * Composite Group junction will be null, except for the final condition ( |
| * subject to the fact that complete expansion flag is false. Otherwise even |
| * for final condition , the array will be null) as that array will be used to |
| * get the final position of iterators in the resultant StructBag |
| * |
| * @param intermediateResults SelectResults object containing the intermediate |
| * resultset obtained by evaluation of previous filter evaluatable |
| * composite conditions of the CompositeGroupJunction |
| * @param indxInfo Array of IndexInfo objects ( size 2), representing the |
| * range index for the two operands of the condition |
| * @param context ExecutionContext object |
| * @param completeExpansionNeeded A boolean when true indicates that the final |
| * result from Composite GroupJunction needs to be evaluated to the |
| * query from clause ( top ) level. |
| * @param iterOperands CompiledValue representing the conditions which are to |
| * be iter evaluated. This can exist only if instead of |
| * AllGroupJunction we have a single CompositeGroupJunction |
| * @param indpdntItrs Array of RuntimeIterators representing the independent |
| * iterators of their representative groups forming the |
| * CompositeGroupJunction * |
| * @return SelectResults The Result object created by evaluating the filter |
| * evaluatable condition merged with the intermediate results |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException |
| * @throws NameResolutionException |
| * @throws QueryInvocationTargetException |
| * |
| */ |
| static SelectResults getRelationshipIndexResultsMergedWithIntermediateResults( |
| SelectResults intermediateResults, IndexInfo[] indxInfo, |
| ExecutionContext context, boolean completeExpansionNeeded, |
| CompiledValue iterOperands, RuntimeIterator[] indpdntItrs) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| ObjectType resultType1 = indxInfo[0]._index.getResultSetType(); |
| int indexFieldsSize1 = resultType1 instanceof StructType ? ((StructTypeImpl) resultType1) |
| .getFieldNames().length |
| : 1; |
| ObjectType resultType2 = indxInfo[1]._index.getResultSetType(); |
| int indexFieldsSize2 = resultType2 instanceof StructType ? ((StructTypeImpl) resultType2) |
| .getFieldNames().length |
| : 1; |
| /* |
| * Asif : even if th complete expansion is needed pass the flag of complete |
| * expansion as false. Thus for LHS & RHS we will get the expnasionList for |
| * that individual group. |
| */ |
| IndexConditioningHelper ich1 = new IndexConditioningHelper(indxInfo[0], |
| context, indexFieldsSize1, |
| false/* Asif : pass it as false, irrespective of actual value */, |
| iterOperands, null); |
| IndexConditioningHelper ich2 = new IndexConditioningHelper(indxInfo[1], |
| context, indexFieldsSize2, |
| false/* Asif : pass it as false, irrespective of actual value */, |
| iterOperands, null); |
| //Asif : We cannot have a condition where in intermediateResultset is empty |
| // or null & complete |
| // expansion flag true because in that case instead of this function we should |
| // have called |
| //getconditionedRelationshipIndexResultsExpandedToTopOrCGJLevel |
| int noOfIndexesToUse = (intermediateResults == null || intermediateResults |
| .isEmpty()) ? 2 : 0; |
| RuntimeIterator[] resultFieldsItrMapping = null; |
| List allItrs = context.getCurrentIterators(); |
| IndexConditioningHelper singleUsableICH = null; |
| IndexConditioningHelper nonUsableICH = null; |
| List finalList = completeExpansionNeeded ? allItrs |
| : indpdntItrs == null ? new ArrayList() : null; |
| // Asif : the set will contain those iterators which we don't have to expand |
| // to |
| // either because they are already present ( because of intermediate results or |
| // because index result already contains them |
| Set expnItrsToIgnore = null; |
| if (noOfIndexesToUse == 0) { |
| //If the intermediate Resultset is not empty then check if the resultset |
| // fields of intermediate |
| //resultset contains any independent iterator of the current condition |
| noOfIndexesToUse = 2; |
| StructType stype = (StructType) intermediateResults.getCollectionType() |
| .getElementType(); |
| String[] fieldNames = stype.getFieldNames(); |
| int len = fieldNames.length; |
| resultFieldsItrMapping = new RuntimeIterator[len]; |
| String fieldName = null; |
| String lhsID = ich1.indpndntItr.getInternalId(); |
| String rhsID = ich2.indpndntItr.getInternalId(); |
| for (int i = 0; i < len; ++i) { |
| fieldName = fieldNames[i]; |
| if (noOfIndexesToUse != 0) { |
| if (fieldName.equals(lhsID)) { |
| --noOfIndexesToUse; |
| singleUsableICH = ich2; |
| nonUsableICH = ich1; |
| } |
| else if (fieldName.equals(rhsID)) { |
| --noOfIndexesToUse; |
| singleUsableICH = ich1; |
| nonUsableICH = ich2; |
| } |
| } |
| int pos = Integer.parseInt(fieldName.substring(4)); |
| RuntimeIterator itrPrsntInIntermdtRes = (RuntimeIterator) allItrs |
| .get(pos - 1); |
| resultFieldsItrMapping[i] = itrPrsntInIntermdtRes; |
| //Asif : the iterator below is already present in resultset so needs to |
| // be ignored for expansion |
| if (completeExpansionNeeded) { |
| if (expnItrsToIgnore == null) { |
| expnItrsToIgnore = new HashSet(); |
| } |
| expnItrsToIgnore.add(itrPrsntInIntermdtRes); |
| } |
| else if (indpdntItrs == null) { |
| // Asif:We will need to know the intermediate iterators so as to know |
| // the final list which will be used to obtain the correct structset. |
| //But if the independent group of iterators is passed, the final list needs |
| // to be calculated |
| // on its basis |
| finalList.add(itrPrsntInIntermdtRes); |
| } |
| } |
| if (noOfIndexesToUse == 0) { |
| singleUsableICH = null; |
| } |
| } |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| if (noOfIndexesToUse == 2) { |
| List data = null; |
| try { |
| ArrayList resultData = new ArrayList(); |
| observer.beforeIndexLookup(indxInfo[0]._index, |
| OQLLexerTokenTypes.TOK_EQ, null); |
| observer.beforeIndexLookup(indxInfo[1]._index, |
| OQLLexerTokenTypes.TOK_EQ, null); |
| if (context.getBucketList() != null) { |
| data = queryEquijoinConditionBucketIndexes(indxInfo, context); |
| } else { |
| data = indxInfo[0]._index.queryEquijoinCondition(indxInfo[1]._index, context); |
| } |
| } |
| finally { |
| observer.afterIndexLookup(data); |
| } |
| //For sure we need to evaluate both the conditions & expand it only to |
| // its own respective |
| // Ignore the boolean of reshuffling needed etc for this case |
| List totalExpList = new ArrayList(); |
| totalExpList.addAll(ich1.expansionList); |
| totalExpList.addAll(ich2.expansionList); |
| if (completeExpansionNeeded) { |
| if (expnItrsToIgnore == null) { |
| //Asif : The expnItrsToIgnore set being null at this point implies |
| // that though |
| // complete expansion flag is true but intermediate result set is |
| // empty |
| Support |
| .Assert( |
| intermediateResults == null || intermediateResults.isEmpty(), |
| "expnItrsToIgnore should not have been null if the intermediate result set is not empty"); |
| expnItrsToIgnore = new HashSet(); |
| } |
| expnItrsToIgnore.addAll(ich1.finalList); |
| expnItrsToIgnore.addAll(ich2.finalList); |
| //Asif : identify the iterators which we need to expand to |
| //TODO:Asif :Make the code compact by using a common function to take |
| // care of this |
| int size = finalList.size(); |
| for (int i = 0; i < size; ++i) { |
| RuntimeIterator currItr = (RuntimeIterator) finalList.get(i); |
| //Asif :If the runtimeIterators of scope not present in CheckSet add |
| // it |
| // to |
| // the expansion list |
| if (!expnItrsToIgnore.contains(currItr)) { |
| totalExpList.add(currItr); |
| } |
| } |
| } |
| else { |
| //Asif : If the independent itrs passed is not null, this implies |
| // that we are evaluating the last filterable cc of the AND junction |
| // so the final resultset should have placement of iterators in the |
| // order of indpendent iterators present in CGJ. Otherwise we will have |
| // struct |
| //set mismatch while doing intersection with GroupJunction results |
| if (indpdntItrs != null) { |
| finalList = getDependentItrChainForIndpndntItrs(indpdntItrs, context); |
| } |
| else { |
| finalList.addAll(ich1.finalList); |
| finalList.addAll(ich2.finalList); |
| } |
| } |
| List[] checkList = new List[] { ich1.checkList, ich2.checkList}; |
| StructType stype = createStructTypeForRuntimeIterators(finalList); |
| SelectResults returnSet = QueryUtils.createStructCollection(context, stype) ; |
| RuntimeIterator[][] mappings = new RuntimeIterator[2][]; |
| mappings[0] = ich1.indexFieldToItrsMapping; |
| mappings[1] = ich2.indexFieldToItrsMapping; |
| List[] totalCheckList = new List[] { ich1.checkList, ich2.checkList}; |
| RuntimeIterator[][] resultMappings = new RuntimeIterator[1][]; |
| resultMappings[0] = resultFieldsItrMapping; |
| Iterator dataItr = data.iterator(); |
| IndexCutDownExpansionHelper[] icdeh = new IndexCutDownExpansionHelper[] { |
| new IndexCutDownExpansionHelper(ich1.checkList, context), |
| new IndexCutDownExpansionHelper(ich2.checkList, context)}; |
| ListIterator expansionListIterator = totalExpList.listIterator(); |
| if (dataItr.hasNext()) { |
| observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeMergeJoinOfDoubleIndexResults(indxInfo[0]._index, |
| indxInfo[1]._index, data); |
| boolean doMergeWithIntermediateResults = intermediateResults != null |
| && !intermediateResults.isEmpty(); |
| int maxCartesianDepth = totalExpList.size() |
| + (doMergeWithIntermediateResults ? 1 : 0); |
| while (dataItr.hasNext()) { |
| //TODO:Asif Change the code in range Index so that while collecting |
| // data |
| // instead of creating |
| //two dimensional object array , we create one dimensional Object |
| // array |
| // of size 2, & each elemnt |
| //stores an Object array |
| Object[][] values = (Object[][]) dataItr.next(); |
| //Asif : Before doing the cartesian of the Results , we need to |
| // clear |
| // the CheckSet of InexCutDownExpansionHelper. This is needed |
| // because for |
| // a new key , the row of sets needs to be considered fresh as |
| // presence of old row in |
| // checkset may cause us to wrongly skip the similar row of a set , |
| // even when the row in its entirety |
| // is unique ( made by different data in the other set) |
| if (doMergeWithIntermediateResults) { |
| mergeRelationshipIndexResultsWithIntermediateResults(returnSet, |
| new SelectResults[] { intermediateResults}, resultMappings, |
| values, mappings, expansionListIterator, finalList, context, |
| checkList, iterOperands, icdeh, 0, maxCartesianDepth); |
| } |
| else { |
| mergeAndExpandCutDownRelationshipIndexResults(values, returnSet, |
| mappings, expansionListIterator, finalList, context, |
| totalCheckList, iterOperands, icdeh, 0 /* Level */); |
| } |
| if (icdeh[0].cutDownNeeded) icdeh[0].checkSet.clear(); |
| } |
| } |
| finally { |
| observer.afterMergeJoinOfDoubleIndexResults(returnSet); |
| } |
| } |
| return returnSet; |
| } |
| else if (noOfIndexesToUse == 1) { |
| //Asif : There exists one independent iterator in the current condition |
| // which is also a part of the intermediate resultset |
| // Identify the final List which will depend upon the complete expansion |
| // flag |
| // Identify the iterators to be expanded to, which will also depend upon |
| // complete |
| // expansion flag.. |
| List totalExpList = new ArrayList(); |
| totalExpList.addAll(singleUsableICH.expansionList); |
| if (completeExpansionNeeded) { |
| Support |
| .Assert( |
| expnItrsToIgnore != null, |
| "expnItrsToIgnore should not have been null as we are in this block itself indicates that intermediate results was not null"); |
| expnItrsToIgnore.addAll(singleUsableICH.finalList); |
| //Asif : identify the iterators which we need to expand to |
| //TODO:Asif :Make the code compact by using a common function to take |
| // care of this |
| int size = finalList.size(); |
| for (int i = 0; i < size; ++i) { |
| RuntimeIterator currItr = (RuntimeIterator) finalList.get(i); |
| //Asif :If the runtimeIterators of scope not present in CheckSet add |
| // it |
| // to |
| // the expansion list |
| if (!expnItrsToIgnore.contains(currItr)) { |
| totalExpList.add(currItr); |
| } |
| } |
| } |
| else { |
| // Asif : If the independent itrs passed is not null, this implies |
| // that we are evaluating the last filterable cc of the AND junction |
| // so the final resultset should have placement of iterators in the |
| // order of indpendent iterators present in CGJ. Otherwise we will havve |
| // struct |
| //set mismatch while doing intersection with GroupJunction results |
| if (indpdntItrs != null) { |
| finalList = getDependentItrChainForIndpndntItrs(indpdntItrs, context); |
| } |
| else { |
| finalList.addAll(singleUsableICH.finalList); |
| } |
| } |
| // List[] checkList = new List[] { ich1.checkList, ich2.checkList}; |
| StructType stype = createStructTypeForRuntimeIterators(finalList); |
| SelectResults returnSet = QueryUtils.createStructCollection(context, stype) ; |
| //Asif :Obtain the empty resultset for the single usable index |
| IndexProtocol singleUsblIndex = singleUsableICH.indxInfo._index; |
| CompiledValue nonUsblIndxPath = nonUsableICH.indxInfo._path; |
| ObjectType singlUsblIndxResType = singleUsblIndex.getResultSetType(); |
| // int singleUsblIndexFieldsSize = -1; |
| SelectResults singlUsblIndxRes = null; |
| if (singlUsblIndxResType instanceof StructType) { |
| singlUsblIndxRes = QueryUtils.createStructCollection(context,(StructTypeImpl) singlUsblIndxResType); |
| // singleUsblIndexFieldsSize = ((StructTypeImpl) singlUsblIndxResType) |
| // .getFieldNames().length; |
| } |
| else { |
| singlUsblIndxRes = QueryUtils.createResultCollection(context, singlUsblIndxResType); |
| // singleUsblIndexFieldsSize = 1; |
| } |
| //Asif iterate over the intermediate structset |
| Iterator intrmdtRsItr = intermediateResults.iterator(); |
| observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeIndexLookup(singleUsblIndex, OQLLexerTokenTypes.TOK_EQ, |
| null); |
| observer.beforeIterJoinOfSingleIndexResults(singleUsblIndex, |
| nonUsableICH.indxInfo._index); |
| while (intrmdtRsItr.hasNext()) { |
| Struct strc = (Struct) intrmdtRsItr.next(); |
| Object[] val = strc.getFieldValues(); |
| int len = val.length; |
| for (int i = 0; i < len; ++i) { |
| resultFieldsItrMapping[i].setCurrent(val[i]); |
| } |
| //TODO:Asif : Issue relevant index use callbacks to QueryObserver |
| Object key = nonUsblIndxPath.evaluate(context); |
| //TODO:Asif : Check this logic out |
| if (key != null && key.equals(QueryService.UNDEFINED)) continue; |
| singleUsblIndex.query(key, OQLLexerTokenTypes.TOK_EQ, |
| singlUsblIndxRes, context); |
| cutDownAndExpandIndexResults(returnSet, singlUsblIndxRes, |
| singleUsableICH.indexFieldToItrsMapping, totalExpList, finalList, |
| context, singleUsableICH.checkList, iterOperands); |
| singlUsblIndxRes.clear(); |
| } |
| } |
| finally { |
| observer.afterIterJoinOfSingleIndexResults(returnSet); |
| observer.afterIndexLookup(returnSet); |
| } |
| return returnSet; |
| } |
| else { |
| //Asif : This condition is filter evaluatable but both the RHS group as |
| // well as |
| // LHS group of iterators are present in the intermediate resultset. As a |
| // result indexes |
| // cannot be used for this condition. This condition needs to be iter |
| // evaluated. |
| // For BETTER PERF INDEXES SHOULD BE REMOVED FROM THIS CONDITION SO THAT |
| // IT BECOMES |
| //PART OF ITER OPERANDS |
| if (logger.isDebugEnabled()) { |
| StringBuffer tempBuffLhs = new StringBuffer(); |
| StringBuffer tempBuffRhs = new StringBuffer(); |
| ich1.indxInfo._path.generateCanonicalizedExpression(tempBuffLhs, |
| context); |
| ich2.indxInfo._path.generateCanonicalizedExpression(tempBuffRhs, |
| context); |
| logger.debug("For better performance indexes are not used for the condition {} = {}", tempBuffLhs, tempBuffRhs); |
| } |
| CompiledValue reconstructedVal = new CompiledComparison( |
| ich1.indxInfo._path, ich2.indxInfo._path, OQLLexerTokenTypes.TOK_EQ); |
| //Add this reconstructed value to the iter operand if any |
| CompiledValue finalVal = reconstructedVal; |
| if (iterOperands != null) { |
| //Asif : The type of CompiledJunction has to be AND junction as this |
| // function gets invoked only for AND . Also it is OK if we have |
| // iterOperands which |
| //itself is a CompiledJunction. We can have a tree of CompiledJunction |
| // with its |
| // operands being a CompiledComparison & a CompiledJunction. We can live |
| // without |
| // creating a flat structure |
| finalVal = new CompiledJunction(new CompiledValue[] { iterOperands, |
| reconstructedVal}, OQLLexerTokenTypes.LITERAL_and); |
| } |
| RuntimeIterator[][] resultMappings = new RuntimeIterator[1][]; |
| resultMappings[0] = resultFieldsItrMapping; |
| return cartesian(new SelectResults[] { intermediateResults}, |
| resultMappings, Collections.EMPTY_LIST, finalList, context, finalVal); |
| } |
| } |
| |
| /** |
| * Asif :This function is used to evaluate a filter evaluatable composite |
| * condition. It gets invoked either from a CompositeGroupJunction of "OR" |
| * type or a where clause containing single composite condition. In the later |
| * case the boolean completeExpansion flag is always true. While in the former |
| * case it may be true or false. If it is false, the array of independent |
| * iterators passed is not null. |
| * |
| * @param data A List object whose elements are two dimensional object array. |
| * Each element of the List represent a value which satisfies the |
| * equi-join condition. Since there may be more than one tuples on |
| * either side of the equality condition which meet the criteria for |
| * a given value, we require a 2 dimensional Object array. The |
| * cartesian of the two rows will give us the set of tuples |
| * satisfying the join criteria. Each element of the row of Object |
| * Array may be either an Object or a Struct object. |
| * |
| * |
| * @param indxInfo An array of IndexInfo objects of size 2 , representing the |
| * range indexes of the two operands. The other Index maps to the 0th |
| * Object array row of the List object ( data ) & so on. |
| * @param context ExecutionContext object |
| * @param completeExpansionNeeded boolean if true indicates that the CGJ needs |
| * to be expanded to the query from clause ( top level ) |
| * @param iterOperands This will be null as for OR junction we cannot have |
| * iter operand |
| * @param indpdntItrs Array of independent iterators representing the various |
| * Groups forming the composite group junction. It will be null, if |
| * complete expansion flag is true |
| * @return SelectResults objet representing the result obtained by evaluating |
| * a filter evaluatable composite condition in an OR junction. The |
| * returned Result is expanded either to the CompositeGroupJunction |
| * level or to the top level as the case may be |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException |
| * @throws NameResolutionException |
| * @throws QueryInvocationTargetException |
| * |
| */ |
| static SelectResults getconditionedRelationshipIndexResultsExpandedToTopOrCGJLevel( |
| List data, IndexInfo[] indxInfo, ExecutionContext context, |
| boolean completeExpansionNeeded, CompiledValue iterOperands, |
| RuntimeIterator[] indpdntItrs) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| ObjectType resultType1 = indxInfo[0]._index.getResultSetType(); |
| int indexFieldsSize1 = resultType1 instanceof StructType ? ((StructTypeImpl) resultType1) |
| .getFieldNames().length |
| : 1; |
| ObjectType resultType2 = indxInfo[1]._index.getResultSetType(); |
| int indexFieldsSize2 = resultType2 instanceof StructType ? ((StructTypeImpl) resultType2) |
| .getFieldNames().length |
| : 1; |
| /* |
| * Asif : even if th complete expansion is needed pass the flag of complete |
| * expansion as false. Thus for LHS & RHS we will get the expansionList for |
| * that individual group. Thus the total expansion List wil contain sum of |
| * the individual expansion lists plus all the iterators of the current |
| * scope which are dependent on any other groups or are composite iterators ( |
| * i.e dependent on both the independent groups currently under |
| * consideration |
| */ |
| IndexConditioningHelper ich1 = new IndexConditioningHelper(indxInfo[0], |
| context, indexFieldsSize1, |
| false/* Asif : pass it as false, irrespective of actual value */, |
| iterOperands, null); |
| IndexConditioningHelper ich2 = new IndexConditioningHelper(indxInfo[1], |
| context, indexFieldsSize2, |
| false/* Asif : pass it as false, irrespective of actual value */, |
| iterOperands, null); |
| List totalExpList = new ArrayList(); |
| totalExpList.addAll(ich1.expansionList); |
| totalExpList.addAll(ich2.expansionList); |
| // List[] checkList = new List[] { ich1.checkList, ich2.checkList}; |
| List totalFinalList = null; |
| if (completeExpansionNeeded) { |
| totalFinalList = context.getCurrentIterators(); |
| Set expnItrsAlreadyAccounted = new HashSet(); |
| expnItrsAlreadyAccounted.addAll(ich1.finalList); |
| expnItrsAlreadyAccounted.addAll(ich2.finalList); |
| int size = totalFinalList.size(); |
| for (int i = 0; i < size; ++i) { |
| RuntimeIterator currItr = (RuntimeIterator) totalFinalList.get(i); |
| //Asif :If the runtimeIterators of scope not present in CheckSet add it |
| // to |
| // the expansion list |
| if (!expnItrsAlreadyAccounted.contains(currItr)) { |
| totalExpList.add(currItr); |
| } |
| } |
| } |
| else { |
| totalFinalList = new ArrayList(); |
| for (int i = 0; i < indpdntItrs.length; ++i) { |
| RuntimeIterator indpndntItr = indpdntItrs[i]; |
| if (indpndntItr == ich1.finalList.get(0)) { |
| totalFinalList.addAll(ich1.finalList); |
| } |
| else if (indpndntItr == ich2.finalList.get(0)) { |
| totalFinalList.addAll(ich2.finalList); |
| } |
| else { |
| List temp = context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(indpndntItr); |
| totalFinalList.addAll(temp); |
| totalExpList.addAll(temp); |
| } |
| } |
| } |
| Support |
| .Assert( |
| totalFinalList.size() > 1, |
| " Since we are in relationship index this itself means that we have atleast two RuntimeIterators"); |
| StructType stype = createStructTypeForRuntimeIterators(totalFinalList); |
| SelectResults returnSet = QueryUtils.createStructCollection(context, stype) ; |
| RuntimeIterator[][] mappings = new RuntimeIterator[2][]; |
| mappings[0] = ich1.indexFieldToItrsMapping; |
| mappings[1] = ich2.indexFieldToItrsMapping; |
| List[] totalCheckList = new List[] { ich1.checkList, ich2.checkList}; |
| Iterator dataItr = data.iterator(); |
| IndexCutDownExpansionHelper[] icdeh = new IndexCutDownExpansionHelper[] { |
| new IndexCutDownExpansionHelper(ich1.checkList, context), |
| new IndexCutDownExpansionHelper(ich2.checkList, context)}; |
| ListIterator expansionListIterator = totalExpList.listIterator(); |
| if (dataItr.hasNext()) { |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeMergeJoinOfDoubleIndexResults(ich1.indxInfo._index, |
| ich2.indxInfo._index, data); |
| while (dataItr.hasNext()) { |
| //TODO:Asif Change the code in range Index so that while collecting |
| // data |
| // instead of creating |
| //two dimensional object array , we create one dimensional Object |
| // array |
| // of size 2, & each elemnt |
| //stores an Object array |
| Object[][] values = (Object[][]) dataItr.next(); |
| //Asif : Before doing the cartesian of the Results , we need to clear |
| // the |
| // CheckSet of IndexCutDownExpansionHelper. This is needed because for |
| // a |
| // new key , the row |
| // of sets needs to be considered fresh as presence of old row in |
| // checkset |
| // may cause us to wrongly |
| //skip the similar row of a set , even when the row in its entirety |
| // is |
| // unique ( made by |
| // different data in the other set) |
| mergeAndExpandCutDownRelationshipIndexResults(values, returnSet, |
| mappings, expansionListIterator, totalFinalList, context, |
| totalCheckList, iterOperands, icdeh, 0 /* Level */); |
| if (icdeh[0].cutDownNeeded) icdeh[0].checkSet.clear(); |
| } |
| } |
| finally { |
| observer.afterMergeJoinOfDoubleIndexResults(returnSet); |
| } |
| } |
| return returnSet; |
| } |
| |
| /* |
| * Asif : This function is used ony for testing the private visibility |
| * function |
| */ |
| static SelectResults testCutDownAndExpandIndexResults(List dataList) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| return cutDownAndExpandIndexResults((SelectResults) dataList.get(0), |
| (RuntimeIterator[]) dataList.get(1), (List) dataList.get(2), |
| (List) dataList.get(3), (ExecutionContext) dataList.get(4), |
| (List) dataList.get(5), null); |
| } |
| |
| static List queryEquijoinConditionBucketIndexes(IndexInfo[] indxInfo, |
| ExecutionContext context) |
| throws QueryInvocationTargetException, TypeMismatchException, |
| FunctionDomainException, NameResolutionException { |
| List data = null; |
| ArrayList resultData = new ArrayList(); |
| AbstractIndex index0 = (AbstractIndex) indxInfo[0]._index; |
| AbstractIndex index1 = (AbstractIndex) indxInfo[1]._index; |
| PartitionedRegion pr0 = null; |
| PartitionedRegion pr1 = null; |
| IndexProtocol i0 = null; |
| IndexProtocol i1 = null; |
| |
| if (index0.getRegion() instanceof BucketRegion) { |
| pr0 = ((BucketRegion) index0.getRegion()).getPartitionedRegion(); |
| } |
| |
| if (index1.getRegion() instanceof BucketRegion) { |
| pr1 = ((BucketRegion) index1.getRegion()).getPartitionedRegion(); |
| } |
| |
| for (Object b : context.getBucketList()) { |
| i0 = (pr0 != null) ? PartitionedIndex.getBucketIndex(pr0, |
| index0.getName(), (Integer) b) : indxInfo[0]._index; |
| i1 = (pr1 != null) ? PartitionedIndex.getBucketIndex(pr1, |
| index1.getName(), (Integer) b) : indxInfo[1]._index; |
| |
| if (i0 == null || i1 == null) { |
| continue; |
| } |
| data = i0.queryEquijoinCondition(i1, context); |
| resultData.addAll(data); |
| } |
| data = resultData; |
| return data; |
| } |
| } |
| /** |
| * |
| * @author asif This is a helper class which provides information on how an |
| * index data be used so as to make it compatible with the query. |
| * |
| */ |
| |
| class IndexConditioningHelper { |
| |
| /** |
| * boolean if true implies that the index results need to be iterated so as to |
| * make it compatible with from clause. Shuffling may be needed for any of the |
| * following reasons: 1) Match level not zero ( implying index result |
| * expansion or cutdown) 2) Match level zero , but the relative positions of |
| * iterators in the List of iterators for the group not matching the positions |
| * in the index result StructBag 3) Iter operand is not null. * |
| * |
| */ |
| //Asif :If shuffling is not needed , then it can be bcoz of two reasons |
| //1) The Index results is a ResultSet & match level is zero ( in that case we |
| // don't have to do anything) |
| //2) The Index results is a StructBag with match level as zero & inddex |
| // fields matching |
| // the order of RuntimeIterators. In that case we just have to change the |
| // StructType of the StructBag |
| boolean shufflingNeeded = true; |
| /** |
| * An arary of RuntimeIterators whose size is equal to the number of fields in |
| * the Index results. It identifies the RuntimeIterator for the field in the |
| * Index Results. Thus the Runtime Iterator at position 0 will be that for |
| * field 0 in the index result & so on. For those index fields which do not |
| * have a Runtime Iterator assosciated , the value is null (This is the case |
| * if index results require cut down) |
| * |
| */ |
| RuntimeIterator[] indexFieldToItrsMapping = null; |
| /** |
| * The List containing RuntimeIterators to which the index results need to be |
| * expanded This will usually be Final List of RuntimeIterators - |
| * RuntimeIteratosr already accounted for in the index results |
| */ |
| //Asif : The default is initialized as empty List rather than null to avoid |
| // Null Pointer Exception in the function |
| // getconditionedRelationshipIndexResults |
| List expansionList = Collections.EMPTY_LIST; |
| /** |
| * The List containing RuntimeIterators which define the final SelectResults |
| * after the relevant expansion/cutdown of index results |
| */ |
| //Asif : Though in case of single index usage , if no shuffling is needed ( |
| // exact match) we |
| // do not need finalList , but it is used in relation ship index , even if |
| // match level is zero. |
| // So we should never leave it as null |
| List finalList = null; |
| /** |
| * This is the List of RuntimeIterators which gets created only if the index |
| * resulst require a cutdown. In such cases , it identifies those Runtime |
| * Iterators of Index Results which will be selected to form the result tuple. |
| * The RuntimeIterators in this List will have corresponding fields in the |
| * resultset obtained from Index usage. This List will be populated only if |
| * there exists fields in index resultset which will not be selected.If all |
| * the fields of index resultset will be used , then this List should be null |
| * or empty. It is used in preventing unnecessary expansion of same type, when |
| * a similar expansion has already occured. as for eg |
| * |
| * consider a index result containing 3 fields field1 field2 & field3 . Assume |
| * that field3 is for cutdown. Since the expansion iterators can either be |
| * independent of all the fields in the index result or at the max be |
| * dependent on field1 & field2, we should expand for a given combination of |
| * field1 & field2 , only once ( as we have resulst as Set, we can only have |
| * unique entries) ie. suppose a index result tuple has values ( 1,2 , 3 ) & ( |
| * 1,2,4) , we should expand only once ( as field with value 3 & 4 are to be |
| * discarded). |
| * |
| * |
| * |
| */ |
| /* |
| * Asif: Below Can be null or empty collections if the match level is exact & |
| * no shuffling needed |
| */ |
| List checkList = null; |
| /** |
| * Asif : This field is meaninful iff the match level is zero, no shuffling |
| * needed & there exists a StructBag (& not a ResultBag) |
| */ |
| StructType structType = null; |
| /** |
| * Independent Iterator for the Group to which the Path expression belongs to |
| */ |
| RuntimeIterator indpndntItr = null; |
| /** |
| * Indexnfo object for the path expression |
| */ |
| IndexInfo indxInfo = null; |
| |
| //TODO:Asif :Test this function out |
| public IndexConditioningHelper(IndexInfo indexInfo, ExecutionContext context, |
| int indexFieldsSize, boolean completeExpansion, |
| CompiledValue iterOperands, RuntimeIterator grpIndpndntItr) { |
| /* |
| * 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. The grpIndpndtItr |
| * passed can be null if the where clause comprises of just this condition. |
| * However if it is invoked from GroupJunction , it will be not null |
| * |
| */ |
| this.indxInfo = indexInfo; |
| List grpItrs = null; |
| int size = indexInfo.mapping.length; |
| this.indpndntItr = grpIndpndntItr; |
| this.indexFieldToItrsMapping = new RuntimeIterator[indexFieldsSize]; |
| // Asif Obtain the grpIndpndt iterator if it is passed as null |
| if (this.indpndntItr == null) { |
| Set set1 = new HashSet(); |
| context.computeUtlimateDependencies(indexInfo._path, set1); |
| Support |
| .Assert( |
| set1.size() == 1, |
| " Since we are in Indexed Evaluate that means there has to be exactly one independent iterator for this compiled comparison"); |
| // The ultimate independent RuntimeIterator |
| this.indpndntItr = (RuntimeIterator) set1.iterator().next(); |
| Support |
| .Assert( |
| this.indpndntItr.getScopeID() == context.currentScope().getScopeID()/*context.getScopeCount()*/, |
| " Since we are in Indexed Evaluate that means the current scope count & indpenedent iterator's scope count should match"); |
| } |
| if (indexInfo._matchLevel == 0 |
| && (!completeExpansion || context.getCurrentIterators().size() == size)) { |
| //Asif: Don't do anything , just change the StructType if the set is |
| // structset. |
| if (size > 1) { |
| // The Index resultset is a structType. |
| Support |
| .Assert( |
| indexInfo._index.getResultSetType() instanceof StructType, |
| " If the match level is zero & the size of mapping array is 1 then Index is surely ResultBag else StructBag"); |
| //Asif : The independent iterator is added as the first element |
| grpItrs = context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(this.indpndntItr); |
| //Asif : Check if reshuffling is needed or just changing the struct |
| // type will suffice |
| boolean isReshufflingNeeded = false; |
| int pos = -1; |
| for (int i = 0; i < size; ++i) { |
| pos = indexInfo.mapping[i]; |
| isReshufflingNeeded = isReshufflingNeeded || (pos != (i + 1)); |
| this.indexFieldToItrsMapping[pos - 1] = (RuntimeIterator) grpItrs |
| .get(i); |
| } |
| this.finalList = grpItrs; |
| //Asif : Even if Reshuffle is not need but if the iter conditions are |
| // present we need to do evaluation |
| // We can avoid iterating over the set iff reshuffling is not needed & |
| // there is no iter eval condition |
| if (isReshufflingNeeded || iterOperands != null) { |
| //this.expansionList = Collections.EMPTY_LIST; |
| this.checkList = null; |
| //indexReults = QueryUtils.cutDownAndExpandIndexResults(indexReults, |
| // indexFieldToItrsMapping, Collections.EMPTY_LIST, grpItrs, |
| //context, Collections.EMPTY_LIST, iterOperands); |
| } |
| else { |
| this.structType = QueryUtils |
| .createStructTypeForRuntimeIterators(grpItrs); |
| //indexReults.setElementType(structType); |
| //Asif : Shuffling is not needed. Index results is a StructBag |
| // with match level zero & no expansion needed & index fields map |
| // with the RuntimeIterators. But we need to change the StructType |
| //of the StructBag |
| this.shufflingNeeded = false; |
| } |
| } |
| else { |
| //Asif : The finalList should not be left uninitialized, & if the match |
| // level is zero |
| // & the Index Results is a ResultBag ( & not an StructBag ) implying |
| // indexFieldsSize of |
| //1 , then the final List should contain only the independent iterator |
| this.finalList = new ArrayList(); |
| this.finalList.add(this.indpndntItr); |
| Support |
| .Assert( |
| this.indexFieldToItrsMapping.length == 1, |
| "In this else block , it should be guaranteed that there exists only one iterator in query as well as index from clause & that should be nothing but the independent RuntimeIterator of the group "); |
| this.indexFieldToItrsMapping[0] = this.indpndntItr; |
| //Asif :Shuffling is needed if iter operand is not null even if index results is a ResultSet |
| // with match level zero & no expansion needed |
| this.shufflingNeeded = (iterOperands != null) ; |
| } |
| } |
| else { |
| //Asif : There is some expansion or truncation needed on the data |
| // obtained from index.Identify a the iterators belonging to this group |
| // The independent iterator is added as the first element |
| grpItrs = context |
| .getCurrScopeDpndntItrsBasedOnSingleIndpndntItr(this.indpndntItr); |
| //Asif Create an array of RuntimeIterators which map to the fields of the |
| // Index set. |
| // For those fields which do not have corresponding RuntimeIterator , keep |
| // it as null; |
| int pos = -1; |
| this.finalList = completeExpansion ? context.getCurrentIterators() |
| : grpItrs; |
| // This is the List of runtimeIterators which have corresponding fields |
| // in the resultset obtained from Index usage. This List will be populated |
| // only if there exists fields in index resultset which will not be |
| // selected |
| // If all the fields of index resultset will be used , then this List |
| // should |
| // be null or empty |
| this.checkList = new ArrayList(); |
| //This List contains the RuntimeIterators which are missing from |
| // index resultset but are present in the final iterators |
| this.expansionList = new LinkedList(finalList); |
| RuntimeIterator tempItr = null; |
| //boolean cutDownNeeded = false; |
| int unMappedFields = indexFieldsSize; |
| for (int i = 0; i < size; ++i) { |
| pos = indexInfo.mapping[i]; |
| if (pos > 0) { |
| tempItr = (RuntimeIterator) grpItrs.get(i); |
| this.indexFieldToItrsMapping[pos - 1] = tempItr; |
| this.expansionList.remove(tempItr); |
| this.checkList.add(tempItr); |
| --unMappedFields; |
| } |
| } |
| boolean cutDownNeeded = unMappedFields > 0; |
| if (!cutDownNeeded) this.checkList = null; |
| /* |
| * indexReults = QueryUtils.cutDownAndExpandIndexResults(indexReults, |
| * indexFieldToItrsMapping, expansionList, finalList, context, checkList, |
| * iterOperands); |
| */ |
| } |
| } |
| } |
| /** |
| * |
| * @author asif This is a helper class which contains informaion on how to |
| * expand / cutdown index results for making it compatible with the |
| * query. |
| * |
| */ |
| |
| class IndexCutDownExpansionHelper { |
| |
| /** |
| * booelan which identifies if a cutdown of index results is needed or not. |
| */ |
| boolean cutDownNeeded = false; |
| /** |
| * A SelectResults ( ResultBag or StructBag) object used to prevent |
| * unnecessary expansion of index results as described in |
| * IndexConditionalHelper class. |
| */ |
| SelectResults checkSet = null; |
| /** |
| * ObjectType for the checkSet object ( An ObjectType for a ResultBag & |
| * StructType for a StructBag) |
| */ |
| ObjectType checkType = null; |
| int checkSize = -1; |
| |
| IndexCutDownExpansionHelper(List checkList, ExecutionContext context) { |
| cutDownNeeded = checkList != null && (checkSize = checkList.size()) > 0; |
| if (cutDownNeeded) { |
| Boolean orderByClause = (Boolean)context.cacheGet(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX); |
| boolean useLinkedDataStructure = false; |
| boolean nullValuesAtStart = true; |
| if(orderByClause != null && orderByClause.booleanValue()) { |
| List orderByAttrs = (List)context.cacheGet(CompiledValue.ORDERBY_ATTRIB); |
| useLinkedDataStructure =orderByAttrs.size()==1; |
| nullValuesAtStart = !((CompiledSortCriterion)orderByAttrs.get(0)).getCriterion(); |
| } |
| if (checkSize > 1) { |
| |
| checkType = QueryUtils.createStructTypeForRuntimeIterators(checkList); |
| if (useLinkedDataStructure) { |
| checkSet = context.isDistinct() ? new LinkedStructSet((StructTypeImpl) checkType) |
| : new SortedResultsBag<Struct>((StructTypeImpl)checkType, nullValuesAtStart); |
| } else { |
| checkSet = QueryUtils.createStructCollection(context, (StructTypeImpl)checkType) ; |
| } |
| } |
| else { |
| checkType = ((RuntimeIterator) checkList.get(0)).getElementType(); |
| if (useLinkedDataStructure) { |
| checkSet = context.isDistinct() ? new LinkedResultSet(checkType) : |
| new SortedResultsBag(checkType, nullValuesAtStart); |
| } else { |
| checkSet = QueryUtils.createResultCollection(context, checkType) ; |
| } |
| } |
| } |
| } |
| |
| } |