Merged asterix_stabilization r585:r605.

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_fix_agg@606 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyEqRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyEqRule.java
index c770e9f..1a2cd2a 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyEqRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyEqRule.java
@@ -140,6 +140,8 @@
             FunctionIdentifier simFunctionIdentifier = FuzzyUtils.getFunctionIdentifier(simFuncName);
             ScalarFunctionCallExpression similarityExp = new ScalarFunctionCallExpression(
                     FunctionUtils.getFunctionInfo(simFunctionIdentifier), similarityArgs);
+            // Add annotations from the original fuzzy-eq function.
+            similarityExp.getAnnotations().putAll(funcExp.getAnnotations());
             ArrayList<Mutable<ILogicalExpression>> cmpArgs = new ArrayList<Mutable<ILogicalExpression>>();
             cmpArgs.add(new MutableObject<ILogicalExpression>(similarityExp));
             IAObject simThreshold = FuzzyUtils.getSimThreshold(aqlMetadata, simFuncName);
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyJoinRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyJoinRule.java
index d5131a9..e700971 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyJoinRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/FuzzyJoinRule.java
@@ -33,6 +33,7 @@
 import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
 import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -46,6 +47,11 @@
 
 public class FuzzyJoinRule implements IAlgebraicRewriteRule {
 
+	private static HashSet<FunctionIdentifier> simFuncs = new HashSet<FunctionIdentifier>();
+    static {
+        simFuncs.add(AsterixBuiltinFunctions.SIMILARITY_JACCARD_CHECK);
+    }
+	
     private static final String AQLPLUS = ""
             //
             // -- - Stage 3 - --
@@ -133,20 +139,30 @@
             return false;
         }
 
-        // find fuzzy join condition
+        // Find GET_ITEM function.
         AbstractBinaryJoinOperator joinOp = (AbstractBinaryJoinOperator) op;
         Mutable<ILogicalExpression> expRef = joinOp.getCondition();
