Finished code for fixing this issue. Next: Add more tests.

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix-fix-issue-9@276 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
index 8543edd..c9f9f93 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
@@ -21,6 +21,7 @@
 import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
 import edu.uci.ics.asterix.om.types.ARecordType;
 import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.AUnionType;
 import edu.uci.ics.asterix.om.types.IAType;
 import edu.uci.ics.asterix.om.util.NonTaggedFormatUtil;
 import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
@@ -32,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.IVariableTypeEnvironment;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
 import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -139,22 +141,23 @@
 
             AssignOperator assign = new AssignOperator(secondaryKeyVars, expressions);
             ProjectOperator project = new ProjectOperator(projectVars);
+            assign.getInputs().add(new MutableObject<ILogicalOperator>(project));
+            project.getInputs().add(new MutableObject<ILogicalOperator>(currentTop));
+            context.computeAndSetTypeEnvironmentForOperator(project);
+            context.computeAndSetTypeEnvironmentForOperator(assign);
             if (index.getKind() == IndexKind.BTREE) {
                 for (LogicalVariable secondaryKeyVar : secondaryKeyVars) {
                     secondaryExpressions.add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(
                             secondaryKeyVar)));
                 }
-                Mutable<ILogicalExpression> filterExpression = createFilterExpression(secondaryKeyVars);
+                Mutable<ILogicalExpression> filterExpression = createFilterExpression(secondaryKeyVars,
+                        context.getOutputTypeEnvironment(assign));
                 AqlIndex dataSourceIndex = new AqlIndex(index, metadata, datasetName);
                 IndexInsertDeleteOperator indexUpdate = new IndexInsertDeleteOperator(dataSourceIndex,
                         insertOp.getPrimaryKeyExpressions(), secondaryExpressions, filterExpression,
                         insertOp.getOperation());
                 indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(assign));
-                assign.getInputs().add(new MutableObject<ILogicalOperator>(project));
-                project.getInputs().add(new MutableObject<ILogicalOperator>(currentTop));
                 currentTop = indexUpdate;
-                context.computeAndSetTypeEnvironmentForOperator(project);
-                context.computeAndSetTypeEnvironmentForOperator(assign);
                 context.computeAndSetTypeEnvironmentForOperator(indexUpdate);
             } else if (index.getKind() == IndexKind.RTREE) {
                 IAType spatialType = null;
@@ -185,20 +188,17 @@
                     secondaryExpressions.add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(
                             secondaryKeyVar)));
                 }
-                Mutable<ILogicalExpression> filterExpression = createFilterExpression(keyVarList);
+                AssignOperator assignCoordinates = new AssignOperator(keyVarList, keyExprList);
+                assignCoordinates.getInputs().add(new MutableObject<ILogicalOperator>(assign));
+                context.computeAndSetTypeEnvironmentForOperator(assignCoordinates);
+                Mutable<ILogicalExpression> filterExpression = createFilterExpression(keyVarList,
+                        context.getOutputTypeEnvironment(assignCoordinates));
                 AqlIndex dataSourceIndex = new AqlIndex(index, metadata, datasetName);
                 IndexInsertDeleteOperator indexUpdate = new IndexInsertDeleteOperator(dataSourceIndex,
                         insertOp.getPrimaryKeyExpressions(), secondaryExpressions, filterExpression,
                         insertOp.getOperation());
-                AssignOperator assignCoordinates = new AssignOperator(keyVarList, keyExprList);
-                indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(assignCoordinates));
-                assignCoordinates.getInputs().add(new MutableObject<ILogicalOperator>(assign));
-                assign.getInputs().add(new MutableObject<ILogicalOperator>(project));
-                project.getInputs().add(new MutableObject<ILogicalOperator>(currentTop));
-                currentTop = indexUpdate;
-                context.computeAndSetTypeEnvironmentForOperator(project);
-                context.computeAndSetTypeEnvironmentForOperator(assign);
-                context.computeAndSetTypeEnvironmentForOperator(assignCoordinates);
+                indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(assignCoordinates));                
+                currentTop = indexUpdate;                
                 context.computeAndSetTypeEnvironmentForOperator(indexUpdate);
             }
 
@@ -208,21 +208,31 @@
         return true;
     }
 
