| /* |
| * 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.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| 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.SelectResults; |
| import org.apache.geode.cache.query.TypeMismatchException; |
| import org.apache.geode.cache.query.internal.index.AbstractIndex; |
| import org.apache.geode.cache.query.internal.index.IndexProtocol; |
| import org.apache.geode.cache.query.internal.index.PartitionedIndex; |
| import org.apache.geode.cache.query.types.ObjectType; |
| |
| public class DerivedInfo { |
| public Map<String, SelectResults> derivedResults; |
| public List<Object[]> newDerivatives; |
| public List successfulOps = new LinkedList(); |
| public List originalOps; |
| public CompiledValue currentOp; |
| private List expansionList; |
| |
| public DerivedInfo() { |
| derivedResults = new HashMap<String, SelectResults>(); |
| newDerivatives = new ArrayList<Object[]>(); |
| } |
| |
| public List getExpansionList() { |
| return expansionList; |
| } |
| |
| public void setExpansionList(List expansionList) { |
| this.expansionList = expansionList; |
| } |
| |
| public void setOriginalOps(List opsList) { |
| originalOps = new LinkedList(opsList); |
| } |
| |
| public List getRemainingOps() { |
| List remainingOps = new LinkedList(originalOps); |
| remainingOps.removeAll(successfulOps); |
| return remainingOps; |
| } |
| |
| public void addDerivedResults(IndexInfo indexInfo, SelectResults selectResults) { |
| IndexProtocol index = indexInfo._index; |
| String key = QueryUtils.getCompiledIdFromPath(indexInfo._path).getId() + ":" |
| + index.getCanonicalizedIteratorDefinitions()[0]; |
| if (derivedResults.containsKey(key)) { |
| for (Object result : selectResults) { |
| if (!derivedResults.get(key).contains(result)) { |
| derivedResults.get(key).add(result); |
| } |
| } |
| } else { |
| derivedResults.put(key, selectResults); |
| } |
| newDerivatives.add( |
| new Object[] {QueryUtils.getCompiledIdFromPath(indexInfo._path).getId(), selectResults}); |
| successfulOps.add(currentOp); |
| } |
| |
| public void addDerivedResults(IndexInfo indexInfo, SelectResults[] srs) { |
| addDerivedResults(indexInfo, srs[0]); |
| // Nested / range index is not supported at this time due to the way we cross the results |
| // This solution would have duplicates. The problem is the way we doNestedIteration. The map |
| // would |
| // have all values be associated with the current nested level object which is not what the |
| // values would represent |
| // IndexProtocol index = indexInfo._index; |
| // String[] definitions = index.getCanonicalizedIteratorDefinitions(); |
| // for (int i = 0 ; i < definitions.length; i++) { |
| // String key = QueryUtils.getCompiledIdFromPath(indexInfo._path).getId() + ":" + |
| // definitions[i]; |
| // if (derivedResults.containsKey(key)) { |
| // derivedResults.get(key).addAll(srs[i]); |
| // } |
| // else { |
| // derivedResults.put(key, srs[i]); |
| // } |
| // } |
| // |
| // int indexToIterateOn = QueryUtils.figureOutWhichStructIndexToExtract(index); |
| // newDerivatives.add(new Object[]{getCompiledIdFromPath(indexInfo._path).getId(), |
| // srs[indexToIterateOn]}); |
| // successfulOps.add(currentOp); |
| |
| } |
| |
| public void computeDerivedJoinResults(IndexInfo theCallingIndex, ExecutionContext context, |
| CompiledValue iterOps) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // Call this computeDerivedResults() |
| // We are looking for join conditions so we can filter eval instead of iterate eval |
| // Then we can apply the rest of the ops on the results |
| if (theCallingIndex != null && iterOps != null) { |
| if (iterOps instanceof CompiledJunction) { |
| List opsList = ((CompiledJunction) iterOps).getOperands(); |
| this.setOriginalOps(opsList); |
| createDerivedJoinResultsFromOpsList( |
| (QueryUtils.getCompiledIdFromPath(theCallingIndex._path)).getId(), context, opsList); |
| } else if (iterOps.getType() == CompiledValue.COMPARISON) { |
| createDerivedJoinResultsFromCC( |
| (QueryUtils.getCompiledIdFromPath(theCallingIndex._path)).getId(), |
| (CompiledComparison) iterOps, context); |
| } |
| } |
| } |
| |
| private void createDerivedJoinResultsFromOpsList(String theCallingIndexId, |
| ExecutionContext context, List opsList) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| Iterator iter = opsList.iterator(); |
| while (iter.hasNext()) { |
| CompiledValue cv = (CompiledValue) iter.next(); |
| this.currentOp = cv; |
| |
| if (cv.getType() == CompiledValue.COMPARISON) { |
| createDerivedJoinResultsFromCC(theCallingIndexId, (CompiledComparison) cv, context); |
| } |
| } |
| // Now let's derive from our derivatives (for multiple join clauses that can be chained, such as |
| // a.id = 1 and a.id = b.id and b.id = c.id |
| List<Object[]> newDerivatives = new ArrayList<Object[]>(this.newDerivatives); |
| this.newDerivatives.clear(); |
| if (newDerivatives.size() > 0) { |
| Iterator<Object[]> iterator = newDerivatives.iterator(); |
| while (iterator.hasNext()) { |
| Object[] idDerivedAndResults = iterator.next(); |
| derivedDerivative(idDerivedAndResults, context, this.getExpansionList()); |
| } |
| } |
| } |
| |
| private void derivedDerivative(Object[] idDerivedAndResults, ExecutionContext context, |
| List expansionList) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| |
| String idDerived = (String) idDerivedAndResults[0]; |
| SelectResults results = (SelectResults) idDerivedAndResults[1]; |
| RuntimeIterator ritr = getMatchingRuntimeIterator(idDerived, expansionList); |
| List remainingOps = this.getRemainingOps(); |
| Iterator iterator = results.iterator(); |
| while (iterator.hasNext()) { |
| Object val = iterator.next(); |
| ritr.setCurrent(val); |
| createDerivedJoinResultsFromOpsList(idDerived, context, remainingOps); |
| } |
| |
| } |
| |
| private RuntimeIterator getMatchingRuntimeIterator(String receiverId, List expansionList) |
| throws QueryInvocationTargetException { |
| Iterator iterator = expansionList.iterator(); |
| while (iterator.hasNext()) { |
| RuntimeIterator ritr = (RuntimeIterator) iterator.next(); |
| if (ritr.getCmpIteratorDefn().getName().equals(receiverId)) { |
| return ritr; |
| } |
| } |
| throw new QueryInvocationTargetException("Unable to locate correct iterator for " + receiverId); |
| } |
| |
| /* |
| * Example query : "Select * from /region1 r, /region2 s where r.id = 1 and r.id = s.id" Up until |
| * this point we have evaluated the r.id portion We determine if the path (r) matches any of the |
| * paths in the current cc (r.id = s.id) If so we figure out which side it matches (in this case |
| * the left side and create a new compiled comparison This new cc will set the left side as s.id |
| * and the right side as the evaluated value, in this case it happens to be 1 but it could be |
| * another field from the object instead. |
| */ |
| private void createDerivedJoinResultsFromCC(String theCallingIndexReceiverId, |
| CompiledComparison cc, ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| if (isCompiledPath(cc._right) && matchingPathIds(theCallingIndexReceiverId, cc._left)) { |
| evaluateDerivedJoin(context, cc._right, new CompiledLiteral(cc._left.evaluate(context)), |
| cc.getOperator()); |
| } else if (isCompiledPath(cc._left) && matchingPathIds(theCallingIndexReceiverId, cc._right)) { |
| evaluateDerivedJoin(context, cc._left, new CompiledLiteral(cc._right.evaluate(context)), |
| cc.getOperator()); |
| } |
| } |
| |
| /* |
| * Called by createDerivedJoinResultsFromCCa Creates the new cc, executes the cc and releases any |
| * newly obtain index locks |
| */ |
| private void evaluateDerivedJoin(ExecutionContext context, CompiledValue newLeftSide, |
| CompiledValue newRightSide, int operator) throws TypeMismatchException, |
| FunctionDomainException, NameResolutionException, QueryInvocationTargetException { |
| CompiledComparison dcc = createDerivedJoin(context, newLeftSide, newRightSide, operator); |
| IndexInfo[] indexInfos = (IndexInfo[]) dcc.getIndexInfo(context); |
| try { |
| if (indexInfos != null && isValidIndexTypeToDerive(indexInfos[0]._getIndex())) { |
| populateDerivedResultsFromDerivedJoin(context, dcc, indexInfos[0]); |
| } |
| } finally { |
| if (indexInfos != null) { |
| Index index = (Index) indexInfos[0]._index; |
| Index prIndex = ((AbstractIndex) index).getPRIndex(); |
| if (prIndex != null) { |
| ((PartitionedIndex) prIndex).releaseIndexReadLockForRemove(); |
| } else { |
| ((AbstractIndex) index).releaseIndexReadLockForRemove(); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Does the evaluation/execution of the cc and stores them into our map We prevent limit and order |
| * by to be conducted by the index at this time as we do not those applied We have no idea what |
| * the other operands are and do not want to limit results as the first X results may not fulfill |
| * all operands. |
| */ |
| private void populateDerivedResultsFromDerivedJoin(ExecutionContext context, |
| CompiledComparison dcc, IndexInfo indexInfo) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| // overwrite context values to disable limit, order by etc that should not be done by a derived |
| // join |
| // If we apply limit at this point, we cannot guarantee that after we iterate, the we do not |
| // continue to |
| // reduce the count below the limited amount |
| Boolean originalCanApplyLimit = |
| (Boolean) context.cacheGet(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX); |
| context.cachePut(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX, Boolean.FALSE); |
| Boolean originalCanApplyOrderBy = |
| (Boolean) context.cacheGet(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX); |
| context.cachePut(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX, Boolean.FALSE); |
| |
| SelectResults sr = dcc.filterEvaluate(context, null, false, null, null, false, false, false); |
| |
| context.cachePut(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX, originalCanApplyLimit); |
| context.cachePut(CompiledValue.CAN_APPLY_ORDER_BY_AT_INDEX, originalCanApplyOrderBy); |
| ObjectType ot = indexInfo._index.getResultSetType(); |
| // The following if block is not currently used other than the else |
| // This would be needed once we figure out how to handle nested object indexes (range, map, etc) |
| // The issue we have right now with these indexes is the results will come back as a tuple, if |
| // we use those as is, we end up |
| // reusing the evaluated values even if they did not come from the top level object leading to |
| // duplicate results or incorrect tupling |
| if (ot.isStructType()) { |
| // createObjectResultsFromStructResults(indexInfo, sr); |
| } else if (ot.isMapType()) { |
| |
| } else if (ot.isCollectionType()) { |
| |
| } else { |
| this.addDerivedResults(dcc.getIndexInfo(context)[0], sr); |
| } |
| } |
| |
| // Not used at this time. Was left over from attempt to speed up Range Indexes |
| /* |
| * private void createObjectResultsFromStructResults(IndexInfo indexInfo, SelectResults sr) { |
| * Iterator srIterator = sr.iterator(); SelectResults[] newSrs = null; |
| * |
| * while (srIterator.hasNext()) { Struct struct = (Struct) srIterator.next(); Object[] fieldValues |
| * = struct.getFieldValues(); int structLength = struct.getFieldValues().length; if (newSrs == |
| * null) { newSrs = new FakeSelectResults[structLength]; for (int x = 0; x < structLength; x++) { |
| * newSrs[x] = new FakeSelectResults(); } } for (int i = 0; i < structLength; i++) { |
| * newSrs[i].add(fieldValues[i]); } } |
| * |
| * if (newSrs != null) { this.addDerivedResults(indexInfo, newSrs); } } |
| */ |
| |
| private boolean isValidIndexTypeToDerive(IndexProtocol index) { |
| ObjectType type = index.getResultSetType(); |
| return !(type.isCollectionType() || type.isMapType() || type.isStructType()); |
| } |
| |
| private CompiledComparison createDerivedJoin(ExecutionContext context, CompiledValue newLeft, |
| CompiledValue newRight, int op) throws TypeMismatchException, NameResolutionException { |
| CompiledComparison cc = new CompiledComparison(newLeft, newRight, op); |
| cc.computeDependencies(context); |
| return cc; |
| } |
| |
| // Given a compiled value, we check to see if the receiver id of a CompiledPath matches the |
| // receiverId passed in |
| private boolean matchingPathIds(String receiverId, CompiledValue cv) { |
| if (isCompiledPath(cv)) { |
| CompiledPath path = (CompiledPath) cv; |
| return receiverId.equals(QueryUtils.getCompiledIdFromPath(path).getId()); |
| } |
| return false; |
| } |
| |
| private boolean isCompiledPath(CompiledValue cv) { |
| return cv.getType() == CompiledValue.PATH; |
| } |
| |
| } |