-        Mutable<ILogicalExpression> fuzzyExpRef = getSimilarityExpression(expRef);
-        if (fuzzyExpRef == null) {
+        Mutable<ILogicalExpression> getItemExprRef = getSimilarityExpression(expRef);
+        if (getItemExprRef == null) {
             return false;
         }
-
-        AbstractFunctionCallExpression funcExp = (AbstractFunctionCallExpression) fuzzyExpRef.getValue();
+        // Check if the GET_ITEM function is on one of the supported similarity-check functions.
+        AbstractFunctionCallExpression getItemFuncExpr = (AbstractFunctionCallExpression) getItemExprRef.getValue();
+        Mutable<ILogicalExpression> argRef = getItemFuncExpr.getArguments().get(0);
+    	AbstractFunctionCallExpression simFuncExpr = (AbstractFunctionCallExpression) argRef.getValue();
+        if (!simFuncs.contains(simFuncExpr.getFunctionIdentifier())) {
+        	return false;
+        }
+        // Skip this rule based on annotations.
+        if (simFuncExpr.getAnnotations().containsKey(IndexedNLJoinExpressionAnnotation.INSTANCE)) {
+        	return false;
+        }
+        
         List<Mutable<ILogicalOperator>> inputOps = joinOp.getInputs();
         ILogicalOperator leftInputOp = inputOps.get(0).getValue();
         ILogicalOperator rightInputOp = inputOps.get(1).getValue();
 
-        List<Mutable<ILogicalExpression>> inputExps = funcExp.getArguments();
+        List<Mutable<ILogicalExpression>> inputExps = simFuncExpr.getArguments();
 
         ILogicalExpression inputExp0 = inputExps.get(0).getValue();
         ILogicalExpression inputExp1 = inputExps.get(1).getValue();
@@ -315,9 +331,9 @@
         ILogicalOperator outputOp = plan.getRoots().get(0).getValue();
 
         SelectOperator extraSelect = null;
-        if (fuzzyExpRef != expRef) {
+        if (getItemExprRef != expRef) {
             // more than one join condition
-            fuzzyExpRef.setValue(ConstantExpression.TRUE);
+            getItemExprRef.setValue(ConstantExpression.TRUE);
             switch (joinOp.getJoinKind()) {
                 case INNER: {
                     extraSelect = new SelectOperator(expRef);
@@ -343,19 +359,19 @@
         return true;
     }
 
-    /*
-     * look for FUZZY_EQ function call
+    /**
+     * Look for GET_ITEM function call.
      */
     private Mutable<ILogicalExpression> getSimilarityExpression(Mutable<ILogicalExpression> expRef) {
         ILogicalExpression exp = expRef.getValue();
         if (exp.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
-            AbstractFunctionCallExpression funcExp = (AbstractFunctionCallExpression) exp;
-            if (funcExp.getFunctionIdentifier().equals(AsterixBuiltinFunctions.FUZZY_EQ)) {
-                return expRef;
-            } else if (funcExp.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.AND)
-                    || funcExp.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.OR)) {
+            AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) exp;
+            if (funcExpr.getFunctionIdentifier().equals(AsterixBuiltinFunctions.GET_ITEM)) {
+            	return expRef;
+            }
+            if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.AND)) {
                 for (int i = 0; i < 2; i++) {
-                    Mutable<ILogicalExpression> expRefRet = getSimilarityExpression(funcExp.getArguments().get(i));
+                    Mutable<ILogicalExpression> expRefRet = getSimilarityExpression(funcExpr.getArguments().get(i));
                     if (expRefRet != null) {
                         return expRefRet;
                     }
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SimilarityCheckRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SimilarityCheckRule.java
index 60e5a3e..5726ce3 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SimilarityCheckRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/SimilarityCheckRule.java
@@ -7,8 +7,10 @@
 import org.apache.commons.lang3.mutable.MutableObject;
 
 import edu.uci.ics.asterix.aql.util.FunctionUtils;
+import edu.uci.ics.asterix.om.base.ADouble;
 import edu.uci.ics.asterix.om.base.AFloat;
 import edu.uci.ics.asterix.om.base.AInt32;
+import edu.uci.ics.asterix.om.base.IAObject;
 import edu.uci.ics.asterix.om.constants.AsterixConstantValue;
 import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
 import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
@@ -127,7 +129,7 @@
     	Mutable<ILogicalExpression> simFuncExprRef = null;
     	ScalarFunctionCallExpression simCheckFuncExpr = null;
     	AssignOperator matchingAssign = null;
-    	for (int i = 0; i < assigns.size(); i++) {
+     	for (int i = 0; i < assigns.size(); i++) {
     		AssignOperator assign = assigns.get(i);
     		for (int j = 0; j < assign.getVariables().size(); j++) {
     			// Check if variables match.
@@ -216,12 +218,21 @@
         ScalarFunctionCallExpression simCheckFuncExpr = null; 
         // Look for jaccard function call, and GE or GT.
         if (funcExpr.getFunctionIdentifier() == AsterixBuiltinFunctions.SIMILARITY_JACCARD) {
-            AFloat aFloat = (AFloat) constVal.getObject();
-            AFloat jaccThresh;
-            if (normFuncIdent == AlgebricksBuiltinFunctions.GE) {
-                jaccThresh = aFloat;
+            IAObject jaccThresh;
+            if (normFuncIdent == AlgebricksBuiltinFunctions.GE) {                
+                if (constVal.getObject() instanceof AFloat) {
+                    jaccThresh = constVal.getObject();                    
+                } else {
+                    jaccThresh = new AFloat((float)((ADouble) constVal.getObject()).getDoubleValue());
+                }
             } else if (normFuncIdent == AlgebricksBuiltinFunctions.GT) {
-                float f = aFloat.getFloatValue() + Float.MIN_VALUE;
+                float threshVal = 0.0f;            
+                if (constVal.getObject() instanceof AFloat) {
+                    threshVal = ((AFloat) constVal.getObject()).getFloatValue();
+                } else {
+                    threshVal = (float)((ADouble) constVal.getObject()).getDoubleValue();                    
+                }
+                float f = threshVal + Float.MIN_VALUE;
                 if (f > 1.0f) f = 1.0f;
                 jaccThresh = new AFloat(f);
             } else {
@@ -253,6 +264,10 @@
             simCheckFuncExpr = new ScalarFunctionCallExpression(
                     FunctionUtils.getFunctionInfo(AsterixBuiltinFunctions.EDIT_DISTANCE_CHECK), similarityArgs);
         }
+        // Preserve all annotations.
+        if (simCheckFuncExpr != null) {
+        	simCheckFuncExpr.getAnnotations().putAll(funcExpr.getAnnotations());
+        }
         return simCheckFuncExpr;
     }
     
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/am/BTreeAccessMethod.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/am/BTreeAccessMethod.java
index 414dca3..6baf1b5 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/am/BTreeAccessMethod.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/am/BTreeAccessMethod.java
@@ -1,6 +1,7 @@
 package edu.uci.ics.asterix.optimizer.rules.am;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -116,6 +117,9 @@
         // If we can't figure out how to integrate a certain funcExpr into the current predicate, we just bail by setting this flag.
         boolean couldntFigureOut = false;
         boolean doneWithExprs = false;
+        // TODO: For now don't consider prefix searches.
+        BitSet setLowKeys = new BitSet(numSecondaryKeys);
+        BitSet setHighKeys = new BitSet(numSecondaryKeys);
         // Go through the func exprs listed as optimizable by the chosen index, 
         // and formulate a range predicate on the secondary-index keys.
         for (Integer exprIndex : exprList) {
@@ -132,11 +136,17 @@
                         lowKeyLimits[keyPos] = highKeyLimits[keyPos] = limit;
                         lowKeyInclusive[keyPos] = highKeyInclusive[keyPos] = true;
                         lowKeyConstants[keyPos] = highKeyConstants[keyPos] = optFuncExpr.getConstantVal(0);
+                        setLowKeys.set(keyPos);
+                        setHighKeys.set(keyPos);
                     } else {
                         couldntFigureOut = true;
                     }
-                    // Mmmm, we would need an inference system here.
-                    doneWithExprs = true;
+                    // TODO: For now don't consider prefix searches.
+                    // If high and low keys are set, we exit for now.
+                    if (setLowKeys.cardinality() == numSecondaryKeys
+                            && setHighKeys.cardinality() == numSecondaryKeys) {
+                    	doneWithExprs = true;
+                    }             
                     break;
                 }
                 case HIGH_EXCLUSIVE: {
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/translator/AqlExpressionToPlanTranslator.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/translator/AqlExpressionToPlanTranslator.java
index 35fb3ae..e5712df 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/translator/AqlExpressionToPlanTranslator.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/translator/AqlExpressionToPlanTranslator.java
@@ -115,6 +115,7 @@
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation.BroadcastSide;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
@@ -577,6 +578,12 @@
 			f = new ScalarFunctionCallExpression(
 					FunctionUtils.getFunctionInfo(fi), args);
 		}
+		// Put hints into function call expr.
+		if (fcall.hasHints()) {
+			for (IExpressionAnnotation hint : fcall.getHints()) {
+				f.getAnnotations().put(hint, hint);
+			}
+		}
 		AssignOperator op = new AssignOperator(v,
 				new MutableObject<ILogicalExpression>(f));
 		if (topOp != null) {
@@ -828,6 +835,14 @@
 			}
 		}
 
+		// Add hints as annotations.
+		if (op.hasHints() && currExpr instanceof AbstractFunctionCallExpression) {
+			AbstractFunctionCallExpression currFuncExpr = (AbstractFunctionCallExpression) currExpr;
+			for (IExpressionAnnotation hint : op.getHints()) {
+				currFuncExpr.getAnnotations().put(hint, hint);
+			}
+		}
+		
 		LogicalVariable assignedVar = context.newVar();
 		AssignOperator a = new AssignOperator(assignedVar,
 				new MutableObject<ILogicalExpression>(currExpr));
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/api/common/APIFramework.java b/asterix-app/src/main/java/edu/uci/ics/asterix/api/common/APIFramework.java
index eae4c46..57b2ff0 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/api/common/APIFramework.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/api/common/APIFramework.java
@@ -469,36 +469,38 @@
         ICompiler compiler = compilerFactory.createCompiler(planAndMetadata.getPlan(),
                 planAndMetadata.getMetadataProvider(), t.getVarCounter());
         if (pc.isOptimize()) {
-            compiler.optimize();
-            if (true) {
-                StringBuilder buffer = new StringBuilder();
-                PlanPrettyPrinter.printPhysicalOps(planAndMetadata.getPlan(), buffer, 0);
-                out.print(buffer);
-            } else if (pc.isPrintOptimizedLogicalPlanParam()) {
-                switch (pdf) {
-                    case HTML: {
-                        out.println("<H1>Optimized logical plan:</H1>");
-                        out.println("<PRE>");
-                        break;
-                    }
-                    case TEXT: {
-                        out.println("----------Optimized plan ");
-                        break;
-                    }
-                }
-
-                if (q != null) {
-                    StringBuilder buffer = new StringBuilder();
-                    PlanPrettyPrinter.printPlan(planAndMetadata.getPlan(), buffer, pvisitor, 0);
-                    out.print(buffer);
-                }
-                switch (pdf) {
-                    case HTML: {
-                        out.println("</PRE>");
-                        break;
-                    }
-                }
-            }
+        	compiler.optimize();        	
+        	if (pc.isPrintOptimizedLogicalPlanParam()) {
+        		if (pc.isPrintPhysicalOpsOnly()) {
+        			// For Optimizer tests.
+        			StringBuilder buffer = new StringBuilder();
+        			PlanPrettyPrinter.printPhysicalOps(planAndMetadata.getPlan(), buffer, 0);
+        			out.print(buffer);
+        		} else {
+        			switch (pdf) {
+        			case HTML: {
+        				out.println("<H1>Optimized logical plan:</H1>");
+        				out.println("<PRE>");
+        				break;
+        			}
+        			case TEXT: {
+        				out.println("----------Optimized plan ");
+        				break;
+        			}
+        			}
+        			if (q != null) {
+        				StringBuilder buffer = new StringBuilder();
+        				PlanPrettyPrinter.printPlan(planAndMetadata.getPlan(), buffer, pvisitor, 0);
+        				out.print(buffer);
+        			}
+        			switch (pdf) {
+        			case HTML: {
+        				out.println("</PRE>");
+        				break;
+        			}
+        			}
+        		}
+        	}
         }
 
         if (!pc.isGenerateJobSpec()) {
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
index 15b959e..cf2b54c 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
@@ -98,16 +98,17 @@
                 out.println("</PRE>");
             }
         } catch (ParseException pe) {
-            String message = pe.getMessage();
-            message = message.replace("<", "&lt");
-            message = message.replace(">", "&gt");
-            int pos = message.indexOf("line");
-            int columnPos = message.indexOf(",", pos + 1 + "line".length());
-            int lineNo = Integer.parseInt(message.substring(pos + "line".length() + 1, columnPos));
-            String line = query.split("\n")[lineNo - 1];
-            out.println("SyntaxError:" + message);
-            out.println("==> " + line);
-
+        	String message = pe.getMessage();
+        	message = message.replace("<", "&lt");
+        	message = message.replace(">", "&gt");
+        	out.println("SyntaxError:" + message);
+        	int pos = message.indexOf("line");
+        	if (pos > 0) {
+        		int columnPos = message.indexOf(",", pos + 1 + "line".length());
+        		int lineNo = Integer.parseInt(message.substring(pos + "line".length() + 1, columnPos));
+        		String line = query.split("\n")[lineNo - 1];            
+        		out.println("==> " + line);
+        	}
         } catch (Exception e) {
             out.println(e.getMessage());
        }
diff --git a/asterix-app/src/test/java/edu/uci/ics/asterix/test/optimizer/OptimizerTest.java b/asterix-app/src/test/java/edu/uci/ics/asterix/test/optimizer/OptimizerTest.java
index d60971b..b31fa92 100644
--- a/asterix-app/src/test/java/edu/uci/ics/asterix/test/optimizer/OptimizerTest.java
+++ b/asterix-app/src/test/java/edu/uci/ics/asterix/test/optimizer/OptimizerTest.java
@@ -126,7 +126,7 @@
             AsterixJavaClient asterix = new AsterixJavaClient(
                     AsterixHyracksIntegrationUtil.getHyracksClientConnection(), query, plan);
             try {
-                asterix.compile(true, false, false, false, true, true, false);
+                asterix.compile(true, false, false, true, true, true, false);
             } catch (AsterixException e) {
                 plan.close();
                 query.close();
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_01.aql
index f6f2f84..ffb9b8a 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_01.aql
@@ -44,5 +44,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_02.aql
index 1951e6f..f94fb85 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_02.aql
@@ -44,5 +44,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_03.aql
index d791b85..669c336 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-fuzzyeq-jaccard_03.aql
@@ -38,5 +38,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_01.aql
index 5f6f59b..63ff85c 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_01.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_02.aql
index 0754282..b708069 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_02.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_03.aql
index 4dbc4d5..21932ef 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard-check_03.aql
@@ -35,5 +35,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_01.aql
index ddf386e..c519be9 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_01.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_02.aql
index 50c3db6..7cd595a 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_02.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_03.aql
index 50729ba..c7ad4c4 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/olist-jaccard_03.aql
@@ -35,5 +35,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_01.aql
index 1fa479d..74d043e 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_01.aql
@@ -44,5 +44,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_02.aql
index e5b532f..da1ebe9 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_02.aql
@@ -44,5 +44,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_03.aql
index a881c89..283d80a 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-fuzzyeq-jaccard_03.aql
@@ -38,5 +38,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where $a.interests ~= $b.interests and $a.cid < $b.cid
+where $a.interests /*+ indexnl */ ~= $b.interests and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_01.aql
index 5d95894..7f5e92d 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_01.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_02.aql
index 561e15f..d367bb8 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_02.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_03.aql
index 87d78e5..0bdd09e 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard-check_03.aql
@@ -35,5 +35,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard-check($a.interests, $b.interests, 0.7f)[0] and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_01.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_01.aql
index 864ede7..8188e07 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_01.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_01.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_02.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_02.aql
index 4d9f89e..ba64230 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_02.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_02.aql
@@ -41,5 +41,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_03.aql b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_03.aql
index 5eae45b..5007579 100644
--- a/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_03.aql
+++ b/asterix-app/src/test/resources/optimizerts/queries/inverted-index-join/ulist-jaccard_03.aql
@@ -35,5 +35,5 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers')
-where similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.7f and $a.cid < $b.cid
 return {"arec": $a, "brec": $b }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/orders-composite-index-search.aql b/asterix-app/src/test/resources/optimizerts/queries/orders-composite-index-search.aql
new file mode 100644
index 0000000..d82be3c
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/orders-composite-index-search.aql
@@ -0,0 +1,30 @@
+drop dataverse index_search if exists;
+create dataverse index_search;
+use dataverse index_search;
+
+create type OrderType as closed {
+  o_orderkey: int32, 
+  o_custkey: int32, 
+  o_orderstatus: string, 
+  o_totalprice: double, 
+  o_orderdate: string, 
+  o_orderpriority: string,
+  o_clerk: string, 
+  o_shippriority: int32, 
+  o_comment: string
+}
+
+create dataset Orders(OrderType) partitioned by key o_orderkey;
+
+create index idx_Custkey_Orderstatus on Orders(o_custkey, o_orderstatus);
+
+write output to nc1:"/tmp/index_search.adm";
+
+for $o in dataset('Orders')
+where
+  $o.o_custkey = 40 and $o.o_orderstatus = "P"
+return {  
+  "o_orderkey": $o.o_orderkey,
+  "o_custkey": $o.o_custkey,
+  "o_orderstatus": $o.o_orderstatus
+}
diff --git a/asterix-app/src/test/resources/optimizerts/results/orders-composite-index-search.plan b/asterix-app/src/test/resources/optimizerts/results/orders-composite-index-search.plan
new file mode 100644
index 0000000..2b19a64
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/orders-composite-index-search.plan
@@ -0,0 +1,16 @@
+-- SINK_WRITE  |PARTITIONED|
+  -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- ASSIGN  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- BTREE_SEARCH  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                -- STABLE_SORT [$$23(ASC)]  |LOCAL|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- BTREE_SEARCH  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterix-app/src/test/resources/runtimets/ignore.txt b/asterix-app/src/test/resources/runtimets/ignore.txt
index 4467bc2..172d920 100644
--- a/asterix-app/src/test/resources/runtimets/ignore.txt
+++ b/asterix-app/src/test/resources/runtimets/ignore.txt
@@ -2,13 +2,7 @@
 scan/spatial_types_02.aql
 scan/temp_types_02.aql
 fuzzyjoin/dblp-splits-3_1.aql
-fuzzyjoin/dblp-csx-aqlplus_1.aql
-fuzzyjoin/dblp-csx-aqlplus_2.aql
-fuzzyjoin/dblp-csx-aqlplus_3.aql
 fuzzyjoin/events-users-aqlplus_1.aql
-fuzzyjoin/dblp-aqlplus_1.aql
-fuzzyjoin/dblp-csx-dblp-aqlplus_1.aql
-fuzzyjoin/user-vis-int-vis-user-lot-aqlplus_1.aql
 subset-collection/04.aql
 custord/freq-clerk.aql
 custord/denorm-cust-order_01.aql
diff --git a/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-olist-jaccard.aql b/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-olist-jaccard.aql
index 91fcd80..ddccd2e 100644
--- a/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-olist-jaccard.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-olist-jaccard.aql
@@ -41,7 +41,7 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.9f 
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.9f 
       and $a.cid < $b.cid
 order by $a.cid, $b.cid
 return { "a": $a.interests, "b": $b.interests }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-ulist-jaccard.aql b/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-ulist-jaccard.aql
index 2b2d52c..e0dba06 100644
--- a/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-ulist-jaccard.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/index-join/inverted-index-ulist-jaccard.aql
@@ -41,7 +41,7 @@
 
 for $a in dataset('Customers')
 for $b in dataset('Customers2')
-where similarity-jaccard($a.interests, $b.interests) >= 0.9f 
+where /*+ indexnl */ similarity-jaccard($a.interests, $b.interests) >= 0.9f 
       and $a.cid < $b.cid
 order by $a.cid, $b.cid
 return { "a": $a.interests, "b": $b.interests }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/concat1.aql b/asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
similarity index 81%
rename from asterix-app/src/test/resources/runtimets/queries/string/concat1.aql
rename to asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
index a6ce63b..2a0b1ab 100644
--- a/asterix-app/src/test/resources/runtimets/queries/string/concat1.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/string/concat_01.aql
@@ -2,7 +2,7 @@
 create dataverse test;
 use dataverse test;
 
-write output to nc1:"rttest/string_concat1.adm";
+write output to nc1:"rttest/string_concat_01.adm";
 
 let $x :=  ["aa", "25991", "bb", "31526"]
 let $c := string-concat($x)
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql b/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql
new file mode 100644
index 0000000..c716fcb
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/string/concat_02.aql
@@ -0,0 +1,15 @@
+/*
+ * Description    : Test concat-string function with nulls in the list which is passed as an argument.
+ * Success        : Yes
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+write output to nc1:"rttest/string_concat_02.adm";
+
+let $a := string-concat([null])
+let $b := string-concat([null, "foo"])
+let $c := string-concat(["foo", null])
+return {"a": $a, "b": $b, "c": $c}
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/length.aql b/asterix-app/src/test/resources/runtimets/queries/string/length_01.aql
similarity index 81%
rename from asterix-app/src/test/resources/runtimets/queries/string/length.aql
rename to asterix-app/src/test/resources/runtimets/queries/string/length_01.aql
index d78d986..8186f6b 100644
--- a/asterix-app/src/test/resources/runtimets/queries/string/length.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/string/length_01.aql
@@ -2,7 +2,7 @@
 create dataverse test;
 use dataverse test;
 
-write output to nc1:"rttest/string_length.adm";
+write output to nc1:"rttest/string_length_01.adm";
 
 let $c1 := string-length("hellow")
 let $c2 := string-length("")
diff --git a/asterix-app/src/test/resources/runtimets/queries/string/length_02.aql b/asterix-app/src/test/resources/runtimets/queries/string/length_02.aql
new file mode 100644
index 0000000..3cc33e7
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/string/length_02.aql
@@ -0,0 +1,8 @@
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+write output to nc1:"rttest/string_length_02.adm";
+
+for $x in ["ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "ninety"]
+return string-length($x)
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/string/concat1.adm b/asterix-app/src/test/resources/runtimets/results/string/concat_01.adm
similarity index 100%
rename from asterix-app/src/test/resources/runtimets/results/string/concat1.adm
rename to asterix-app/src/test/resources/runtimets/results/string/concat_01.adm
diff --git a/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm b/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm
new file mode 100644
index 0000000..ba4be9f
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/string/concat_02.adm
@@ -0,0 +1 @@
+{ "a": null, "b": null, "c": null }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/string/length.adm b/asterix-app/src/test/resources/runtimets/results/string/length_01.adm
similarity index 100%
rename from asterix-app/src/test/resources/runtimets/results/string/length.adm
rename to asterix-app/src/test/resources/runtimets/results/string/length_01.adm
diff --git a/asterix-app/src/test/resources/runtimets/results/string/length_02.adm b/asterix-app/src/test/resources/runtimets/results/string/length_02.adm
new file mode 100644
index 0000000..930236d
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/string/length_02.adm
@@ -0,0 +1,8 @@
+3
+6
+6
+5
+5
+5
+7
+6
diff --git a/asterix-app/src/test/resources/runtimets/results/writers/serialized_01.adm b/asterix-app/src/test/resources/runtimets/results/writers/serialized_01.adm
index f2aee15..c503a33 100644
--- a/asterix-app/src/test/resources/runtimets/results/writers/serialized_01.adm
+++ b/asterix-app/src/test/resources/runtimets/results/writers/serialized_01.adm
Binary files differ
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/base/AbstractExpression.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/base/AbstractExpression.java
new file mode 100644
index 0000000..e83a1ed
--- /dev/null
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/base/AbstractExpression.java
@@ -0,0 +1,25 @@
+package edu.uci.ics.asterix.aql.base;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
+
+public abstract class AbstractExpression implements Expression {
+	protected List<IExpressionAnnotation> hints;
+	
+	public void addHint(IExpressionAnnotation hint) {
+    	if (hints == null) {
+    		hints = new ArrayList<IExpressionAnnotation>();
+    	}
+    	hints.add(hint);
+    }
+    
+    public boolean hasHints() {
+    	return hints != null;
+    }
+    
+    public List<IExpressionAnnotation> getHints() {
+    	return hints;
+    }
+}
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CallExpr.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CallExpr.java
index cda7e69..d1f83b0 100644
--- a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CallExpr.java
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CallExpr.java
@@ -2,17 +2,18 @@
 
 import java.util.List;
 
+import edu.uci.ics.asterix.aql.base.AbstractExpression;
 import edu.uci.ics.asterix.aql.base.Expression;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlExpressionVisitor;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlVisitorWithVoidReturn;
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 import edu.uci.ics.asterix.om.functions.AsterixFunction;
 
-public class CallExpr implements Expression {
+public class CallExpr extends AbstractExpression {
     private AsterixFunction ident;
     private List<Expression> exprList;
-    private boolean isBuiltin;
-
+    private boolean isBuiltin;    
+    
     public CallExpr() {
     }
 
@@ -32,7 +33,7 @@
     public List<Expression> getExprList() {
         return exprList;
     }
-
+          
     public void setExprList(List<Expression> exprList) {
         this.exprList = exprList;
     }
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/OperatorExpr.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/OperatorExpr.java
index b6bb55b..23d2179 100644
--- a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/OperatorExpr.java
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/OperatorExpr.java
@@ -2,12 +2,13 @@
 
 import java.util.ArrayList;
 
+import edu.uci.ics.asterix.aql.base.AbstractExpression;
 import edu.uci.ics.asterix.aql.base.Expression;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlExpressionVisitor;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlVisitorWithVoidReturn;
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 
-public class OperatorExpr implements Expression {
+public class OperatorExpr extends AbstractExpression {
     private ArrayList<Expression> exprList;
     private ArrayList<OperatorType> opList;
     private ArrayList<Integer> exprBroadcastIdx;
diff --git a/asterix-aql/src/main/javacc/AQL.jj b/asterix-aql/src/main/javacc/AQL.jj
index 3e63b35..9a47c77 100644
--- a/asterix-aql/src/main/javacc/AQL.jj
+++ b/asterix-aql/src/main/javacc/AQL.jj
@@ -39,6 +39,8 @@
 import edu.uci.ics.asterix.common.annotations.*;
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 import edu.uci.ics.asterix.om.functions.AsterixFunction;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
 
 
 public class AQLParser extends ScopeChecker {
@@ -59,6 +61,7 @@
     // optimizer hints
     private static final String HASH_GROUP_BY_HINT = "hash";
     private static final String BROADCAST_JOIN_HINT = "bcast";
+    private static final String INDEXED_NESTED_LOOP_JOIN_HINT = "indexnl";
     private static final String INMEMORY_HINT = "inmem";
     private static final String VAL_FILE_HINT = "val-files";
     private static final String VAL_FILE_SAME_INDEX_HINT = "val-file-same-idx";
@@ -1248,14 +1251,15 @@
   OperatorExpr op = null;
   Expression operand = null;
   boolean broadcast = false;
+  IExpressionAnnotation annotation = null;
 }
 {
     operand = AddExpr()
-    {
-      if (operand instanceof VariableExpr) {
-        String hint = getHint(token);
+    { 
+      if (operand instanceof VariableExpr) {        
+        String hint = getHint(token);        
         if (hint != null && hint.equals(BROADCAST_JOIN_HINT)) {
-          broadcast = true;       
+          broadcast = true;
         }
       }
     } 
@@ -1263,6 +1267,10 @@
     (
       LOOKAHEAD(2)( "<" | ">" | "<=" | ">=" | "=" | "!=" |"~=")
   	  {
+  	    String mhint = getHint(token);
+  	    if (mhint != null && mhint.equals(INDEXED_NESTED_LOOP_JOIN_HINT)) {
+          annotation = IndexedNLJoinExpressionAnnotation.INSTANCE;
+        }
   	    if (op == null) {
   	      op = new OperatorExpr();
   	      op.addOperand(operand, broadcast);
@@ -1275,18 +1283,21 @@
 	  
  	  operand = AddExpr()
 	  {
-         broadcast = false;
-	     if (operand instanceof VariableExpr) {
-           String hint = getHint(token);
+         broadcast = false;	     
+         if (operand instanceof VariableExpr) {
+           String hint = getHint(token);        
            if (hint != null && hint.equals(BROADCAST_JOIN_HINT)) {
              broadcast = true;
            }
-         } 
+         }
          op.addOperand(operand, broadcast);
       } 
     )?
  	
  	{
+ 	  if (annotation != null) {
+ 	    op.addHint(annotation);
+ 	  }
  	  return op==null? operand: op;
  	}
 }
@@ -1730,14 +1741,18 @@
 Expression FunctionCallExpr() throws ParseException:
 {
   CallExpr pf = new CallExpr();
-  List<Expression > argList = new ArrayList<Expression >();
+  List<Expression> argList = new ArrayList<Expression>();
   Expression tmp;
   int arity = 0;
   Token funcName;
 }
-{
+{   
     ( <IDENTIFIER> | <DATASET> )
     {
+        String hint = getHint(token);
+        if (hint != null && hint.startsWith(INDEXED_NESTED_LOOP_JOIN_HINT)) {
+          pf.addHint(IndexedNLJoinExpressionAnnotation.INSTANCE);
+        }
 		funcName = getToken(0);
     }
      <LEFTPAREN> (tmp = Expression()
@@ -1746,7 +1761,7 @@
        arity ++;
      } ("," tmp = Expression() { argList.add(tmp); arity++; })*)? <RIGHTPAREN>
 
-     {
+     {       
        AsterixFunction fd = lookupFunctionSignature(funcName.toString(), arity);
 	     if(fd == null)
 	     {
diff --git a/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/entities/Index.java b/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/entities/Index.java
index 7c6d9ed..2aade27 100644
--- a/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/entities/Index.java
+++ b/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/entities/Index.java
@@ -123,4 +123,27 @@
         }
         throw new AlgebricksException("Could not find field " + expr + " in the schema.");
     }
+    
+    @Override
+    public int hashCode() {
+    	return indexName.hashCode() ^ datasetName.hashCode() ^ dataverseName.hashCode();
+    }
+    
+    @Override
+    public boolean equals(Object other) {
+    	if (!(other instanceof Index)) {
+    		return false;
+    	}
+    	Index otherIndex = (Index) other;
+    	if (!indexName.equals(otherIndex.getIndexName())) {
+    		return false;
+    	}
+    	if (!datasetName.equals(otherIndex.getDatasetName())) {
+    		return false;
+    	}
+    	if (!dataverseName.equals(otherIndex.getDataverseName())) {
+    		return false;
+    	}
+    	return true;
+    }
 }
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/formats/nontagged/AqlBinaryComparatorFactoryProvider.java b/asterix-om/src/main/java/edu/uci/ics/asterix/formats/nontagged/AqlBinaryComparatorFactoryProvider.java
index ede7b99..93167cf 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/formats/nontagged/AqlBinaryComparatorFactoryProvider.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/formats/nontagged/AqlBinaryComparatorFactoryProvider.java
@@ -2,6 +2,7 @@
 
 import java.io.Serializable;
 
+import edu.uci.ics.asterix.dataflow.data.nontagged.comparators.ADateTimeAscBinaryComparatorFactory;
 import edu.uci.ics.asterix.dataflow.data.nontagged.comparators.AObjectAscBinaryComparatorFactory;
 import edu.uci.ics.asterix.dataflow.data.nontagged.comparators.AObjectDescBinaryComparatorFactory;
 import edu.uci.ics.asterix.dataflow.data.nontagged.comparators.BooleanBinaryComparatorFactory;
@@ -55,7 +56,7 @@
     public IBinaryComparatorFactory getBinaryComparatorFactory(Object type, boolean ascending) {
         if (type == null) {
             return anyBinaryComparatorFactory(ascending);
-        }
+        }        
         IAType aqlType = (IAType) type;
         switch (aqlType.getTypeTag()) {
             case ANY:
@@ -100,6 +101,9 @@
             case RECTANGLE: {
                 return addOffset(RectangleBinaryComparatorFactory.INSTANCE, ascending);
             }
+            case DATETIME: {
+            	return addOffset(ADateTimeAscBinaryComparatorFactory.INSTANCE, ascending);
+            }
             default: {
                 throw new NotImplementedException("No binary comparator factory implemented for type "
                         + aqlType.getTypeTag() + " .");
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
index c27be6e..a624cf9 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
@@ -434,7 +434,7 @@
 
         // and then, Asterix builtin functions
         add(ANY_COLLECTION_MEMBER, NonTaggedCollectionMemberResultType.INSTANCE);
-        add(AVG, OptionalADoubleTypeComputer.INSTANCE);
+        addPrivateFunction(AVG, OptionalADoubleTypeComputer.INSTANCE);
         add(BOOLEAN_CONSTRUCTOR, UnaryBooleanOrNullFunctionTypeComputer.INSTANCE);
         add(CARET, NonTaggedNumericAddSubMulDivTypeComputer.INSTANCE);
         add(CIRCLE_CONSTRUCTOR, OptionalACircleTypeComputer.INSTANCE);
@@ -456,7 +456,7 @@
             }
         });
         add(CONTAINS, ABooleanTypeComputer.INSTANCE);
-        add(COUNT, AInt32TypeComputer.INSTANCE);
+        addPrivateFunction(COUNT, AInt32TypeComputer.INSTANCE);
         add(COUNTHASHED_GRAM_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE);
         add(COUNTHASHED_WORD_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE);
         add(CREATE_CIRCLE, ACircleTypeComputer.INSTANCE);
@@ -491,7 +491,7 @@
         add(GET_HANDLE, null); // TODO
         add(GET_ITEM, NonTaggedGetItemResultType.INSTANCE);
         add(GET_DATA, null); // TODO
-        add(GLOBAL_AVG, OptionalADoubleTypeComputer.INSTANCE);
+        addPrivateFunction(GLOBAL_AVG, OptionalADoubleTypeComputer.INSTANCE);
         add(GRAM_TOKENS, OrderedListOfAStringTypeComputer.INSTANCE);
         add(GLOBAL_AVG, OptionalADoubleTypeComputer.INSTANCE);
         add(HASHED_GRAM_TOKENS, OrderedListOfAInt32TypeComputer.INSTANCE);
@@ -512,7 +512,7 @@
         add(LIKE, BinaryBooleanOrNullFunctionTypeComputer.INSTANCE);
         add(LINE_CONSTRUCTOR, OptionalALineTypeComputer.INSTANCE);
         add(LISTIFY, OrderedListConstructorResultType.INSTANCE);
-        add(LOCAL_AVG, NonTaggedLocalAvgTypeComputer.INSTANCE);
+        addPrivateFunction(LOCAL_AVG, NonTaggedLocalAvgTypeComputer.INSTANCE);
         add(MAKE_FIELD_INDEX_HANDLE, null); // TODO
         add(MAKE_FIELD_NAME_HANDLE, null); // TODO
         add(MAX, NonTaggedSumTypeComputer.INSTANCE);
@@ -537,7 +537,7 @@
 
         add(STRING_TO_CODEPOINT, OrderedListOfAInt32TypeComputer.INSTANCE);
         add(CODEPOINT_TO_STRING, AStringTypeComputer.INSTANCE);
-        add(STRING_CONCAT, AStringTypeComputer.INSTANCE);
+        add(STRING_CONCAT, OptionalAStringTypeComputer.INSTANCE);        
         add(SUBSTRING2, Substring2TypeComputer.INSTANCE);
         add(STRING_LENGTH, UnaryStringInt32OrNullTypeComputer.INSTANCE);
         add(STRING_LOWERCASE, UnaryStringOrNullTypeComputer.INSTANCE);
@@ -625,7 +625,7 @@
             }
         });
         add(SUBSTRING, SubstringTypeComputer.INSTANCE);
-        add(SUM, NonTaggedSumTypeComputer.INSTANCE);
+        addPrivateFunction(SUM, NonTaggedSumTypeComputer.INSTANCE);
         add(LOCAL_SUM, NonTaggedSumTypeComputer.INSTANCE);
         add(SWITCH_CASE, NonTaggedSwitchCaseComputer.INSTANCE);
         add(REG_EXP, ABooleanTypeComputer.INSTANCE);
@@ -829,6 +829,13 @@
         asterixFunctionIdToInfo.put(fi, functionInfo);
     }
 
+    private static IFunctionInfo addPrivateFunction(FunctionIdentifier fi, IResultTypeComputer typeComputer) {
+        IFunctionInfo functionInfo = getAsterixFunctionInfo(fi);
+        builtinFunctionsSet.put(functionInfo, functionInfo);
+        funTypeComputer.put(functionInfo, typeComputer);
+        return functionInfo;
+    }
+
     private static void addAgg(FunctionIdentifier fi) {
         builtinAggregateFunctions.add(getAsterixFunctionInfo(fi));
     }
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/typecomputer/impl/NonTaggedCollectionMemberResultType.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/typecomputer/impl/NonTaggedCollectionMemberResultType.java
index 7caf99b..2a39d83 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/typecomputer/impl/NonTaggedCollectionMemberResultType.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/typecomputer/impl/NonTaggedCollectionMemberResultType.java
@@ -1,8 +1,5 @@
 package edu.uci.ics.asterix.om.typecomputer.impl;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import edu.uci.ics.asterix.om.typecomputer.base.IResultTypeComputer;
 import edu.uci.ics.asterix.om.types.ATypeTag;
 import edu.uci.ics.asterix.om.types.AUnionType;
@@ -28,18 +25,13 @@
             IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
         AbstractFunctionCallExpression f = (AbstractFunctionCallExpression) expression;
         IAType type = (IAType) env.getType(f.getArguments().get(0).getValue());
-        if (type.getTypeTag() == ATypeTag.UNION && NonTaggedFormatUtil.isOptionalField((AUnionType) type))
+        if (type.getTypeTag() == ATypeTag.UNION && NonTaggedFormatUtil.isOptionalField((AUnionType) type)) {
             type = ((AUnionType) type).getUnionList().get(NonTaggedFormatUtil.OPTIONAL_TYPE_INDEX_IN_UNION_LIST);
-        if (type.getTypeTag() == ATypeTag.ANY)
-            return BuiltinType.ANY;
-        else {
-            if (((AbstractCollectionType) type).getItemType().getTypeTag() == ATypeTag.NULL)
-                return BuiltinType.ANULL;
-            List<IAType> unionList = new ArrayList<IAType>();
-            unionList.add(BuiltinType.ANULL);
-            unionList.add(((AbstractCollectionType) type).getItemType());
-            return new AUnionType(unionList, "CollectionMemberResult");
         }
+        if (type.getTypeTag() == ATypeTag.ANY) {
+            return BuiltinType.ANY;
+        }
+        return ((AbstractCollectionType) type).getItemType();
     }
 
 }
\ No newline at end of file
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/aggregates/std/GlobalAvgAggregateDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/aggregates/std/GlobalAvgAggregateDescriptor.java
index fe2242c..7d49228 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/aggregates/std/GlobalAvgAggregateDescriptor.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/aggregates/std/GlobalAvgAggregateDescriptor.java
@@ -133,12 +133,19 @@
                                         + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serBytes[0]));
                             }
                         }
-                        int offset1 = ARecordSerializerDeserializer.getFieldOffsetById(serBytes, 0, 1, false);
+                                               
+                        // The record length helps us determine whether the input record fields are nullable.
+                        int recordLength = ARecordSerializerDeserializer.getRecordLength(serBytes, 1);
+                        int nullBitmapSize = 1;
+                        if (recordLength == 29) {
+                            nullBitmapSize = 0;
+                        }
+                        int offset1 = ARecordSerializerDeserializer.getFieldOffsetById(serBytes, 0, nullBitmapSize, false);
                         if (offset1 == 0) // the sum is null
                             metNull = true;
                         else
                             globalSum += ADoubleSerializerDeserializer.getDouble(serBytes, offset1);
-                        int offset2 = ARecordSerializerDeserializer.getFieldOffsetById(serBytes, 1, 1, false);
+                        int offset2 = ARecordSerializerDeserializer.getFieldOffsetById(serBytes, 1, nullBitmapSize, false);
                         if (offset2 != 0) // the count is not null
                             globalCount += AInt32SerializerDeserializer.getInt(serBytes, offset2);
 
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/common/AsterixListAccessor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/common/AsterixListAccessor.java
new file mode 100644
index 0000000..fd6b691
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/common/AsterixListAccessor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2009-2010 by The Regents of the University of California
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * you may obtain a copy of the License from
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.uci.ics.asterix.runtime.evaluators.common;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+import edu.uci.ics.asterix.common.exceptions.AsterixException;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AUnorderedListSerializerDeserializer;
+import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.EnumDeserializer;
+import edu.uci.ics.asterix.om.util.NonTaggedFormatUtil;
+
+/**
+ * Utility class for accessing serialized unordered and ordered lists. 
+ */
+public class AsterixListAccessor {
+
+	protected byte[] listBytes;
+	protected int start;
+	protected ATypeTag listType;
+	protected ATypeTag itemType;
+	protected int size;
+	
+	public ATypeTag getListType() {
+		return listType;
+	}
+
+	public ATypeTag getItemType() {
+		return itemType;
+	}
+
+	public boolean itemsAreSelfDescribing() {
+		return itemType == ATypeTag.ANY;
+	}
+
+	public void reset(byte[] listBytes, int start) throws AsterixException {
+		this.listBytes = listBytes;
+		this.start = start;
+		listType = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(listBytes[start]);		
+		if (listType != ATypeTag.UNORDEREDLIST && listType != ATypeTag.ORDEREDLIST) {
+			throw new AsterixException("Unsupported type: " + listType);
+		}
+		itemType = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(listBytes[start + 1]);
+		if (listType == ATypeTag.UNORDEREDLIST) {
+			size = AUnorderedListSerializerDeserializer.getNumberOfItems(listBytes, start);
+		} else {
+			size = AOrderedListSerializerDeserializer.getNumberOfItems(listBytes, start);
+		}
+	}
+
+	public int size() {
+		return size;
+	}
+	
+	public int getItemOffset(int itemIndex) throws AsterixException {
+		if (listType == ATypeTag.UNORDEREDLIST) {
+			return AUnorderedListSerializerDeserializer.getItemOffset(listBytes, start, itemIndex);
+		} else {
+			return AOrderedListSerializerDeserializer.getItemOffset(listBytes, start, itemIndex);
+		}
+	}
+	
+	public int getItemLength(int itemOffset) throws AsterixException {
+		ATypeTag itemType = getItemType(itemOffset);
+		return NonTaggedFormatUtil.getFieldValueLength(listBytes, itemOffset, itemType, itemsAreSelfDescribing());
+	}
+	
+	public ATypeTag getItemType(int itemOffset) throws AsterixException {
+		if (itemType == ATypeTag.ANY) {
+			return EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(listBytes[itemOffset]);
+		} else {
+			return itemType;
+		}
+	}
+	
+	public void writeItem(int itemIndex, DataOutput dos) throws AsterixException, IOException {
+		int itemOffset = getItemOffset(itemIndex);
+		int itemLength = getItemLength(itemOffset);
+		if (itemsAreSelfDescribing()) {
+			++itemLength;
+		} else {
+			dos.writeByte(itemType.serialize());
+		}
+		dos.write(listBytes, itemOffset, itemLength);
+	}
+	
+	public byte[] getByteArray() {
+		return listBytes;
+	}
+	
+	public int getStart() {
+		return start;
+	}
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
index 1c2cee7..2b53f35 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/StringConcatDescriptor.java
@@ -1,18 +1,20 @@
 package edu.uci.ics.asterix.runtime.evaluators.functions;
 
+import java.io.DataOutput;
+import java.io.IOException;
+
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 import edu.uci.ics.asterix.common.functions.FunctionConstants;
-import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
 import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
 import edu.uci.ics.asterix.om.base.ANull;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
 import edu.uci.ics.asterix.om.types.ATypeTag;
 import edu.uci.ics.asterix.om.types.BuiltinType;
-import edu.uci.ics.asterix.om.types.EnumDeserializer;
 import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
-import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.asterix.runtime.evaluators.common.AsterixListAccessor;
 import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
 import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
@@ -21,8 +23,6 @@
 import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
 import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
 import edu.uci.ics.hyracks.dataflow.common.data.util.StringUtils;
-import java.io.DataOutput;
-import java.io.IOException;
 
 /**
  * @author Xiaoyu Ma
@@ -37,24 +37,22 @@
             return new StringConcatDescriptor();
         }
     };
-    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
-    private final static byte SER_ORDEREDLIST_TYPE_TAG = ATypeTag.ORDEREDLIST.serialize();
-    private final byte stringTypeTag = ATypeTag.STRING.serialize();
-
+    
     @Override
     public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) {
         return new ICopyEvaluatorFactory() {
-
+            
             private static final long serialVersionUID = 1L;
 
             @Override
             public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
                 return new ICopyEvaluator() {
 
-                    private DataOutput out = output.getDataOutput();
-                    private ICopyEvaluatorFactory listEvalFactory = args[0];
-                    private ArrayBackedValueStorage outInputList = new ArrayBackedValueStorage();
-                    private ICopyEvaluator evalList = listEvalFactory.createEvaluator(outInputList);
+                    private final AsterixListAccessor listAccessor = new AsterixListAccessor();
+                    private final DataOutput out = output.getDataOutput();
+                    private final ICopyEvaluatorFactory listEvalFactory = args[0];
+                    private final ArrayBackedValueStorage outInputList = new ArrayBackedValueStorage();
+                    private final ICopyEvaluator evalList = listEvalFactory.createEvaluator(outInputList);
                     @SuppressWarnings("unchecked")
                     private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
                             .getSerializerDeserializer(BuiltinType.ANULL);
@@ -64,32 +62,39 @@
                         try {
                             outInputList.reset();
                             evalList.evaluate(tuple);
-                            byte[] serOrderedList = outInputList.getByteArray();
-                            if (serOrderedList[0] == SER_NULL_TYPE_TAG) {
+                            byte[] listBytes = outInputList.getByteArray();
+                            try {
+                                listAccessor.reset(listBytes, 0);
+                            } catch (AsterixException e) {
+                                throw new AlgebricksException(e);
+                            }
+                            if (listAccessor.getItemType() == ATypeTag.NULL) {
                                 nullSerde.serialize(ANull.NULL, out);
                                 return;
                             }
-                            if (serOrderedList[0] != SER_ORDEREDLIST_TYPE_TAG) {
-                                throw new AlgebricksException("Expects String List."
-                                        + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serOrderedList[0]));
-                            }
-                            int size = AOrderedListSerializerDeserializer.getNumberOfItems(serOrderedList);
                             try {
                                 // calculate length first
-                                int utf_8_len = 0;
-                                for (int i = 0; i < size; i++) {
-                                    int itemOffset = AOrderedListSerializerDeserializer
-                                            .getItemOffset(serOrderedList, i);
-                                    utf_8_len += UTF8StringPointable.getUTFLength(serOrderedList, itemOffset);
+                                int utf8Len = 0;
+                                for (int i = 0; i < listAccessor.size(); i++) {
+                                    int itemOffset = listAccessor.getItemOffset(i);
+                                    ATypeTag itemType = listAccessor.getItemType(itemOffset);
+                                    if (itemType != ATypeTag.STRING) {
+                                        if (itemType == ATypeTag.NULL) {
+                                            nullSerde.serialize(ANull.NULL, out);
+                                            return;
+                                        }
+                                        throw new AlgebricksException("Unsupported type " + itemType
+                                                + " in list passed to string-concat function.");
+                                    }
+                                    utf8Len += UTF8StringPointable.getUTFLength(listBytes, itemOffset);
                                 }
-                                out.writeByte(stringTypeTag);
-                                StringUtils.writeUTF8Len(utf_8_len, out);
-                                for (int i = 0; i < size; i++) {
-                                    int itemOffset = AOrderedListSerializerDeserializer
-                                            .getItemOffset(serOrderedList, i);
-                                    utf_8_len = UTF8StringPointable.getUTFLength(serOrderedList, itemOffset);
-                                    for (int j = 0; j < utf_8_len; j++) {
-                                        out.writeByte(serOrderedList[2 + itemOffset + j]);
+                                out.writeByte(ATypeTag.STRING.serialize());
+                                StringUtils.writeUTF8Len(utf8Len, out);
+                                for (int i = 0; i < listAccessor.size(); i++) {
+                                    int itemOffset = listAccessor.getItemOffset(i);
+                                    utf8Len = UTF8StringPointable.getUTFLength(listBytes, itemOffset);
+                                    for (int j = 0; j < utf8Len; j++) {
+                                        out.writeByte(listBytes[2 + itemOffset + j]);
                                     }
                                 }
                             } catch (AsterixException ex) {
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/unnestingfunctions/std/ScanCollectionDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/unnestingfunctions/std/ScanCollectionDescriptor.java
index cf39008..ef89e9f 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/unnestingfunctions/std/ScanCollectionDescriptor.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/unnestingfunctions/std/ScanCollectionDescriptor.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2010 by The Regents of the University of California
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * you may obtain a copy of the License from
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package edu.uci.ics.asterix.runtime.unnestingfunctions.std;
 
 import java.io.DataOutput;
@@ -5,16 +20,9 @@
 
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
 import edu.uci.ics.asterix.common.functions.FunctionConstants;
-import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AOrderedListSerializerDeserializer;
-import edu.uci.ics.asterix.dataflow.data.nontagged.serde.AUnorderedListSerializerDeserializer;
-import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
-import edu.uci.ics.asterix.om.base.ANull;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
 import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
-import edu.uci.ics.asterix.om.types.ATypeTag;
-import edu.uci.ics.asterix.om.types.BuiltinType;
-import edu.uci.ics.asterix.om.types.EnumDeserializer;
-import edu.uci.ics.asterix.om.util.NonTaggedFormatUtil;
+import edu.uci.ics.asterix.runtime.evaluators.common.AsterixListAccessor;
 import edu.uci.ics.asterix.runtime.unnestingfunctions.base.AbstractUnnestingFunctionDynamicDescriptor;
 import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
 import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -22,7 +30,6 @@
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyUnnestingFunction;
 import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyUnnestingFunctionFactory;
-import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
 import edu.uci.ics.hyracks.data.std.api.IDataOutputProvider;
 import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
 import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
@@ -51,14 +58,7 @@
     public static class ScanCollectionUnnestingFunctionFactory implements ICopyUnnestingFunctionFactory {
 
         private static final long serialVersionUID = 1L;
-
         private ICopyEvaluatorFactory listEvalFactory;
-        private final static byte SER_ORDEREDLIST_TYPE_TAG = ATypeTag.ORDEREDLIST.serialize();
-        private final static byte SER_UNORDEREDLIST_TYPE_TAG = ATypeTag.UNORDEREDLIST.serialize();
-        private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
-        private ATypeTag itemTag;
-        private byte serItemTypeTag;
-        private boolean selfDescList = false;
 
         public ScanCollectionUnnestingFunctionFactory(ICopyEvaluatorFactory arg) {
             this.listEvalFactory = arg;
@@ -71,84 +71,37 @@
 
             return new ICopyUnnestingFunction() {
 
+            	private final AsterixListAccessor listAccessor = new AsterixListAccessor();
                 private ArrayBackedValueStorage inputVal = new ArrayBackedValueStorage();
                 private ICopyEvaluator argEval = listEvalFactory.createEvaluator(inputVal);
-                @SuppressWarnings("unchecked")
-                private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
-                        .getSerializerDeserializer(BuiltinType.ANULL);
-                private int numItems;
-                private int pos;
-                private int itemOffset;
-                private int itemLength;
-                private byte serListTag;
+                private int itemIndex;
 
                 @Override
                 public void init(IFrameTupleReference tuple) throws AlgebricksException {
                     try {
                         inputVal.reset();
                         argEval.evaluate(tuple);
-                        byte[] serList = inputVal.getByteArray();
-
-                        if (serList[0] == SER_NULL_TYPE_TAG) {
-                            nullSerde.serialize(ANull.NULL, out);
-                            return;
-                        }
-
-                        if (serList[0] != SER_ORDEREDLIST_TYPE_TAG && serList[0] != SER_UNORDEREDLIST_TYPE_TAG) {
-                            throw new AlgebricksException("Scan collection is not defined for values of type"
-                                    + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serList[0]));
-                        }
-
-                        serListTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(inputVal.getByteArray()[0])
-                                .serialize();
-                        if (serListTag == SER_ORDEREDLIST_TYPE_TAG)
-                            numItems = AOrderedListSerializerDeserializer.getNumberOfItems(inputVal.getByteArray());
-                        else
-                            numItems = AUnorderedListSerializerDeserializer.getNumberOfItems(inputVal.getByteArray());
-
-                        itemTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serList[1]);
-                        if (itemTag == ATypeTag.ANY)
-                            selfDescList = true;
-                        else
-                            serItemTypeTag = serList[1];
-
-                        pos = 0;
-                    } catch (IOException e) {
-                        throw new AlgebricksException(e);
+                        listAccessor.reset(inputVal.getByteArray(), 0);
+                        itemIndex = 0;
+                    } catch (AsterixException e) {
+                    	throw new AlgebricksException(e);
                     }
                 }
 
                 @Override
                 public boolean step() throws AlgebricksException {
-
                     try {
-                        if (pos < numItems) {
-                            byte[] serList = inputVal.getByteArray();
-
-                            try {
-                                if (serListTag == SER_ORDEREDLIST_TYPE_TAG) {
-                                    itemOffset = AOrderedListSerializerDeserializer.getItemOffset(serList, pos);
-                                } else {
-                                    itemOffset = AUnorderedListSerializerDeserializer.getItemOffset(serList, pos);
-                                }
-                                if (selfDescList)
-                                    itemTag = EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(serList[itemOffset]);
-                                itemLength = NonTaggedFormatUtil.getFieldValueLength(serList, itemOffset, itemTag,
-                                        selfDescList);
-                                if (!selfDescList)
-                                    out.writeByte(serItemTypeTag);
-                                out.write(serList, itemOffset, itemLength + (!selfDescList ? 0 : 1));
-                            } catch (AsterixException e) {
-                                throw new AlgebricksException(e);
-                            }
-                            ++pos;
+                        if (itemIndex < listAccessor.size()) {
+                        	listAccessor.writeItem(itemIndex, out);
+                        	++itemIndex;
                             return true;
-                        } else
-                            return false;
-
+                        }                        
                     } catch (IOException e) {
                         throw new AlgebricksException(e);
+                    } catch (AsterixException e) {
+                    	throw new AlgebricksException(e);
                     }
+                    return false;
                 }
 
             };