blob: bfe00ce01bb8f4c6d017705ab63c24afc13b0fb0 [file] [log] [blame]
/*=========================================================================
* 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;
}
}