/* * 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.*; import org.apache.logging.log4j.Logger; import org.apache.geode.cache.*; import org.apache.geode.cache.query.*; import org.apache.geode.cache.query.internal.parse.*; import org.apache.geode.cache.query.types.*; import org.apache.geode.cache.query.internal.types.TypeUtils; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; /** */ public class CompiledIteratorDef extends AbstractCompiledValue { private static final Logger logger = LogService.getLogger(); private String name; private ObjectType elementType; private CompiledValue collectionExpr; /** * Creates a new instance of CompiledIteratorDef name and type can be null */ public CompiledIteratorDef(String name, ObjectType elementType, CompiledValue collectionExpr) { this.name = name; this.elementType = elementType == null ? TypeUtils.OBJECT_TYPE : elementType; this.collectionExpr = collectionExpr; } @Override public List getChildren() { return Collections.singletonList(collectionExpr); } /** * Returns a RuntimeIterator (or null if evaluates to null or UNDEFINED); the collection expr is * evaluated lazily after dependencies are known */ public Object evaluate(ExecutionContext context) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { throw new UnsupportedOperationException( LocalizedStrings.CompiledIteratorDef_NOT_TO_BE_EVALUATED_DIRECTLY.toLocalizedString()); } public RuntimeIterator getRuntimeIterator(ExecutionContext context) throws TypeMismatchException, AmbiguousNameException, NameResolutionException { RuntimeIterator rIter = null; // check cached in context rIter = (RuntimeIterator) context.cacheGet(this); if (rIter != null) { return rIter; } ObjectType type = this.elementType; if (type.equals(TypeUtils.OBJECT_TYPE)) { // check to see if there's a typecast for this collection ObjectType typc = getCollectionElementTypeCast(); if (typc != null) { type = typc; } else { // Try to determine better type // Now only works for CompiledPaths // Does not determine type of nested query if (!(this.collectionExpr instanceof CompiledSelect)) { type = computeElementType(context); } } } rIter = new RuntimeIterator(this, type); // Rahul : generate from clause should take care of bucket region substitution if // necessary and then set the definition. String fromClause = genFromClause(context); rIter.setDefinition(fromClause); /** * Asif : If the type of RunTimeIterator is still ObjectType & if the RuneTimeIterator is * independent of any iterator of the scopes less than or equal to its own scope, we can * evaluate the collection via RuntimeIterator. This will initialize the Collection of * RuntimeIterator , which is OK. The code in RuntimeIterator will be rectified such that the * ElementType of that RuntimeIterator is taken from the collection * */ if (type.equals(TypeUtils.OBJECT_TYPE) && !this.isDependentOnAnyIteratorOfScopeLessThanItsOwn(context)) { // The current Iterator definition is independent , so lets evaluate // the collection try { rIter.evaluateCollection(context); } catch (QueryExecutionTimeoutException qet) { throw qet; } catch (RegionNotFoundException re) { throw re; } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("Exception while getting runtime iterator.", e); } throw new TypeMismatchException( LocalizedStrings.CompiledIteratorDef_EXCEPTION_IN_EVALUATING_THE_COLLECTION_EXPRESSION_IN_GETRUNTIMEITERATOR_EVEN_THOUGH_THE_COLLECTION_IS_INDEPENDENT_OF_ANY_RUNTIMEITERATOR .toLocalizedString(), e); } } // cache in context context.cachePut(this, rIter); return rIter; } ObjectType getCollectionElementTypeCast() throws TypeMismatchException { ObjectType typ = this.collectionExpr.getTypecast(); if (typ != null) { if (!(typ instanceof CollectionType)) { throw new TypeMismatchException( LocalizedStrings.CompiledIteratorDef_AN_ITERATOR_DEFINITION_MUST_BE_A_COLLECTION_TYPE_NOT_A_0 .toLocalizedString(typ)); } if (typ instanceof MapType) { // we iterate over map entries return ((MapType) typ).getEntryType(); } return ((CollectionType) typ).getElementType(); } return null; } /** Evaluate just the collectionExpr */ SelectResults evaluateCollection(ExecutionContext context) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { return evaluateCollection(context, null); } /** * Evaluate just the collectionExpr * * @param stopAtIter the RuntimeIterator associated with this iterator defn -- don't use this or * any subsequent runtime iterators to evaluate. */ SelectResults evaluateCollection(ExecutionContext context, RuntimeIterator stopAtIter) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException { Object coll; // Check if query execution on this thread is Canceled. // QueryMonitor.isQueryExecutionCanceled(); context.currentScope().setLimit(stopAtIter); try { coll = this.collectionExpr.evaluate(context); } finally { context.currentScope().setLimit(null); } // if we don't have an elementType and there's a typecast, apply the // element type here if (TypeUtils.OBJECT_TYPE.equals(this.elementType)) { ObjectType elmTypc = getCollectionElementTypeCast(); if (elmTypc != null) { this.elementType = elmTypc; } } // PR bucketRegion substitution should have already happened // at the expression evaluation level SelectResults sr = prepareIteratorDef(coll, this.elementType, context); return sr; } public int getType() { return OQLLexerTokenTypes.ITERATOR_DEF; } // for test purposes... public String getName() { return this.name; } public ObjectType getElementType() { return this.elementType; } public CompiledValue getCollectionExpr() { return this.collectionExpr; } public void setCollectionExpr(CompiledValue collectionExpr) { this.collectionExpr = collectionExpr; } /** * TODO:Asif : We need to implement the belwo method of computeDependencies Once we come to * implement changes for partitioned region querying, as in that case if first iterator itself is * a Select Query , then ideally we cannot call that CompiledIteratorDef independent ( which will * be the case at present). When we use this commented function we will also need to take care of * correctly implementing the function isDependentOnCurrentScope etc functions. * * public Set computeDependencies(ExecutionContext context) throws TypeMismatchException, * AmbiguousNameException { //Asif : If a CompiledIteratorDef has a collection expression which * boils down to //a CompiledRegion or CompiledBindArgumnet , then its dependency is empty . In * such cases // we will assume that the current CompiledIteratorDef has a dependency on itself. * // This will be required once we start the changes for partitionedRegion Querying //But when we * are doing check of whether the CompiledIteratorDef is dependent on its // own RuntimeIterator * we will still return false. Set set = this.collectionExpr.computeDependencies(context); Set * retSet = null; if(set.isEmpty()){ retSet = * context.addDependency(this,this.getRuntimeIterator(context)); }else { retSet = * context.addDependencies(this, set); } return retSet; } */ @Override public Set computeDependencies(ExecutionContext context) throws TypeMismatchException, AmbiguousNameException, NameResolutionException { return context.addDependencies(this, this.collectionExpr.computeDependencies(context)); } // @todo ericz this method is overly complex, duplicating logic already // in query evaluation itself. It is overly complex ==> It will not be // necessary once we have full typing support. // There is a limitation here that it assumes that the collectionExpr is some // path on either a RuntimeIterator or a Region. private ObjectType computeElementType(ExecutionContext context) throws AmbiguousNameException { ObjectType type = PathUtils.computeElementTypeOfExpression(context, this.collectionExpr); // if it's a Map, we want the Entry type, not the value type if (type.isMapType()) { return ((MapType) type).getEntryType(); } if (type.isCollectionType()) { // includes Regions and arrays return ((CollectionType) type).getElementType(); } return type; } /** * Convert the given object to a SelectResults. Must be a collection of some sort. The obj passed * in must be unmodified, but the resulting SelectResults may or may not be modifiable. Return * null if obj is null or UNDEFINED. */ private SelectResults prepareIteratorDef(Object obj, ObjectType elementType, ExecutionContext context) throws TypeMismatchException { if (obj == null) { return null; } if (obj == QueryService.UNDEFINED) { return null; } if (obj instanceof SelectResults) { // probably came from nested query or is a QRegion already from region // path SelectResults sr = (SelectResults) obj; // override the elementType if not Object.class (does not apply to // StructBags) if (!elementType.equals(TypeUtils.OBJECT_TYPE)) { sr.setElementType(elementType); } return sr; } if (obj instanceof Region) { // this can happen if region passed in as parameter QRegion qRegion = new QRegion((Region) obj, false, context); if (!elementType.equals(TypeUtils.OBJECT_TYPE)) { // override the valueConstraint, if any qRegion.setElementType(elementType); } return qRegion; } // if this is a domain collection, it should be unmodifiable // if obj is a Collection but not a SelectResults, it must be from the // domain, otherwise it would be a SelectResults. if (obj instanceof Collection) { // do not lose ordering and duplicate information, ResultsCollectionWrapper res = new ResultsCollectionWrapper(elementType, (Collection) obj); res.setModifiable(false); return res; } // Object[] is wrapped and considered a domain object so unmodifiable if (obj instanceof Object[]) { // the element type is specified in the array itself, unless we have // something more specific if (elementType.equals(TypeUtils.OBJECT_TYPE)) { // if we don't have // constraint info elementType = TypeUtils.getObjectType(obj.getClass().getComponentType()); } // do not lose ordering and duplicate information, ResultsCollectionWrapper res = new ResultsCollectionWrapper(elementType, Arrays.asList((Object[]) obj)); res.setModifiable(false); return res; } // @todo primitive arrays? if (obj instanceof Map) { if (elementType.equals(TypeUtils.OBJECT_TYPE)) { // if we don't have more // specific type info, // use Map.Entry elementType = TypeUtils.getObjectType(Map.Entry.class); } ResultsCollectionWrapper res = new ResultsCollectionWrapper(elementType, ((Map) obj).entrySet()); res.setModifiable(false); return res; } else { obj = new Object[] {obj}; // the element type is specified in the array itself, unless we have // something more specific if (elementType.equals(TypeUtils.OBJECT_TYPE)) { // if we don't have // constraint info elementType = TypeUtils.getObjectType(obj.getClass().getComponentType()); } // do not lose ordering and duplicate information, ResultsCollectionWrapper res = new ResultsCollectionWrapper(elementType, Arrays.asList((Object[]) obj)); res.setModifiable(false); return res; } } String genFromClause(ExecutionContext context) throws AmbiguousNameException, TypeMismatchException, NameResolutionException { StringBuffer sbuff = new StringBuffer(); collectionExpr.generateCanonicalizedExpression(sbuff, context); return sbuff.toString(); } boolean isDependentOnAnyIterator(ExecutionContext context) { return context.isDependentOnAnyIterator(this); } /** * Checks if the iterator in question is dependent on any other RuntimeIterator of its own or * lesser scope. * * @param context */ boolean isDependentOnAnyIteratorOfScopeLessThanItsOwn(ExecutionContext context) { // Asif : Get the list of all iterators on which the colelction expression // is ultimately dependent on // Set indpRitrs = new HashSet(); // context.computeUtlimateDependencies(this, indpRitrs); // Asif:If dependent on self then also assume it to be dependent boolean isDep = false; // Asif : Get the list of all iterators on which the colelction expression // is dependent on Set dependencySet = context.getDependencySet(this, true); Iterator itr = dependencySet.iterator(); int currScope = context.currentScope().getScopeID();// context.getScopeCount(); while (itr.hasNext()) { RuntimeIterator ritr = (RuntimeIterator) itr.next(); if (ritr.getScopeID() <= currScope) { isDep = true; break; } } return isDep; } }