/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.cache.query.internal; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes; import org.apache.logging.log4j.Logger; import org.apache.geode.cache.query.AmbiguousNameException; import org.apache.geode.cache.query.FunctionDomainException; import org.apache.geode.cache.query.Index; import org.apache.geode.cache.query.NameResolutionException; import org.apache.geode.cache.query.QueryInvocationTargetException; import org.apache.geode.cache.query.QueryService; import org.apache.geode.cache.query.SelectResults; import org.apache.geode.cache.query.Struct; import org.apache.geode.cache.query.TypeMismatchException; import org.apache.geode.cache.query.internal.index.AbstractIndex; import org.apache.geode.cache.query.internal.index.IndexData; import org.apache.geode.cache.query.internal.index.IndexManager; import org.apache.geode.cache.query.internal.index.IndexProtocol; import org.apache.geode.cache.query.internal.index.IndexUtils; import org.apache.geode.cache.query.internal.index.PartitionedIndex; import org.apache.geode.cache.query.internal.types.StructTypeImpl; import org.apache.geode.cache.query.types.CollectionType; import org.apache.geode.cache.query.types.ObjectType; import org.apache.geode.cache.query.types.StructType; import org.apache.geode.internal.Assert; import org.apache.geode.internal.cache.BucketRegion; import org.apache.geode.internal.cache.CachePerfStats; import org.apache.geode.internal.cache.PartitionedRegion; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; 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 // 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; } /** * 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: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) { // 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, null); 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, IndexInfo theFilteringIndex) 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, theFilteringIndex); 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, IndexInfo theFilteringIndex) 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()) { DerivedInfo derivedInfo = null; if (IndexManager.JOIN_OPTIMIZATION) { derivedInfo = new DerivedInfo(); derivedInfo.setExpansionList(expansionList); } 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 if (IndexManager.JOIN_OPTIMIZATION) { derivedInfo.computeDerivedJoinResults(theFilteringIndex, context, iterOps); } doNestedIterationsForIndex(expansionListIterator.hasNext(), returnSet, finalItrs, expansionListIterator, context, iterOps, limit, derivedInfo.derivedResults); 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; } public static CompiledID getCompiledIdFromPath(CompiledValue path) { int type = path.getType(); if (type == OQLLexerTokenTypes.Identifier) { return (CompiledID) path; } return getCompiledIdFromPath(path.getReceiver()); } // Add comments private static void doNestedIterationsForIndex(boolean continueRecursion, SelectResults resultSet, List finalItrs, ListIterator expansionItrs, ExecutionContext context, CompiledValue iterOps, int limit, Map<String, SelectResults> derivedResults) 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 = null; // Calculate the key to find the derived join results. If we are a non nested lookup it will // be a Compiled Region otherwise it will be a CompiledPath that // we can extract the id from. In the end the result will be the alias which is used as a // prefix CompiledValue collectionExpression = currentLevel.getCmpIteratorDefn().getCollectionExpr(); String key = null; boolean useDerivedResults = true; if (currentLevel.getCmpIteratorDefn().getCollectionExpr() .getType() == OQLLexerTokenTypes.RegionPath) { key = currentLevel.getCmpIteratorDefn().getName() + ":" + currentLevel.getDefinition(); } else if (currentLevel.getCmpIteratorDefn().getCollectionExpr() .getType() == OQLLexerTokenTypes.LITERAL_select) { useDerivedResults = false; } else { key = getCompiledIdFromPath(currentLevel.getCmpIteratorDefn().getCollectionExpr()).getId() + ":" + currentLevel.getDefinition(); } if (useDerivedResults && derivedResults != null && derivedResults.containsKey(key)) { c = derivedResults.get(key); } else { 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, derivedResults); 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, indexInfo); } 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, indexInfo); } 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, singleUsableICH.indxInfo); 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, 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; } } /** * * 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. * * */ // 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); */ } } } /** * * 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); } } } } }