| /* |
| * 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. |
| */ |
| /* |
| * Created on Jan 27, 2008 |
| */ |
| 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.List; |
| import java.util.Set; |
| |
| 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.parse.OQLLexerTokenTypes; |
| import org.apache.geode.cache.query.internal.types.StructTypeImpl; |
| import org.apache.geode.cache.query.internal.types.TypeUtils; |
| import org.apache.geode.cache.query.types.ObjectType; |
| import org.apache.geode.cache.query.types.StructType; |
| |
| /** |
| * This structure contains all the filter evaluatable CompiledComparision conditions which are using |
| * identical index. Presently this Object will be formed only if the junction is an AND and will |
| * either be a part of a GroupJunction or can be a stand alone Junction. In case it is a stand alone |
| * Junction, then it can possibly have a not null Iter Operand, so that it can be evaluated along |
| * with the expansion/truncation of index result. |
| * |
| */ |
| public class RangeJunction extends AbstractGroupOrRangeJunction { |
| private static final int RANGE_SIZE_ESTIMATE = 3; |
| // moved to AbstractGroupOrRangeJunction |
| // private CompiledValue iterOperands; |
| |
| RangeJunction(int operator, RuntimeIterator[] indpndntItr, boolean isCompleteExpansion, |
| CompiledValue[] operands) { |
| super(operator, indpndntItr, isCompleteExpansion, operands); |
| |
| } |
| |
| @Override |
| void addUnevaluatedFilterOperands(List unevaluatedFilterOps) { |
| throw new UnsupportedOperationException("This method should not have been invoked"); |
| } |
| |
| // moved to AbstractGroupOrRangeJunction |
| /* |
| * void addIterOperands(CompiledValue iterOps) { this.iterOperands = iterOps; } |
| */ |
| private RangeJunction(AbstractGroupOrRangeJunction oldGJ, boolean completeExpansion, |
| RuntimeIterator indpnds[], CompiledValue iterOp) { |
| super(oldGJ, completeExpansion, indpnds, iterOp); |
| } |
| |
| @Override |
| AbstractGroupOrRangeJunction recreateFromOld(boolean completeExpansion, RuntimeIterator indpnds[], |
| CompiledValue iterOp) { |
| return new RangeJunction(this, completeExpansion, indpnds, iterOp); |
| } |
| |
| @Override |
| AbstractGroupOrRangeJunction createNewOfSameType(int operator, RuntimeIterator[] indpndntItr, |
| boolean isCompleteExpansion, CompiledValue[] operands) { |
| return new RangeJunction(operator, indpndntItr, isCompleteExpansion, operands); |
| } |
| |
| @Override |
| public PlanInfo getPlanInfo(ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| /* |
| * This function would be called only if the RangeJunction is a part of GroupJunction.It would |
| * be invoked in the organized operands method of GroupJunction. In such case it is guaranteed |
| * that all the operands are the filter operand using the same index. In such case there is zero |
| * possibility o first iterator being either an iter operand or a constant. As those types of |
| * Operands would be part of Group Junction |
| */ |
| return this._operands[0].getPlanInfo(context); |
| } |
| |
| @Override |
| public boolean isConditioningNeededForIndex(RuntimeIterator independentIter, |
| ExecutionContext context, boolean completeExpnsNeeded) |
| throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| return true; |
| } |
| |
| @Override |
| public int getOperator() { |
| return LITERAL_and; |
| } |
| |
| @Override |
| 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; |
| |
| // Go with the lowest cost when hint is used. |
| if (context instanceof QueryExecutionContext && ((QueryExecutionContext) context).hasHints()) { |
| return thisSize <= comparedTo.getSizeEstimate(context); |
| } |
| |
| int thatOperator = comparedTo.getOperator(); |
| switch (thatOperator) { |
| case TOK_EQ: |
| isThisBetter = false; |
| break; |
| case TOK_NE: |
| case TOK_NE_ALT: |
| // Give preference to Range |
| break; |
| case LITERAL_and: |
| // Asif: What to do? Let current be the better one for the want of better estimation |
| break; |
| case TOK_LE: |
| case TOK_LT: |
| case TOK_GE: |
| case TOK_GT: |
| // Give preference to this rather than single condition inequalities as a rangejunction |
| // would possibly be bounded resulting in lesser values |
| break; |
| default: |
| throw new IllegalArgumentException("The operator type =" + thatOperator + " is unknown"); |
| } |
| |
| return isThisBetter; |
| } |
| |
| /** |
| * Segregates the operands of the RangeJunction into iter evaluatable and filter evaluatable. |
| */ |
| @Override |
| OrganizedOperands organizeOperands(ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| // get the list of operands to evaluate, |
| // and evaluate operands that can use indexes first |
| if (getOperator() == LITERAL_and) { |
| return organizeOperandsForAndJunction(context); |
| } else { |
| throw new IllegalStateException( |
| "In the case of an OR junction a RangeJunction should not be formed for now"); |
| } |
| } |
| |
| // TODO:Asif: Currently the condition a!= null AND a =3, would be evalauted |
| // via intersection. This needs to be optmized, but since it is not |
| // common use case as != null is a not required operand , for the time being |
| // ignoring the optmization. |
| /** |
| * For the filter evaluatable conditions , it creates the appropriate JunctionEvaluator ( |
| * NotEqualConditionEvaluator or SingleCondnEvaluator or DoubleCondnRangeJunctionEvaluator ). The |
| * junction Evaluator itself is filter evaluatable. The operands which are of type != null , == |
| * null , != undefined, == undefined are left as it is & are not combined into a Junction |
| * Evaluator. Thus the organized operand of RangeJunction may created atmost one Condition |
| * Evaluator, will retain the operands containing null ,undefined conditions. In case there is a |
| * equality condition , then it may result in a filter having just that condition assuming other |
| * conditions satisfy the equality. In case it turns out that the conditions are mutually |
| * exclusive then the organized operand would just contain a single filter evaluatable |
| * CompiledLiteral (false) ( indicating empty resultset). |
| */ |
| private OrganizedOperands organizeOperandsForAndJunction(ExecutionContext context) |
| throws AmbiguousNameException, FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| List evalOperands = new ArrayList(_operands.length); |
| int evalCount = 0; |
| int lessCondnOp = -1; |
| int greaterCondnOp = -1; |
| CompiledComparison lessCondnOperand = null; |
| CompiledComparison greaterCondnOperand = null; |
| CompiledComparison equalCondnOperand = null; |
| Object equalCondKey = null; |
| Object lessCondnKey = null; |
| Object greaterCondnKey = null; |
| boolean emptyResults = false; |
| Set notEqualTypeKeys = null; |
| boolean possibleRangeFilter = false; |
| IndexInfo indxInfo = null; |
| for (int i = 0; i < _operands.length; i++) { |
| CompiledValue operand = _operands[i]; |
| if (operand.getPlanInfo(context).evalAsFilter) { |
| Indexable cc = (Indexable) operand; |
| if (indxInfo == null) { |
| indxInfo = cc.getIndexInfo(context)[0]; |
| } |
| // TODO: Asif :Try to ensure that a CompiledUndefined never |
| // goes in the RangeJunction. That means modify the |
| // CompiledJunction code to avoid CompiledUndefined's inclusion |
| // That way we can ensure that CompiledComparison only become part |
| // of RangeJunction |
| if (!cc.isRangeEvaluatable()) { |
| evalCount++; |
| evalOperands.add(0, _operands[i]); |
| continue; |
| } |
| CompiledValue ccKey = ((CompiledComparison) cc).getKey(context); |
| Object evaluatedCCKey = ccKey.evaluate(context); |
| int operator = ((CompiledComparison) cc).reflectOnOperator(ccKey); |
| if (evaluatedCCKey == null) { |
| evalCount++; |
| evalOperands.add(0, _operands[i]); |
| continue; |
| } |
| if (equalCondnOperand != null) { |
| emptyResults = !isConditionSatisfied(equalCondKey, evaluatedCCKey, operator); |
| if (emptyResults) { |
| break; |
| } else { |
| continue; |
| } |
| } |
| |
| switch (operator) { |
| case TOK_EQ: |
| possibleRangeFilter = false; |
| equalCondnOperand = (CompiledComparison) cc; |
| equalCondKey = evaluatedCCKey; |
| break; |
| case TOK_NE: |
| case TOK_NE_ALT: |
| possibleRangeFilter = true; |
| if (notEqualTypeKeys == null) { |
| notEqualTypeKeys = new HashSet(_operands.length); |
| } |
| evaluatedCCKey = TypeUtils.indexKeyFor(evaluatedCCKey); |
| notEqualTypeKeys.add(evaluatedCCKey); |
| break; |
| case TOK_GE: |
| case TOK_GT: |
| possibleRangeFilter = true; |
| if (greaterCondnOperand == null) { |
| greaterCondnOperand = (CompiledComparison) cc; |
| greaterCondnKey = evaluatedCCKey; |
| greaterCondnOp = operator; |
| } else { |
| if (isConditionSatisfied(evaluatedCCKey, greaterCondnKey, greaterCondnOp)) { |
| greaterCondnKey = evaluatedCCKey; |
| greaterCondnOperand = (CompiledComparison) cc; |
| greaterCondnOp = operator; |
| } |
| } |
| break; |
| case TOK_LE: |
| case TOK_LT: |
| // Asif: if there exists a previous equal Operand & current |
| // condition's value is greater than the equal operand's value, it |
| // will be empty results |
| possibleRangeFilter = true; |
| if (lessCondnOperand == null) { |
| lessCondnOperand = (CompiledComparison) cc; |
| lessCondnKey = evaluatedCCKey; |
| lessCondnOp = operator; |
| } else { |
| if (isConditionSatisfied(evaluatedCCKey, lessCondnKey, lessCondnOp)) { |
| lessCondnKey = evaluatedCCKey; |
| lessCondnOperand = (CompiledComparison) cc; |
| lessCondnOp = operator; |
| } |
| } |
| break; |
| } |
| |
| } else if (!_operands[i].isDependentOnCurrentScope(context)) { |
| // TODO: Asif :Remove this Assert & else if condition after successful |
| // testing of the build |
| Support.assertionFailed( |
| "An independentoperand should not ever be present as operand inside a GroupJunction as it should always be present only in CompiledJunction"); |
| } else { |
| evalOperands.add(_operands[i]); |
| } |
| } |
| if (!emptyResults) { |
| Filter filter = null; |
| if (equalCondnOperand != null) { |
| // Check if any of the preceding inequality operands, that have not been |
| // checked against the equality operand , are not able to satisfy the |
| // equality. |
| if (lessCondnOperand != null |
| && !this.isConditionSatisfied(equalCondKey, lessCondnKey, lessCondnOp)) { |
| emptyResults = true; |
| } else if (greaterCondnOperand != null |
| && !this.isConditionSatisfied(equalCondKey, greaterCondnKey, greaterCondnOp)) { |
| emptyResults = true; |
| } else if (notEqualTypeKeys != null) { |
| Iterator itr = notEqualTypeKeys.iterator(); |
| while (itr.hasNext() && !emptyResults) { |
| emptyResults = |
| !this.isConditionSatisfied(equalCondKey, itr.next(), OQLLexerTokenTypes.TOK_NE); |
| } |
| } |
| if (!emptyResults) { |
| filter = equalCondnOperand; |
| } |
| } else if (possibleRangeFilter) { |
| if (lessCondnOperand != null && greaterCondnOperand != null) { |
| emptyResults = !checkForRangeBoundednessAndTrimNotEqualKeyset(notEqualTypeKeys, |
| lessCondnKey, lessCondnOp, greaterCondnKey, greaterCondnOp); |
| if (!emptyResults) { |
| filter = new DoubleCondnRangeJunctionEvaluator(lessCondnOp, lessCondnKey, |
| greaterCondnOp, greaterCondnKey, |
| (notEqualTypeKeys == null || notEqualTypeKeys.isEmpty()) ? null : notEqualTypeKeys, |
| indxInfo); |
| } |
| } else if (greaterCondnOperand != null) { |
| filter = generateSingleCondnEvaluatorIfRequired(notEqualTypeKeys, greaterCondnOperand, |
| greaterCondnOp, greaterCondnKey, indxInfo); |
| } else if (lessCondnOperand != null) { |
| filter = generateSingleCondnEvaluatorIfRequired(notEqualTypeKeys, lessCondnOperand, |
| lessCondnOp, lessCondnKey, indxInfo); |
| } else { |
| assert notEqualTypeKeys != null && !notEqualTypeKeys.isEmpty(); |
| // TODO:Asif Ideally if there is a single NotEqualKey we should |
| // not create NotEqualCondnEvaluator instead just add the |
| // CompiledComparison |
| // operand to the eval operands list. But since we do retain the |
| // operand |
| // correponding to the NotEqualKey in this function , we are creating |
| // the NotEqualCondnEvaluator |
| filter = new NotEqualConditionEvaluator(notEqualTypeKeys, indxInfo); |
| } |
| |
| } |
| if (emptyResults) { |
| evalOperands.clear(); |
| evalCount = 1; |
| evalOperands.add(new CompiledLiteral(Boolean.FALSE)); |
| } else if (filter != null) { |
| evalCount++; |
| evalOperands.add(0, filter); |
| } |
| } else { |
| // Asif: Create a new CompiledLiteral with boolean false |
| evalOperands.clear(); |
| evalCount = 1; |
| evalOperands.add(new CompiledLiteral(Boolean.FALSE)); |
| } |
| |
| // If no hints were provided, we continue with our single index solution |
| if (!(context instanceof QueryExecutionContext) |
| || !((QueryExecutionContext) context).hasMultiHints()) { |
| // At the end check if the unevaluatedIterOperand |
| // are null or not. This could be the case only if at top level |
| // GroupJunction is formed having multiple RangeJunctions & other |
| // iter operands & then only one RangeJunction is treated as filter |
| // rest all as iter operands. In that case , the only iter operand is |
| // that which is added externally to RangeJunction. If the top |
| // level was a RangeJunction then the iter operands would have been |
| // part of it at the time of creation of RangeJunction & we would not have |
| // to add it externally. |
| if (getIterOperands() != null) { |
| // Commented the assert for CompiledLike which creates 2 or 3 CompiledComparisons |
| // for the same operand. The protGetPlanInfo in CompiledLike could return evalAsFilter |
| // as true the first time and false the next time for the same operand. |
| // Hence the evalOperands could contain CompiledComparisons more than number of indexes. |
| |
| // Support.Assert(evalOperands.size() == evalCount); |
| evalOperands.add(getIterOperands()); |
| } |
| } |
| return createOrganizedOperandsObject(evalCount, evalOperands); |
| |
| } |
| |
| /** |
| * Checks if key1 operator key2 is true or not. The operator could be =, != , <, >,<=,>= |
| * |
| * @return boolean true if the condition is satisfied else false |
| */ |
| private boolean isConditionSatisfied(Object key1, Object key2, int operator) |
| throws TypeMismatchException { |
| return ((Boolean) TypeUtils.compare(key1, key2, operator)).booleanValue(); |
| } |
| |
| /** |
| * Checks if the Range junction containing less & greater type of inequalities has a lower and |
| * upper bound , in the sense that they do not represent a mutually exclusive condition like a> 10 |
| * and a <9 etc. If the condition is bounded in nature, it further checks if the not equal type |
| * keys fall in the bounded range , else it removes it from the Not Equal Keys set |
| * |
| * @param notEqualKeys Set containing keys of operands having 'Not Equal' (!=) type conditions |
| * @param lessCondnKey Key of the 'Less' condition operand |
| * @param lessOperator Type of 'less' operator ( < or <=) |
| * @param greaterCondnKey Key of the 'greater' condition operand |
| * @param greaterCondnOp Type of 'greater' operator ( > or >=) |
| * @return boolean true if the nature is bounded else false ( unbounded ) |
| */ |
| private boolean checkForRangeBoundednessAndTrimNotEqualKeyset(Set notEqualKeys, |
| Object lessCondnKey, int lessOperator, Object greaterCondnKey, int greaterCondnOp) |
| throws TypeMismatchException { |
| // First check if the range is bounded or (unbounded and mutually |
| // exclusive). |
| // If it is unbounded immediately return a false |
| if (isConditionSatisfied(greaterCondnKey, lessCondnKey, lessOperator) |
| && isConditionSatisfied(lessCondnKey, greaterCondnKey, greaterCondnOp)) { |
| // Nowremove those not equal conditions which do not satisfy the range |
| if (notEqualKeys != null) { |
| Iterator itr = notEqualKeys.iterator(); |
| Object neKey = null; |
| while (itr.hasNext()) { |
| neKey = itr.next(); |
| if (!this.isConditionSatisfied(neKey, greaterCondnKey, greaterCondnOp) |
| || !this.isConditionSatisfied(neKey, lessCondnKey, lessOperator)) { |
| itr.remove(); |
| } |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Creates a Filter of type SingleCondnEvaluator if there exists atleast one key of type "NOT |
| * EQUAL" which satisfies the 'less' or 'greater' type operand. Otherwise the Filter is nothing |
| * but the CompiledComparison representing the 'less' or 'greater' inequality |
| * |
| * @param notEqualKeys Set containing NotEqual type Keys |
| * @param operand CompiledValue representing the 'Less' or 'Greater' operand |
| * @param operator Type of 'Less' or 'Greater' operand |
| * @param condnKey The Key corresponding to the Operand representing the 'Less' or 'Greater' |
| * inequality |
| * @param indxInfo The IndexInfo object for this RangeJunction |
| * @return Filter object of type CompiledComparison or RangeJunction.SingleCondnEvaluator object |
| */ |
| private Filter generateSingleCondnEvaluatorIfRequired(Set notEqualKeys, CompiledValue operand, |
| int operator, Object condnKey, IndexInfo indxInfo) throws TypeMismatchException { |
| Filter rangeFilter; |
| if (notEqualKeys != null) { |
| // Eliminate all the not equal keys which will never be satisfied by |
| // the given greater condn |
| Iterator itr = notEqualKeys.iterator(); |
| while (itr.hasNext()) { |
| Object neKey = itr.next(); |
| if (!((Boolean) TypeUtils.compare(neKey, condnKey, operator)).booleanValue()) { |
| itr.remove(); |
| } |
| } |
| if (notEqualKeys.isEmpty()) { |
| notEqualKeys = null; |
| } |
| } |
| rangeFilter = (notEqualKeys != null) |
| ? new SingleCondnEvaluator(operator, condnKey, notEqualKeys, indxInfo) : (Filter) operand; |
| return rangeFilter; |
| } |
| |
| @Override |
| public Object evaluate(ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| Object r = _operands[0].evaluate(context); // UNDEFINED, null, or a |
| // Boolean |
| |
| // if it's false and the op in this case will always be AND so return |
| // false immediately |
| if (r instanceof Boolean && !((Boolean) r).booleanValue()) |
| return r; |
| if (r == null || r == QueryService.UNDEFINED) |
| r = QueryService.UNDEFINED; // keep going to see if we hit a |
| // short-circuiting truth value |
| else if (!(r instanceof Boolean)) |
| throw new TypeMismatchException( |
| "LITERAL_and/LITERAL_or operands must be of type boolean, not type '" |
| + r.getClass().getName() + "'"); |
| for (int i = 1; i < _operands.length; i++) { |
| Object ri = _operands[i].evaluate(context); // UNDEFINED, null, or |
| // Boolean |
| if (ri instanceof Boolean && !((Boolean) ri).booleanValue()) |
| return ri; |
| if (ri == null || ri == QueryService.UNDEFINED || r == QueryService.UNDEFINED) { |
| r = QueryService.UNDEFINED; |
| continue; // keep going to see if we hit a short-circuiting |
| // truth value |
| } else if (!(ri instanceof Boolean)) |
| throw new TypeMismatchException( |
| "LITERAL_and/LITERAL_or operands must be of type boolean, not type '" |
| + ri.getClass().getName() + "'"); |
| // now do the actual and |
| |
| r = new Boolean(((Boolean) r).booleanValue() && ((Boolean) ri).booleanValue()); |
| |
| } |
| return r; |
| } |
| |
| @Override |
| public int getType() { |
| return LITERAL_and; |
| } |
| |
| @Override |
| public void visitNodes(NodeVisitor visitor) { |
| Support.assertionFailed("Should not have come here"); |
| } |
| |
| @Override |
| public int getSizeEstimate(ExecutionContext context) { |
| // TODO:Asif:Try to estimate better |
| return RANGE_SIZE_ESTIMATE; |
| } |
| |
| /* |
| * private organizeOperandsForORJunction() { } |
| */ |
| /** |
| * Test method which checks if the Filter operand is of type SingleCondnEvaluator |
| */ |
| static boolean isInstanceOfSingleCondnEvaluator(Object o) { |
| return o instanceof RangeJunction.SingleCondnEvaluator; |
| } |
| |
| /** |
| * Test method which checks if the Filter operand is of type NotEqualConditionEvaluator |
| */ |
| static boolean isInstanceOfNotEqualConditionEvaluator(Object o) { |
| return o instanceof RangeJunction.NotEqualConditionEvaluator; |
| } |
| |
| /** |
| * Test method which checks if the Filter operand is of type DoubleCondnRangeJunctionEvaluator |
| */ |
| static boolean isInstanceOfDoubleCondnRangeJunctionEvaluator(Object o) { |
| return o instanceof RangeJunction.DoubleCondnRangeJunctionEvaluator; |
| } |
| |
| /** |
| * Test function which retrieves the "NOT EQUAL KEYS" |
| * |
| * @param o Object of type NotEqualConditionEvaluator from which the set containing the keys for |
| * removal need to be retrieved |
| * @return Unmodifiable Set containing the keys for removal |
| */ |
| static Set getKeysToBeRemoved(Object o) { |
| if (o instanceof NotEqualConditionEvaluator) { |
| if (((NotEqualConditionEvaluator) o).notEqualTypeKeys == null) { |
| return null; |
| } |
| return Collections.unmodifiableSet(((NotEqualConditionEvaluator) o).notEqualTypeKeys); |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the SingleCondnEvaluator operator |
| * |
| * @param o Object of type SingleCondnEvaluator from which the set containing the keys for removal |
| * need to be retrieved |
| * @return int indicating the operator |
| */ |
| static int getSingleCondnEvaluatorOperator(Object o) { |
| if (o instanceof SingleCondnEvaluator) { |
| return ((SingleCondnEvaluator) o).condnOp; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the evaluated Key for a SingleCondnEvaluator operator |
| * |
| * @param o Object of type SingleCondnEvaluator from which the set containing the keys for removal |
| * need to be retrieved |
| * @return Object representing the evaluated Key |
| */ |
| static Object getSingleCondnEvaluatorKey(Object o) { |
| if (o instanceof SingleCondnEvaluator) { |
| return ((SingleCondnEvaluator) o).condnKey; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the LESS type evaluated Key for a DoubleCondnEvaluator operator |
| * |
| * @param o Object of type DoubleCondnEvaluator |
| * @return Object representing the evaluated Key of Less Type |
| */ |
| static Object getDoubleCondnEvaluatorLESSKey(Object o) { |
| if (o instanceof DoubleCondnRangeJunctionEvaluator) { |
| return ((DoubleCondnRangeJunctionEvaluator) o).lessCondnKey; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the GREATER type evaluated Key for a DoubleCondnEvaluator |
| * operator |
| * |
| * @param o Object of type DoubleCondnEvaluator |
| * @return Object representing the evaluated Key of GREATER Type |
| */ |
| static Object getDoubleCondnEvaluatorGreaterKey(Object o) { |
| if (o instanceof DoubleCondnRangeJunctionEvaluator) { |
| return ((DoubleCondnRangeJunctionEvaluator) o).greaterCondnKey; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the operator of Less Type |
| * |
| * @param o Object of type DoubleCondnEvaluator |
| * @return int indicating the operator of less Type |
| */ |
| static int getDoubleCondnEvaluatorOperatorOfLessType(Object o) { |
| if (o instanceof DoubleCondnRangeJunctionEvaluator) { |
| return ((DoubleCondnRangeJunctionEvaluator) o).lessCondnOp; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the operator of GREATER Type |
| * |
| * @param o Object of type DoubleCondnEvaluator |
| * @return int indicating the operator of less Type |
| */ |
| static int getDoubleCondnEvaluatorOperatorOfGreaterType(Object o) { |
| if (o instanceof DoubleCondnRangeJunctionEvaluator) { |
| return ((DoubleCondnRangeJunctionEvaluator) o).greaterCondnOp; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Test function which retrieves the underlying Index for a NotEqualConditionEvaluator operator |
| * |
| * @param o Object of type NotEqualConditionEvaluator from which the index needs to be retrieved |
| */ |
| static Index getIndex(Object o) { |
| if (o instanceof NotEqualConditionEvaluator) { |
| return ((NotEqualConditionEvaluator) o).indxInfo._index; |
| } else { |
| throw new IllegalStateException( |
| "The Object is not of type NotEqualConditionEvaluator"); |
| } |
| } |
| |
| /** |
| * Filter Object created by the RangeJunction on invocation of its organizedOperands method. The |
| * object of this class will be created only if RangeJunction contains more than one 'NOT EQUAL' ( |
| * != ) type conditions ( apart from conditions having null or undefined as key). This class is |
| * also extended by SingleCondnEvaluator and DoubleCondnRangeJunctionEvaluator |
| * |
| * |
| */ |
| private static class NotEqualConditionEvaluator extends AbstractCompiledValue implements Filter { |
| final Set notEqualTypeKeys; |
| |
| final IndexInfo indxInfo; |
| |
| /** |
| * |
| * @param notEqualTypeKeys java.utils.Set object containing the Keys of the 'NOT EQUAL' type |
| * conditions ( a != 3 and a !=5) For DoubleCondnRangeJunctionEvaluator , this may be |
| * null |
| * @param indxInfo The IndexInfo object corresponding to the RangeJunction |
| */ |
| NotEqualConditionEvaluator(Set notEqualTypeKeys, IndexInfo indxInfo) { |
| this.notEqualTypeKeys = notEqualTypeKeys; |
| this.indxInfo = indxInfo; |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit, |
| boolean completeExpansionNeeded, CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, |
| boolean isIntersection, boolean conditioningNeeded, boolean evalProj) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| ObjectType resultType = this.indxInfo._index.getResultSetType(); |
| int indexFieldsSize = -1; |
| SelectResults set = null; |
| 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 (resultType instanceof StructType) { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl) resultType) |
| : new SortedResultsBag<Struct>((StructTypeImpl) resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createStructCollection(context, (StructTypeImpl) resultType); |
| } |
| indexFieldsSize = ((StructTypeImpl) resultType).getFieldNames().length; |
| } else { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("non-StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedResultSet(resultType) |
| : new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createResultCollection(context, resultType); |
| } |
| indexFieldsSize = 1; |
| } |
| // actual index lookup |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| /* |
| * 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. |
| * |
| */ |
| try { |
| observer.beforeIndexLookup(this.indxInfo._index, OQLLexerTokenTypes.TOK_NE, |
| this.notEqualTypeKeys); |
| context.cachePut(CompiledValue.INDEX_INFO, this.indxInfo); |
| this.indxInfo._index.query(set, notEqualTypeKeys, context); |
| } finally { |
| observer.afterIndexLookup(set); |
| } |
| return QueryUtils.getConditionedIndexResults(set, this.indxInfo, context, indexFieldsSize, |
| completeExpansionNeeded, iterOperands, indpndntItrs); |
| } |
| |
| @Override |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Object evaluate(ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| Object evaluatedPath = this.indxInfo._path.evaluate(context); |
| return evaluate(context, evaluatedPath); |
| } |
| |
| @Override |
| public boolean isConditioningNeededForIndex(RuntimeIterator independentIter, |
| ExecutionContext context, boolean completeExpnsNeeded) |
| throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| return true; |
| } |
| |
| public Object evaluate(ExecutionContext context, Object evaluatedPath) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| Iterator itr = this.notEqualTypeKeys.iterator(); |
| while (itr.hasNext()) { |
| Object val = itr.next(); |
| Object result = TypeUtils.compare(evaluatedPath, val, TOK_NE); |
| if (result instanceof Boolean) { |
| if (!((Boolean) result).booleanValue()) { |
| return Boolean.FALSE; |
| } |
| } else { |
| throw new TypeMismatchException( |
| "NotEqualConditionEvaluator should evaluate to boolean type"); |
| } |
| } |
| return Boolean.TRUE; |
| |
| } |
| |
| @Override |
| public int getType() { |
| return NOTEQUALCONDITIONEVALUATOR; |
| } |
| |
| @Override |
| public int getSizeEstimate(ExecutionContext context) { |
| return RANGE_SIZE_ESTIMATE; |
| } |
| |
| @Override |
| public void visitNodes(NodeVisitor visitor) { |
| Support.assertionFailed("Should not have come here"); |
| } |
| |
| @Override |
| public int getOperator() { |
| return LITERAL_and; |
| } |
| |
| @Override |
| public boolean isBetterFilter(Filter comparedTo, ExecutionContext context, 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 thatOperator = comparedTo.getOperator(); |
| |
| // Go with the lowest cost when hint is used. |
| if (context instanceof QueryExecutionContext |
| && ((QueryExecutionContext) context).hasHints()) { |
| return thisSize <= comparedTo.getSizeEstimate(context); |
| } |
| |
| switch (thatOperator) { |
| case TOK_EQ: |
| isThisBetter = false; |
| break; |
| case TOK_NE: |
| case TOK_NE_ALT: |
| // Give preference to Range |
| break; |
| default: |
| throw new IllegalArgumentException("The operator type =" + thatOperator + " is unknown"); |
| } |
| |
| return isThisBetter; |
| } |
| |
| } |
| |
| /** |
| * Filter object of this type gets created if there exists atleast one "NOT EQUAL" type condition |
| * and a single condition containing an inequality of type 'Less' or 'Greater'. The Where clause |
| * may actually contain multiple 'Less' type inequality or multiple 'Greater' type inequality ( |
| * though not both 'Less' and 'Greater' together). The RangeJunction will identify the most |
| * specific inequality for the AND junction. Thus if something like a > 7 and a >=6 , will be |
| * sufficiently represented by a > 7 |
| * |
| * |
| */ |
| private static class SingleCondnEvaluator extends NotEqualConditionEvaluator { |
| protected int condnOp = -1; |
| |
| protected final Object condnKey; |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * |
| * @param operator integer identifying the type of 'Less' or 'Greater' inequality |
| * @param key Object representing the Key for the inequality |
| * @param notEqualKeys Set containing the 'NOT EQUAL' Keys accompanying the 'Less' or 'Greater' |
| * inequality |
| * @param indxInfo The IndexInfo object corresponding to the RangeJunction |
| */ |
| SingleCondnEvaluator(int operator, Object key, Set notEqualKeys, IndexInfo indxInfo) { |
| super(notEqualKeys, indxInfo); |
| this.condnOp = operator; |
| this.condnKey = key; |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit, |
| boolean completeExpansionNeeded, CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, |
| boolean isIntersection, boolean conditioningNeeded, boolean evalProj) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| ObjectType resultType = this.indxInfo._index.getResultSetType(); |
| int indexFieldsSize = -1; |
| SelectResults set = null; |
| 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 (resultType instanceof StructType) { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl) resultType) |
| : new SortedResultsBag<Struct>((StructTypeImpl) resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createStructCollection(context, (StructTypeImpl) resultType); |
| } |
| indexFieldsSize = ((StructTypeImpl) resultType).getFieldNames().length; |
| } else { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("non-StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedResultSet(resultType) |
| : new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createResultCollection(context, resultType); |
| } |
| indexFieldsSize = 1; |
| } |
| // actual index lookup |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| /* |
| * 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. |
| * |
| */ |
| try { |
| observer.beforeIndexLookup(this.indxInfo._index, this.condnOp, this.condnKey); |
| context.cachePut(CompiledValue.INDEX_INFO, this.indxInfo); |
| this.indxInfo._index.query(this.condnKey, this.condnOp, set, notEqualTypeKeys, context); |
| } finally { |
| observer.afterIndexLookup(set); |
| } |
| return QueryUtils.getConditionedIndexResults(set, this.indxInfo, context, indexFieldsSize, |
| completeExpansionNeeded, iterOperands, indpndntItrs); |
| |
| } |
| |
| @Override |
| public Object evaluate(ExecutionContext context) throws TypeMismatchException, |
| FunctionDomainException, NameResolutionException, QueryInvocationTargetException { |
| Object evaluatedPath = this.indxInfo._path.evaluate(context); |
| Boolean result = (Boolean) super.evaluate(context, evaluatedPath); |
| if (result.booleanValue()) { |
| result = (Boolean) TypeUtils.compare(evaluatedPath, this.condnKey, this.condnOp); |
| } |
| return result; |
| } |
| |
| @Override |
| public int getType() { |
| return SINGLECONDNEVALUATOR; |
| } |
| |
| @Override |
| public void visitNodes(NodeVisitor visitor) { |
| Support.assertionFailed("Should not have come here"); |
| } |
| |
| @Override |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * Filter object of this type gets created if there exists a bounded condition like a >7 and a > 8 |
| * and a< 10 and a <11. The RangeJunction will identify the most specific inequality of each type |
| * for the AND junction. Thus the conditions a > 8 and a <10 will be used to form the Object of |
| * this class. For this evaluator only, the notEqualTypeKeys present in its super class may be |
| * null ( if there is no 'NOT EQUAL' type condition satisfying the bounded condition) |
| * |
| * |
| */ |
| private static class DoubleCondnRangeJunctionEvaluator extends NotEqualConditionEvaluator { |
| protected final int lessCondnOp; |
| |
| protected final int greaterCondnOp; |
| |
| protected final Object lessCondnKey; |
| |
| protected final Object greaterCondnKey; |
| |
| /** |
| * |
| * @param lessCondnOp integer identifying the upper bound ( < or <= ) |
| * @param lessCondnKey Object representing the Upper Bound Key |
| * @param greaterCondnOp integer identifying the lower bound ( > or >= ) |
| * @param greaterCondnKey Object representing the lower Bound Key |
| * @param notEqualTypeKeys Set containing the 'NOT EQUAL' Keys accompanying the 'Less' or |
| * 'Greater' inequality |
| * @param indexInfo The IndexInfo object corresponding to the RangeJunction |
| */ |
| DoubleCondnRangeJunctionEvaluator(int lessCondnOp, Object lessCondnKey, int greaterCondnOp, |
| Object greaterCondnKey, Set notEqualTypeKeys, IndexInfo indexInfo) { |
| super(notEqualTypeKeys, indexInfo); |
| this.lessCondnOp = lessCondnOp; |
| this.lessCondnKey = lessCondnKey; |
| this.greaterCondnOp = greaterCondnOp; |
| this.greaterCondnKey = greaterCondnKey; |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, SelectResults iterationLimit, |
| boolean completeExpansionNeeded, CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, |
| boolean isIntersection, boolean conditioningNeeded, boolean evalProj) |
| throws FunctionDomainException, TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| ObjectType resultType = this.indxInfo._index.getResultSetType(); |
| int indexFieldsSize = -1; |
| SelectResults set = null; |
| 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 (resultType instanceof StructType) { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedStructSet((StructTypeImpl) resultType) |
| : new SortedResultsBag<Struct>((StructTypeImpl) resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createStructCollection(context, (StructTypeImpl) resultType); |
| } |
| indexFieldsSize = ((StructTypeImpl) resultType).getFieldNames().length; |
| } else { |
| if (context.getCache().getLogger().fineEnabled()) { |
| context.getCache().getLogger() |
| .fine("non-StructType resultType.class=" + resultType.getClass().getName()); |
| } |
| if (useLinkedDataStructure) { |
| set = context.isDistinct() ? new LinkedResultSet(resultType) |
| : new SortedResultsBag(resultType, nullValuesAtStart); |
| } else { |
| set = QueryUtils.createResultCollection(context, resultType); |
| } |
| indexFieldsSize = 1; |
| } |
| // actual index lookup |
| // Shobhit: Limit can not be applied at index level for RangeJunction as |
| // other conditions are applied after coming out of index query method. |
| context.cachePut(CompiledValue.CAN_APPLY_LIMIT_AT_INDEX, Boolean.FALSE); |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| /* |
| * 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. |
| * |
| */ |
| try { |
| observer.beforeIndexLookup(this.indxInfo._index, this.greaterCondnOp, this.greaterCondnKey, |
| this.lessCondnOp, this.lessCondnKey, this.notEqualTypeKeys); |
| context.cachePut(CompiledValue.INDEX_INFO, this.indxInfo); |
| this.indxInfo._index.query(this.greaterCondnKey, this.greaterCondnOp, this.lessCondnKey, |
| this.lessCondnOp, set, notEqualTypeKeys, context); |
| } finally { |
| observer.afterIndexLookup(set); |
| } |
| return QueryUtils.getConditionedIndexResults(set, this.indxInfo, context, indexFieldsSize, |
| completeExpansionNeeded, iterOperands, indpndntItrs); |
| |
| } |
| |
| @Override |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Object evaluate(ExecutionContext context) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, QueryInvocationTargetException { |
| Object evaluatedPath = this.indxInfo._path.evaluate(context); |
| Boolean result = (Boolean) super.evaluate(context, evaluatedPath); |
| if (result.booleanValue()) { |
| result = (Boolean) TypeUtils.compare(evaluatedPath, this.lessCondnKey, this.lessCondnOp); |
| result = result.booleanValue() |
| ? (Boolean) TypeUtils.compare(evaluatedPath, this.greaterCondnKey, this.greaterCondnOp) |
| : Boolean.FALSE; |
| } |
| return result; |
| } |
| |
| @Override |
| public int getType() { |
| return DOUBLECONDNRANGEJUNCTIONEVALUATOR; |
| } |
| |
| @Override |
| public void visitNodes(NodeVisitor visitor) { |
| Support.assertionFailed("Should not have come here"); |
| } |
| } |
| } |