-    // TODO: Return null here for non-nullable fields.
-    private Mutable<ILogicalExpression> createFilterExpression(List<LogicalVariable> secondaryKeyVars) {
+    @SuppressWarnings("unchecked")
+    private Mutable<ILogicalExpression> createFilterExpression(List<LogicalVariable> secondaryKeyVars,
+            IVariableTypeEnvironment typeEnv) throws AlgebricksException {
         List<Mutable<ILogicalExpression>> filterExpressions = new ArrayList<Mutable<ILogicalExpression>>();
+        // Add 'is not null' to all nullable secondary index keys as a filtering condition.
         for (LogicalVariable secondaryKeyVar : secondaryKeyVars) {
-            // Add 'is not null' to all secondary index keys as a filtering condition.
+            IAType secondaryKeyType = (IAType) typeEnv.getVarType(secondaryKeyVar);
+            if (!isNullableType(secondaryKeyType)) {
+                continue;
+            }            
             ScalarFunctionCallExpression isNullFuncExpr = new ScalarFunctionCallExpression(
                     FunctionUtils.getFunctionInfo(AsterixBuiltinFunctions.IS_NULL),
                     new MutableObject<ILogicalExpression>(new VariableReferenceExpression(secondaryKeyVar)));
             ScalarFunctionCallExpression notFuncExpr = new ScalarFunctionCallExpression(
-                    FunctionUtils.getFunctionInfo(AsterixBuiltinFunctions.NOT),
-                    new MutableObject<ILogicalExpression>(isNullFuncExpr));                    
+                    FunctionUtils.getFunctionInfo(AsterixBuiltinFunctions.NOT), new MutableObject<ILogicalExpression>(
+                            isNullFuncExpr));
             filterExpressions.add(new MutableObject<ILogicalExpression>(notFuncExpr));
         }
+        // No nullable secondary keys.
+        if (filterExpressions.isEmpty()) {
+            return null;
+        }
         Mutable<ILogicalExpression> filterExpression = null;
         if (filterExpressions.size() > 1) {
+            // Create a conjunctive condition.
             filterExpression = new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
                     FunctionUtils.getFunctionInfo(AsterixBuiltinFunctions.AND), filterExpressions));
         } else {
@@ -231,6 +241,13 @@
         return filterExpression;
     }
     
+    private boolean isNullableType(IAType type) {
+        if (type.getTypeTag() == ATypeTag.UNION) {
+            return ((AUnionType)type).isNullableType();
+        }
+        return false;
+    }
+    
     public static IAType keyFieldType(String expr, ARecordType recType) throws AlgebricksException {
         String[] names = recType.getFieldNames();
         int n = names.length;
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/file/SecondaryIndexCreator.java b/asterix-app/src/main/java/edu/uci/ics/asterix/file/SecondaryIndexCreator.java
index b63e019..7da18fb 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/file/SecondaryIndexCreator.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/file/SecondaryIndexCreator.java
@@ -55,6 +55,8 @@
 import edu.uci.ics.hyracks.storage.am.common.impls.NoOpOperationCallbackProvider;
 
 @SuppressWarnings("rawtypes")
+// TODO: We should eventually have a hierarchy of classes that can create all possible index job specs, 
+// not just for creation.
 public abstract class SecondaryIndexCreator {
     protected final PhysicalOptimizationConfig physOptConf;
     
diff --git a/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/declared/AqlMetadataProvider.java b/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/declared/AqlMetadataProvider.java
index a67d0c3..33d36bd 100644
--- a/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/declared/AqlMetadataProvider.java
+++ b/asterix-metadata/src/main/java/edu/uci/ics/asterix/metadata/declared/AqlMetadataProvider.java
@@ -824,7 +824,6 @@
 	}
 
 	@Override
-	// TODO: Use filterExpr.
 	public Pair<IOperatorDescriptor, AlgebricksPartitionConstraint> getIndexInsertRuntime(
 			IDataSourceIndex<String, AqlSourceId> dataSourceIndex,
 			IOperatorSchema propagatedSchema, IOperatorSchema[] inputSchemas,
@@ -857,7 +856,6 @@
 	}
 
 	@Override
-	// TODO: Use filterExpr.
 	public Pair<IOperatorDescriptor, AlgebricksPartitionConstraint> getIndexDeleteRuntime(
 			IDataSourceIndex<String, AqlSourceId> dataSourceIndex,
 			IOperatorSchema propagatedSchema, IOperatorSchema[] inputSchemas,
@@ -893,6 +891,10 @@
 			IOperatorSchema[] inputSchemas, IVariableTypeEnvironment typeEnv,
 			ILogicalExpression filterExpr, JobGenContext context)
 			throws AlgebricksException {
+		// No filtering condition.
+		if (filterExpr == null) {
+			return null;
+		}
 		ILogicalExpressionJobGen exprJobGen = context.getExpressionJobGen();
 		IEvaluatorFactory filterEvalFactory = exprJobGen
 				.createEvaluatorFactory(filterExpr, typeEnv, inputSchemas,