| /*========================================================================= |
| * 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: CompiledUndefined.java,v 1.2 2005/02/01 17:19:20 vaibhav Exp $ |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.cache.query.internal; |
| |
| import java.util.*; |
| |
| import com.gemstone.gemfire.cache.query.*; |
| import com.gemstone.gemfire.cache.query.internal.IndexInfo; |
| 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.types.StructTypeImpl; |
| import com.gemstone.gemfire.cache.query.types.ObjectType; |
| import com.gemstone.gemfire.cache.query.types.StructType; |
| |
| /** |
| * Predefined function for identity of the UNDEFINED literal |
| * |
| * @version $Revision: 1.2 $ |
| * @author ericz |
| * @author asif |
| */ |
| public class CompiledUndefined extends AbstractCompiledValue implements |
| Negatable , Indexable { |
| |
| private CompiledValue _value; |
| private boolean _is_defined; |
| |
| public CompiledUndefined(CompiledValue value, boolean is_defined) { |
| _value = value; |
| _is_defined = is_defined; |
| } |
| |
| @Override |
| public List getChildren() { |
| return Collections.singletonList(this._value); |
| } |
| |
| public int getType() { |
| return FUNCTION; |
| } |
| |
| public Object evaluate(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| boolean b = _value.evaluate(context) == QueryService.UNDEFINED; |
| return Boolean.valueOf(_is_defined ? !b : b); |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults, boolean completeExpansionNeeded, |
| CompiledValue iterOperands, RuntimeIterator[] indpndntItrs, boolean isIntersection, boolean conditioningNeeded, boolean evaluateProjAttrib) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException { |
| // this method is called if we are independent of the iterator, |
| // or if we can use an index. |
| // if we are independent, then we should not have been here in the first |
| // place |
| Support |
| .Assert( |
| this._value.isDependentOnCurrentScope(context), |
| "For a condition which does not depend on any RuntimeIterator of current scope , we should not have been in this function"); |
| IndexInfo idxInfo[] = getIndexInfo(context); |
| ObjectType resultType = idxInfo[0]._index.getResultSetType(); |
| int indexFieldsSize = -1; |
| SelectResults set = null; |
| if (resultType instanceof StructType) { |
| set = QueryUtils.createStructCollection(context, (StructTypeImpl)resultType) ; |
| indexFieldsSize = ((StructTypeImpl) resultType).getFieldNames().length; |
| } |
| else { |
| set = QueryUtils.createResultCollection(context, resultType); |
| indexFieldsSize = 1; |
| } |
| int op = _is_defined ? TOK_NE : TOK_EQ; |
| Object key = QueryService.UNDEFINED; |
| QueryObserver observer = QueryObserverHolder.getInstance(); |
| try { |
| observer.beforeIndexLookup(idxInfo[0]._index, op, key); |
| context.cachePut(CompiledValue.INDEX_INFO, idxInfo[0]); |
| idxInfo[0]._index.query(key, op, set,context); |
| } |
| finally { |
| observer.afterIndexLookup(set); |
| } |
| return QueryUtils.getconditionedIndexResults(set, idxInfo[0], context, |
| indexFieldsSize, completeExpansionNeeded, iterOperands, indpndntItrs); |
| } |
| |
| public int getSizeEstimate(ExecutionContext context) |
| throws FunctionDomainException, TypeMismatchException, |
| NameResolutionException, QueryInvocationTargetException |
| { |
| IndexInfo[] idxInfo = getIndexInfo(context); |
| assert idxInfo.length == 1; |
| |
| if (context instanceof QueryExecutionContext) { |
| QueryExecutionContext qcontext = (QueryExecutionContext) context; |
| if (qcontext.isHinted(idxInfo[0]._index.getName())) { |
| return qcontext.getHintSize(idxInfo[0]._index.getName()); |
| } |
| } |
| |
| int op = _is_defined ? TOK_NE : TOK_EQ; |
| return idxInfo[0]._index.getSizeEstimate(QueryService.UNDEFINED, op, |
| idxInfo[0]._matchLevel); |
| } |
| |
| public int getOperator() { |
| return _is_defined ? TOK_NE : TOK_EQ; |
| } |
| |
| /** |
| * evaluate as a filter, producing an intermediate result set. This may |
| * require iteration if there is no index available. Asif :The boolean true |
| * implies that CompiledComparsion when existing on its own always requires a |
| * Complete expansion to top level iterators. This flag can get toggled to |
| * false only from inside a GroupJunction |
| * |
| * <p>param intermediateResults if this parameter is provided, and we have to |
| * iterate, then iterate over this result set instead of the entire |
| * base collection. |
| */ |
| @Override |
| public SelectResults filterEvaluate(ExecutionContext context, |
| SelectResults iterationLimit) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| return filterEvaluate(context, iterationLimit, |
| true/* Complete Expansion needed */, null, null, true,isConditioningNeededForIndex(null, context, true), false); |
| } |
| |
| /* |
| * Asif : This function should never get invoked as now if a CompiledJunction |
| * or GroupJunction contains a single filterable CompiledUndefined, it should |
| * directly call filterEvaluate rather than auxFilterEvalutae. Overriding this |
| * function just for ensuring that auxFilterEvaluate is not being called by |
| * mistake. |
| */ |
| @Override |
| public SelectResults auxFilterEvaluate(ExecutionContext context, |
| SelectResults intermediateResults) throws FunctionDomainException, |
| TypeMismatchException, NameResolutionException, |
| QueryInvocationTargetException { |
| Support |
| .assertionFailed(" This auxFilterEvaluate of CompiledComparison should never have got invoked."); |
| return null; |
| } |
| |
| @Override |
| public Set computeDependencies(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, NameResolutionException { |
| return context.addDependencies(this, this._value |
| .computeDependencies(context)); |
| } |
| |
| public void negate() { |
| _is_defined = !_is_defined; |
| } |
| |
| // Invariant: the receiver is dependent on the current iterator. |
| @Override |
| protected PlanInfo protGetPlanInfo(ExecutionContext context) |
| throws TypeMismatchException, AmbiguousNameException, NameResolutionException { |
| PlanInfo result = new PlanInfo(); |
| IndexInfo[] indexInfo = getIndexInfo(context); |
| if (indexInfo == null) return result; |
| Support |
| .Assert( |
| indexInfo.length == 1, |
| "For a CompiledUndefined we cannot have a join of two indexes. There should be only a single index to use"); |
| result.indexes.add(indexInfo[0]._index); |
| result.evalAsFilter = true; |
| return result; |
| } |
| |
| |
| 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; |
| //TODO:Asif : Check if the condition is such that Primary Key Index is used |
| // & its key is DEFINED |
| //, then are we returning all the values of the region ? |
| // & that if the key is UNDEFINED are we returning an empty set.? |
| IndexData indexData = QueryUtils.getAvailableIndexIfAny(this._value, |
| context, _is_defined ? TOK_NE : TOK_EQ); |
| IndexProtocol index = null; |
| IndexInfo[] newIndexInfo = null; |
| if (indexData != null) { |
| index = indexData.getIndex(); |
| } |
| if (index != null && index.isValid()) { |
| newIndexInfo = new IndexInfo[1]; |
| /* Pass the Key as null as the key is not of type CompiledValue( but of type QueryService.UNDEFINED)*/ |
| newIndexInfo[0] = new IndexInfo(null, this._value, index, |
| indexData.getMatchLevel(), indexData.getMapping(), |
| _is_defined ? TOK_NE : TOK_EQ); |
| } |
| if (newIndexInfo != null) { |
| privSetIndexInfo(newIndexInfo, context); |
| } else { |
| privSetIndexInfo(NO_INDEXES_IDENTIFIER, context); |
| } |
| return newIndexInfo; |
| } |
| |
| @Override |
| public void generateCanonicalizedExpression(StringBuffer clauseBuffer, |
| ExecutionContext context) throws AmbiguousNameException, |
| TypeMismatchException, NameResolutionException { |
| clauseBuffer.insert(0, ')'); |
| _value.generateCanonicalizedExpression(clauseBuffer, context); |
| if (_is_defined) |
| clauseBuffer.insert(0, "IS_DEFINED("); |
| else |
| clauseBuffer.insert(0, "IS_UNDEFINED("); |
| } |
| |
| //_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); |
| } |
| |
| public boolean isRangeEvaluatable() { |
| return false; |
| } |
| |
| public boolean isProjectionEvaluationAPossibility(ExecutionContext context) |
| { |
| return true; |
| } |
| //TODO:Asif: This should ideally be treated like CompiledComparison in terms evaluation of |
| // iter operands etc |
| public boolean isConditioningNeededForIndex(RuntimeIterator independentIter, |
| ExecutionContext context, boolean completeExpnsNeeded) throws AmbiguousNameException, TypeMismatchException, NameResolutionException { |
| return true; |
| } |
| |
| 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 thisOperator = this.getOperator(); |
| 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: |
| //This is possible only in case of RangeJunction |
| if(thisOperator== TOK_NE || thisOperator == TOK_NE_ALT ) { |
| //Asif: Give preference to range as I am assuming that range will fetch less data |
| // as compared to NOT EQUALs |
| isThisBetter = false; |
| } |
| 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; |
| } |
| } |