| /*========================================================================= |
| * Copyright Copyright (c) 2000-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * more patents listed at http://www.pivotal.io/patents. |
| * $Id: CompiledNegation.java,v 1.1 2005/01/27 06:26:33 vaibhav Exp $ |
| *========================================================================= |
| */ |
| |
| package com.gemstone.gemfire.cache.query.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import com.gemstone.gemfire.cache.Region; |
| import com.gemstone.gemfire.cache.query.AmbiguousNameException; |
| import com.gemstone.gemfire.cache.query.FunctionDomainException; |
| import com.gemstone.gemfire.cache.query.IndexType; |
| import com.gemstone.gemfire.cache.query.NameResolutionException; |
| import com.gemstone.gemfire.cache.query.QueryInvocationTargetException; |
| import com.gemstone.gemfire.cache.query.QueryService; |
| import com.gemstone.gemfire.cache.query.RegionNotFoundException; |
| import com.gemstone.gemfire.cache.query.SelectResults; |
| import com.gemstone.gemfire.cache.query.Struct; |
| import com.gemstone.gemfire.cache.query.TypeMismatchException; |
| import com.gemstone.gemfire.cache.query.internal.index.IndexData; |
| import com.gemstone.gemfire.cache.query.internal.index.IndexProtocol; |
| import com.gemstone.gemfire.cache.query.internal.index.IndexUtils; |
| import com.gemstone.gemfire.cache.query.internal.parse.OQLLexerTokenTypes; |
| import com.gemstone.gemfire.cache.query.internal.types.StructTypeImpl; |
| import com.gemstone.gemfire.cache.query.internal.types.TypeUtils; |
| import com.gemstone.gemfire.cache.query.types.ObjectType; |
| import com.gemstone.gemfire.cache.query.types.StructType; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.internal.logging.LogService; |
| import com.gemstone.gemfire.pdx.internal.PdxString; |
| |
| /** |
| * Class Description |
| * |
| * @version $Revision: 1.1 $ |
| * @author ericz |
| */ |
| |
| |
| public class CompiledIn extends AbstractCompiledValue implements Indexable { |
| private static final Logger logger = LogService.getLogger(); |
| |
| private CompiledValue elm; |
| private CompiledValue colln; |
| |
| public CompiledIn(CompiledValue elm, CompiledValue colln) { |
| this.elm = elm; |
| this.colln = colln; |
| } |
| |
| @Override |
| public List getChildren() { |
| List list = new ArrayList(); |
| list.add(elm); |
| list.add(colln); |
| return list; |
| } |
| |
| public int getType() { |
| return LITERAL_in; |
| } |
| |
| /* |
| * We retrieve the collection from the context cache if it exists |
| * This allows us to not have to reevaluate the sub query on every iteration. |
| * This improves performance for queries such as |
| * "select * from /receipts r where r.type = 'large' and r.id in (select c.id from /customers c where c.status = 'preferred') |
| * The sub query would create a set that would not change and store it |
| * into the context if it does not yet exist |
| */ |
| private Object evaluateColln(ExecutionContext context) |
| throws QueryInvocationTargetException, NameResolutionException, |
| TypeMismatchException, FunctionDomainException { |
| Object evalColln = null; |
| if (this.colln.isDependentOnCurrentScope(context)) { |
| evalColln = this.colln.evaluate(context); |
| } |
| else { |
| evalColln = context.cacheGet(this.colln); |
| if (evalColln == null ) { |
| evalColln = this.colln.evaluate(context); |
| context.cachePut(this.colln, evalColln); |
| } |
| } |
| return evalColln; |
| } |
| |
| public Object evaluate(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| Object evalElm = this.elm.evaluate(context); |
| |
| Object evalColln = evaluateColln(context); |
| |
| if (evalColln == null || evalColln == QueryService.UNDEFINED) { |
| return QueryService.UNDEFINED; |
| } |
| |
| // handle each type of collection that we support |
| if (evalColln instanceof Map) { |
| evalColln = ((Map)evalColln).entrySet(); |
| } |
| |
| if (evalColln instanceof Collection) { |
| Iterator iterator = ((Collection)evalColln).iterator(); |
| while (iterator.hasNext()) { |
| Object evalObj = evalElm; |
| Object collnObj = iterator.next(); |
| if (evalElm instanceof PdxString && collnObj instanceof String) { |
| evalObj = ((PdxString) evalElm).toString(); |
| } else if (collnObj instanceof PdxString && evalElm instanceof String) { |
| collnObj = ((PdxString) collnObj).toString(); |
| } |
| if (TypeUtils.compare(evalObj, collnObj, OQLLexerTokenTypes.TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (!evalColln.getClass().isArray()) { |
| throw new TypeMismatchException(LocalizedStrings.CompiledIn_OPERAND_OF_IN_CANNOT_BE_INTERPRETED_AS_A_COLLECTION_IS_INSTANCE_OF_0.toLocalizedString(evalColln.getClass().getName())); |
| } |
| if (evalColln instanceof Object[]) { |
| Object[] a = (Object[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| if (TypeUtils.compare(evalElm, a[i], TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| // at this point if evalElm is NULL, then it's a type mismatch |
| if (evalElm == null) { |
| throw new TypeMismatchException(LocalizedStrings.CompiledIn_IN_OPERATOR_CHECK_FOR_NULL_IN_PRIMITIVE_ARRAY.toLocalizedString()); |
| } |
| |
| // handle each type of primitive array |
| if (evalColln instanceof long[]) { |
| long[] a = (long[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = Long.valueOf(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof double[]) { |
| double[] a = (double[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = Double.valueOf(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof float[]) { |
| float[] a = (float[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = new Float(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof int[]) { |
| int[] a = (int[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = Integer.valueOf(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof short[]) { |
| short[] a = (short[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = new Short(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof char[]) { |
| char[] a = (char[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = new Character(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| if (evalColln instanceof byte[]) { |
| byte[] a = (byte[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = Byte.valueOf(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| // must be a boolean[] |
| boolean[] a = (boolean[]) evalColln; |
| for (int i = 0; i < a.length; i++) { |
| Object e = Boolean.valueOf(a[i]); |
| if (TypeUtils.compare(evalElm, e, TOK_EQ).equals(Boolean.TRUE)) { |
| return Boolean.TRUE; |
| } |
| } |
| return Boolean.FALSE; |
| } |
| |
| // Asif: If the size of aray is two this implies that it is |
| // a relation ship index & so the key field will be null in both the indexes |
| // as key is not a meaningful entity. The 0th element will refer to LHS |
| // operand |
| // and 1th element will refer to RHS operannd |
| public IndexInfo[] getIndexInfo(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, |
| NameResolutionException { |
| IndexInfo[] indexInfo = privGetIndexInfo(context); |
| if (indexInfo != null) { |
| if (indexInfo == NO_INDEXES_IDENTIFIER) { |
| return null; |
| } else { |
| return indexInfo; |
| } |
| } |
| if (!IndexUtils.indexesEnabled) |
| return null; |
| // get the path and index key to try |
| PathAndKey pAndK = getPathAndKey(context); |
| IndexInfo newIndexInfo[] = null; |
| if (pAndK != null) { |
| CompiledValue path = pAndK._path; |
| CompiledValue indexKey = pAndK._key; |
| IndexData indexData = QueryUtils.getAvailableIndexIfAny(path, context, |
| this.TOK_EQ); |
| IndexProtocol index = null; |
| if (indexData != null) { |
| index = indexData.getIndex(); |
| } |
| if (index != null && index.isValid()) { |
| newIndexInfo = new IndexInfo[1]; |
| newIndexInfo[0] = new IndexInfo(indexKey, path, index, indexData |
| .getMatchLevel(), indexData.getMapping(), this.TOK_EQ); |
| } |
| } |
| if (newIndexInfo != null) { |
| privSetIndexInfo(newIndexInfo, context); |
| } else { |
| privSetIndexInfo(NO_INDEXES_IDENTIFIER, context); |
| } |
| return newIndexInfo; |
| } |
| |
| |
| // _indexInfo is a transient field |
| // if this is just faulted in then can be null |
| private IndexInfo[] privGetIndexInfo(ExecutionContext context) { |
| return (IndexInfo[]) context.cacheGet(this); |
| } |
| |
| private void privSetIndexInfo(IndexInfo[] indexInfo, ExecutionContext context) { |
| context.cachePut(this, indexInfo); |
| } |
| |
| // Invariant: the receiver is dependent on the current iterator. |
| protected PlanInfo protGetPlanInfo(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, NameResolutionException { |
| PlanInfo result = new PlanInfo(); |
| IndexInfo[] indexInfo = getIndexInfo(context); |
| if (indexInfo == null) return result; |
| for (int i = 0; i < indexInfo.length; ++i) { |
| result.indexes.add(indexInfo[i]._index); |
| } |
| result.evalAsFilter = true; |
| String preferredCondn = (String)context.cacheGet(PREF_INDEX_COND); |
| if(preferredCondn != null) { |
| //This means that the system is having only one independent iterator so equi join is ruled out. |
| // thus the first index is guaranteed to be on the condition which may match our preferred index |
| if(indexInfo[0]._index.getCanonicalizedIndexedExpression().equals(preferredCondn) && |
| (indexInfo[0]._index.getType() == IndexType.FUNCTIONAL || indexInfo[0]._index.getType() == IndexType.HASH)) { |
| result.isPreferred = true; |
| } |
| } |
| return result; |
| } |
| |
| public Set computeDependencies(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, NameResolutionException { |
| context.addDependencies(this, this.elm.computeDependencies(context)); |
| return context.addDependencies(this, this.colln.computeDependencies(context)); |
| } |
| |
| /** |
| * specialized optimization for doing a bulk get on a region. |
| * @return a List of entries if optimization was performed, null if no match |
| */ |
| List optimizeBulkGet(CompiledRegion cRgn, ExecutionContext context) |
| throws RegionNotFoundException, TypeMismatchException, |
| NameResolutionException, FunctionDomainException, QueryInvocationTargetException { |
| // check the elm to see if it's the key on a map entry |
| boolean match = false; |
| CompiledValue resolution = null; |
| |
| if (this.elm instanceof CompiledID) { |
| String id = ((CompiledID)this.elm).getId(); |
| if (id.equals("key") || id.equals("getKey")){ |
| resolution = context.resolve(id); |
| if (resolution instanceof CompiledPath) { |
| resolution = ((CompiledPath)resolution).getReceiver(); |
| } |
| } |
| } |
| else if (this.elm instanceof CompiledPath) { |
| CompiledPath cPath = (CompiledPath)this.elm; |
| if (cPath.getTailID().equals("key") || cPath.getTailID().equals("getKey")) { |
| CompiledValue cVal = cPath.getReceiver(); |
| if (cVal instanceof CompiledID) { |
| resolution = context.resolve(((CompiledID)cVal).getId()); |
| } |
| } |
| } |
| else if (this.elm instanceof CompiledOperation) { |
| CompiledOperation cOp = (CompiledOperation)this.elm; |
| if (cOp.getMethodName().equals("key") || cOp.getMethodName().equals("getKey")) { |
| if (cOp.getReceiver(context) instanceof CompiledID) { |
| resolution = context.resolve(((CompiledID)cOp.getReceiver(context)).getId()); |
| } |
| else if (cOp.getReceiver(context) == null) { |
| match = true; // implicit operation on the iterator |
| } |
| } |
| } |
| // only one possible iterator in this case, so it's a match if resolution |
| // is a RuntimeIterator |
| if (!match && !(resolution instanceof RuntimeIterator)) { |
| return null; |
| } |
| |
| // element matches; finally, check to make sure the collection expression |
| // is independent of all iterators |
| if (context.isDependentOnAnyIterator(this.colln)) { |
| return null; |
| } |
| |
| // evaluate the collection |
| Object evalColln = evaluateColln(context); |
| if (evalColln == null || evalColln == QueryService.UNDEFINED) { |
| return null; |
| } |
| |
| // only handle an actual Collection or an Object[] for this optimization |
| Collection colln = null; |
| if (evalColln instanceof Collection) { |
| colln = (Collection)evalColln; |
| } |
| if (evalColln instanceof Object[]) { |
| colln = Arrays.asList((Object[])evalColln); |
| } |
| |
| if (colln != null) { |
| QRegion rgn = (QRegion)cRgn.evaluate(context); |
| |
| // only do this optimization if the region keys is larger than the |
| // collection |
| if (rgn.keySet().size() < colln.size()) { |
| return null; |
| } |
| |
| if (logger.isDebugEnabled()) { |
| logger.debug("Executing BULK GET on keys {}, in region {}", colln, rgn); |
| } |
| List results = new ArrayList(colln.size()); |
| |
| // now do the iteration over this collection |
| for (Iterator itr = colln.iterator(); itr.hasNext(); ) { |
| Object key = itr.next(); |
| Region.Entry entry = rgn.getEntry(key); |
| if (entry != null) { |
| // the region contains this key, so add the entry to the results |
| results.add(entry); |
| } |
| } |
| return results; |
| } |
| return null; |
| } |
| |
| /** |
| * get the path to see if there's an index for, and also determine which |
| * CompiledValue is the key while we're at it |
| */ |
| private PathAndKey getPathAndKey(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException { |
| // RuntimeIterator lIter = context.findRuntimeIterator(_left); |
| // RuntimeIterator rIter = context.findRuntimeIterator(_right); |
| boolean isLeftDependent = context.isDependentOnCurrentScope(this.elm); |
| boolean isRightDependent = context.isDependentOnCurrentScope(this.colln); |
| if ( !isLeftDependent || isRightDependent ) return null; |
| CompiledValue indexKey; |
| CompiledValue path; |
| path = this.elm; |
| indexKey = this.colln; |
| //Asif Do not worry about the nature of the collection. As long as it |
| //is not dependent on the current scope we should be fine |
| |
| return new PathAndKey(path, indexKey); |
| } |
| |
| |
| /** |
| * Asif : Evaluates as a filter taking advantage of indexes if appropriate. |
| * This function has a meaningful implementation only in CompiledComparison & |
| * CompiledUndefined . It is unsupported in other classes. The additional |
| * parameters which it takes are a boolean which is used to indicate whether |
| * the index result set needs to be expanded to the top level or not. The |
| * second is a CompiledValue representing the operands which are only iter |
| * evaluatable. The CompiledValue passed will be null except if a |
| * GroupJunction has only one filter evaluatable condition & rest are iter |
| * operands. In such cases , the iter operands will be evaluated while |
| * expanding/cutting down the index resultset |
| * |
| * @return SelectResults |
| */ |
| public SelectResults filterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults, boolean completeExpansionNeeded, |
| CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, boolean isIntersection, boolean conditioningNeeded, boolean evalProj) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException |
| { |
| // see if we're dependent on the current iterator |
| // if not let super handle it |
| // RuntimeIterator itr = context.getCurrentIterator(); |
| // Support.Assert(itr != null); |
| if (!isDependentOnCurrentScope(context)) |
| return super.filterEvaluate(context, intermediateResults); |
| IndexInfo[] idxInfo = getIndexInfo(context); |
| Support |
| .Assert( |
| idxInfo != null, |
| "a comparison that is dependent, not indexed, and filter evaluated is not possible"); |
| Support.Assert(idxInfo.length == 1, |
| "In operator should have only one index"); |
| |
| return singleBaseCollectionFilterEvaluate(context, intermediateResults, |
| completeExpansionNeeded, iterOperands, idxInfo[0], indpndntItrs, isIntersection,conditioningNeeded,evalProj); |
| |
| } |
| |
| public int getOperator() { |
| return TOK_EQ; |
| } |
| |
| /** **************** PRIVATE METHODS ************************** |
| * @throws QueryInvocationTargetException |
| * @throws NameResolutionException |
| * @throws FunctionDomainException |
| * @throws TypeMismatchException */ |
| |
| private void queryIndex(Object key, |
| IndexInfo indexInfo, |
| SelectResults results, |
| CompiledValue iterOperands, |
| RuntimeIterator[] indpndntItrs, |
| ExecutionContext context, |
| List projAttrib, |
| boolean conditioningNeeded) |
| throws TypeMismatchException, |
| FunctionDomainException, |
| NameResolutionException, |
| QueryInvocationTargetException { |
| |
| assert indexInfo != null; |
| assert indexInfo._index != null; |
| |
| // Get new IndexInfo to put in context as we dont want it to evaluate |
| // key collection again if its a CompiledSelect (Nested Query). |
| IndexInfo contextIndexInfo = new IndexInfo(new CompiledLiteral(key), |
| indexInfo._path(), indexInfo._getIndex(), 0, null, |
| indexInfo._operator()); |
| context.cachePut(CompiledValue.INDEX_INFO, contextIndexInfo); |
| indexInfo._index.query(key, |
| TOK_EQ, |
| results, |
| !conditioningNeeded ? iterOperands : null, |
| indpndntItrs == null ? null : indpndntItrs[0], |
| context, |
| projAttrib, |
| null, |
| false); // is Intersection |
| |
| |
| |
| |
| } |
| /** |
| * evaluate as a filter, involving a single iterator. Use an index if |
| * possible. |
| */ |
| // Invariant: the receiver is dependent on the current iterator. |
| private SelectResults singleBaseCollectionFilterEvaluate( |
| ExecutionContext context, SelectResults intermediateResults, |
| boolean completeExpansionNeeded, CompiledValue iterOperands, |
| IndexInfo indexInfo, RuntimeIterator[] indpndntItr, boolean isIntersection, boolean conditioningNeeded, boolean evalProj) |
| throws TypeMismatchException, AmbiguousNameException, |
| FunctionDomainException, NameResolutionException, |
| QueryInvocationTargetException |
| { |
| ObjectType resultType = indexInfo._index.getResultSetType(); |
| int indexFieldsSize = -1; |
| SelectResults results = null; |
| if (resultType instanceof StructType) { |
| indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length; |
| } |
| else { |
| indexFieldsSize = 1; |
| } |
| 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(); |
| } |
| |
| List projAttrib = null; |
| // //////////////////////////////////////////////////////////////// |
| ObjectType projResultType = null; |
| if (!conditioningNeeded) { |
| projResultType = evalProj ? (ObjectType)context |
| .cacheGet(RESULT_TYPE) : null; |
| if (projResultType != null) { |
| resultType = projResultType; |
| context.cachePut(RESULT_TYPE, Boolean.TRUE); |
| projAttrib = (List)context.cacheGet(PROJ_ATTRIB); |
| } |
| if (isIntersection) { |
| if (resultType instanceof StructType) { |
| context.getCache().getLogger().fine( |
| "StructType resultType.class=" + resultType.getClass().getName()); |
| if(useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType) |
| : new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart); |
| }else { |
| results = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType); |
| } |
| indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length; |
| } |
| else { |
| context.getCache().getLogger().fine( |
| "non-StructType resultType.class=" |
| + resultType.getClass().getName()); |
| if (useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedResultSet(resultType) : |
| new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| results = QueryUtils.createResultCollection(context, resultType) ; |
| } |
| indexFieldsSize = 1; |
| } |
| } |
| else { |
| if (intermediateResults != null && !completeExpansionNeeded) { |
| results = intermediateResults; |
| } |
| else { |
| if (resultType instanceof StructType) { |
| context.getCache().getLogger().fine( |
| "StructType resultType.class=" |
| + resultType.getClass().getName()); |
| if (useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType) |
| :new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart); |
| } else { |
| results = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType) ; |
| } |
| indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length; |
| } |
| else { |
| context.getCache().getLogger().fine( |
| "non-StructType resultType.class=" |
| + resultType.getClass().getName()); |
| if (useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedResultSet(resultType) : |
| new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| results = QueryUtils.createResultCollection(context, resultType) ; |
| } |
| indexFieldsSize = 1; |
| } |
| } |
| } |
| |
| }else { |
| if (resultType instanceof StructType) { |
| context.getCache().getLogger().fine( |
| "StructType resultType.class=" + resultType.getClass().getName()); |
| if (useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedStructSet((StructTypeImpl)resultType) : |
| new SortedResultsBag<Struct>((StructTypeImpl)resultType, nullValuesAtStart); |
| } else { |
| results = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType) ; |
| } |
| indexFieldsSize = ((StructTypeImpl)resultType).getFieldNames().length; |
| } |
| else { |
| context.getCache().getLogger().fine( |
| "non-StructType resultType.class=" |
| + resultType.getClass().getName()); |
| if (useLinkedDataStructure) { |
| results = context.isDistinct() ? new LinkedResultSet(resultType) : |
| new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| results = QueryUtils.createResultCollection(context,resultType); |
| } |
| indexFieldsSize = 1; |
| } |
| } |
| |
| |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| Object evalColln = evaluateColln(context); |
| observer.beforeIndexLookup(indexInfo._index, TOK_EQ, evalColln); |
| //We need to reset the result type just in case the colln turned out to |
| //be a compiled comparison which could change the result type |
| //Exec caches are incorrectly shared across all queries, this would result |
| //in overriding the result type. Once the result type was overridden |
| //multiple projections and class cast exceptions could result due to |
| //unexpected values overwriting expected values |
| if (!conditioningNeeded) { |
| if (projResultType != null) { |
| resultType = projResultType; |
| context.cachePut(RESULT_TYPE, Boolean.TRUE); |
| } |
| } |
| // handle each type of collection that we support |
| if (evalColln instanceof Map) { |
| Iterator itr = ((Map)evalColln).entrySet().iterator(); |
| while (itr.hasNext()) { |
| this.queryIndex(itr.next(),indexInfo, results,iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof Collection) { |
| //Removing duplicates from the collection |
| HashSet set = new HashSet((Collection)evalColln); |
| Iterator itr = set.iterator(); |
| while (itr.hasNext()) { |
| this.queryIndex(itr.next(),indexInfo, results,iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else { |
| if (!evalColln.getClass().isArray()) { |
| throw new TypeMismatchException( |
| "Operand of IN cannot be interpreted as a Collection. " |
| + "Is instance of " + evalColln.getClass().getName()); |
| } |
| if (evalColln instanceof Object[]) { |
| Object[] arr = (Object[])evalColln; |
| for (int i = 0; i < arr.length; ++i) { |
| this.queryIndex(arr[i],indexInfo,results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof long[]) { |
| long[] a = (long[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(Long.valueOf(a[i]),indexInfo,results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof double[]) { |
| double[] a = (double[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(Double.valueOf(a[i]),indexInfo,results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof float[]) { |
| float[] a = (float[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(new Float(a[i]),indexInfo, results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof int[]) { |
| int[] a = (int[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(Integer.valueOf(a[i]),indexInfo, results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| } |
| else if (evalColln instanceof short[]) { |
| short[] a = (short[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(new Short(a[i]),indexInfo, results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof char[]) { |
| char[] a = (char[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(new Character(a[i]),indexInfo,results,iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else if (evalColln instanceof byte[]) { |
| byte[] a = (byte[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| this.queryIndex(new Byte(a[i]),indexInfo,results, iterOperands, indpndntItr, context, projAttrib,conditioningNeeded); |
| } |
| |
| } |
| else { |
| throw new TypeMismatchException( |
| "Operand of IN cannot be interpreted as a Comparable Object. Operand is of type =" |
| + evalColln.getClass()); |
| |
| } |
| } |
| |
| if(conditioningNeeded) { |
| results = QueryUtils.getconditionedIndexResults(results, indexInfo, |
| context, indexFieldsSize, completeExpansionNeeded, |
| iterOperands, indpndntItr); |
| }else { |
| if(isIntersection && intermediateResults != null) { |
| results = QueryUtils.intersection(intermediateResults, results, context); |
| } |
| } |
| return results; |
| } |
| finally { |
| observer.afterIndexLookup(results); |
| } |
| |
| } |
| |
| public boolean isProjectionEvaluationAPossibility(ExecutionContext context) |
| { |
| return true; |
| } |
| |
| @Override |
| public boolean isLimitApplicableAtIndexLevel(ExecutionContext context) { |
| return true; |
| } |
| |
| @Override |
| public boolean isOrderByApplicableAtIndexLevel(ExecutionContext context, |
| String canonicalizedOrderByClause) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| return false; |
| } |
| public boolean isConditioningNeededForIndex(RuntimeIterator independentIter, |
| ExecutionContext context, boolean completeExpnsNeeded) throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| IndexConditioningHelper ich = null; |
| IndexInfo[] idxInfo = getIndexInfo(context); |
| assert idxInfo.length == 1; |
| int indexFieldsSize = -1; |
| boolean conditioningNeeded = true; |
| ObjectType indexRsltType = idxInfo[0]._index.getResultSetType(); |
| if (indexRsltType instanceof StructType) { |
| indexFieldsSize = ((StructTypeImpl)indexRsltType).getFieldNames().length; |
| } |
| else { |
| indexFieldsSize = 1; |
| } |
| |
| if (independentIter != null && indexFieldsSize == 1) { |
| ich = new IndexConditioningHelper(idxInfo[0], context, indexFieldsSize, |
| completeExpnsNeeded, null, independentIter); |
| } |
| conditioningNeeded = ich == null || ich.shufflingNeeded; |
| return conditioningNeeded; |
| |
| } |
| |
| |
| /** |
| * evaluate as a filter, producing an intermediate result set. This may |
| * require iteration if there is no index available. Asif :The booelan true |
| * implies that CompiledComparsion when existing on its own always requires a |
| * Completeexpansion to top level iterators. This flag can get toggled to |
| * false only from inside a GroupJunction |
| * |
| * @param intermediateResults |
| * if this parameter is provided, and we have to iterate, then |
| * iterate over this result set instead of the entire base |
| * collection. |
| */ |
| public SelectResults filterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| // Asif : This function can be invoked only if the where clause contains |
| // a single condition which is CompiledComparison. |
| // If a CompiledComparison exists inside a GroupJunction, then it will |
| // always |
| // call the overloaded filterEvalauate with the RuntimeIterator passed |
| // as not null. |
| // Thus if the RuntimeIterator array passed is null then it is |
| // guaranteed |
| // that the condition was a isolatory condition ( directly in where |
| // clause) |
| // and the final iterators to which we need to expand to is all the |
| // iterators |
| // of the scope |
| RuntimeIterator indpndntItr = null; |
| List currentScopeIndpndntItrs = context.getAllIndependentIteratorsOfCurrentScope(); |
| Set rntmItrs = QueryUtils.getCurrentScopeUltimateRuntimeIteratorsIfAny(this,context); |
| if(rntmItrs.size() ==1 && currentScopeIndpndntItrs.size() == 1 ) { |
| indpndntItr = (RuntimeIterator)rntmItrs.iterator().next(); |
| } |
| |
| return filterEvaluate(context, intermediateResults, true/* |
| * Complete |
| * Expansion needed |
| */, null, indpndntItr !=null?new RuntimeIterator[]{indpndntItr}:null/* |
| * Asif :It is safe to pass null as the independent iterator |
| * to which the condition belongs is required only if boolean |
| * complete expansion turns out to be false, which can happen |
| * only in case of CompiledComparison/CompiledUndefined |
| * called from roupJunction or CompositeGroupJunction |
| */,true /*is intersection*/,this.isConditioningNeededForIndex(indpndntItr, context, true), true); |
| } |
| |
| /* |
| * Asif : This function should never get invoked as now if a CompiledJunction |
| * or GroupJunction contains a single filterable CompiledComparison it should |
| * directly call filterEvaluate rather than auxFilterEvalutae. Overriding this |
| * function just for ensuring that auxFilterEvaluate is not being called by |
| * mistake. |
| */ |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| Support |
| .assertionFailed(" This auxFilterEvaluate of CompiledIn should never have got invoked."); |
| return null; |
| } |
| public boolean isBetterFilter(Filter comparedTo, ExecutionContext context, final int thisSize) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException |
| { |
| //If the current filter is equality & comparedTo filter is also equality based , then |
| // return the one with lower size estimate is better |
| boolean isThisBetter = true; |
| |
| int thatSize = comparedTo.getSizeEstimate(context); |
| int thatOperator = comparedTo.getOperator() ; |
| |
| //Go with the lowest cost when hint is used. |
| if (context instanceof QueryExecutionContext && ((QueryExecutionContext)context).hasHints()) { |
| return thisSize <= thatSize; |
| } |
| |
| switch(thatOperator) { |
| case TOK_EQ: |
| case TOK_NE: |
| case TOK_NE_ALT: |
| isThisBetter = thisSize < thatSize; |
| break; |
| case LITERAL_and: |
| //Asif: Give preference to IN . Is this right? It does not appear . Ideally we need to get |
| //some estimate on Range. This case is possible only in case of RangeJunction |
| break; |
| case TOK_LE: |
| case TOK_LT: |
| case TOK_GE: |
| case TOK_GT: |
| //Give preference to this rather than that as this is more deterministic |
| break; |
| default : |
| throw new IllegalArgumentException("The operator type ="+ thatOperator + " is unknown"); |
| } |
| |
| return isThisBetter; |
| } |
| public int getSizeEstimate(ExecutionContext context)throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| IndexInfo[] idxInfo = getIndexInfo(context); |
| if (idxInfo == null) { |
| // Asif: This implies it is an independent condition. So evaluate it first |
| // in filter operand |
| return 0; |
| } |
| assert idxInfo.length == 1; |
| Object key = idxInfo[0].evaluateIndexKey(context); |
| if (key != null && key.equals(QueryService.UNDEFINED)) |
| return 0; |
| |
| if (context instanceof QueryExecutionContext) { |
| QueryExecutionContext qcontext = (QueryExecutionContext) context; |
| if (qcontext.isHinted(idxInfo[0]._index.getName())) { |
| return qcontext.getHintSize(idxInfo[0]._index.getName()); |
| } |
| } |
| |
| Object evalColln = evaluateColln(context); |
| |
| int size = 0; |
| // handle each type of collection that we support |
| if (evalColln instanceof Map) { |
| Iterator itr = ((Map)evalColln).entrySet().iterator(); |
| while (itr.hasNext()) { |
| size+=idxInfo[0]._index.getSizeEstimate(itr.next(), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else if (evalColln instanceof Collection) { |
| Iterator itr = ((Collection)evalColln).iterator(); |
| while (itr.hasNext()) { |
| size+=idxInfo[0]._index.getSizeEstimate(itr.next(), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else { |
| if (!evalColln.getClass().isArray()) { |
| throw new TypeMismatchException( |
| "Operand of IN cannot be interpreted as a Collection. " |
| + "Is instance of " + evalColln.getClass().getName()); |
| } |
| if (evalColln instanceof Object[]) { |
| Object[] arr = (Object[])evalColln; |
| for (int i = 0; i < arr.length; ++i) { |
| size+=idxInfo[0]._index.getSizeEstimate(arr[i], TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else if (evalColln instanceof long[]) { |
| long[] a = (long[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(Long.valueOf(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else if (evalColln instanceof double[]) { |
| double[] a = (double[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(Double.valueOf(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else if (evalColln instanceof float[]) { |
| float[] a = (float[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(new Float(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| |
| } |
| |
| } |
| else if (evalColln instanceof int[]) { |
| int[] a = (int[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(Integer.valueOf(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| } |
| else if (evalColln instanceof short[]) { |
| short[] a = (short[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(new Short(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| |
| } |
| |
| } |
| else if (evalColln instanceof char[]) { |
| char[] a = (char[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(new Character(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| } |
| |
| } |
| else if (evalColln instanceof byte[]) { |
| byte[] a = (byte[])evalColln; |
| for (int i = 0; i < a.length; i++) { |
| size+=idxInfo[0]._index.getSizeEstimate(new Byte(a[i]), TOK_EQ, idxInfo[0]._matchLevel); |
| |
| } |
| |
| } |
| else { |
| throw new TypeMismatchException( |
| "Operand of IN cannot be interpreted as a Comparable Object. Operand is of type =" |
| + evalColln.getClass()); |
| |
| } |
| } |
| return size; |
| |
| } |
| |
| |
| /* Inner classes for passing stuff around */ |
| class PathAndKey { |
| |
| CompiledValue _path; |
| CompiledValue _key; |
| |
| PathAndKey(CompiledValue path, CompiledValue indexKey) { |
| _path = path; |
| _key = indexKey; |
| } |
| } |
| |
| |
| public boolean isRangeEvaluatable() { |
| return false; |
| } |
| |
| } |