You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ma...@apache.org on 2014/09/25 18:30:27 UTC

[1/2] PHOENIX-1168 Support non-correlated sub-queries in where clause having a comparison operator with no modifier or a comparison operator modified by ANY, SOME or ALL

Repository: phoenix
Updated Branches:
  refs/heads/master ea5a797eb -> a0694b77c


http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
index a74e0f1..d4c119b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
@@ -150,7 +150,7 @@ public abstract class BaseQueryPlan implements QueryPlan {
         return iterator(Collections.<SQLCloseable>emptyList());
     }
 
-    public final ResultIterator iterator(final List<SQLCloseable> dependencies) throws SQLException {
+    public final ResultIterator iterator(final List<? extends SQLCloseable> dependencies) throws SQLException {
         if (context.getScanRanges() == ScanRanges.NOTHING) {
             return ResultIterator.EMPTY_ITERATOR;
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
index 6154c88..0d09f79 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/HashJoinPlan.java
@@ -17,14 +17,14 @@
  */
 package org.apache.phoenix.execute;
 
-import static java.util.Collections.emptyMap;
 import static org.apache.phoenix.util.LogUtil.addCustomAnnotations;
 
 import java.sql.ParameterMetaData;
 import java.sql.SQLException;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
@@ -36,6 +36,7 @@ import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.cache.ServerCacheClient.ServerCache;
+import org.apache.phoenix.compile.ColumnProjector;
 import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
@@ -45,12 +46,14 @@ import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.compile.ScanRanges;
 import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.compile.WhereCompiler;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.expression.AndExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.InListExpression;
 import org.apache.phoenix.expression.LiteralExpression;
-import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
+import org.apache.phoenix.expression.RowValueConstructorExpression;
 import org.apache.phoenix.iterate.ResultIterator;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.job.JobManager.JobCallable;
@@ -66,9 +69,11 @@ import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
+import org.apache.phoenix.schema.PArrayDataType;
 import org.apache.phoenix.schema.PDataType;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.SQLCloseable;
 import org.apache.phoenix.util.SQLCloseables;
 
@@ -80,29 +85,43 @@ public class HashJoinPlan implements QueryPlan {
     private final FilterableStatement statement;
     private final BaseQueryPlan plan;
     private final HashJoinInfo joinInfo;
-    private final List<Expression>[] hashExpressions;
-    private final Expression[] keyRangeLhsExpressions;
-    private final Expression[] keyRangeRhsExpressions;
-    private final QueryPlan[] hashPlans;
-    private final TupleProjector[] clientProjectors;
-    private final boolean[] hasFilters;
+    private final SubPlan[] subPlans;
+    private final boolean recompileWhereClause;
     private final boolean forceHashJoinRangeScan;
     private final boolean forceHashJoinSkipScan;
-
-    public HashJoinPlan(FilterableStatement statement, 
-            BaseQueryPlan plan, HashJoinInfo joinInfo,
-            List<Expression>[] hashExpressions, Expression[] keyRangeLhsExpressions,
-            Expression[] keyRangeRhsExpressions, QueryPlan[] hashPlans, 
-            TupleProjector[] clientProjectors, boolean[] hasFilters) {
+    private List<SQLCloseable> dependencies;
+    private HashCacheClient hashClient;
+    private int maxServerCacheTimeToLive;
+    private AtomicLong firstJobEndTime;
+    private Map<String, String> customAnnotations;
+    private List<Expression> keyRangeExpressions;
+    
+    public static HashJoinPlan create(FilterableStatement statement, 
+            QueryPlan plan, HashJoinInfo joinInfo, SubPlan[] subPlans) {
+        if (plan instanceof BaseQueryPlan)
+            return new HashJoinPlan(statement, (BaseQueryPlan) plan, joinInfo, subPlans, joinInfo == null);
+        
+        assert (plan instanceof HashJoinPlan);
+        HashJoinPlan hashJoinPlan = (HashJoinPlan) plan;
+        assert hashJoinPlan.joinInfo == null;
+        SubPlan[] mergedSubPlans = new SubPlan[hashJoinPlan.subPlans.length + subPlans.length];
+        int i = 0;
+        for (SubPlan subPlan : hashJoinPlan.subPlans) {
+            mergedSubPlans[i++] = subPlan;
+        }
+        for (SubPlan subPlan : subPlans) {
+            mergedSubPlans[i++] = subPlan;
+        }
+        return new HashJoinPlan(statement, hashJoinPlan.plan, joinInfo, mergedSubPlans, true);
+    }
+    
+    private HashJoinPlan(FilterableStatement statement, 
+            BaseQueryPlan plan, HashJoinInfo joinInfo, SubPlan[] subPlans, boolean recompileWhereClause) {
         this.statement = statement;
         this.plan = plan;
         this.joinInfo = joinInfo;
-        this.hashExpressions = hashExpressions;
-        this.keyRangeLhsExpressions = keyRangeLhsExpressions;
-        this.keyRangeRhsExpressions = keyRangeRhsExpressions;
-        this.hashPlans = hashPlans;
-        this.clientProjectors = clientProjectors;
-        this.hasFilters = hasFilters;
+        this.subPlans = subPlans;
+        this.recompileWhereClause = recompileWhereClause;
         this.forceHashJoinRangeScan = plan.getStatement().getHint().hasHint(Hint.RANGE_SCAN_HASH_JOIN);
         this.forceHashJoinSkipScan = plan.getStatement().getHint().hasHint(Hint.SKIP_SCAN_HASH_JOIN);
     }
@@ -124,47 +143,27 @@ public class HashJoinPlan implements QueryPlan {
 
     @Override
     public ResultIterator iterator() throws SQLException {
-        ImmutableBytesPtr[] joinIds = joinInfo.getJoinIds();
-        assert (joinIds.length == hashExpressions.length && joinIds.length == hashPlans.length);
-
-        final HashCacheClient hashClient = new HashCacheClient(plan.getContext().getConnection());
-        Scan scan = plan.getContext().getScan();
-        final ScanRanges ranges = plan.getContext().getScanRanges();
-
-        int count = joinIds.length;
-        final PhoenixConnection connection = getContext().getConnection();
-        ConnectionQueryServices services = getContext().getConnection().getQueryServices();
+        int count = subPlans.length;
+        PhoenixConnection connection = getContext().getConnection();
+        ConnectionQueryServices services = connection.getQueryServices();
         ExecutorService executor = services.getExecutor();
-        List<Future<ServerCache>> futures = new ArrayList<Future<ServerCache>>(count);
-        List<SQLCloseable> dependencies = new ArrayList<SQLCloseable>(count);
-        List<Expression> keyRangeExpressions = new ArrayList<Expression>();
-        @SuppressWarnings("unchecked")
-        final List<ImmutableBytesWritable>[] keyRangeRhsValues = new List[count];  
-        final int maxServerCacheTimeToLive = services.getProps().getInt(QueryServices.MAX_SERVER_CACHE_TIME_TO_LIVE_MS_ATTRIB, QueryServicesOptions.DEFAULT_MAX_SERVER_CACHE_TIME_TO_LIVE_MS);
-        final AtomicLong firstJobEndTime = new AtomicLong(0);
-        SQLException firstException = null;
+        List<Future<Object>> futures = Lists.<Future<Object>>newArrayListWithExpectedSize(count);
+        dependencies = Lists.newArrayList();
+        if (joinInfo != null) {
+            hashClient = new HashCacheClient(plan.getContext().getConnection());
+            maxServerCacheTimeToLive = services.getProps().getInt(QueryServices.MAX_SERVER_CACHE_TIME_TO_LIVE_MS_ATTRIB, QueryServicesOptions.DEFAULT_MAX_SERVER_CACHE_TIME_TO_LIVE_MS);
+            firstJobEndTime = new AtomicLong(0);
+            customAnnotations = connection.getCustomTracingAnnotations();
+            keyRangeExpressions = new CopyOnWriteArrayList<Expression>();
+        }
+        
         for (int i = 0; i < count; i++) {
             final int index = i;
-            if (keyRangeRhsExpressions[index] != null) {
-                keyRangeRhsValues[index] = new ArrayList<ImmutableBytesWritable>();
-            }
-            futures.add(executor.submit(new JobCallable<ServerCache>() {
+            futures.add(executor.submit(new JobCallable<Object>() {
 
                 @Override
-                public ServerCache call() throws Exception {
-                    QueryPlan hashPlan = hashPlans[index];
-                    ServerCache cache = hashClient.addHashCache(ranges, hashPlan.iterator(), 
-                            clientProjectors[index], hashPlan.getEstimatedSize(), hashExpressions[index], plan.getTableRef(), keyRangeRhsExpressions[index], keyRangeRhsValues[index]);
-                    long endTime = System.currentTimeMillis();
-                    boolean isSet = firstJobEndTime.compareAndSet(0, endTime);
-                    if (!isSet && (endTime - firstJobEndTime.get()) > maxServerCacheTimeToLive) {
-                    	Map<String, String> customAnnotations = emptyMap();
-                    	if (connection != null) {
-                    		customAnnotations = connection.getCustomTracingAnnotations();
-                    	}
-                        LOG.warn(addCustomAnnotations("Hash plan [" + index + "] execution seems too slow. Earlier hash cache(s) might have expired on servers.", customAnnotations));
-                    }
-                    return cache;
+                public Object call() throws Exception {
+                    return subPlans[index].execute(HashJoinPlan.this);
                 }
 
                 @Override
@@ -173,21 +172,19 @@ public class HashJoinPlan implements QueryPlan {
                 }
             }));
         }
+        
+        SQLException firstException = null;
         for (int i = 0; i < count; i++) {
             try {
-                ServerCache cache = futures.get(i).get();
-                joinIds[i].set(cache.getId());
-                dependencies.add(cache);
-                if (keyRangeRhsExpressions[i] != null) {
-                    keyRangeExpressions.add(createKeyRangeExpression(keyRangeLhsExpressions[i], keyRangeRhsExpressions[i], keyRangeRhsValues[i], plan.getContext().getTempPtr(), hasFilters[i]));
-                }
+                Object result = futures.get(i).get();
+                subPlans[i].postProcess(result, this);
             } catch (InterruptedException e) {
                 if (firstException == null) {
-                    firstException = new SQLException("Hash plan [" + i + "] execution interrupted.", e);
+                    firstException = new SQLException("Sub plan [" + i + "] execution interrupted.", e);
                 }
             } catch (ExecutionException e) {
                 if (firstException == null) {
-                    firstException = new SQLException("Encountered exception in hash plan [" + i + "] execution.", 
+                    firstException = new SQLException("Encountered exception in sub plan [" + i + "] execution.", 
                             e.getCause());
                 }
             }
@@ -196,16 +193,26 @@ public class HashJoinPlan implements QueryPlan {
             SQLCloseables.closeAllQuietly(dependencies);
             throw firstException;
         }
-
-        HashJoinInfo.serializeHashJoinIntoScan(scan, joinInfo);
-        if (!keyRangeExpressions.isEmpty()) {
+        
+        boolean hasKeyRangeExpressions = keyRangeExpressions != null && !keyRangeExpressions.isEmpty();
+        if (recompileWhereClause || hasKeyRangeExpressions) {
             StatementContext context = plan.getContext();
             PTable table = context.getCurrentTable().getTable();
             ParseNode viewWhere = table.getViewStatement() == null ? null : new SQLParser(table.getViewStatement()).parseQuery().getWhere();
             context.setResolver(FromCompiler.getResolverForQuery((SelectStatement) (plan.getStatement()), plan.getContext().getConnection()));
-            WhereCompiler.compile(plan.getContext(), plan.getStatement(), viewWhere, keyRangeExpressions, true);
+            if (recompileWhereClause) {
+                WhereCompiler.compile(plan.getContext(), plan.getStatement(), viewWhere);                
+            }
+            if (hasKeyRangeExpressions) {
+                WhereCompiler.compile(plan.getContext(), plan.getStatement(), viewWhere, keyRangeExpressions, true);
+            }
         }
 
+        if (joinInfo != null) {
+            Scan scan = plan.getContext().getScan();
+            HashJoinInfo.serializeHashJoinIntoScan(scan, joinInfo);
+        }
+        
         return plan.iterator(dependencies);
     }
 
@@ -260,40 +267,20 @@ public class HashJoinPlan implements QueryPlan {
 
     @Override
     public ExplainPlan getExplainPlan() throws SQLException {
-        List<String> mainQuerySteps = plan.getExplainPlan().getPlanSteps();
-        List<String> planSteps = Lists.newArrayList(mainQuerySteps);
-        int count = hashPlans.length;
-        planSteps.add("    PARALLEL EQUI-JOIN " + count + " HASH TABLES:");
+        List<String> planSteps = Lists.newArrayList(plan.getExplainPlan().getPlanSteps());
+        int count = subPlans.length;
+        planSteps.add("    PARALLEL EQUI/SEMI/ANTI-JOIN " + count + " TABLES:");
         for (int i = 0; i < count; i++) {
-            boolean earlyEvaluation = joinInfo.earlyEvaluation()[i];
-            boolean skipMerge = joinInfo.getSchemas()[i].getFieldCount() == 0;
-            planSteps.add("    BUILD HASH TABLE " + i + (earlyEvaluation ? "" : "(DELAYED EVALUATION)") + (skipMerge ? " (SKIP MERGE)" : ""));
-            List<String> steps = hashPlans[i].getExplainPlan().getPlanSteps();
-            for (String step : steps) {
-                planSteps.add("        " + step);
-            }
+            planSteps.addAll(subPlans[i].getPreSteps(this));
         }
-        String dynamicFilters = null;
-        int filterCount = 0;
         for (int i = 0; i < count; i++) {
-            if (keyRangeLhsExpressions[i] != null) {
-                if (filterCount == 1) {
-                    dynamicFilters = "(" + dynamicFilters + ")";
-                }
-                String filter = keyRangeLhsExpressions[i].toString() 
-                        + (useInClause(hasFilters[i]) ? " IN " : " BETWEEN MIN/MAX OF ") 
-                        + "(" + keyRangeRhsExpressions[i].toString() + ")";
-                dynamicFilters = dynamicFilters == null ? filter : (dynamicFilters + " AND (" + filter + ")");
-                filterCount++;
-            }
-        }
-        if (dynamicFilters != null) {
-            planSteps.add("    DYNAMIC SERVER FILTER BY " + dynamicFilters);
+            planSteps.addAll(subPlans[i].getPostSteps(this));
         }
-        if (joinInfo.getPostJoinFilterExpression() != null) {
+        
+        if (joinInfo != null && joinInfo.getPostJoinFilterExpression() != null) {
             planSteps.add("    AFTER-JOIN SERVER FILTER BY " + joinInfo.getPostJoinFilterExpression().toString());
         }
-        if (joinInfo.getLimit() != null) {
+        if (joinInfo != null && joinInfo.getLimit() != null) {
             planSteps.add("    JOIN-SCANNER " + joinInfo.getLimit() + " ROW LIMIT");
         }
 
@@ -330,5 +317,158 @@ public class HashJoinPlan implements QueryPlan {
         return false;
     }
 
+    protected interface SubPlan {
+        public Object execute(HashJoinPlan parent) throws SQLException;
+        public void postProcess(Object result, HashJoinPlan parent) throws SQLException;
+        public List<String> getPreSteps(HashJoinPlan parent) throws SQLException;
+        public List<String> getPostSteps(HashJoinPlan parent) throws SQLException;
+    }
+    
+    public static class WhereClauseSubPlan implements SubPlan {
+        private final QueryPlan plan;
+        private final SelectStatement select;
+        private final boolean expectSingleRow;
+        
+        public WhereClauseSubPlan(QueryPlan plan, SelectStatement select, boolean expectSingleRow) {
+            this.plan = plan;
+            this.select = select;
+            this.expectSingleRow = expectSingleRow;
+        }
+
+        @Override
+        public Object execute(HashJoinPlan parent) throws SQLException {
+            List<Object> values = Lists.<Object> newArrayList();
+            ResultIterator iterator = plan.iterator();
+            RowProjector projector = plan.getProjector();
+            ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+            int columnCount = projector.getColumnCount();
+            int rowCount = 0;
+            PDataType baseType = null;
+            for (Tuple tuple = iterator.next(); tuple != null; tuple = iterator.next()) {
+                if (expectSingleRow && rowCount >= 1)
+                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS).build().buildException();
+                
+                if (columnCount == 1) {
+                    ColumnProjector columnProjector = projector.getColumnProjector(0);
+                    baseType = columnProjector.getExpression().getDataType();
+                    Object value = columnProjector.getValue(tuple, baseType, ptr);
+                    values.add(value);
+                } else {
+                    List<Expression> expressions = Lists.<Expression>newArrayListWithExpectedSize(columnCount);
+                    for (int i = 0; i < columnCount; i++) {
+                        ColumnProjector columnProjector = projector.getColumnProjector(i);
+                        PDataType type = columnProjector.getExpression().getDataType();
+                        Object value = columnProjector.getValue(tuple, type, ptr);
+                        expressions.add(LiteralExpression.newConstant(value, type));
+                    }
+                    Expression expression = new RowValueConstructorExpression(expressions, true);
+                    baseType = expression.getDataType();
+                    expression.evaluate(null, ptr);
+                    values.add(baseType.toObject(ptr));
+                }
+                rowCount++;
+            }
+            
+            Object result = expectSingleRow ? (values.isEmpty() ? null : values.get(0)) : PArrayDataType.instantiatePhoenixArray(baseType, values.toArray());
+            parent.getContext().setSubqueryResult(select, result);
+            return null;
+        }
+
+        @Override
+        public void postProcess(Object result, HashJoinPlan parent) throws SQLException {
+        }
+
+        @Override
+        public List<String> getPreSteps(HashJoinPlan parent) throws SQLException {
+            List<String> steps = Lists.newArrayList();
+            steps.add("    EXECUTE " + (expectSingleRow ? "SINGLE" : "MULTIPLE") + "-ROW SUBQUERY");
+            for (String step : plan.getExplainPlan().getPlanSteps()) {
+                steps.add("        " + step);
+            }
+            return steps;
+        }
+
+        @Override
+        public List<String> getPostSteps(HashJoinPlan parent) throws SQLException {
+            return Collections.<String>emptyList();
+        }
+    }
+    
+    public static class HashSubPlan implements SubPlan {        
+        private final int index;
+        private final QueryPlan plan;
+        private final List<Expression> hashExpressions;
+        private final Expression keyRangeLhsExpression;
+        private final Expression keyRangeRhsExpression;
+        private final TupleProjector clientProjector;
+        private final boolean hasFilters;
+        
+        public HashSubPlan(int index, QueryPlan subPlan, 
+                List<Expression> hashExpressions,
+                Expression keyRangeLhsExpression, 
+                Expression keyRangeRhsExpression, 
+                TupleProjector clientProjector, boolean hasFilters) {
+            this.index = index;
+            this.plan = subPlan;
+            this.hashExpressions = hashExpressions;
+            this.keyRangeLhsExpression = keyRangeLhsExpression;
+            this.keyRangeRhsExpression = keyRangeRhsExpression;
+            this.clientProjector = clientProjector;
+            this.hasFilters = hasFilters;
+        }
+
+        @Override
+        public Object execute(HashJoinPlan parent) throws SQLException {
+            ScanRanges ranges = parent.plan.getContext().getScanRanges();
+            List<ImmutableBytesWritable> keyRangeRhsValues = null;
+            if (keyRangeRhsExpression != null) {
+                keyRangeRhsValues = Lists.<ImmutableBytesWritable>newArrayList();
+            }
+            ServerCache cache = parent.hashClient.addHashCache(ranges, plan.iterator(), 
+                    clientProjector, plan.getEstimatedSize(), hashExpressions, parent.plan.getTableRef(), keyRangeRhsExpression, keyRangeRhsValues);
+            long endTime = System.currentTimeMillis();
+            boolean isSet = parent.firstJobEndTime.compareAndSet(0, endTime);
+            if (!isSet && (endTime - parent.firstJobEndTime.get()) > parent.maxServerCacheTimeToLive) {
+                LOG.warn(addCustomAnnotations("Hash plan [" + index + "] execution seems too slow. Earlier hash cache(s) might have expired on servers.", parent.customAnnotations));
+            }
+            if (keyRangeRhsValues != null) {
+                parent.keyRangeExpressions.add(parent.createKeyRangeExpression(keyRangeLhsExpression, keyRangeRhsExpression, keyRangeRhsValues, plan.getContext().getTempPtr(), hasFilters));
+            }
+            return cache;
+        }
+
+        @Override
+        public void postProcess(Object result, HashJoinPlan parent)
+                throws SQLException {
+            ServerCache cache = (ServerCache) result;
+            parent.joinInfo.getJoinIds()[index].set(cache.getId());
+            parent.dependencies.add(cache);
+        }
+
+        @Override
+        public List<String> getPreSteps(HashJoinPlan parent) throws SQLException {
+            List<String> steps = Lists.newArrayList();
+            boolean earlyEvaluation = parent.joinInfo.earlyEvaluation()[index];
+            boolean skipMerge = parent.joinInfo.getSchemas()[index].getFieldCount() == 0;
+            steps.add("    BUILD HASH TABLE " + index + (earlyEvaluation ? "" : "(DELAYED EVALUATION)") + (skipMerge ? " (SKIP MERGE)" : ""));
+            for (String step : plan.getExplainPlan().getPlanSteps()) {
+                steps.add("        " + step);
+            }
+            return steps;
+        }
+
+        @Override
+        public List<String> getPostSteps(HashJoinPlan parent) throws SQLException {
+            if (keyRangeLhsExpression == null)
+                return Collections.<String> emptyList();
+            
+            String step = "    DYNAMIC SERVER FILTER BY " + keyRangeLhsExpression.toString() 
+                    + (parent.useInClause(hasFilters) ? " IN " : " BETWEEN MIN/MAX OF ") 
+                    + "(" + keyRangeRhsExpression.toString() + ")";
+            return Collections.<String> singletonList(step);
+        }
+        
+    }
 }
 
+

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
index 008ae7b..8e352e6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java
@@ -29,6 +29,7 @@ import java.util.List;
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.expression.function.InlineArrayElemRefExpression;
 import org.apache.phoenix.expression.visitor.ExpressionVisitor;
 import org.apache.phoenix.schema.PDataType;
 import org.apache.phoenix.schema.SortOrder;
@@ -112,7 +113,7 @@ public class ComparisonExpression extends BaseCompoundExpression {
         PDataType lhsExprDataType = lhsExpr.getDataType();
         PDataType rhsExprDataType = rhsExpr.getDataType();
         
-        if (lhsExpr instanceof RowValueConstructorExpression || rhsExpr instanceof RowValueConstructorExpression) {
+        if ((lhsExpr instanceof RowValueConstructorExpression || rhsExpr instanceof RowValueConstructorExpression) && !(lhsExpr instanceof InlineArrayElemRefExpression) && !(rhsExpr instanceof InlineArrayElemRefExpression)) {
             if (op == CompareOp.EQUAL || op == CompareOp.NOT_EQUAL) {
                 List<Expression> andNodes = Lists.<Expression>newArrayListWithExpectedSize(Math.max(lhsExpr.getChildren().size(), rhsExpr.getChildren().size()));
                 rewriteRVCAsEqualityExpression(lhsExpr, rhsExpr, andNodes, ptr);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 4af65b9..eb1fda5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -500,6 +500,14 @@ public class ParseNodeFactory {
             throw new IllegalArgumentException("Unexpcted CompareOp of " + op);
         }
     }
+    
+    public ArrayAnyComparisonNode arrayAny(ParseNode rhs, ComparisonParseNode compareNode) {
+        return new ArrayAnyComparisonNode(rhs, compareNode);
+    }
+    
+    public ArrayAllComparisonNode arrayAll(ParseNode rhs, ComparisonParseNode compareNode) {
+        return new ArrayAllComparisonNode(rhs, compareNode);
+    }
 
     public ArrayAnyComparisonNode wrapInAny(CompareOp op, ParseNode lhs, ParseNode rhs) {
         return new ArrayAnyComparisonNode(rhs, comparison(op, lhs, elementRef(Arrays.<ParseNode>asList(rhs, literal(1)))));
@@ -598,8 +606,14 @@ public class ParseNodeFactory {
                 statement.getBindCount(), statement.isAggregate(), statement.hasSequence());
     }
 
-    public SubqueryParseNode subquery(SelectStatement select) {
-        return new SubqueryParseNode(select);
+    public SelectStatement select(SelectStatement statement, LimitNode limit) {
+        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(),
+                statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), limit,
+                statement.getBindCount(), statement.isAggregate(), statement.hasSequence());
+    }
+
+    public SubqueryParseNode subquery(SelectStatement select, boolean expectSingleRow) {
+        return new SubqueryParseNode(select, expectSingleRow);
     }
 
     public LimitNode limit(BindParseNode b) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
index 34d22d6..412fde0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
@@ -348,6 +348,17 @@ public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode> {
     }
 
     @Override
+    public ParseNode visitLeave(final InParseNode node, List<ParseNode> nodes) throws SQLException {
+        ParseNode normNode = leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.in(children.get(0), children.get(1), node.isNegate());
+            }
+        });
+        return normNode;
+    }
+    
+    @Override
     public ParseNode visitLeave(final IsNullParseNode node, List<ParseNode> nodes) throws SQLException {
         return leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
             @Override
@@ -429,6 +440,11 @@ public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode> {
     }
 
     @Override
+    public ParseNode visit(SubqueryParseNode node) throws SQLException {
+        return node;
+    }
+    
+    @Override
     public List<ParseNode> newElementList(int size) {
         nodeCount += size;
         return new ArrayList<ParseNode>(size);
@@ -546,13 +562,25 @@ public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode> {
 	}
 
 	@Override
-    public ParseNode visitLeave(ArrayAnyComparisonNode node, List<ParseNode> l) throws SQLException {
-        return node;
+    public ParseNode visitLeave(ArrayAnyComparisonNode node, final List<ParseNode> nodes) throws SQLException {
+        ParseNode normNode = leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.arrayAny(nodes.get(0), (ComparisonParseNode) nodes.get(1));
+            }
+        });
+        return normNode;
     }
 
     @Override
-    public ParseNode visitLeave(ArrayAllComparisonNode node, List<ParseNode> l) throws SQLException {
-        return node;
+    public ParseNode visitLeave(ArrayAllComparisonNode node, final List<ParseNode> nodes) throws SQLException {
+        ParseNode normNode = leaveCompoundNode(node, nodes, new CompoundNodeFactory() {
+            @Override
+            public ParseNode createNode(List<ParseNode> children) {
+                return NODE_FACTORY.arrayAll(nodes.get(0), (ComparisonParseNode) nodes.get(1));
+            }
+        });
+        return normNode;
     }
  
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
index 5308677..01925ff 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeVisitor.java
@@ -18,7 +18,6 @@
 package org.apache.phoenix.parse;
 
 import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
 import java.util.List;
 
 
@@ -82,6 +81,9 @@ public interface ParseNodeVisitor<E> {
     public boolean visitEnter(InListParseNode node) throws SQLException;
     public E visitLeave(InListParseNode node, List<E> l) throws SQLException;
     
+    public boolean visitEnter(InParseNode node) throws SQLException;
+    public E visitLeave(InParseNode node, List<E> l) throws SQLException;
+    
     public boolean visitEnter(IsNullParseNode node) throws SQLException;
     public E visitLeave(IsNullParseNode node, List<E> l) throws SQLException;
     
@@ -90,7 +92,8 @@ public interface ParseNodeVisitor<E> {
     public E visit(BindParseNode node) throws SQLException;
     public E visit(WildcardParseNode node) throws SQLException;
     public E visit(TableWildcardParseNode node) throws SQLException;
-    public E visit(FamilyWildcardParseNode node) throws SQLException;  
+    public E visit(FamilyWildcardParseNode node) throws SQLException;
+    public E visit(SubqueryParseNode node) throws SQLException;
     public E visit(ParseNode node) throws SQLException;  
     
     public boolean visitEnter(StringConcatParseNode node) throws SQLException;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatementRewriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatementRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatementRewriter.java
index 194f3bc..ab06d46 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatementRewriter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatementRewriter.java
@@ -183,4 +183,17 @@ public class SelectStatementRewriter extends ParseNodeRewriter {
     public ParseNode visitLeave(InListParseNode node, List<ParseNode> c) throws SQLException {
         return c.isEmpty() ? null : node;
     }
+    
+    @Override
+    public boolean visitEnter(InParseNode node) throws SQLException {
+        if (removeNodes.contains(node)) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override
+    public ParseNode visitLeave(InParseNode node, List<ParseNode> c) throws SQLException {
+        return c.isEmpty() ? null : node;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/StatelessTraverseAllParseNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/StatelessTraverseAllParseNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/StatelessTraverseAllParseNodeVisitor.java
index 0be5e01..5e9f727 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/StatelessTraverseAllParseNodeVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/StatelessTraverseAllParseNodeVisitor.java
@@ -91,6 +91,11 @@ public class StatelessTraverseAllParseNodeVisitor extends TraverseAllParseNodeVi
     public Void visitLeave(InListParseNode node, List<Void> l) throws SQLException {
         return null;
     }
+    
+    @Override
+    public Void visitLeave(InParseNode node, List<Void> l) throws SQLException {
+        return null;
+    }
 
     @Override
     public Void visitLeave(StringConcatParseNode node, List<Void> l) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/SubqueryParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SubqueryParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SubqueryParseNode.java
index a3bad0d..92c5284 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/SubqueryParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SubqueryParseNode.java
@@ -30,14 +30,20 @@ import java.sql.SQLException;
  */
 public class SubqueryParseNode extends TerminalParseNode {
     private final SelectStatement select;
+    private final boolean expectSingleRow;
 
-    SubqueryParseNode(SelectStatement select) {
+    SubqueryParseNode(SelectStatement select, boolean expectSingleRow) {
         this.select = select;
+        this.expectSingleRow = expectSingleRow;
     }
     
     public SelectStatement getSelectNode() {
         return select;
     }
+    
+    public boolean expectSingleRow() {
+        return expectSingleRow;
+    }
 
     @Override
     public <T> T accept(ParseNodeVisitor<T> visitor) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseAllParseNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseAllParseNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseAllParseNodeVisitor.java
index af20278..d6b444f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseAllParseNodeVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseAllParseNodeVisitor.java
@@ -75,6 +75,11 @@ public abstract class TraverseAllParseNodeVisitor<T> extends BaseParseNodeVisito
     }
     
     @Override
+    public boolean visitEnter(InParseNode node) throws SQLException {
+        return true;
+    }
+    
+    @Override
     public boolean visitEnter(IsNullParseNode node) throws SQLException {
         return true;
     }
@@ -143,6 +148,11 @@ public abstract class TraverseAllParseNodeVisitor<T> extends BaseParseNodeVisito
     public T visit(FamilyWildcardParseNode node) throws SQLException {
         return null;
     }
+
+    @Override
+    public T visit(SubqueryParseNode node) throws SQLException {
+        return null;
+    }
     
     @Override
     public boolean visitEnter(StringConcatParseNode node) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseNoParseNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseNoParseNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseNoParseNodeVisitor.java
index 4c0fbea..37be462 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseNoParseNodeVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/TraverseNoParseNodeVisitor.java
@@ -100,6 +100,16 @@ public abstract class TraverseNoParseNodeVisitor<T> extends BaseParseNodeVisitor
     }
     
     @Override
+    public boolean visitEnter(InParseNode node) throws SQLException {
+        return false;
+    }
+    
+    @Override
+    public T visitLeave(InParseNode node, List<T> l) throws SQLException {
+        return null;
+    }
+    
+    @Override
     public boolean visitEnter(IsNullParseNode node) throws SQLException {
         return false;
     }
@@ -138,6 +148,11 @@ public abstract class TraverseNoParseNodeVisitor<T> extends BaseParseNodeVisitor
     public T visit(FamilyWildcardParseNode node) throws SQLException {
         return null;
     }
+    
+    @Override
+    public T visit(SubqueryParseNode node) throws SQLException {
+        return null;
+    }
 
     @Override
     public T visitLeave(AndParseNode node, List<T> l) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
index 95c869a..a08b0e3 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
@@ -59,10 +59,10 @@ public class JoinQueryCompilerTest extends BaseConnectionlessQueryTest {
         assertEquals(
         		"CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
         		"    SERVER FILTER BY FIRST KEY ONLY\n" +
-        		"    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+        		"    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
         		"    BUILD HASH TABLE 0\n" +
         		"        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-        		"            PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+        		"            PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
         		"            BUILD HASH TABLE 0\n" +
         		"                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + "\n" +
         		"                    SERVER FILTER BY NAME LIKE 'C%'\n" +

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
index 100b4ad..c7a7c3e 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
@@ -406,33 +406,33 @@ public abstract class BaseTest {
         builder.put("SumDoubleTest","create table SumDoubleTest" +
                 "   (id varchar not null primary key, d DOUBLE, f FLOAT, ud UNSIGNED_DOUBLE, uf UNSIGNED_FLOAT, i integer, de decimal)");
         builder.put(JOIN_ORDER_TABLE_FULL_NAME, "create table " + JOIN_ORDER_TABLE_FULL_NAME +
-                "   (\"order_id\" char(15) not null primary key, " +
-                "    \"customer_id\" char(10), " +
-                "    \"item_id\" char(10), " +
+                "   (\"order_id\" varchar(15) not null primary key, " +
+                "    \"customer_id\" varchar(10), " +
+                "    \"item_id\" varchar(10), " +
                 "    price integer, " +
                 "    quantity integer, " +
                 "    date timestamp)");
         builder.put(JOIN_CUSTOMER_TABLE_FULL_NAME, "create table " + JOIN_CUSTOMER_TABLE_FULL_NAME +
-                "   (\"customer_id\" char(10) not null primary key, " +
+                "   (\"customer_id\" varchar(10) not null primary key, " +
                 "    name varchar, " +
-                "    phone char(12), " +
+                "    phone varchar(12), " +
                 "    address varchar, " +
-                "    loc_id char(5), " +
+                "    loc_id varchar(5), " +
                 "    date date)");
         builder.put(JOIN_ITEM_TABLE_FULL_NAME, "create table " + JOIN_ITEM_TABLE_FULL_NAME +
-                "   (\"item_id\" char(10) not null primary key, " +
+                "   (\"item_id\" varchar(10) not null primary key, " +
                 "    name varchar, " +
                 "    price integer, " +
                 "    discount1 integer, " +
                 "    discount2 integer, " +
-                "    \"supplier_id\" char(10), " +
+                "    \"supplier_id\" varchar(10), " +
                 "    description varchar)");
         builder.put(JOIN_SUPPLIER_TABLE_FULL_NAME, "create table " + JOIN_SUPPLIER_TABLE_FULL_NAME +
-                "   (\"supplier_id\" char(10) not null primary key, " +
+                "   (\"supplier_id\" varchar(10) not null primary key, " +
                 "    name varchar, " +
-                "    phone char(12), " +
+                "    phone varchar(12), " +
                 "    address varchar, " +
-                "    loc_id char(5))");
+                "    loc_id varchar(5))");
         tableDDLMap = builder.build();
     }
     


[2/2] git commit: PHOENIX-1168 Support non-correlated sub-queries in where clause having a comparison operator with no modifier or a comparison operator modified by ANY, SOME or ALL

Posted by ma...@apache.org.
PHOENIX-1168 Support non-correlated sub-queries in where clause having a comparison operator with no modifier or a comparison operator modified by ANY, SOME or ALL


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/a0694b77
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/a0694b77
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/a0694b77

Branch: refs/heads/master
Commit: a0694b77c1e16c90af96d3092b1f941052493244
Parents: ea5a797
Author: maryannxue <ma...@apache.org>
Authored: Thu Sep 25 12:30:03 2014 -0400
Committer: maryannxue <ma...@apache.org>
Committed: Thu Sep 25 12:30:03 2014 -0400

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/HashJoinIT.java  | 322 +++++++++++++-----
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   4 +-
 .../phoenix/compile/ExpressionCompiler.java     |  37 ++
 .../apache/phoenix/compile/JoinCompiler.java    |   7 +
 .../apache/phoenix/compile/QueryCompiler.java   |  57 ++--
 .../phoenix/compile/StatementContext.java       |  15 +
 .../phoenix/compile/StatementNormalizer.java    |  13 +
 .../apache/phoenix/compile/WhereCompiler.java   |  46 ++-
 .../phoenix/exception/SQLExceptionCode.java     |   1 +
 .../apache/phoenix/execute/BaseQueryPlan.java   |   2 +-
 .../apache/phoenix/execute/HashJoinPlan.java    | 334 +++++++++++++------
 .../expression/ComparisonExpression.java        |   3 +-
 .../apache/phoenix/parse/ParseNodeFactory.java  |  18 +-
 .../apache/phoenix/parse/ParseNodeRewriter.java |  36 +-
 .../apache/phoenix/parse/ParseNodeVisitor.java  |   7 +-
 .../phoenix/parse/SelectStatementRewriter.java  |  13 +
 .../StatelessTraverseAllParseNodeVisitor.java   |   5 +
 .../apache/phoenix/parse/SubqueryParseNode.java |   8 +-
 .../parse/TraverseAllParseNodeVisitor.java      |  10 +
 .../parse/TraverseNoParseNodeVisitor.java       |  15 +
 .../phoenix/compile/JoinQueryCompilerTest.java  |   4 +-
 .../java/org/apache/phoenix/query/BaseTest.java |  22 +-
 22 files changed, 738 insertions(+), 241 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
index dcd96d3..ceba009 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
@@ -119,7 +119,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME,
                 /* 
@@ -132,7 +132,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -147,7 +147,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /* 
@@ -160,7 +160,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -174,7 +174,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -184,7 +184,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" + 
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -197,7 +197,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    SERVER FILTER BY (NAME >= 'T1' AND NAME <= 'T5')\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY (NAME >= 'S1' AND NAME <= 'S5')",
@@ -211,7 +211,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    SERVER FILTER BY (NAME = 'T1' OR NAME = 'T5')\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY (NAME = 'S1' OR NAME = 'S5')",
@@ -222,7 +222,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY QUANTITY < 5000\n" +
@@ -236,7 +236,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY i1.item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -250,7 +250,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    SERVER SORTED BY [I1.NAME, I2.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    DYNAMIC SERVER FILTER BY item_id BETWEEN MIN/MAX OF (I2.supplier_id)",
@@ -263,7 +263,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY order_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + "\n" +
                 "    BUILD HASH TABLE 1\n" +
@@ -279,10 +279,10 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    SERVER SORTED BY [O.order_id]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + "\n" +
                 "    DYNAMIC SERVER FILTER BY item_id BETWEEN MIN/MAX OF (O.item_id)",
@@ -302,15 +302,15 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.customer_id, I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "    DYNAMIC SERVER FILTER BY customer_id IN (O.customer_id)",
@@ -325,7 +325,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME,
                 /* 
@@ -340,7 +340,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -356,7 +356,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -373,7 +373,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -400,15 +400,15 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -422,7 +422,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY PageFilter 4\n" +
                 "    SERVER 4 ROW LIMIT\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
@@ -437,7 +437,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
                 "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
@@ -452,7 +452,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT",
@@ -464,7 +464,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -477,7 +477,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -499,7 +499,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.0:NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -513,7 +513,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.:item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -528,7 +528,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /* 
@@ -541,7 +541,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -555,7 +555,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -565,7 +565,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -578,7 +578,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']",
                 /*
@@ -590,7 +590,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *         AND (supp.name = 'S1' OR supp.name = 'S5')
                  */
                 "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_item ['T1'] - ['T5']\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + JOIN_SCHEMA + ".idx_supplier ['S1'] - ['S5']",
                 /*
@@ -600,7 +600,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY QUANTITY < 5000\n" +
@@ -613,7 +613,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY i1.item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -628,7 +628,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [I1.0:NAME, I2.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item",
                 /*
@@ -640,7 +640,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY order_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer\n" +
                 "    BUILD HASH TABLE 1\n" +
@@ -658,10 +658,10 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.order_id]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer",
                 /*
@@ -680,15 +680,15 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.customer_id, I.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "    DYNAMIC SERVER FILTER BY customer_id IN (O.customer_id)",
@@ -703,7 +703,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -719,7 +719,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY",
@@ -735,7 +735,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -752,7 +752,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC, I.IID]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -779,15 +779,15 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -801,7 +801,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY PageFilter 4\n" +
                 "    SERVER 4 ROW LIMIT\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
                 "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
@@ -816,7 +816,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_SCHEMA + ".idx_item\n" +
                 "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
@@ -831,7 +831,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT",
@@ -843,7 +843,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -856,7 +856,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -878,7 +878,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.0:NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -893,7 +893,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.:item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -909,7 +909,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /* 
@@ -923,7 +923,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.0:NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -937,7 +937,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
                 /*
@@ -947,7 +947,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -961,7 +961,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768,'T1'] - [-32768,'T5']\n" +
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_SUPPLIER_TABLE_DISPLAY_NAME +" [-32768,'S1'] - [-32768,'S5']\n" +
                 "        CLIENT MERGE SORT",
@@ -975,7 +975,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768,'T1'] - [-32768,'T5']\n" +
                 "CLIENT MERGE SORT\n" + 
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_SUPPLIER_TABLE_DISPLAY_NAME +" [-32768,'S1'] - [-32768,'S5']\n" + 
                 "        CLIENT MERGE SORT",
@@ -987,7 +987,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "CLIENT MERGE SORT\n" + 
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY QUANTITY < 5000\n" +
@@ -1002,7 +1002,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY i1.item_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n"  +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -1018,7 +1018,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [I1.0:NAME, I2.0:NAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -1032,7 +1032,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  *     ORDER BY order_id
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -1052,10 +1052,10 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.order_id]\n"+
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_CUSTOMER_TABLE_DISPLAY_NAME+" [-32768]\n"+
                 "                CLIENT MERGE SORT\n" +
@@ -1076,16 +1076,16 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.customer_id, I.0:NAME]\n"+
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
                 "                CLIENT MERGE SORT\n" +
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "    DYNAMIC SERVER FILTER BY customer_id IN (O.customer_id)",
@@ -1100,7 +1100,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [I.NAME]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME+" [-32768]\n"+
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -1117,7 +1117,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
                 "CLIENT MERGE SORT\n" +
                 "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "            SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -1134,7 +1134,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n"+
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -1151,7 +1151,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY FIRST KEY ONLY\n" +
                 "    SERVER SORTED BY [O.Q DESC, I.IID]\n"+
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
@@ -1178,16 +1178,16 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
                 "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "            SERVER FILTER BY order_id != '000000000000003'\n" +
-                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "            BUILD HASH TABLE 0\n" +
                 "                CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +  MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "                    SERVER FILTER BY NAME != 'T3'\n" +
                 "                CLIENT MERGE SORT\n" +      
-                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "                    BUILD HASH TABLE 0\n" +
                 "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
                 /*
@@ -1201,7 +1201,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "    SERVER FILTER BY PageFilter 4\n" +
                 "    SERVER 4 ROW LIMIT\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "        CLIENT MERGE SORT\n" +      
@@ -1217,7 +1217,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
                 "CLIENT 4 ROW LIMIT\n" +
-                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 2 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -1233,7 +1233,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT",
@@ -1245,7 +1245,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -1258,7 +1258,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                  */
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "CLIENT MERGE SORT\n" +
-                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    PARALLEL EQUI/SEMI/ANTI-JOIN 1 TABLES:\n" +
                 "    BUILD HASH TABLE 0\n" +
                 "        CLIENT PARALLEL 4-WAY FULL SCAN OVER TEMP_TABLE_COMPOSITE_PK\n" +
                 "        CLIENT MERGE SORT\n" +
@@ -3883,5 +3883,153 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
         }
     }
 
+    @Test
+    public void testNonCorrelatedSubquery() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            String query = "SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + JOIN_ORDER_TABLE_FULL_NAME + ") ORDER BY \"item_id\"";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" >= ALL (SELECT \"item_id\" FROM " + JOIN_ORDER_TABLE_FULL_NAME + ") ORDER BY \"item_id\"";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "invalid001");
+            assertEquals(rs.getString(2), "INVALID-1");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" < ANY (SELECT \"item_id\" FROM " + JOIN_ORDER_TABLE_FULL_NAME + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" < (SELECT max(\"item_id\") FROM " + JOIN_ORDER_TABLE_FULL_NAME + ")";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000001");
+            assertEquals(rs.getString(2), "T1");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000002");
+            assertEquals(rs.getString(2), "T2");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000005");
+            assertEquals(rs.getString(2), "T5");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testNonCorrelatedSubqueryWithRowConstructor() throws Exception {
+        String tempItemJoinTable = "TEMP_ITEM_JOIN_TABLE";
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE " + tempItemJoinTable 
+                    + "   (item_id varchar(10) NOT NULL, " 
+                    + "    item_name varchar NOT NULL, "
+                    + "    co_item_id varchar(10), "
+                    + "    co_item_name varchar "
+                    + "   CONSTRAINT pk PRIMARY KEY (item_id, item_name)) " 
+                    + "   SALT_BUCKETS=4");
+            
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into " + tempItemJoinTable + "(item_id, item_name, co_item_id, co_item_name) " + "values (?, ?, ?, ?)");
+            upsertStmt.setString(1, "0000000001");
+            upsertStmt.setString(2, "T1");
+            upsertStmt.setString(3, "0000000002");
+            upsertStmt.setString(4, "T3");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "0000000004");
+            upsertStmt.setString(2, "T4");
+            upsertStmt.setString(3, "0000000003");
+            upsertStmt.setString(4, "T3");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "0000000003");
+            upsertStmt.setString(2, "T4");
+            upsertStmt.setString(3, "0000000005");
+            upsertStmt.setString(4, "T5");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "0000000006");
+            upsertStmt.setString(2, "T6");
+            upsertStmt.setString(3, "0000000001");
+            upsertStmt.setString(4, "T1");
+            upsertStmt.execute();
+            conn.commit();
+            
+            String query = "SELECT * FROM " + tempItemJoinTable + " WHERE (item_id, item_name) != ALL (SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + ")";
+            PreparedStatement statement = conn.prepareStatement(query);
+            ResultSet rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000003");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000005");
+            assertEquals(rs.getString(4), "T5");
+
+            assertFalse(rs.next());
+            
+            query = "SELECT * FROM " + tempItemJoinTable + " WHERE (item_id, item_name) IN (SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" NOT IN (SELECT \"item_id\" FROM " + JOIN_ORDER_TABLE_FULL_NAME + "))"
+                    + " OR (co_item_id, co_item_name) IN (SELECT \"item_id\", name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " WHERE \"item_id\" IN (SELECT \"item_id\" FROM " + JOIN_ORDER_TABLE_FULL_NAME + "))";
+            statement = conn.prepareStatement(query);
+            rs = statement.executeQuery();
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000004");
+            assertEquals(rs.getString(2), "T4");
+            assertEquals(rs.getString(3), "0000000003");
+            assertEquals(rs.getString(4), "T3");
+            assertTrue (rs.next());
+            assertEquals(rs.getString(1), "0000000006");
+            assertEquals(rs.getString(2), "T6");
+            assertEquals(rs.getString(3), "0000000001");
+            assertEquals(rs.getString(4), "T1");
+
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
 }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 8432f6e..893c3de 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -551,7 +551,7 @@ select_expression returns [SelectStatement ret]
     ;
     
 subquery_expression returns [ParseNode ret]
-    :  s=select_expression {$ret = factory.subquery(s);}
+    :  s=select_expression {$ret = factory.subquery(s, false);}
     ;
     
 // Parse a full select expression structure.
@@ -713,7 +713,7 @@ comparison_op returns [CompareOp ret]
 	;
 	
 boolean_expression returns [ParseNode ret]
-    :   l=value_expression ((op=comparison_op (r=value_expression | ((all=ALL | any=ANY) LPAREN r=value_expression RPAREN)) {$ret = all != null ? factory.wrapInAll(op, l, r) : any != null ? factory.wrapInAny(op, l, r) : factory.comparison(op,l,r); } )
+    :   l=value_expression ((op=comparison_op (r=value_expression | (LPAREN r=subquery_expression RPAREN) | ((all=ALL | any=ANY) LPAREN r=value_expression RPAREN)  | ((all=ALL | any=ANY) LPAREN r=subquery_expression RPAREN)) {$ret = all != null ? factory.wrapInAll(op, l, r) : any != null ? factory.wrapInAny(op, l, r) : factory.comparison(op,l,r); } )
                   |  (IS n=NOT? NULL {$ret = factory.isNull(l,n!=null); } )
                   |  ( n=NOT? ((LIKE r=value_expression {$ret = factory.like(l,r,n!=null,LikeType.CASE_SENSITIVE); } )
                       |        (ILIKE r=value_expression {$ret = factory.like(l,r,n!=null,LikeType.CASE_INSENSITIVE); } )

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
index cb3bc74..1511539 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
@@ -82,6 +82,7 @@ import org.apache.phoenix.parse.FunctionParseNode;
 import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunctionInfo;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
 import org.apache.phoenix.parse.InListParseNode;
+import org.apache.phoenix.parse.InParseNode;
 import org.apache.phoenix.parse.IsNullParseNode;
 import org.apache.phoenix.parse.LikeParseNode;
 import org.apache.phoenix.parse.LiteralParseNode;
@@ -93,6 +94,7 @@ import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.RowValueConstructorParseNode;
 import org.apache.phoenix.parse.SequenceValueParseNode;
 import org.apache.phoenix.parse.StringConcatParseNode;
+import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.parse.SubtractParseNode;
 import org.apache.phoenix.parse.UnsupportedAllParseNodeVisitor;
 import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
@@ -107,6 +109,7 @@ import org.apache.phoenix.schema.PDatum;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTable.IndexType;
 import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.PhoenixArray;
 import org.apache.phoenix.schema.RowKeyValueAccessor;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TableRef;
@@ -115,6 +118,8 @@ import org.apache.phoenix.util.ExpressionUtil;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.SchemaUtil;
 
+import com.google.common.collect.Lists;
+
 
 public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expression> {
     private boolean isAggregate;
@@ -1239,4 +1244,36 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
     public boolean visitEnter(ArrayConstructorNode node) throws SQLException {
         return true;
     }
+
+    @Override
+    public boolean visitEnter(InParseNode node) throws SQLException {
+        return true;
+    }
+
+    @Override
+    public Expression visitLeave(InParseNode node, List<Expression> l)
+            throws SQLException {
+        Expression firstChild = l.get(0);
+        LiteralExpression secondChild = (LiteralExpression) l.get(1);
+        ImmutableBytesWritable ptr = context.getTempPtr();
+        ParseNode firstChildNode = node.getChildren().get(0);
+        
+        if (firstChildNode instanceof BindParseNode) {
+            context.getBindManager().addParamMetaData((BindParseNode)firstChildNode, firstChild);
+        }
+        
+        List<Expression> children = Lists.<Expression> newArrayList(firstChild);
+        PhoenixArray array = (PhoenixArray) secondChild.getValue();
+        PDataType type = PDataType.fromTypeId(array.getBaseType());
+        for (Object obj : (Object[]) array.getArray()) {
+            children.add(LiteralExpression.newConstant(obj, type));
+        }
+        return wrapGroupByExpression(InListExpression.create(children, node.isNegate(), ptr));
+    }
+
+    @Override
+    public Expression visit(SubqueryParseNode node) throws SQLException {
+        Object result = context.getSubqueryResult(node.getSelectNode());
+        return LiteralExpression.newConstant(result);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index 81d169c..41f58e0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -56,6 +56,7 @@ import org.apache.phoenix.parse.FunctionParseNode;
 import org.apache.phoenix.parse.HintNode;
 import org.apache.phoenix.parse.HintNode.Hint;
 import org.apache.phoenix.parse.InListParseNode;
+import org.apache.phoenix.parse.InParseNode;
 import org.apache.phoenix.parse.IsNullParseNode;
 import org.apache.phoenix.parse.JoinTableNode;
 import org.apache.phoenix.parse.JoinTableNode.JoinType;
@@ -833,6 +834,12 @@ public class JoinCompiler {
                 List<Void> l) throws SQLException {
             return leaveBooleanNode(node, l);
         }
+
+        @Override
+        public Void visitLeave(InParseNode node,
+                List<Void> l) throws SQLException {
+            return leaveBooleanNode(node, l);
+        }
         
         @Override
         public Void visitLeave(IsNullParseNode node, List<Void> l) 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
index 6f9d688..1512d82 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
@@ -21,6 +21,7 @@ import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.util.Pair;
@@ -33,8 +34,9 @@ import org.apache.phoenix.compile.JoinCompiler.ProjectedPTableWrapper;
 import org.apache.phoenix.compile.JoinCompiler.Table;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.execute.AggregatePlan;
-import org.apache.phoenix.execute.BaseQueryPlan;
 import org.apache.phoenix.execute.HashJoinPlan;
+import org.apache.phoenix.execute.HashJoinPlan.HashSubPlan;
+import org.apache.phoenix.execute.HashJoinPlan.WhereClauseSubPlan;
 import org.apache.phoenix.execute.ScanPlan;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.RowValueConstructorExpression;
@@ -50,6 +52,7 @@ import org.apache.phoenix.parse.JoinTableNode.JoinType;
 import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.SQLParser;
 import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
@@ -181,15 +184,10 @@ public class QueryCompiler {
             int count = joinSpecs.size();
             ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[count];
             List<Expression>[] joinExpressions = new List[count];
-            List<Expression>[] hashExpressions = new List[count];
-            Expression[] keyRangeLhsExpressions = new Expression[count];
-            Expression[] keyRangeRhsExpressions = new Expression[count];
-            boolean[] hasFilters = new boolean[count];
             JoinType[] joinTypes = new JoinType[count];
             PTable[] tables = new PTable[count];
             int[] fieldPositions = new int[count];
-            QueryPlan[] joinPlans = new QueryPlan[count];
-            TupleProjector[] clientProjectors = new TupleProjector[count];
+            HashSubPlan[] subPlans = new HashSubPlan[count];
             fieldPositions[0] = projectedTable.getTable().getColumns().size() - projectedTable.getTable().getPKColumns().size();
             boolean forceProjection = table.isSubselect();
             boolean needsProject = forceProjection || asSubquery;
@@ -197,9 +195,9 @@ public class QueryCompiler {
                 JoinSpec joinSpec = joinSpecs.get(i);
                 Scan subScan = ScanUtil.newScan(originalScan);
                 StatementContext subContext = new StatementContext(statement, context.getResolver(), subScan, new SequenceManager(statement));
-                joinPlans[i] = compileJoinQuery(subContext, binds, joinSpec.getJoinTable(), true);
+                QueryPlan joinPlan = compileJoinQuery(subContext, binds, joinSpec.getJoinTable(), true);
                 ColumnResolver resolver = subContext.getResolver();
-                clientProjectors[i] = subContext.getClientTupleProjector();
+                TupleProjector clientProjector = subContext.getClientTupleProjector();
                 boolean hasPostReference = joinSpec.getJoinTable().hasPostReference();
                 if (hasPostReference) {
                     PTableWrapper subProjTable = ((JoinedTableColumnResolver) (resolver)).getPTableWrapper();
@@ -216,29 +214,30 @@ public class QueryCompiler {
                 joinIds[i] = new ImmutableBytesPtr(emptyByteArray); // place-holder
                 Pair<List<Expression>, List<Expression>> joinConditions = joinSpec.compileJoinConditions(context, leftResolver, resolver);
                 joinExpressions[i] = joinConditions.getFirst();
-                hashExpressions[i] = joinConditions.getSecond();
-                Pair<Expression, Expression> keyRangeExpressions = getKeyExpressionCombinations(context, tableRef, joinSpec.getType(), joinExpressions[i], hashExpressions[i]);
-                keyRangeLhsExpressions[i] = keyRangeExpressions.getFirst();
-                keyRangeRhsExpressions[i] = keyRangeExpressions.getSecond();
-                hasFilters[i] = joinSpec.getJoinTable().hasFilters();
+                List<Expression> hashExpressions = joinConditions.getSecond();
+                Pair<Expression, Expression> keyRangeExpressions = getKeyExpressionCombinations(context, tableRef, joinSpec.getType(), joinExpressions[i], hashExpressions);
+                Expression keyRangeLhsExpression = keyRangeExpressions.getFirst();
+                Expression keyRangeRhsExpression = keyRangeExpressions.getSecond();
+                boolean hasFilters = joinSpec.getJoinTable().hasFilters();
                 joinTypes[i] = joinSpec.getType();
                 if (i < count - 1) {
                     fieldPositions[i + 1] = fieldPositions[i] + (tables[i] == null ? 0 : (tables[i].getColumns().size() - tables[i].getPKColumns().size()));
                 }
+                subPlans[i] = new HashSubPlan(i, joinPlan, hashExpressions, keyRangeLhsExpression, keyRangeRhsExpression, clientProjector, hasFilters);
             }
             if (needsProject) {
                 TupleProjector.serializeProjectorIntoScan(context.getScan(), initialProjectedTable.createTupleProjector());
             }
             context.setCurrentTable(tableRef);
             context.setResolver(needsProject ? projectedTable.createColumnResolver() : joinTable.getOriginalResolver());
-            BaseQueryPlan plan = compileSingleQuery(context, query, binds, asSubquery, joinTable.isAllLeftJoin());
+            QueryPlan plan = compileSingleQuery(context, query, binds, asSubquery, joinTable.isAllLeftJoin());
             Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
             Integer limit = null;
             if (query.getLimit() != null && !query.isAggregate() && !query.isDistinct() && query.getOrderBy().isEmpty()) {
                 limit = LimitCompiler.compile(context, query);
             }
             HashJoinInfo joinInfo = new HashJoinInfo(projectedTable.getTable(), joinIds, joinExpressions, joinTypes, starJoinVector, tables, fieldPositions, postJoinFilterExpression, limit, forceProjection);
-            return new HashJoinPlan(joinTable.getStatement(), plan, joinInfo, hashExpressions, keyRangeLhsExpressions, keyRangeRhsExpressions, joinPlans, clientProjectors, hasFilters);
+            return HashJoinPlan.create(joinTable.getStatement(), plan, joinInfo, subPlans);
         }
         
         JoinSpec lastJoinSpec = joinSpecs.get(joinSpecs.size() - 1);
@@ -286,7 +285,7 @@ public class QueryCompiler {
             TupleProjector.serializeProjectorIntoScan(context.getScan(), rhsProjTable.createTupleProjector());
             context.setCurrentTable(rhsTableRef);
             context.setResolver(projectedTable.createColumnResolver());
-            BaseQueryPlan rhsPlan = compileSingleQuery(context, rhs, binds, asSubquery, type == JoinType.Right);
+            QueryPlan rhsPlan = compileSingleQuery(context, rhs, binds, asSubquery, type == JoinType.Right);
             Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
             Integer limit = null;
             if (rhs.getLimit() != null && !rhs.isAggregate() && !rhs.isDistinct() && rhs.getOrderBy().isEmpty()) {
@@ -294,7 +293,7 @@ public class QueryCompiler {
             }
             HashJoinInfo joinInfo = new HashJoinInfo(projectedTable.getTable(), joinIds, new List[] {joinExpressions}, new JoinType[] {type == JoinType.Inner ? type : JoinType.Left}, new boolean[] {true}, new PTable[] {lhsProjTable.getTable()}, new int[] {fieldPosition}, postJoinFilterExpression, limit, forceProjection);
             Pair<Expression, Expression> keyRangeExpressions = getKeyExpressionCombinations(context, rhsTableRef, type, joinExpressions, hashExpressions);
-            return new HashJoinPlan(joinTable.getStatement(), rhsPlan, joinInfo, new List[] {hashExpressions}, new Expression[] {keyRangeExpressions.getFirst()}, new Expression[] {keyRangeExpressions.getSecond()}, new QueryPlan[] {lhsPlan}, new TupleProjector[] {clientProjector}, new boolean[] {lhsJoin.hasFilters()});
+            return HashJoinPlan.create(joinTable.getStatement(), rhsPlan, joinInfo, new HashSubPlan[] {new HashSubPlan(0, lhsPlan, hashExpressions, keyRangeExpressions.getFirst(), keyRangeExpressions.getSecond(), clientProjector, lhsJoin.hasFilters())});
         }
         
         // Do not support queries like "A right join B left join C" with hash-joins.
@@ -336,7 +335,7 @@ public class QueryCompiler {
         return statement.getConnection().getQueryServices().getOptimizer().optimize(statement, plan);
     }
     
-    protected BaseQueryPlan compileSingleQuery(StatementContext context, SelectStatement select, List<Object> binds, boolean asSubquery, boolean allowPageFilter) throws SQLException{
+    protected QueryPlan compileSingleQuery(StatementContext context, SelectStatement select, List<Object> binds, boolean asSubquery, boolean allowPageFilter) throws SQLException{
         PhoenixConnection connection = statement.getConnection();
         ColumnResolver resolver = context.getResolver();
         TableRef tableRef = context.getCurrentTable();
@@ -360,7 +359,7 @@ public class QueryCompiler {
         // Don't pass groupBy when building where clause expression, because we do not want to wrap these
         // expressions as group by key expressions since they're pre, not post filtered.
         context.setResolver(FromCompiler.getResolverForQuery(select, connection));
-        WhereCompiler.compile(context, select, viewWhere);
+        Set<SubqueryParseNode> subqueries = WhereCompiler.compile(context, select, viewWhere);
         context.setResolver(resolver); // recover resolver
         OrderBy orderBy = OrderByCompiler.compile(context, select, groupBy, limit); 
         RowProjector projector = ProjectionCompiler.compile(context, select, groupBy, asSubquery ? Collections.<PDatum>emptyList() : targetColumns);
@@ -375,11 +374,21 @@ public class QueryCompiler {
             }
         }
         ParallelIteratorFactory parallelIteratorFactory = asSubquery ? null : this.parallelIteratorFactory;
-        if (select.isAggregate() || select.isDistinct()) {
-            return new AggregatePlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, groupBy, having);
-        } else {
-            return new ScanPlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, allowPageFilter);
+        QueryPlan plan = select.isAggregate() || select.isDistinct() ? 
+                  new AggregatePlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, groupBy, having)
+                : new ScanPlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, allowPageFilter);
+        if (!subqueries.isEmpty()) {
+            int count = subqueries.size();
+            WhereClauseSubPlan[] subPlans = new WhereClauseSubPlan[count];
+            int i = 0;
+            for (SubqueryParseNode subqueryNode : subqueries) {
+                SelectStatement stmt = subqueryNode.getSelectNode();
+                subPlans[i++] = new WhereClauseSubPlan(compileSubquery(stmt), stmt, subqueryNode.expectSingleRow());
+            }
+            plan = HashJoinPlan.create(select, plan, null, subPlans);
         }
+        
+        return plan;
     }
 }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
index 1c75527..56e63ae 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
@@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.join.TupleProjector;
+import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
@@ -78,6 +79,8 @@ public class StatementContext {
     private TupleProjector clientTupleProjector;
     private TimeRange scanTimeRange = null;
     
+    private Map<SelectStatement, Object> subqueryResults;
+    
     public StatementContext(PhoenixStatement statement) {
         this(statement, FromCompiler.EMPTY_TABLE_RESOLVER, new Scan(), new SequenceManager(statement));
     }
@@ -99,6 +102,7 @@ public class StatementContext {
         this.currentTable = resolver != null && !resolver.getTables().isEmpty() ? resolver.getTables().get(0) : null;
         this.whereConditionColumns = new ArrayList<Pair<byte[],byte[]>>();
         this.dataColumns = this.currentTable == null ? Collections.<PColumn, Integer>emptyMap() : Maps.<PColumn, Integer>newLinkedHashMap();
+        this.subqueryResults = Maps.<SelectStatement, Object>newHashMap();
     }
 
     /**
@@ -289,5 +293,16 @@ public class StatementContext {
     public TimeRange getScanTimeRange() {
     	return this.scanTimeRange;
     }
+    
+    public boolean isSubqueryResultAvailable(SelectStatement select) {
+        return subqueryResults.containsKey(select);
+    }
 
+    public Object getSubqueryResult(SelectStatement select) {
+        return subqueryResults.get(select);
+    }
+    
+    public void setSubqueryResult(SelectStatement select, Object result) {
+        subqueryResults.put(select, result);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
index e29a20a..88aa81c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
@@ -34,6 +34,7 @@ import org.apache.phoenix.parse.NamedTableNode;
 import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.ParseNodeRewriter;
 import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.parse.TableName;
 import org.apache.phoenix.parse.TableNode;
 import org.apache.phoenix.parse.TableNodeVisitor;
@@ -150,6 +151,18 @@ public class StatementNormalizer extends ParseNodeRewriter {
              nodes = normNodes;
              node = NODE_FACTORY.comparison(node.getInvertFilterOp(), nodes.get(0), nodes.get(1));
          }
+         // Add limit 2 to direct comparison with sub-query without ANY/SOME/ALL modifiers.
+         ParseNode rhs = nodes.get(1);
+         if (rhs instanceof SubqueryParseNode) {
+             SelectStatement subquery = ((SubqueryParseNode) rhs).getSelectNode();
+             subquery = NODE_FACTORY.select(subquery, NODE_FACTORY.limit(NODE_FACTORY.literal(2)));
+             rhs = NODE_FACTORY.subquery(subquery, true);
+             List<ParseNode> normNodes = Lists.newArrayListWithExpectedSize(2);
+             normNodes.add(nodes.get(0));
+             normNodes.add(rhs);
+             nodes = normNodes;
+             node = NODE_FACTORY.comparison(node.getFilterOp(), nodes.get(0), nodes.get(1));
+         }
          return super.visitLeave(node, nodes);
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
index 2e72f43..27a4490 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
@@ -43,10 +43,12 @@ import org.apache.phoenix.parse.FilterableStatement;
 import org.apache.phoenix.parse.HintNode.Hint;
 import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.ParseNodeFactory;
+import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.StatelessTraverseAllParseNodeVisitor;
+import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
 import org.apache.phoenix.schema.ColumnRef;
-import org.apache.phoenix.schema.LocalIndexDataColumnRef;
 import org.apache.phoenix.schema.PDataType;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTable.IndexType;
@@ -75,7 +77,7 @@ public class WhereCompiler {
     private WhereCompiler() {
     }
 
-    public static Expression compile(StatementContext context, FilterableStatement statement) throws SQLException {
+    public static Set<SubqueryParseNode> compile(StatementContext context, FilterableStatement statement) throws SQLException {
         return compile(context, statement, null);
     }
     
@@ -89,7 +91,7 @@ public class WhereCompiler {
      * @throws ColumnNotFoundException if column name could not be resolved
      * @throws AmbiguousColumnException if an unaliased column name is ambiguous across multiple tables
      */
-    public static Expression compile(StatementContext context, FilterableStatement statement, ParseNode viewWhere) throws SQLException {
+    public static Set<SubqueryParseNode> compile(StatementContext context, FilterableStatement statement, ParseNode viewWhere) throws SQLException {
         return compile(context, statement, viewWhere, Collections.<Expression>emptyList(), false);
     }
     
@@ -103,10 +105,21 @@ public class WhereCompiler {
      * @throws ColumnNotFoundException if column name could not be resolved
      * @throws AmbiguousColumnException if an unaliased column name is ambiguous across multiple tables
      */    
-    public static Expression compile(StatementContext context, FilterableStatement statement, ParseNode viewWhere, List<Expression> dynamicFilters, boolean hashJoinOptimization) throws SQLException {
+    public static Set<SubqueryParseNode> compile(StatementContext context, FilterableStatement statement, ParseNode viewWhere, List<Expression> dynamicFilters, boolean hashJoinOptimization) throws SQLException {
+        ParseNode where = statement.getWhere();
+        Set<SubqueryParseNode> subqueryNodes = Sets.<SubqueryParseNode> newHashSet();
+        SubqueryParseNodeVisitor subqueryVisitor = new SubqueryParseNodeVisitor(context, subqueryNodes);
+        if (where != null) {
+            where.accept(subqueryVisitor);
+        }
+        if (viewWhere != null) {
+            viewWhere.accept(subqueryVisitor);
+        }
+        if (!subqueryNodes.isEmpty())
+            return subqueryNodes;
+        
         Set<Expression> extractedNodes = Sets.<Expression>newHashSet();
         WhereExpressionCompiler whereCompiler = new WhereExpressionCompiler(context);
-        ParseNode where = statement.getWhere();
         Expression expression = where == null ? LiteralExpression.newConstant(true,PDataType.BOOLEAN,true) : where.accept(whereCompiler);
         if (whereCompiler.isAggregate()) {
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.AGGREGATE_IN_WHERE).build().buildException();
@@ -128,7 +141,7 @@ public class WhereCompiler {
         expression = WhereOptimizer.pushKeyExpressionsToScan(context, statement, expression, extractedNodes);
         setScanFilter(context, statement, expression, whereCompiler.disambiguateWithFamily, hashJoinOptimization);
 
-        return expression;
+        return subqueryNodes;
     }
     
     private static class WhereExpressionCompiler extends ExpressionCompiler {
@@ -212,7 +225,6 @@ public class WhereCompiler {
      */
     private static void setScanFilter(StatementContext context, FilterableStatement statement, Expression whereClause, boolean disambiguateWithFamily, boolean hashJoinOptimization) {
         Scan scan = context.getScan();
-        assert scan.getFilter() == null;
 
         if (LiteralExpression.isFalse(whereClause)) {
             context.setScanRanges(ScanRanges.NOTHING);
@@ -259,4 +271,24 @@ public class WhereCompiler {
             ScanUtil.andFilterAtBeginning(scan, scanRanges.getSkipScanFilter());
         }
     }
+    
+    private static class SubqueryParseNodeVisitor extends StatelessTraverseAllParseNodeVisitor {
+        private final StatementContext context;
+        private final Set<SubqueryParseNode> subqueryNodes;
+        
+        SubqueryParseNodeVisitor(StatementContext context, Set<SubqueryParseNode> subqueryNodes) {
+            this.context = context;
+            this.subqueryNodes = subqueryNodes;
+        }
+        
+        @Override
+        public Void visit(SubqueryParseNode node) throws SQLException {
+            SelectStatement select = node.getSelectNode();
+            if (!context.isSubqueryResultAvailable(select)) {
+                this.subqueryNodes.add(node);
+            }
+            return null;                
+        }
+        
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0694b77/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index 3d5e302..bbb09dc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -78,6 +78,7 @@ public enum SQLExceptionCode {
     SERVER_ARITHMETIC_ERROR(212, "22012", "Arithmetic error on server."),
     VALUE_OUTSIDE_RANGE(213,"22003","Value outside range."),
     VALUE_IN_LIST_NOT_CONSTANT(214, "22008", "Values in IN must evaluate to a constant."),
+    SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS(215, "22015", "Single-row subquery returns more than one row."),
     
     /**
      * Constraint Violation (errorcode 03, sqlstate 23)