Supprt querying meta record with meta().

Change-Id: Ie65417b6baf209ca0ab413cfa4a5f7fc5156ca63
Reviewed-on: https://asterix-gerrit.ics.uci.edu/685
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/ExternalDataLookupPOperator.java b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/ExternalDataLookupPOperator.java
index 4c0818b..1f5b47e 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/ExternalDataLookupPOperator.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/ExternalDataLookupPOperator.java
@@ -114,7 +114,8 @@
     public void computeDeliveredProperties(ILogicalOperator op, IOptimizationContext context)
             throws AlgebricksException {
         AqlDataSource ds = new DatasetDataSource(datasetId, datasetId.getDataverseName(), datasetId.getDatasourceName(),
-                recordType, null /*external dataset doesn't have meta records.*/, AqlDataSourceType.EXTERNAL_DATASET);
+                recordType, null /*external dataset doesn't have meta records.*/, AqlDataSourceType.EXTERNAL_DATASET,
+                dataset.getDatasetDetails());
         IDataSourcePropertiesProvider dspp = ds.getPropertiesProvider();
         AbstractScanOperator as = (AbstractScanOperator) op;
         deliveredProperties = dspp.computePropertiesVector(as.getVariables());
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
index ad5283a..8627b97 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
@@ -50,6 +50,7 @@
 import org.apache.asterix.optimizer.rules.IntroduceStaticTypeCastForInsertRule;
 import org.apache.asterix.optimizer.rules.IntroduceUnnestForCollectionToSequenceRule;
 import org.apache.asterix.optimizer.rules.LoadRecordFieldsRule;
+import org.apache.asterix.optimizer.rules.MetaFunctionToMetaVariableRule;
 import org.apache.asterix.optimizer.rules.NestGroupByRule;
 import org.apache.asterix.optimizer.rules.PushAggFuncIntoStandaloneAggregateRule;
 import org.apache.asterix.optimizer.rules.PushAggregateIntoGroupbyRule;
@@ -167,6 +168,7 @@
         normalization.add(new ConstantFoldingRule());
         normalization.add(new RemoveRedundantSelectRule());
         normalization.add(new UnnestToDataScanRule());
+        normalization.add(new MetaFunctionToMetaVariableRule());
         normalization.add(new IfElseToSwitchCaseFunctionRule());
         normalization.add(new FuzzyEqRule());
         normalization.add(new SimilarityCheckRule());
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ConstantFoldingRule.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ConstantFoldingRule.java
index ebe01a7..c534454 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ConstantFoldingRule.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ConstantFoldingRule.java
@@ -90,7 +90,7 @@
             AsterixBuiltinFunctions.GET_RECORD_FIELD_VALUE, AsterixBuiltinFunctions.FIELD_ACCESS_NESTED,
             AsterixBuiltinFunctions.GET_ITEM, AsterixBuiltinFunctions.OPEN_RECORD_CONSTRUCTOR,
             AsterixBuiltinFunctions.FIELD_ACCESS_BY_INDEX, AsterixBuiltinFunctions.CAST_RECORD,
-            AsterixBuiltinFunctions.CAST_LIST);
+            AsterixBuiltinFunctions.CAST_LIST, AsterixBuiltinFunctions.META);
 
     /** Throws exceptions in substituiteProducedVariable, setVarType, and one getVarType method. */
     private static final IVariableTypeEnvironment _emptyTypeEnv = new IVariableTypeEnvironment() {
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
index 956e6c5..ab09188 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceSecondaryIndexInsertDeleteRule.java
@@ -18,7 +18,6 @@
  */
 package org.apache.asterix.optimizer.rules;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -558,67 +557,62 @@
             throws AsterixException, AlgebricksException {
         ARecordType enforcedType = initialType;
         for (int i = 0; i < index.getKeyFieldNames().size(); i++) {
-            try {
-                Stack<Pair<ARecordType, String>> nestedTypeStack = new Stack<Pair<ARecordType, String>>();
-                List<String> splits = index.getKeyFieldNames().get(i);
-                ARecordType nestedFieldType = enforcedType;
-                boolean openRecords = false;
-                String bridgeName = nestedFieldType.getTypeName();
-                int j;
-                // Build the stack for the enforced type
-                for (j = 1; j < splits.size(); j++) {
-                    nestedTypeStack.push(new Pair<ARecordType, String>(nestedFieldType, splits.get(j - 1)));
-                    bridgeName = nestedFieldType.getTypeName();
-                    nestedFieldType = (ARecordType) enforcedType.getSubFieldType(splits.subList(0, j));
-                    if (nestedFieldType == null) {
-                        openRecords = true;
-                        break;
-                    }
+
+            Stack<Pair<ARecordType, String>> nestedTypeStack = new Stack<Pair<ARecordType, String>>();
+            List<String> splits = index.getKeyFieldNames().get(i);
+            ARecordType nestedFieldType = enforcedType;
+            boolean openRecords = false;
+            String bridgeName = nestedFieldType.getTypeName();
+            int j;
+            // Build the stack for the enforced type
+            for (j = 1; j < splits.size(); j++) {
+                nestedTypeStack.push(new Pair<ARecordType, String>(nestedFieldType, splits.get(j - 1)));
+                bridgeName = nestedFieldType.getTypeName();
+                nestedFieldType = (ARecordType) enforcedType.getSubFieldType(splits.subList(0, j));
+                if (nestedFieldType == null) {
+                    openRecords = true;
+                    break;
                 }
-                if (openRecords == true) {
-                    // create the smallest record
-                    enforcedType = new ARecordType(splits.get(splits.size() - 2),
-                            new String[] { splits.get(splits.size() - 1) },
-                            new IAType[] { AUnionType.createNullableType(index.getKeyFieldTypes().get(i)) }, true);
-                    // create the open part of the nested field
-                    for (int k = splits.size() - 3; k > (j - 2); k--) {
-                        enforcedType = new ARecordType(splits.get(k), new String[] { splits.get(k + 1) },
-                                new IAType[] { AUnionType.createNullableType(enforcedType) }, true);
-                    }
-                    // Bridge the gap
-                    Pair<ARecordType, String> gapPair = nestedTypeStack.pop();
-                    ARecordType parent = gapPair.first;
-
-                    IAType[] parentFieldTypes = ArrayUtils.addAll(parent.getFieldTypes().clone(),
-                            new IAType[] { AUnionType.createNullableType(enforcedType) });
-                    enforcedType = new ARecordType(bridgeName,
-                            ArrayUtils.addAll(parent.getFieldNames(), enforcedType.getTypeName()), parentFieldTypes,
-                            true);
-
-                } else {
-                    // Schema is closed all the way to the field
-                    // enforced fields are either null or strongly typed
-                    enforcedType = new ARecordType(nestedFieldType.getTypeName(),
-                            ArrayUtils.addAll(nestedFieldType.getFieldNames(), splits.get(splits.size() - 1)),
-                            ArrayUtils.addAll(nestedFieldType.getFieldTypes(),
-                                    AUnionType.createNullableType(index.getKeyFieldTypes().get(i))),
-                            nestedFieldType.isOpen());
+            }
+            if (openRecords == true) {
+                // create the smallest record
+                enforcedType = new ARecordType(splits.get(splits.size() - 2),
+                        new String[] { splits.get(splits.size() - 1) },
+                        new IAType[] { AUnionType.createNullableType(index.getKeyFieldTypes().get(i)) }, true);
+                // create the open part of the nested field
+                for (int k = splits.size() - 3; k > (j - 2); k--) {
+                    enforcedType = new ARecordType(splits.get(k), new String[] { splits.get(k + 1) },
+                            new IAType[] { AUnionType.createNullableType(enforcedType) }, true);
                 }
+                // Bridge the gap
+                Pair<ARecordType, String> gapPair = nestedTypeStack.pop();
+                ARecordType parent = gapPair.first;
 
-                // Create the enforcedtype for the nested fields in the schema, from the ground up
-                if (nestedTypeStack.size() > 0) {
-                    while (!nestedTypeStack.isEmpty()) {
-                        Pair<ARecordType, String> nestedTypePair = nestedTypeStack.pop();
-                        ARecordType nestedRecType = nestedTypePair.first;
-                        IAType[] nestedRecTypeFieldTypes = nestedRecType.getFieldTypes().clone();
-                        nestedRecTypeFieldTypes[nestedRecType.getFieldIndex(nestedTypePair.second)] = enforcedType;
-                        enforcedType = new ARecordType(nestedRecType.getTypeName(), nestedRecType.getFieldNames(),
-                                nestedRecTypeFieldTypes, nestedRecType.isOpen());
-                    }
+                IAType[] parentFieldTypes = ArrayUtils.addAll(parent.getFieldTypes().clone(),
+                        new IAType[] { AUnionType.createNullableType(enforcedType) });
+                enforcedType = new ARecordType(bridgeName,
+                        ArrayUtils.addAll(parent.getFieldNames(), enforcedType.getTypeName()), parentFieldTypes, true);
+
+            } else {
+                // Schema is closed all the way to the field
+                // enforced fields are either null or strongly typed
+                enforcedType = new ARecordType(nestedFieldType.getTypeName(),
+                        ArrayUtils.addAll(nestedFieldType.getFieldNames(), splits.get(splits.size() - 1)),
+                        ArrayUtils.addAll(nestedFieldType.getFieldTypes(),
+                                AUnionType.createNullableType(index.getKeyFieldTypes().get(i))),
+                        nestedFieldType.isOpen());
+            }
+
+            // Create the enforcedtype for the nested fields in the schema, from the ground up
+            if (nestedTypeStack.size() > 0) {
+                while (!nestedTypeStack.isEmpty()) {
+                    Pair<ARecordType, String> nestedTypePair = nestedTypeStack.pop();
+                    ARecordType nestedRecType = nestedTypePair.first;
+                    IAType[] nestedRecTypeFieldTypes = nestedRecType.getFieldTypes().clone();
+                    nestedRecTypeFieldTypes[nestedRecType.getFieldIndex(nestedTypePair.second)] = enforcedType;
+                    enforcedType = new ARecordType(nestedRecType.getTypeName(), nestedRecType.getFieldNames(),
+                            nestedRecTypeFieldTypes, nestedRecType.isOpen());
                 }
-
-            } catch (IOException e) {
-                throw new AsterixException(e);
             }
         }
         return enforcedType;
@@ -628,6 +622,7 @@
      * This method takes a list of {fields}: a subset of {recordFields}, the original record variable
      * and populate expressions with expressions which evaluate to those fields (using field access functions) and
      * variables to represent them
+     *
      * @param fields
      *            desired fields
      * @param recordFields
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/MetaFunctionToMetaVariableRule.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/MetaFunctionToMetaVariableRule.java
new file mode 100644
index 0000000..cd06303
--- /dev/null
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/MetaFunctionToMetaVariableRule.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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 org.apache.asterix.optimizer.rules;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.metadata.declared.AqlDataSource;
+import org.apache.asterix.om.functions.AsterixBuiltinFunctions;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionReferenceTransform;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+
+/**
+ * This rule rewrites all meta() function calls in a query plan
+ * to proper variable references.
+ */
+public class MetaFunctionToMetaVariableRule implements IAlgebraicRewriteRule {
+    // The rule can only apply once.
+    private boolean hasApplied = false;
+    private boolean rewritten = false;
+
+    @Override
+    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        if (hasApplied) {
+            return false;
+        }
+        hasApplied = true;
+        visit(opRef);
+        return rewritten;
+    }
+
+    private ILogicalExpressionReferenceTransformWithCondition visit(Mutable<ILogicalOperator> opRef)
+            throws AlgebricksException {
+        ILogicalOperator op = opRef.getValue();
+
+        // Reaches NTS or ETS.
+        if (op.getInputs().size() == 0) {
+            return NoOpExpressionReferenceTransform.INSTANCE;
+        }
+
+        // Datascan returns an useful transform if the meta part presents in the dataset.
+        if (op.getOperatorTag() == LogicalOperatorTag.DATASOURCESCAN) {
+            DataSourceScanOperator scanOp = (DataSourceScanOperator) op;
+            ILogicalExpressionReferenceTransformWithCondition inputTransfomer = visit(op.getInputs().get(0));
+            AqlDataSource dataSource = (AqlDataSource) scanOp.getDataSource();
+            if (!dataSource.hasMeta()) {
+                return inputTransfomer;
+            };
+            List<LogicalVariable> allVars = scanOp.getVariables();
+            LogicalVariable dataVar = dataSource.getDataRecordVariable(allVars);
+            LogicalVariable metaVar = dataSource.getMetaVariable(allVars);
+            LogicalExpressionReferenceTransform currentTransformer = new LogicalExpressionReferenceTransform(dataVar,
+                    metaVar);
+            if (inputTransfomer.equals(NoOpExpressionReferenceTransform.INSTANCE)) {
+                return currentTransformer;
+            } else {
+                // Requires an argument variable to resolve ambiguity.
+                List<ILogicalExpressionReferenceTransformWithCondition> transformers = new ArrayList<>();
+                inputTransfomer.setVariableRequired();
+                currentTransformer.setVariableRequired();
+                transformers.add(inputTransfomer);
+                transformers.add(currentTransformer);
+                return new CompositeExpressionReferenceTransform(transformers);
+            }
+        }
+
+        // Visits children in the depth-first order.
+        List<ILogicalExpressionReferenceTransformWithCondition> transformers = new ArrayList<>();
+        for (Mutable<ILogicalOperator> childRef : op.getInputs()) {
+            ILogicalExpressionReferenceTransformWithCondition transformer = visit(childRef);
+            if (!transformer.equals(NoOpExpressionReferenceTransform.INSTANCE)) {
+                transformers.add(transformer);
+            }
+        }
+        ILogicalExpressionReferenceTransformWithCondition currentTransformer = null;
+        if (transformers.size() == 0) {
+            currentTransformer = NoOpExpressionReferenceTransform.INSTANCE;
+        } else if (transformers.size() == 1) {
+            currentTransformer = transformers.get(0);
+        } else {
+            // Transformers in a CompositeTransformer should require argument variable check.
+            for (ILogicalExpressionReferenceTransformWithCondition transformer : transformers) {
+                transformer.setVariableRequired();
+            }
+            currentTransformer = new CompositeExpressionReferenceTransform(transformers);
+        }
+        rewritten |= op.acceptExpressionTransform(currentTransformer);
+        return currentTransformer;
+    }
+}
+
+interface ILogicalExpressionReferenceTransformWithCondition extends ILogicalExpressionReferenceTransform {
+    default void setVariableRequired() {
+
+    }
+}
+
+class NoOpExpressionReferenceTransform implements ILogicalExpressionReferenceTransformWithCondition {
+    static NoOpExpressionReferenceTransform INSTANCE = new NoOpExpressionReferenceTransform();
+
+    private NoOpExpressionReferenceTransform() {
+
+    }
+
+    @Override
+    public boolean transform(Mutable<ILogicalExpression> expression) throws AlgebricksException {
+        return false;
+    }
+
+}
+
+class LogicalExpressionReferenceTransform implements ILogicalExpressionReferenceTransformWithCondition {
+    private final LogicalVariable dataVar;
+    private final LogicalVariable metaVar;
+    private boolean variableRequired = false;
+
+    LogicalExpressionReferenceTransform(LogicalVariable dataVar, LogicalVariable metaVar) {
+        this.dataVar = dataVar;
+        this.metaVar = metaVar;
+    }
+
+    @Override
+    public void setVariableRequired() {
+        this.variableRequired = true;
+    }
+
+    @Override
+    public boolean transform(Mutable<ILogicalExpression> exprRef) throws AlgebricksException {
+        ILogicalExpression expr = exprRef.getValue();
+        if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+            return false;
+        }
+        AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) expr;
+        List<Mutable<ILogicalExpression>> argRefs = funcExpr.getArguments();
+
+        // Recursively transform argument expressions.
+        for (Mutable<ILogicalExpression> argRef : argRefs) {
+            transform(argRef);
+        }
+
+        if (!funcExpr.getFunctionIdentifier().equals(AsterixBuiltinFunctions.META)) {
+            return false;
+        }
+        // The user query provides more than one parameter for the meta function.
+        if (argRefs.size() > 1) {
+            throw new AlgebricksException("The meta function can at most have one argument!");
+        }
+
+        // The user query provides exact one parameter for the meta function.
+        if (argRefs.size() == 1) {
+            ILogicalExpression argExpr = argRefs.get(0).getValue();
+            if (argExpr.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
+                return false;
+            }
+            VariableReferenceExpression argVarExpr = (VariableReferenceExpression) argExpr;
+            LogicalVariable argVar = argVarExpr.getVariableReference();
+            if (!dataVar.equals(argVar)) {
+                return false;
+            }
+            exprRef.setValue(new VariableReferenceExpression(metaVar));
+            return true;
+        }
+
+        // The user query provides zero parameter for the meta function.
+        if (variableRequired) {
+            throw new AlgebricksException("Cannot resolve to ambiguity on the meta function call --"
+                    + " there are more than once dataset choices!");
+        }
+        exprRef.setValue(new VariableReferenceExpression(metaVar));
+        return true;
+    }
+}
+
+class CompositeExpressionReferenceTransform implements ILogicalExpressionReferenceTransformWithCondition {
+    private final List<ILogicalExpressionReferenceTransformWithCondition> transformers;
+
+    public CompositeExpressionReferenceTransform(List<ILogicalExpressionReferenceTransformWithCondition> transforms) {
+        this.transformers = transforms;
+    }
+
+    @Override
+    public boolean transform(Mutable<ILogicalExpression> expression) throws AlgebricksException {
+        // Tries transfomations one by one.
+        for (ILogicalExpressionReferenceTransform transformer : transformers) {
+            if (transformer.transform(expression)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
index cbf0dec..0152c12 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
@@ -18,7 +18,6 @@
  */
 package org.apache.asterix.optimizer.rules.am;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
@@ -229,9 +228,11 @@
                     //Prune indexes based on field types
                     List<IAType> indexedTypes = new ArrayList<IAType>();
                     //retrieve types of expressions joined/selected with an indexed field
-                    for (int j = 0; j < optFuncExpr.getNumLogicalVars(); j++)
-                        if (j != exprAndVarIdx.second)
+                    for (int j = 0; j < optFuncExpr.getNumLogicalVars(); j++) {
+                        if (j != exprAndVarIdx.second) {
                             indexedTypes.add(optFuncExpr.getFieldType(j));
+                        }
+                    }
 
                     //add constants in case of select
                     if (indexedTypes.size() < 2 && optFuncExpr.getNumLogicalVars() == 1
@@ -247,8 +248,9 @@
 
                                 @Override
                                 public Object getVarType(LogicalVariable var) throws AlgebricksException {
-                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second)))
+                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second))) {
                                         return keyType;
+                                    }
                                     throw new IllegalArgumentException();
                                 }
 
@@ -256,8 +258,9 @@
                                 public Object getVarType(LogicalVariable var, List<LogicalVariable> nonNullVariables,
                                         List<List<LogicalVariable>> correlatedNullableVariableLists)
                                                 throws AlgebricksException {
-                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second)))
+                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second))) {
                                         return keyType;
+                                    }
                                     throw new IllegalArgumentException();
                                 }
 
@@ -282,9 +285,11 @@
                     boolean jaccardSimilarity = optFuncExpr.getFuncExpr().getFunctionIdentifier().getName()
                             .startsWith("similarity-jaccard-check");
 
-                    for (int j = 0; j < indexedTypes.size(); j++)
-                        for (int k = j + 1; k < indexedTypes.size(); k++)
+                    for (int j = 0; j < indexedTypes.size(); j++) {
+                        for (int k = j + 1; k < indexedTypes.size(); k++) {
                             typeMatch &= isMatched(indexedTypes.get(j), indexedTypes.get(k), jaccardSimilarity);
+                        }
+                    }
 
                     // Check if any field name in the optFuncExpr matches.
                     if (optFuncExpr.findFieldName(keyField) != -1) {
@@ -333,8 +338,9 @@
 
     private boolean isMatched(IAType type1, IAType type2, boolean useListDomain) throws AlgebricksException {
         if (ATypeHierarchy.isSameTypeDomain(Index.getNonNullableType(type1).first.getTypeTag(),
-                Index.getNonNullableType(type2).first.getTypeTag(), useListDomain))
+                Index.getNonNullableType(type2).first.getTypeTag(), useListDomain)) {
             return true;
+        }
         return ATypeHierarchy.canPromote(Index.getNonNullableType(type1).first.getTypeTag(),
                 Index.getNonNullableType(type2).first.getTypeTag());
     }
@@ -438,9 +444,10 @@
             if (index.getKeyFieldNames().contains(fieldName) && index.getPendingOp() == IMetadataEntity.PENDING_NO_OP) {
                 indexCandidates.add(index);
                 if (optFuncExpr.getFieldType(varIdx) == BuiltinType.ANULL
-                        || optFuncExpr.getFieldType(varIdx) == BuiltinType.ANY)
+                        || optFuncExpr.getFieldType(varIdx) == BuiltinType.ANY) {
                     optFuncExpr.setFieldType(varIdx,
                             index.getKeyFieldTypes().get(index.getKeyFieldNames().indexOf(fieldName)));
+                }
                 analysisCtx.addIndexExpr(matchedSubTree.dataset, index, matchedFuncExprIndex, varIdx);
             }
         }
@@ -455,9 +462,10 @@
             IOptimizationContext context) throws AlgebricksException {
         int optFuncExprIndex = 0;
         List<Index> datasetIndexes = new ArrayList<Index>();
-        if (subTree.dataSourceType != DataSourceType.COLLECTION_SCAN)
+        if (subTree.dataSourceType != DataSourceType.COLLECTION_SCAN) {
             datasetIndexes = metadataProvider.getDatasetIndexes(subTree.dataset.getDataverseName(),
                     subTree.dataset.getDatasetName());
+        }
         for (IOptimizableFuncExpr optFuncExpr : analysisCtx.matchedFuncExprs) {
             // Try to match variables from optFuncExpr to assigns or unnests.
             for (int assignOrUnnestIndex = 0; assignOrUnnestIndex < subTree.assignsAndUnnests
@@ -744,12 +752,8 @@
                 }
 
                 if (!isByName) {
-                    try {
-                        fieldName = ((ARecordType) recordType.getSubFieldType(parentFieldNames))
-                                .getFieldNames()[fieldIndex];
-                    } catch (IOException e) {
-                        throw new AlgebricksException(e);
-                    }
+                    fieldName = ((ARecordType) recordType.getSubFieldType(parentFieldNames))
+                            .getFieldNames()[fieldIndex];
                 }
                 optFuncExpr.setSourceVar(funcVarIndex, ((AssignOperator) op).getVariables().get(assignVarIndex));
                 //add fieldName to the nested fieldName, return
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
index f39d190..294a098 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
@@ -19,7 +19,6 @@
 
 package org.apache.asterix.optimizer.rules.am;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -36,6 +35,7 @@
 import org.apache.asterix.metadata.entities.ExternalDatasetDetails;
 import org.apache.asterix.metadata.entities.Index;
 import org.apache.asterix.metadata.utils.DatasetUtils;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.om.base.ABoolean;
 import org.apache.asterix.om.base.AInt32;
 import org.apache.asterix.om.base.AInt64;
@@ -47,7 +47,6 @@
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
-import org.apache.asterix.om.util.NonTaggedFormatUtil;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.commons.lang3.mutable.MutableObject;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
@@ -82,14 +81,17 @@
  */
 public class AccessMethodUtils {
 
-    public static void appendPrimaryIndexTypes(Dataset dataset, IAType itemType, List<Object> target)
-            throws IOException, AlgebricksException {
+    public static void appendPrimaryIndexTypes(Dataset dataset, IAType itemType, IAType metaItemType,
+            List<Object> target) throws AlgebricksException {
         ARecordType recordType = (ARecordType) itemType;
-        List<List<String>> partitioningKeys = DatasetUtils.getPartitioningKeys(dataset);
-        for (List<String> partitioningKey : partitioningKeys) {
-            target.add(recordType.getSubFieldType(partitioningKey));
-        }
+        ARecordType metaRecordType = (ARecordType) metaItemType;
+        target.addAll(KeyFieldTypeUtils.getPartitoningKeyTypes(dataset, recordType, metaRecordType));
+        // Adds data record type.
         target.add(itemType);
+        // Adds meta record type if any.
+        if (dataset.hasMetaPart()) {
+            target.add(metaItemType);
+        }
     }
 
     public static ConstantExpression createStringConstant(String str) {
@@ -172,8 +174,9 @@
                 constantExpressionType);
         for (IOptimizableFuncExpr optFuncExpr : analysisCtx.matchedFuncExprs) {
             //avoid additional optFuncExpressions in case of a join
-            if (optFuncExpr.getFuncExpr().equals(funcExpr))
+            if (optFuncExpr.getFuncExpr().equals(funcExpr)) {
                 return true;
+            }
         }
         analysisCtx.matchedFuncExprs.add(newOptFuncExpr);
         return true;
@@ -196,61 +199,29 @@
                 new LogicalVariable[] { fieldVar1, fieldVar2 }, new ILogicalExpression[0], new IAType[0]);
         for (IOptimizableFuncExpr optFuncExpr : analysisCtx.matchedFuncExprs) {
             //avoid additional optFuncExpressions in case of a join
-            if (optFuncExpr.getFuncExpr().equals(funcExpr))
+            if (optFuncExpr.getFuncExpr().equals(funcExpr)) {
                 return true;
+            }
         }
         analysisCtx.matchedFuncExprs.add(newOptFuncExpr);
         return true;
     }
 
-    public static int getNumSecondaryKeys(Index index, ARecordType recordType) throws AlgebricksException {
-        switch (index.getIndexType()) {
-            case BTREE:
-            case SINGLE_PARTITION_WORD_INVIX:
-            case SINGLE_PARTITION_NGRAM_INVIX:
-            case LENGTH_PARTITIONED_WORD_INVIX:
-            case LENGTH_PARTITIONED_NGRAM_INVIX: {
-                return index.getKeyFieldNames().size();
-            }
-            case RTREE: {
-                Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(index.getKeyFieldTypes().get(0),
-                        index.getKeyFieldNames().get(0), recordType);
-                IAType keyType = keyPairType.first;
-                int numDimensions = NonTaggedFormatUtil.getNumDimensions(keyType.getTypeTag());
-                return numDimensions * 2;
-            }
-            default: {
-                throw new AlgebricksException("Unknown index kind: " + index.getIndexType());
-            }
-        }
-    }
-
     /**
      * Appends the types of the fields produced by the given secondary index to dest.
      */
-    public static void appendSecondaryIndexTypes(Dataset dataset, ARecordType recordType, Index index,
-            boolean primaryKeysOnly, List<Object> dest) throws AlgebricksException {
+    public static void appendSecondaryIndexTypes(Dataset dataset, ARecordType recordType, ARecordType metaRecordType,
+            Index index, boolean primaryKeysOnly, List<Object> dest) throws AlgebricksException {
         if (!primaryKeysOnly) {
             switch (index.getIndexType()) {
                 case BTREE:
                 case SINGLE_PARTITION_WORD_INVIX:
                 case SINGLE_PARTITION_NGRAM_INVIX: {
-                    for (int i = 0; i < index.getKeyFieldNames().size(); i++) {
-                        Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(
-                                index.getKeyFieldTypes().get(i), index.getKeyFieldNames().get(i), recordType);
-                        dest.add(keyPairType.first);
-                    }
+                    dest.addAll(KeyFieldTypeUtils.getBTreeIndexKeyTypes(index, recordType, metaRecordType));
                     break;
                 }
                 case RTREE: {
-                    Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(
-                            index.getKeyFieldTypes().get(0), index.getKeyFieldNames().get(0), recordType);
-                    IAType keyType = keyPairType.first;
-                    IAType nestedKeyType = NonTaggedFormatUtil.getNestedSpatialType(keyType.getTypeTag());
-                    int numKeys = getNumSecondaryKeys(index, recordType);
-                    for (int i = 0; i < numKeys; i++) {
-                        dest.add(nestedKeyType);
-                    }
+                    dest.addAll(KeyFieldTypeUtils.getRTreeIndexKeyTypes(index, recordType, metaRecordType));
                     break;
                 }
                 case LENGTH_PARTITIONED_NGRAM_INVIX:
@@ -268,20 +239,13 @@
                 throw new AlgebricksException(e);
             }
         } else {
-            List<List<String>> partitioningKeys = DatasetUtils.getPartitioningKeys(dataset);
-            for (List<String> partitioningKey : partitioningKeys) {
-                try {
-                    dest.add(recordType.getSubFieldType(partitioningKey));
-                } catch (IOException e) {
-                    throw new AlgebricksException(e);
-                }
-            }
+            dest.addAll(KeyFieldTypeUtils.getPartitoningKeyTypes(dataset, recordType, metaRecordType));
         }
     }
 
-    public static void appendSecondaryIndexOutputVars(Dataset dataset, ARecordType recordType, Index index,
-            boolean primaryKeysOnly, IOptimizationContext context, List<LogicalVariable> dest)
-                    throws AlgebricksException {
+    public static void appendSecondaryIndexOutputVars(Dataset dataset, ARecordType recordType,
+            ARecordType metaRecordType, Index index, boolean primaryKeysOnly, IOptimizationContext context,
+            List<LogicalVariable> dest) throws AlgebricksException {
         int numPrimaryKeys = 0;
         if (dataset.getDatasetType() == DatasetType.EXTERNAL) {
             numPrimaryKeys = IndexingConstants
@@ -289,7 +253,7 @@
         } else {
             numPrimaryKeys = DatasetUtils.getPartitioningKeys(dataset).size();
         }
-        int numSecondaryKeys = getNumSecondaryKeys(index, recordType);
+        int numSecondaryKeys = KeyFieldTypeUtils.getNumSecondaryKeys(index, recordType, metaRecordType);
         int numVars = (primaryKeysOnly) ? numPrimaryKeys : numPrimaryKeys + numSecondaryKeys;
         for (int i = 0; i < numVars; i++) {
             dest.add(context.newVar());
@@ -422,9 +386,10 @@
         return indexExprs.get(0).second;
     }
 
-    public static UnnestMapOperator createSecondaryIndexUnnestMap(Dataset dataset, ARecordType recordType, Index index,
-            ILogicalOperator inputOp, AccessMethodJobGenParams jobGenParams, IOptimizationContext context,
-            boolean outputPrimaryKeysOnly, boolean retainInput) throws AlgebricksException {
+    public static UnnestMapOperator createSecondaryIndexUnnestMap(Dataset dataset, ARecordType recordType,
+            ARecordType metaRecordType, Index index, ILogicalOperator inputOp, AccessMethodJobGenParams jobGenParams,
+            IOptimizationContext context, boolean outputPrimaryKeysOnly, boolean retainInput)
+                    throws AlgebricksException {
         // The job gen parameters are transferred to the actual job gen via the UnnestMapOperator's function arguments.
         ArrayList<Mutable<ILogicalExpression>> secondaryIndexFuncArgs = new ArrayList<Mutable<ILogicalExpression>>();
         jobGenParams.writeToFuncArgs(secondaryIndexFuncArgs);
@@ -432,9 +397,10 @@
         List<LogicalVariable> secondaryIndexUnnestVars = new ArrayList<LogicalVariable>();
         List<Object> secondaryIndexOutputTypes = new ArrayList<Object>();
         // Append output variables/types generated by the secondary-index search (not forwarded from input).
-        appendSecondaryIndexOutputVars(dataset, recordType, index, outputPrimaryKeysOnly, context,
+        appendSecondaryIndexOutputVars(dataset, recordType, metaRecordType, index, outputPrimaryKeysOnly, context,
                 secondaryIndexUnnestVars);
-        appendSecondaryIndexTypes(dataset, recordType, index, outputPrimaryKeysOnly, secondaryIndexOutputTypes);
+        appendSecondaryIndexTypes(dataset, recordType, metaRecordType, index, outputPrimaryKeysOnly,
+                secondaryIndexOutputTypes);
         // An index search is expressed as an unnest over an index-search function.
         IFunctionInfo secondaryIndexSearch = FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.INDEX_SEARCH);
         UnnestingFunctionCallExpression secondaryIndexSearchFunc = new UnnestingFunctionCallExpression(
@@ -452,9 +418,9 @@
     }
 
     public static UnnestMapOperator createPrimaryIndexUnnestMap(AbstractDataSourceOperator dataSourceOp,
-            Dataset dataset, ARecordType recordType, ILogicalOperator inputOp, IOptimizationContext context,
-            boolean sortPrimaryKeys, boolean retainInput, boolean retainNull, boolean requiresBroadcast)
-                    throws AlgebricksException {
+            Dataset dataset, ARecordType recordType, ARecordType metaRecordType, ILogicalOperator inputOp,
+            IOptimizationContext context, boolean sortPrimaryKeys, boolean retainInput, boolean retainNull,
+            boolean requiresBroadcast) throws AlgebricksException {
         List<LogicalVariable> primaryKeyVars = AccessMethodUtils.getPrimaryKeyVarsFromSecondaryUnnestMap(dataset,
                 inputOp);
         // Optionally add a sort on the primary-index keys before searching the primary index.
@@ -488,11 +454,7 @@
         List<Object> primaryIndexOutputTypes = new ArrayList<Object>();
         // Append output variables/types generated by the primary-index search (not forwarded from input).
         primaryIndexUnnestVars.addAll(dataSourceOp.getVariables());
-        try {
-            appendPrimaryIndexTypes(dataset, recordType, primaryIndexOutputTypes);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
-        }
+        appendPrimaryIndexTypes(dataset, recordType, metaRecordType, primaryIndexOutputTypes);
         // An index search is expressed as an unnest over an index-search function.
         IFunctionInfo primaryIndexSearch = FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.INDEX_SEARCH);
         AbstractFunctionCallExpression primaryIndexSearchFunc = new ScalarFunctionCallExpression(primaryIndexSearch,
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
index 1bbb13b..15dbe95 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
@@ -19,7 +19,6 @@
 
 package org.apache.asterix.optimizer.rules.am;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -481,7 +480,7 @@
         }
 
         UnnestMapOperator secondaryIndexUnnestOp = AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
-                chosenIndex, inputOp, jobGenParams, context, false, retainInput);
+                metaRecordType, chosenIndex, inputOp, jobGenParams, context, false, retainInput);
 
         // Generate the rest of the upstream plan which feeds the search results into the primary index.
         UnnestMapOperator primaryIndexUnnestOp = null;
@@ -494,7 +493,7 @@
             return externalDataAccessOp;
         } else if (!isPrimaryIndex) {
             primaryIndexUnnestOp = AccessMethodUtils.createPrimaryIndexUnnestMap(dataSourceOp, dataset, recordType,
-                    secondaryIndexUnnestOp, context, true, retainInput, retainNull, false);
+                    metaRecordType, secondaryIndexUnnestOp, context, true, retainInput, retainNull, false);
 
             // Adds equivalence classes --- one equivalent class between a primary key
             // variable and a record field-access expression.
@@ -502,11 +501,7 @@
                     dataSourceOp.getVariables(), recordType, metaRecordType, dataset, context);
         } else {
             List<Object> primaryIndexOutputTypes = new ArrayList<Object>();
-            try {
-                AccessMethodUtils.appendPrimaryIndexTypes(dataset, recordType, primaryIndexOutputTypes);
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            AccessMethodUtils.appendPrimaryIndexTypes(dataset, recordType, metaRecordType, primaryIndexOutputTypes);
             List<LogicalVariable> scanVariables = dataSourceOp.getVariables();
             primaryIndexUnnestOp = new UnnestMapOperator(scanVariables, secondaryIndexUnnestOp.getExpressionRef(),
                     primaryIndexOutputTypes, retainInput);
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceLSMComponentFilterRule.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceLSMComponentFilterRule.java
index 08a4e74..a2bf6d9 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceLSMComponentFilterRule.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceLSMComponentFilterRule.java
@@ -29,6 +29,7 @@
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
 import org.apache.asterix.metadata.utils.DatasetUtils;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.om.base.AInt32;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.constants.AsterixConstantValue;
@@ -109,7 +110,7 @@
 
         for (int i = 0; i < analysisCtx.matchedFuncExprs.size(); i++) {
             IOptimizableFuncExpr optFuncExpr = analysisCtx.matchedFuncExprs.get(i);
-            boolean found = findMacthedExprFieldName(optFuncExpr, op, dataset, recType, datasetIndexes);
+            boolean found = findMacthedExprFieldName(optFuncExpr, op, dataset, recType, datasetIndexes, context);
             if (found && optFuncExpr.getFieldName(0).equals(filterFieldName)) {
                 optFuncExprs.add(optFuncExpr);
             }
@@ -301,7 +302,8 @@
     }
 
     private boolean findMacthedExprFieldName(IOptimizableFuncExpr optFuncExpr, AbstractLogicalOperator op,
-            Dataset dataset, ARecordType recType, List<Index> datasetIndexes) throws AlgebricksException {
+            Dataset dataset, ARecordType recType, List<Index> datasetIndexes, IOptimizationContext context)
+                    throws AlgebricksException {
         AbstractLogicalOperator descendantOp = (AbstractLogicalOperator) op.getInputs().get(0).getValue();
         while (descendantOp != null) {
             if (descendantOp.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
@@ -368,7 +370,10 @@
                         }
                     }
 
-                    int numSecondaryKeys = AccessMethodUtils.getNumSecondaryKeys(index, recType);
+                    IAType metaItemType = ((AqlMetadataProvider) context.getMetadataProvider())
+                            .findType(dataset.getMetaItemTypeDataverseName(), dataset.getMetaItemTypeName());
+                    ARecordType metaRecType = (ARecordType) metaItemType;
+                    int numSecondaryKeys = KeyFieldTypeUtils.getNumSecondaryKeys(index, recType, metaRecType);
                     List<String> fieldName;
                     if (varIndex >= numSecondaryKeys) {
                         fieldName = DatasetUtils.getPartitioningKeys(dataset).get(varIndex - numSecondaryKeys);
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
index 824fe7a..70d7088 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
@@ -66,7 +66,6 @@
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
@@ -364,13 +363,13 @@
     @Override
     public ILogicalOperator createSecondaryToPrimaryPlan(Mutable<ILogicalExpression> conditionRef,
             OptimizableOperatorSubTree indexSubTree, OptimizableOperatorSubTree probeSubTree, Index chosenIndex,
-            AccessMethodAnalysisContext analysisCtx,
-            boolean retainInput, boolean retainNull, boolean requiresBroadcast, IOptimizationContext context)
-                    throws AlgebricksException {
+            AccessMethodAnalysisContext analysisCtx, boolean retainInput, boolean retainNull, boolean requiresBroadcast,
+            IOptimizationContext context) throws AlgebricksException {
 
         IOptimizableFuncExpr optFuncExpr = AccessMethodUtils.chooseFirstOptFuncExpr(chosenIndex, analysisCtx);
         Dataset dataset = indexSubTree.dataset;
         ARecordType recordType = indexSubTree.recordType;
+        ARecordType metaRecordType = indexSubTree.metaRecordType;
         // we made sure indexSubTree has datasource scan
         DataSourceScanOperator dataSourceScan = (DataSourceScanOperator) indexSubTree.dataSourceRef.getValue();
 
@@ -407,11 +406,11 @@
         }
         jobGenParams.setKeyVarList(keyVarList);
         UnnestMapOperator secondaryIndexUnnestOp = AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
-                chosenIndex, inputOp, jobGenParams, context, true, retainInput);
+                metaRecordType, chosenIndex, inputOp, jobGenParams, context, true, retainInput);
 
         // Generate the rest of the upstream plan which feeds the search results into the primary index.
         UnnestMapOperator primaryIndexUnnestOp = AccessMethodUtils.createPrimaryIndexUnnestMap(dataSourceScan, dataset,
-                recordType, secondaryIndexUnnestOp, context, true, retainInput, retainNull, false);
+                recordType, metaRecordType, secondaryIndexUnnestOp, context, true, retainInput, retainNull, false);
 
         return primaryIndexUnnestOp;
     }
@@ -436,8 +435,8 @@
     public boolean applySelectPlanTransformation(Mutable<ILogicalOperator> selectRef,
             OptimizableOperatorSubTree subTree, Index chosenIndex, AccessMethodAnalysisContext analysisCtx,
             IOptimizationContext context) throws AlgebricksException {
-        ILogicalOperator indexPlanRootOp = createSecondaryToPrimaryPlan(null, subTree, null, chosenIndex, analysisCtx, false,
-                false, false, context);
+        ILogicalOperator indexPlanRootOp = createSecondaryToPrimaryPlan(null, subTree, null, chosenIndex, analysisCtx,
+                false, false, false, context);
         // Replace the datasource scan with the new plan rooted at primaryIndexUnnestMap.
         subTree.dataSourceRef.setValue(indexPlanRootOp);
         return true;
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
index 3c925e2..111fcf4 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
@@ -179,6 +179,7 @@
         IOptimizableFuncExpr optFuncExpr = AccessMethodUtils.chooseFirstOptFuncExpr(chosenIndex, analysisCtx);
         Dataset dataset = indexSubTree.dataset;
         ARecordType recordType = indexSubTree.recordType;
+        ARecordType metaRecordType = indexSubTree.metaRecordType;
 
         int optFieldIdx = AccessMethodUtils.chooseFirstOptFuncVar(chosenIndex, analysisCtx);
         Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(optFuncExpr.getFieldType(optFieldIdx),
@@ -239,7 +240,7 @@
         }
 
         UnnestMapOperator secondaryIndexUnnestOp = AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
-                chosenIndex, assignSearchKeys, jobGenParams, context, false, retainInput);
+                metaRecordType, chosenIndex, assignSearchKeys, jobGenParams, context, false, retainInput);
 
         // Generate the rest of the upstream plan which feeds the search results into the primary index.
         if (dataset.getDatasetType() == DatasetType.EXTERNAL) {
@@ -248,8 +249,8 @@
             return externalDataAccessOp;
         } else {
             UnnestMapOperator primaryIndexUnnestOp = AccessMethodUtils.createPrimaryIndexUnnestMap(dataSourceOp,
-                    dataset, recordType, secondaryIndexUnnestOp, context, true, retainInput, false, false);
-
+                    dataset, recordType, metaRecordType, secondaryIndexUnnestOp, context, true, retainInput, false,
+                    false);
             return primaryIndexUnnestOp;
         }
     }
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/EquivalenceClassUtils.java b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/EquivalenceClassUtils.java
index e85fecf..242e15b 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/EquivalenceClassUtils.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/EquivalenceClassUtils.java
@@ -103,7 +103,7 @@
         for (int pkIndex = 0; pkIndex < primaryKey.size(); ++pkIndex) {
             LogicalVariable referredRecordVar = recordVar;
             String pkFieldName = primaryKey.get(pkIndex).get(0);
-            int source = keySourceIndicators == null ? 0 : keySourceIndicators.get(pkIndex);
+            int source = keySourceIndicators.get(pkIndex);
             Integer fieldIndexInRecord;
             if (source == 0) {
                 // The field is from the main record.
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index c635125..28de56b 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -457,8 +457,8 @@
         IAType metaItemType = metadataProvider.findType(dataset.getMetaItemTypeDataverseName(),
                 dataset.getMetaItemTypeName());
         DatasetDataSource dataSource = new DatasetDataSource(sourceId, dataset.getDataverseName(),
-                dataset.getDatasetName(), itemType, metaItemType, AqlDataSourceType.INTERNAL_DATASET);
-
+                dataset.getDatasetName(), itemType, metaItemType, AqlDataSourceType.INTERNAL_DATASET,
+                dataset.getDatasetDetails());
         return dataSource;
     }
 
diff --git a/asterix-algebra/src/main/java/org/apache/asterix/translator/util/ValidateUtil.java b/asterix-algebra/src/main/java/org/apache/asterix/translator/util/ValidateUtil.java
index 95a8802..03e3fe8 100644
--- a/asterix-algebra/src/main/java/org/apache/asterix/translator/util/ValidateUtil.java
+++ b/asterix-algebra/src/main/java/org/apache/asterix/translator/util/ValidateUtil.java
@@ -18,12 +18,12 @@
  */
 package org.apache.asterix.translator.util;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.asterix.common.config.DatasetConfig.IndexType;
 import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.BuiltinType;
@@ -47,8 +47,7 @@
      * @throws AsterixException
      *             (if the validation failed), IOException
      */
-    public static void validateFilterField(ARecordType recType, List<String> filterField)
-            throws AsterixException, IOException {
+    public static void validateFilterField(ARecordType recType, List<String> filterField) throws AsterixException {
         IAType fieldType = recType.getSubFieldType(filterField);
         if (fieldType == null) {
             throw new AsterixException("A field with this name  \"" + filterField + "\" could not be found.");
@@ -88,7 +87,7 @@
      */
     public static List<IAType> validatePartitioningExpressions(ARecordType recType, ARecordType metaRecType,
             List<List<String>> partitioningExprs, List<Integer> keySourceIndicators, boolean autogenerated)
-                    throws AsterixException, IOException {
+                    throws AsterixException {
         List<IAType> partitioningExprTypes = new ArrayList<IAType>(partitioningExprs.size());
         if (autogenerated) {
             if (partitioningExprs.size() > 1) {
@@ -104,11 +103,10 @@
                         + ". Autogenerated primary keys must be of type " + ATypeTag.UUID + ".");
             }
         } else {
-            for (int i = 0; i < partitioningExprs.size(); i++) {
-                List<String> fieldName = partitioningExprs.get(i);
-                boolean useMeta = keySourceIndicators.get(i) > 0;
-                IAType fieldType = useMeta ? metaRecType.getSubFieldType(fieldName)
-                        : recType.getSubFieldType(fieldName);
+            partitioningExprTypes = KeyFieldTypeUtils.getKeyTypes(recType, metaRecType, partitioningExprs,
+                    keySourceIndicators);
+            for (int fidx = 0; fidx < partitioningExprTypes.size(); ++fidx) {
+                IAType fieldType = partitioningExprTypes.get(fidx);
                 switch (fieldType.getTypeTag()) {
                     case INT8:
                     case INT16:
@@ -124,13 +122,13 @@
                     case DATETIME:
                     case YEARMONTHDURATION:
                     case DAYTIMEDURATION:
-                        partitioningExprTypes.add(fieldType);
                         break;
                     case UNION:
-                        throw new AsterixException("The partitioning key \"" + fieldName + "\" cannot be nullable");
+                        throw new AsterixException(
+                                "The partitioning key \"" + partitioningExprs.get(fidx) + "\" cannot be nullable");
                     default:
-                        throw new AsterixException("The partitioning key \"" + fieldName + "\" cannot be of type "
-                                + fieldType.getTypeTag() + ".");
+                        throw new AsterixException("The partitioning key \"" + partitioningExprs.get(fidx)
+                                + "\" cannot be of type " + fieldType.getTypeTag() + ".");
                 }
             }
         }
@@ -151,12 +149,15 @@
      * @throws AsterixException
      *             (if the validation failed), IOException
      */
-    public static void validateKeyFields(ARecordType recType, List<List<String>> keyFieldNames,
-            List<IAType> keyFieldTypes, IndexType indexType) throws AsterixException, IOException {
+    public static void validateKeyFields(ARecordType recType, ARecordType metaRecType, List<List<String>> keyFieldNames,
+            List<Integer> keySourceIndicators, List<IAType> keyFieldTypes, IndexType indexType)
+                    throws AsterixException {
+        List<IAType> fieldTypes = KeyFieldTypeUtils.getKeyTypes(recType, metaRecType, keyFieldNames,
+                keySourceIndicators);
         int pos = 0;
         boolean openFieldCompositeIdx = false;
-        for (List<String> fieldName : keyFieldNames) {
-            IAType fieldType = recType.getSubFieldType(fieldName);
+        for (IAType fieldType : fieldTypes) {
+            List<String> fieldName = keyFieldNames.get(pos);
             if (fieldType == null) {
                 fieldType = keyFieldTypes.get(pos);
                 if (keyFieldTypes.get(pos) == BuiltinType.ANULL) {
diff --git a/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java b/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
index 75f325b..00bc254 100644
--- a/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
+++ b/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
@@ -141,6 +141,7 @@
 import org.apache.asterix.metadata.feeds.FeedMetadataUtil;
 import org.apache.asterix.metadata.utils.DatasetUtils;
 import org.apache.asterix.metadata.utils.ExternalDatasetsRegistry;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.metadata.utils.MetadataLockManager;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.ATypeTag;
@@ -811,6 +812,7 @@
         CreateIndexStatement stmtCreateIndex = (CreateIndexStatement) stmt;
         String dataverseName = getActiveDataverse(stmtCreateIndex.getDataverseName());
         String datasetName = stmtCreateIndex.getDatasetName().getValue();
+        List<Integer> keySourceIndicators = stmtCreateIndex.getFieldSourceIndicators();
 
         MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
         boolean bActiveTxn = true;
@@ -838,19 +840,24 @@
             indexName = stmtCreateIndex.getIndexName().getValue();
             Index idx = MetadataManager.INSTANCE.getIndex(metadataProvider.getMetadataTxnContext(), dataverseName,
                     datasetName, indexName);
-
-            String itemTypeName = ds.getItemTypeName();
             Datatype dt = MetadataManager.INSTANCE.getDatatype(metadataProvider.getMetadataTxnContext(),
-                    ds.getItemTypeDataverseName(), itemTypeName);
-            IAType itemType = dt.getDatatype();
-            ARecordType aRecordType = (ARecordType) itemType;
+                    ds.getItemTypeDataverseName(), ds.getItemTypeName());
+            ARecordType aRecordType = (ARecordType) dt.getDatatype();
+            ARecordType metaRecordType = null;
+            if (ds.hasMetaPart()) {
+                Datatype metaDt = MetadataManager.INSTANCE.getDatatype(metadataProvider.getMetadataTxnContext(),
+                        ds.getMetaItemTypeDataverseName(), ds.getMetaItemTypeName());
+                metaRecordType = (ARecordType) metaDt.getDatatype();
+            }
 
             List<List<String>> indexFields = new ArrayList<List<String>>();
             List<IAType> indexFieldTypes = new ArrayList<IAType>();
+            int keyIndex = 0;
             for (Pair<List<String>, TypeExpression> fieldExpr : stmtCreateIndex.getFieldExprs()) {
                 IAType fieldType = null;
-                boolean isOpen = aRecordType.isOpen();
-                ARecordType subType = aRecordType;
+                ARecordType subType = KeyFieldTypeUtils.chooseSource(keySourceIndicators, keyIndex, aRecordType,
+                        metaRecordType);
+                boolean isOpen = subType.isOpen();
                 int i = 0;
                 if (fieldExpr.first.size() > 1 && !isOpen) {
                     for (; i < fieldExpr.first.size() - 1;) {
@@ -885,9 +892,11 @@
 
                 indexFields.add(fieldExpr.first);
                 indexFieldTypes.add(fieldType);
+                ++keyIndex;
             }
 
-            ValidateUtil.validateKeyFields(aRecordType, indexFields, indexFieldTypes, stmtCreateIndex.getIndexType());
+            ValidateUtil.validateKeyFields(aRecordType, metaRecordType, indexFields, keySourceIndicators,
+                    indexFieldTypes, stmtCreateIndex.getIndexType());
 
             if (idx != null) {
                 if (stmtCreateIndex.getIfNotExists()) {
@@ -995,8 +1004,8 @@
 
             //#. add a new index with PendingAddOp
             Index index = new Index(dataverseName, datasetName, indexName, stmtCreateIndex.getIndexType(), indexFields,
-                    stmtCreateIndex.getFieldSourceIndicators(), indexFieldTypes, stmtCreateIndex.getGramLength(),
-                    stmtCreateIndex.isEnforced(), false, IMetadataEntity.PENDING_ADD_OP);
+                    keySourceIndicators, indexFieldTypes, stmtCreateIndex.getGramLength(), stmtCreateIndex.isEnforced(),
+                    false, IMetadataEntity.PENDING_ADD_OP);
             MetadataManager.INSTANCE.addIndex(metadataProvider.getMetadataTxnContext(), index);
 
             ARecordType enforcedType = null;
diff --git a/asterix-app/src/main/java/org/apache/asterix/file/SecondaryIndexOperationsHelper.java b/asterix-app/src/main/java/org/apache/asterix/file/SecondaryIndexOperationsHelper.java
index 24df771..bd49293 100644
--- a/asterix-app/src/main/java/org/apache/asterix/file/SecondaryIndexOperationsHelper.java
+++ b/asterix-app/src/main/java/org/apache/asterix/file/SecondaryIndexOperationsHelper.java
@@ -20,7 +20,6 @@
 package org.apache.asterix.file;
 
 import java.io.DataOutput;
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -255,12 +254,7 @@
             secondaryBTreeFields[i] = i;
         }
 
-        IAType type;
-        try {
-            type = itemType.getSubFieldType(filterFieldName);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
-        }
+        IAType type = itemType.getSubFieldType(filterFieldName);
         filterCmpFactories[0] = AqlBinaryComparatorFactoryProvider.INSTANCE.getBinaryComparatorFactory(type, true);
         filterTypeTraits[0] = AqlTypeTraitProvider.INSTANCE.getTypeTrait(type);
         secondaryFilterFields[0] = getNumSecondaryKeys() + numPrimaryKeys;
@@ -278,12 +272,7 @@
         primaryBloomFilterKeyFields = new int[numPrimaryKeys];
         ISerializerDeserializerProvider serdeProvider = metadataProvider.getFormat().getSerdeProvider();
         for (int i = 0; i < numPrimaryKeys; i++) {
-            IAType keyType;
-            try {
-                keyType = itemType.getSubFieldType(partitioningKeys.get(i));
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            IAType keyType = itemType.getSubFieldType(partitioningKeys.get(i));
             primaryRecFields[i] = serdeProvider.getSerializerDeserializer(keyType);
             primaryComparatorFactories[i] = AqlBinaryComparatorFactoryProvider.INSTANCE
                     .getBinaryComparatorFactory(keyType, true);
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-1/dataset_with_meta-1.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-1/dataset_with_meta-1.1.ddl.aql
index 6539dbc..f2682fa 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-1/dataset_with_meta-1.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-1/dataset_with_meta-1.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
     id: string
 }
 
@@ -30,6 +30,6 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key id;
 
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-2/dataset_with_meta-2.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-2/dataset_with_meta-2.1.ddl.aql
index cec01d2..2f4f181 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-2/dataset_with_meta-2.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-2/dataset_with_meta-2.1.ddl.aql
@@ -20,7 +20,7 @@
 drop dataverse meta if exists;
 create dataverse meta;
 use dataverse meta;
-create type EmptyType as open {
+create type AuxiliaryType as open {
     id: string
 }
 
@@ -34,6 +34,6 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(meta.EmptyType)
+create dataset Book(LineType) with meta(meta.AuxiliaryType)
 primary key id;
 
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-3/dataset_with_meta-3.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-3/dataset_with_meta-3.1.ddl.aql
index 78bd7ab..138a211 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-3/dataset_with_meta-3.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-3/dataset_with_meta-3.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
     id: string
 }
 
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-5/dataset_with_meta-5.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-5/dataset_with_meta-5.1.ddl.aql
index ddb42c8..07df579 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-5/dataset_with_meta-5.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-5/dataset_with_meta-5.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
   "key":int32
 }
 
@@ -31,6 +31,6 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key meta()."key";
 
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-6/dataset_with_meta-6.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-6/dataset_with_meta-6.1.ddl.aql
index 510137f..c987a4c 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-6/dataset_with_meta-6.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-6/dataset_with_meta-6.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
     id: string
 }
 
@@ -30,7 +30,7 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key id;
 
 create index MetaIndex on Book(meta().id) type btree;
diff --git a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-7/dataset_with_meta-6.1.ddl.aql b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-7/dataset_with_meta-6.1.ddl.aql
index 41b50d0..94f253f 100644
--- a/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-7/dataset_with_meta-6.1.ddl.aql
+++ b/asterix-app/src/test/resources/metadata/queries/basic/dataset_with_meta-7/dataset_with_meta-6.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
     id: string
 }
 
@@ -30,7 +30,7 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key id;
 
 create index MetaIndex on Book(meta().id, id) type btree;
diff --git a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-1/dataset_with_meta-1.1.adm b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-1/dataset_with_meta-1.1.adm
index 776e07f..e41fe03 100644
--- a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-1/dataset_with_meta-1.1.adm
+++ b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-1/dataset_with_meta-1.1.adm
@@ -1 +1 @@
-{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "id" ] ], "PrimaryKey": [ [ "id" ] ], "Autogenerated": false }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Feb 24 17:32:57 PST 2016", "DatasetId": 101i32, "PendingOp": 0i32, "MetatypeDataverseName": "test", "MetatypeName": "EmptyType" }
+{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "id" ] ], "PrimaryKey": [ [ "id" ] ], "Autogenerated": false }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Feb 24 17:32:57 PST 2016", "DatasetId": 101i32, "PendingOp": 0i32, "MetatypeDataverseName": "test", "MetatypeName": "AuxiliaryType" }
diff --git a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-2/dataset_with_meta-2.1.adm b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-2/dataset_with_meta-2.1.adm
index f3f00e9..2663a16 100644
--- a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-2/dataset_with_meta-2.1.adm
+++ b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-2/dataset_with_meta-2.1.adm
@@ -1 +1 @@
-{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "id" ] ], "PrimaryKey": [ [ "id" ] ], "Autogenerated": false }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Feb 24 17:32:57 PST 2016", "DatasetId": 101i32, "PendingOp": 0i32, "MetatypeDataverseName": "meta", "MetatypeName": "EmptyType" }
+{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "id" ] ], "PrimaryKey": [ [ "id" ] ], "Autogenerated": false }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Feb 24 17:32:57 PST 2016", "DatasetId": 101i32, "PendingOp": 0i32, "MetatypeDataverseName": "meta", "MetatypeName": "AuxiliaryType" }
diff --git a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-5/dataset_with_meta-5.3.adm b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-5/dataset_with_meta-5.3.adm
index 485ca4e..74ac8f9 100644
--- a/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-5/dataset_with_meta-5.3.adm
+++ b/asterix-app/src/test/resources/metadata/results/basic/dataset_with_meta-5/dataset_with_meta-5.3.adm
@@ -1 +1 @@
-{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "key" ] ], "PrimaryKey": [ [ "key" ] ], "Autogenerated": false, "KeySourceIndicator": [ 1i8 ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Mon Feb 29 15:58:41 PST 2016", "DatasetId": 105i32, "PendingOp": 0i32, "MetatypeDataverseName": "test", "MetatypeName": "EmptyType" }
+{ "DataverseName": "test", "DatasetName": "Book", "DatatypeDataverseName": "test", "DatatypeName": "LineType", "DatasetType": "INTERNAL", "GroupName": "DEFAULT_NG_ALL_NODES", "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ], "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ [ "key" ] ], "PrimaryKey": [ [ "key" ] ], "Autogenerated": false, "KeySourceIndicator": [ 1i8 ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Mon Feb 29 15:58:41 PST 2016", "DatasetId": 105i32, "PendingOp": 0i32, "MetatypeDataverseName": "test", "MetatypeName": "AuxiliaryType" }
diff --git a/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-1.aql b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-1.aql
new file mode 100644
index 0000000..c07dcb1
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-1.aql
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  id:int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta().id;
+
+
+for $x in dataset Book
+where meta($x).id >10
+return $x;
diff --git a/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-2.aql b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-2.aql
new file mode 100644
index 0000000..f236645
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta-2.aql
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  id:int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta().id;
+
+
+for $x in dataset Book
+where meta().id >10
+return $x;
diff --git a/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_index_join.aql b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_index_join.aql
new file mode 100644
index 0000000..d1ec9bc
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_index_join.aql
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  id:int32
+}
+
+create type BookType as open {
+  id:int32,
+  text: string
+}
+
+create type ChapterType as open {
+  id: int32,
+  book_id: int32,
+  abstract: string
+}
+
+create dataset Book(BookType) with meta(AuxiliaryType)
+primary key meta().id;
+
+create dataset Chapter(ChapterType)
+primary key id;
+
+
+for $c in dataset Chapter
+for $b in dataset Book
+where $c.book_id /*+ indexnl */= meta().id
+return {
+   "id": meta().id,
+   "abstract": $c.abstract
+};
diff --git a/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_self_index_join.aql b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_self_index_join.aql
new file mode 100644
index 0000000..953802e
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/meta/primary_index_with_meta_self_index_join.aql
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  id:int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta().id;
+
+
+for $x in dataset Book
+for $y in dataset Book
+where $x.id /*+ indexnl */= meta($y).id
+return {
+   "id": meta($y).id,
+   "text": $x.text
+};
diff --git a/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta-1.plan b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta-1.plan
new file mode 100644
index 0000000..6f13ebb
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta-1.plan
@@ -0,0 +1,8 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- 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/optimizerts/results/meta/primary_index_with_meta-2.plan b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta-2.plan
new file mode 100644
index 0000000..6f13ebb
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta-2.plan
@@ -0,0 +1,8 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- 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/optimizerts/results/meta/primary_index_with_meta_index_join.plan b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta_index_join.plan
new file mode 100644
index 0000000..1c763ec
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta_index_join.plan
@@ -0,0 +1,17 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- BTREE_SEARCH  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STABLE_SORT [$$16(ASC)]  |PARTITIONED|
+                  -- HASH_PARTITION_EXCHANGE [$$16]  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ASSIGN  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta_self_index_join.plan b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta_self_index_join.plan
new file mode 100644
index 0000000..b39eb44
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/meta/primary_index_with_meta_self_index_join.plan
@@ -0,0 +1,17 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- BTREE_SEARCH  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STABLE_SORT [$$17(ASC)]  |PARTITIONED|
+                  -- HASH_PARTITION_EXCHANGE [$$17]  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ASSIGN  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-1/query_dataset_with_meta-1.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-1/query_dataset_with_meta-1.1.ddl.aql
index ca5a534..f146ef6 100644
--- a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-1/query_dataset_with_meta-1.1.ddl.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-1/query_dataset_with_meta-1.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
   "key":int32
 }
 
@@ -30,5 +30,5 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key id;
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-2/query_dataset_with_meta-2.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-2/query_dataset_with_meta-2.1.ddl.aql
index a341b29..704ece0 100644
--- a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-2/query_dataset_with_meta-2.1.ddl.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta-2/query_dataset_with_meta-2.1.ddl.aql
@@ -21,7 +21,7 @@
 create dataverse test;
 use dataverse test;
 
-create type EmptyType as open {
+create type AuxiliaryType as open {
   "key":int32
 }
 
@@ -30,5 +30,5 @@
   text: string
 }
 
-create dataset Book(LineType) with meta(EmptyType)
+create dataset Book(LineType) with meta(AuxiliaryType)
 primary key meta()."key";
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.ddl.aql
new file mode 100644
index 0000000..704ece0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.ddl.aql
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  "key":int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta()."key";
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.2.update.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.2.update.aql
new file mode 100644
index 0000000..bd244d0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.2.update.aql
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.3.query.aql
new file mode 100644
index 0000000..0ca33ab
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.3.query.aql
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+use dataverse test;
+
+for $x in dataset Book
+for $y in dataset Book
+where meta()."key" = $y.id
+return $x;
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.4.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.4.ddl.aql
new file mode 100644
index 0000000..dc10acd
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.4.ddl.aql
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.ddl.aql
new file mode 100644
index 0000000..704ece0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.ddl.aql
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  "key":int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta()."key";
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.2.update.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.2.update.aql
new file mode 100644
index 0000000..bd244d0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.2.update.aql
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.3.query.aql
new file mode 100644
index 0000000..246046d
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.3.query.aql
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+use dataverse test;
+
+for $x in dataset Book
+where meta($x)."key" >10
+return $x;
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.4.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.4.ddl.aql
new file mode 100644
index 0000000..dc10acd
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.4.ddl.aql
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.ddl.aql
new file mode 100644
index 0000000..704ece0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.ddl.aql
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type AuxiliaryType as open {
+  "key":int32
+}
+
+create type LineType as open {
+  id:int32,
+  text: string
+}
+
+create dataset Book(LineType) with meta(AuxiliaryType)
+primary key meta()."key";
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.2.update.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.2.update.aql
new file mode 100644
index 0000000..bd244d0
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.2.update.aql
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.3.query.aql
new file mode 100644
index 0000000..6b8e4a9
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.3.query.aql
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+use dataverse test;
+
+for $x in dataset Book
+where meta()."key" >10
+return $x;
+
diff --git a/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.4.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.4.ddl.aql
new file mode 100644
index 0000000..dc10acd
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.4.ddl.aql
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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.
+ */
+
+drop dataverse test if exists;
diff --git a/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.adm b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.adm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_failure/query_dataset_with_meta_failure.1.adm
diff --git a/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.adm b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.adm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-1/query_dataset_with_meta_primary_index-1.1.adm
diff --git a/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.adm b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.adm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/meta/query_dataset_with_meta_primary_index-2/query_dataset_with_meta_primary_index-2.1.adm
diff --git a/asterix-app/src/test/resources/runtimets/testsuite.xml b/asterix-app/src/test/resources/runtimets/testsuite.xml
index 05f131e..adcb8cd 100644
--- a/asterix-app/src/test/resources/runtimets/testsuite.xml
+++ b/asterix-app/src/test/resources/runtimets/testsuite.xml
@@ -2916,6 +2916,22 @@
                 <output-dir compare="Text">query_dataset_with_meta-2</output-dir>
             </compilation-unit>
         </test-case>
+        <test-case FilePath="meta">
+            <compilation-unit name="query_dataset_with_meta_primary_index-1">
+                <output-dir compare="Text">query_dataset_with_meta_primary_index-1</output-dir>
+            </compilation-unit>
+        </test-case>
+        <test-case FilePath="meta">
+            <compilation-unit name="query_dataset_with_meta_primary_index-2">
+                <output-dir compare="Text">query_dataset_with_meta_primary_index-2</output-dir>
+            </compilation-unit>
+        </test-case>
+        <test-case FilePath="meta">
+            <compilation-unit name="query_dataset_with_meta_failure">
+                <output-dir compare="Text">query_dataset_with_meta_failure</output-dir>
+                <expected-error>org.apache.hyracks.algebricks.common.exceptions.AlgebricksException: Cannot resolve to ambiguity on the meta function call -- there are more than once dataset choices!</expected-error>
+            </compilation-unit>
+        </test-case>
     </test-group>
     <test-group name="misc">
         <test-case FilePath="misc">
diff --git a/asterix-common/src/main/java/org/apache/asterix/common/exceptions/AsterixException.java b/asterix-common/src/main/java/org/apache/asterix/common/exceptions/AsterixException.java
index 75175f0..afccede 100644
--- a/asterix-common/src/main/java/org/apache/asterix/common/exceptions/AsterixException.java
+++ b/asterix-common/src/main/java/org/apache/asterix/common/exceptions/AsterixException.java
@@ -18,7 +18,9 @@
  */
 package org.apache.asterix.common.exceptions;
 
-public class AsterixException extends Exception {
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+
+public class AsterixException extends AlgebricksException {
     private static final long serialVersionUID = 1L;
 
     public AsterixException() {
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlDataSource.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlDataSource.java
index a10e191..dcd10b7 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlDataSource.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlDataSource.java
@@ -209,4 +209,20 @@
         this.properties = properties;
     }
 
+    public LogicalVariable getMetaVariable(List<LogicalVariable> dataScanVariables) {
+        if (hasMeta()) {
+            return dataScanVariables.get(dataScanVariables.size() - 1);
+        } else {
+            return null;
+        }
+    }
+
+    public LogicalVariable getDataRecordVariable(List<LogicalVariable> dataScanVariables) {
+        if (hasMeta()) {
+            return dataScanVariables.get(dataScanVariables.size() - 2);
+        } else {
+            return dataScanVariables.get(dataScanVariables.size() - 1);
+        }
+    }
+
 }
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlMetadataProvider.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlMetadataProvider.java
index 7ce7ba3..e0084f8 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlMetadataProvider.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/AqlMetadataProvider.java
@@ -783,12 +783,7 @@
         }
 
         for (int j = 0; j < pidxKeyFieldCount; ++j, ++i) {
-            IAType keyType = null;
-            try {
-                keyType = recType.getSubFieldType(pidxKeyFieldNames.get(j));
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            IAType keyType = recType.getSubFieldType(pidxKeyFieldNames.get(j));
             comparatorFactories[i] = AqlBinaryComparatorFactoryProvider.INSTANCE.getBinaryComparatorFactory(keyType,
                     true);
             typeTraits[i] = AqlTypeTraitProvider.INSTANCE.getTypeTrait(keyType);
@@ -989,7 +984,7 @@
         AqlDataSourceType datasourceType = dataset.getDatasetType().equals(DatasetType.EXTERNAL)
                 ? AqlDataSourceType.EXTERNAL_DATASET : AqlDataSourceType.INTERNAL_DATASET;
         return new DatasetDataSource(aqlId, aqlId.getDataverseName(), aqlId.getDatasourceName(), itemType, metaItemType,
-                datasourceType);
+                datasourceType, dataset.getDatasetDetails());
     }
 
     @Override
@@ -1510,8 +1505,6 @@
 
         } catch (MetadataException e) {
             throw new AlgebricksException(e);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
         }
     }
 
@@ -1680,8 +1673,6 @@
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
         } catch (MetadataException e) {
             throw new AlgebricksException(e);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
         }
     }
 
@@ -1892,8 +1883,6 @@
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
         } catch (MetadataException e) {
             throw new AlgebricksException(e);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
         }
     }
 
@@ -2038,7 +2027,7 @@
                         NoOpOperationCallbackFactory.INSTANCE);
             }
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
-        } catch (MetadataException | IOException e) {
+        } catch (MetadataException e) {
             throw new AlgebricksException(e);
         }
     }
@@ -2654,8 +2643,6 @@
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
         } catch (MetadataException e) {
             throw new AlgebricksException(e);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
         }
     }
 
@@ -2808,7 +2795,7 @@
                     filterFactory, false, indexName, null, modificationCallbackFactory,
                     NoOpOperationCallbackFactory.INSTANCE, prevFieldPermutation);
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
-        } catch (MetadataException | IOException e) {
+        } catch (MetadataException e) {
             throw new AlgebricksException(e);
         }
     }
@@ -2959,8 +2946,6 @@
             return new Pair<IOperatorDescriptor, AlgebricksPartitionConstraint>(op, splitsAndConstraint.second);
         } catch (MetadataException e) {
             throw new AlgebricksException(e);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
         }
     }
 }
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/DatasetDataSource.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/DatasetDataSource.java
index 8b5956f..5483d49 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/DatasetDataSource.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/DatasetDataSource.java
@@ -21,10 +21,12 @@
 import java.io.IOException;
 import java.util.List;
 
+import org.apache.asterix.metadata.IDatasetDetails;
 import org.apache.asterix.metadata.MetadataManager;
 import org.apache.asterix.metadata.MetadataTransactionContext;
 import org.apache.asterix.metadata.entities.Dataset;
-import org.apache.asterix.metadata.utils.DatasetUtils;
+import org.apache.asterix.metadata.entities.InternalDatasetDetails;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
@@ -36,7 +38,8 @@
     private Dataset dataset;
 
     public DatasetDataSource(AqlSourceId id, String datasourceDataverse, String datasourceName, IAType itemType,
-            IAType metaItemType, AqlDataSourceType datasourceType) throws AlgebricksException {
+            IAType metaItemType, AqlDataSourceType datasourceType, IDatasetDetails datasetDetails)
+                    throws AlgebricksException {
         super(id, itemType, metaItemType, datasourceType);
         MetadataTransactionContext ctx = null;
         try {
@@ -49,7 +52,7 @@
             MetadataManager.INSTANCE.commitTransaction(ctx);
             switch (dataset.getDatasetType()) {
                 case INTERNAL:
-                    initInternalDataset(itemType, metaItemType);
+                    initInternalDataset(itemType, metaItemType, datasetDetails);
                     break;
                 case EXTERNAL:
                     initExternalDataset(itemType);
@@ -74,13 +77,17 @@
         return dataset;
     }
 
-    private void initInternalDataset(IAType itemType, IAType metaItemType) throws IOException, AlgebricksException {
-        List<List<String>> partitioningKeys = DatasetUtils.getPartitioningKeys(dataset);
+    private void initInternalDataset(IAType itemType, IAType metaItemType, IDatasetDetails datasetDetails)
+            throws IOException, AlgebricksException {
+        InternalDatasetDetails internalDatasetDetails = (InternalDatasetDetails) datasetDetails;
         ARecordType recordType = (ARecordType) itemType;
-        int n = partitioningKeys.size();
+        ARecordType metaRecordType = (ARecordType) metaItemType;
+        List<IAType> partitioningKeyTypes = KeyFieldTypeUtils.getPartitioningKeyTypes(internalDatasetDetails,
+                recordType, metaRecordType);
+        int n = partitioningKeyTypes.size();
         schemaTypes = metaItemType == null ? new IAType[n + 1] : new IAType[n + 2];
-        for (int i = 0; i < n; i++) {
-            schemaTypes[i] = recordType.getSubFieldType(partitioningKeys.get(i));
+        for (int keyIndex = 0; keyIndex < n; ++keyIndex) {
+            schemaTypes[keyIndex] = partitioningKeyTypes.get(keyIndex);
         }
         schemaTypes[n] = itemType;
         if (metaItemType != null) {
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Index.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Index.java
index db3ccc1..3ff9a57 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Index.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Index.java
@@ -19,17 +19,16 @@
 
 package org.apache.asterix.metadata.entities;
 
-import java.io.IOException;
 import java.util.List;
 
 import org.apache.asterix.common.config.DatasetConfig.IndexType;
+import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.metadata.MetadataCache;
 import org.apache.asterix.metadata.api.IMetadataEntity;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.util.NonTaggedFormatUtil;
-import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Pair;
 
 /**
@@ -130,7 +129,7 @@
         return !isPrimaryIndex();
     }
 
-    public static Pair<IAType, Boolean> getNonNullableType(IAType keyType) throws AlgebricksException {
+    public static Pair<IAType, Boolean> getNonNullableType(IAType keyType) throws AsterixException {
         boolean nullable = false;
         if (NonTaggedFormatUtil.isOptional(keyType)) {
             keyType = ((AUnionType) keyType).getNullableType();
@@ -140,7 +139,7 @@
     }
 
     public static Pair<IAType, Boolean> getNonNullableOpenFieldType(IAType fieldType, List<String> fieldName,
-            ARecordType recType) throws AlgebricksException {
+            ARecordType recType) throws AsterixException {
         Pair<IAType, Boolean> keyPairType = null;
         IAType subType = recType;
         for (int i = 0; i < fieldName.size(); i++) {
@@ -157,18 +156,14 @@
     }
 
     public static Pair<IAType, Boolean> getNonNullableKeyFieldType(List<String> expr, ARecordType recType)
-            throws AlgebricksException {
+            throws AsterixException {
         IAType keyType = Index.keyFieldType(expr, recType);
         return getNonNullableType(keyType);
     }
 
-    private static IAType keyFieldType(List<String> expr, ARecordType recType) throws AlgebricksException {
+    private static IAType keyFieldType(List<String> expr, ARecordType recType) throws AsterixException {
         IAType fieldType = recType;
-        try {
-            fieldType = recType.getSubFieldType(expr);
-        } catch (IOException e) {
-            throw new AlgebricksException("Could not find field " + expr + " in the schema.", e);
-        }
+        fieldType = recType.getSubFieldType(expr);
         return fieldType;
     }
 
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/InternalDatasetDetails.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/InternalDatasetDetails.java
index 6971037..b0c6c51 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/InternalDatasetDetails.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/InternalDatasetDetails.java
@@ -20,6 +20,7 @@
 package org.apache.asterix.metadata.entities;
 
 import java.io.DataOutput;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.asterix.builders.IARecordBuilder;
@@ -75,6 +76,13 @@
         this.partitioningStrategy = partitioningStrategy;
         this.partitioningKeys = partitioningKey;
         this.primaryKeys = primaryKey;
+        if (keyFieldIndicators == null) {
+            // Create a dummy list.
+            keyFieldIndicators = new ArrayList<>();
+            for (int index = 0; index < partitioningKey.size(); ++index) {
+                keyFieldIndicators.add(0);
+            }
+        }
         this.keySourceIndicators = keyFieldIndicators;
         this.primaryKeyTypes = primaryKeyType;
         this.autogenerated = autogenerated;
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/IndexTupleTranslator.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/IndexTupleTranslator.java
index f310a03..cedf0b4 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/IndexTupleTranslator.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/IndexTupleTranslator.java
@@ -29,6 +29,7 @@
 
 import org.apache.asterix.builders.OrderedListBuilder;
 import org.apache.asterix.common.config.DatasetConfig.IndexType;
+import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.common.transactions.JobId;
 import org.apache.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
 import org.apache.asterix.metadata.MetadataException;
@@ -38,6 +39,7 @@
 import org.apache.asterix.metadata.entities.AsterixBuiltinTypeMap;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.metadata.utils.KeyFieldTypeUtils;
 import org.apache.asterix.om.base.ABoolean;
 import org.apache.asterix.om.base.ACollectionCursor;
 import org.apache.asterix.om.base.AInt32;
@@ -142,18 +144,7 @@
             IAType fieldType = AsterixBuiltinTypeMap.getTypeFromTypeName(metadataNode, jobId, dvName, typeName, false);
             searchKeyType.add(fieldType);
         }
-        // index key type information is not persisted, thus we extract type information from the record metadata
-        if (searchKeyType.isEmpty()) {
-            Dataset dSet = metadataNode.getDataset(jobId, dvName, dsName);
-            String datatypeName = dSet.getItemTypeName();
-            String datatypeDataverseName = dSet.getItemTypeDataverseName();
-            ARecordType recordDt = (ARecordType) metadataNode.getDatatype(jobId, datatypeDataverseName, datatypeName)
-                    .getDatatype();
-            for (int i = 0; i < searchKey.size(); i++) {
-                IAType fieldType = recordDt.getSubFieldType(searchKey.get(i));
-                searchKeyType.add(fieldType);
-            }
-        }
+
         int isEnforcedFieldPos = rec.getType().getFieldIndex(INDEX_ISENFORCED_FIELD_NAME);
         Boolean isEnforcingKeys = false;
         if (isEnforcedFieldPos > 0) {
@@ -183,6 +174,27 @@
                 keyFieldSourceIndicator.add(0);
             }
         }
+
+        // index key type information is not persisted, thus we extract type information from the record metadata
+        if (searchKeyType.isEmpty()) {
+            Dataset dSet = metadataNode.getDataset(jobId, dvName, dsName);
+            String datatypeName = dSet.getItemTypeName();
+            String datatypeDataverseName = dSet.getItemTypeDataverseName();
+            ARecordType recordDt = (ARecordType) metadataNode.getDatatype(jobId, datatypeDataverseName, datatypeName)
+                    .getDatatype();
+            String metatypeName = dSet.getMetaItemTypeName();
+            String metatypeDataverseName = dSet.getMetaItemTypeDataverseName();
+            ARecordType metaDt = null;
+            if (metatypeName != null && metatypeDataverseName != null) {
+                metaDt = (ARecordType) metadataNode.getDatatype(jobId, metatypeDataverseName, metatypeName)
+                        .getDatatype();
+            }
+            try {
+                searchKeyType = KeyFieldTypeUtils.getKeyTypes(recordDt, metaDt, searchKey, keyFieldSourceIndicator);
+            } catch (AsterixException e) {
+                throw new MetadataException(e);
+            }
+        }
         return new Index(dvName, dsName, indexName, indexStructure, searchKey, keyFieldSourceIndicator, searchKeyType,
                 gramLength, isEnforcingKeys, isPrimaryIndex, pendingOp);
     }
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/DatasetUtils.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/DatasetUtils.java
index 19471de..4e8c34c 100644
--- a/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/DatasetUtils.java
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/DatasetUtils.java
@@ -20,7 +20,6 @@
 package org.apache.asterix.metadata.utils;
 
 import java.io.DataOutput;
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -73,12 +72,7 @@
             }
         } else {
             for (int i = 0; i < partitioningKeys.size(); i++) {
-                IAType keyType;
-                try {
-                    keyType = itemType.getSubFieldType(partitioningKeys.get(i));
-                } catch (IOException e) {
-                    throw new AlgebricksException(e);
-                }
+                IAType keyType = itemType.getSubFieldType(partitioningKeys.get(i));
                 bcfs[i] = comparatorFactoryProvider.getBinaryComparatorFactory(keyType, true);
             }
         }
@@ -105,12 +99,7 @@
         List<List<String>> partitioningKeys = getPartitioningKeys(dataset);
         IBinaryHashFunctionFactory[] bhffs = new IBinaryHashFunctionFactory[partitioningKeys.size()];
         for (int i = 0; i < partitioningKeys.size(); i++) {
-            IAType keyType;
-            try {
-                keyType = itemType.getSubFieldType(partitioningKeys.get(i));
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            IAType keyType = itemType.getSubFieldType(partitioningKeys.get(i));
             bhffs[i] = hashFunProvider.getBinaryHashFunctionFactory(keyType);
         }
         return bhffs;
@@ -125,12 +114,7 @@
         int numKeys = partitioningKeys.size();
         ITypeTraits[] typeTraits = new ITypeTraits[numKeys + 1];
         for (int i = 0; i < numKeys; i++) {
-            IAType keyType;
-            try {
-                keyType = itemType.getSubFieldType(partitioningKeys.get(i));
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            IAType keyType = itemType.getSubFieldType(partitioningKeys.get(i));
             typeTraits[i] = AqlTypeTraitProvider.INSTANCE.getTypeTrait(keyType);
         }
         typeTraits[numKeys] = AqlTypeTraitProvider.INSTANCE.getTypeTrait(itemType);
@@ -159,12 +143,7 @@
             return null;
         }
         IBinaryComparatorFactory[] bcfs = new IBinaryComparatorFactory[1];
-        IAType type;
-        try {
-            type = itemType.getSubFieldType(filterField);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
-        }
+        IAType type = itemType.getSubFieldType(filterField);
         bcfs[0] = comparatorFactoryProvider.getBinaryComparatorFactory(type, true);
         return bcfs;
     }
@@ -179,13 +158,7 @@
             return null;
         }
         ITypeTraits[] typeTraits = new ITypeTraits[1];
-
-        IAType type;
-        try {
-            type = itemType.getSubFieldType(filterField);
-        } catch (IOException e) {
-            throw new AlgebricksException(e);
-        }
+        IAType type = itemType.getSubFieldType(filterField);
         typeTraits[0] = AqlTypeTraitProvider.INSTANCE.getTypeTrait(type);
         return typeTraits;
     }
diff --git a/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtils.java b/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtils.java
new file mode 100644
index 0000000..60ca42f
--- /dev/null
+++ b/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/KeyFieldTypeUtils.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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 org.apache.asterix.metadata.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.config.DatasetConfig.DatasetType;
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.metadata.entities.InternalDatasetDetails;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.om.util.NonTaggedFormatUtil;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+public class KeyFieldTypeUtils {
+
+    /**
+     * Get the types of primary key (partitioning key) fields
+     *
+     * @param dataset,
+     *            the dataset to consider.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return a list of IATypes, one for each corresponding primary key field.
+     * @throws AsterixException
+     */
+    public static List<IAType> getPartitoningKeyTypes(Dataset dataset, ARecordType recordType,
+            ARecordType metaRecordType) throws AsterixException {
+        if (dataset.getDatasetType() != DatasetType.INTERNAL) {
+            return null;
+        }
+        InternalDatasetDetails datasetDetails = (InternalDatasetDetails) dataset.getDatasetDetails();
+        return getPartitioningKeyTypes(datasetDetails, recordType, metaRecordType);
+    }
+
+    /**
+     * Get the types of primary key (partitioning key) fields
+     *
+     * @param datasetDetails,
+     *            contains specific data structures for an internal dataset.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return a list of IATypes, one for each corresponding primary key field.
+     * @throws AsterixException
+     */
+    public static List<IAType> getPartitioningKeyTypes(InternalDatasetDetails datasetDetails, ARecordType recordType,
+            ARecordType metaRecordType) throws AsterixException {
+        List<Integer> keySourceIndicators = datasetDetails.getKeySourceIndicator();
+        List<List<String>> partitioningKeys = datasetDetails.getPartitioningKey();
+        return getKeyTypes(recordType, metaRecordType, partitioningKeys, keySourceIndicators);
+    }
+
+    /**
+     * Get the types of key fields for an index, either primary or secondary.
+     *
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType,
+     *            the auxiliary meta record type.
+     * @param keys,
+     *            the list of key fields.
+     * @param keySourceIndicators,
+     *            a list of integers to indicate that each key field is from the main record or the auxiliary meta record.
+     * @return a list of IATypes, one for each corresponding index key field.
+     * @throws AsterixException
+     */
+    public static List<IAType> getKeyTypes(ARecordType recordType, ARecordType metaRecordType, List<List<String>> keys,
+            List<Integer> keySourceIndicators) throws AsterixException {
+        List<IAType> keyTypes = new ArrayList<>();
+        int index = 0;
+        for (List<String> partitioningKey : keys) {
+            keyTypes.add(chooseSource(keySourceIndicators, index, recordType, metaRecordType)
+                    .getSubFieldType(partitioningKey));
+            ++index;
+        }
+        return keyTypes;
+    }
+
+    /**
+     * Get the types of BTree index key fields
+     *
+     * @param index,
+     *            the index to consider.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return a list of IATypes, one for each corresponding index key field.
+     * @throws AsterixException
+     */
+    public static List<IAType> getBTreeIndexKeyTypes(Index index, ARecordType recordType, ARecordType metaRecordType)
+            throws AsterixException {
+        List<Integer> keySourceIndicators = index.getKeyFieldSourceIndicators();
+        List<IAType> indexKeyTypes = new ArrayList<>();
+        for (int i = 0; i < index.getKeyFieldNames().size(); i++) {
+            Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(index.getKeyFieldTypes().get(i),
+                    index.getKeyFieldNames().get(i), chooseSource(keySourceIndicators, i, recordType, metaRecordType));
+            indexKeyTypes.add(keyPairType.first);
+        }
+        return indexKeyTypes;
+    }
+
+    /**
+     * Get the types of RTree index key fields
+     *
+     * @param index,
+     *            the index to consider.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return a list of IATypes, one for each corresponding index key field.
+     * @throws AsterixException
+     */
+    public static List<IAType> getRTreeIndexKeyTypes(Index index, ARecordType recordType, ARecordType metaRecordType)
+            throws AsterixException {
+        List<Integer> keySourceIndicators = index.getKeyFieldSourceIndicators();
+        List<IAType> indexKeyTypes = new ArrayList<>();
+        ARecordType targetRecType = chooseSource(keySourceIndicators, 0, recordType, metaRecordType);
+        Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(index.getKeyFieldTypes().get(0),
+                index.getKeyFieldNames().get(0), targetRecType);
+        IAType keyType = keyPairType.first;
+        IAType nestedKeyType = NonTaggedFormatUtil.getNestedSpatialType(keyType.getTypeTag());
+        int numKeys = KeyFieldTypeUtils.getNumSecondaryKeys(index, targetRecType, metaRecordType);
+        for (int i = 0; i < numKeys; i++) {
+            indexKeyTypes.add(nestedKeyType);
+        }
+        return indexKeyTypes;
+    }
+
+    /**
+     * Get the number of secondary index keys.
+     *
+     * @param index,
+     *            the index to consider.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return the number of secondary index keys.
+     * @throws AsterixException
+     */
+    public static int getNumSecondaryKeys(Index index, ARecordType recordType, ARecordType metaRecordType)
+            throws AsterixException {
+        List<Integer> keySourceIndicators = index.getKeyFieldSourceIndicators();
+        switch (index.getIndexType()) {
+            case BTREE:
+            case SINGLE_PARTITION_WORD_INVIX:
+            case SINGLE_PARTITION_NGRAM_INVIX:
+            case LENGTH_PARTITIONED_WORD_INVIX:
+            case LENGTH_PARTITIONED_NGRAM_INVIX: {
+                return index.getKeyFieldNames().size();
+            }
+            case RTREE: {
+                Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType(index.getKeyFieldTypes().get(0),
+                        index.getKeyFieldNames().get(0),
+                        chooseSource(keySourceIndicators, 0, recordType, metaRecordType));
+                IAType keyType = keyPairType.first;
+                int numDimensions = NonTaggedFormatUtil.getNumDimensions(keyType.getTypeTag());
+                return numDimensions * 2;
+            }
+            default: {
+                throw new AsterixException("Unknown index kind: " + index.getIndexType());
+            }
+        }
+    }
+
+    /**
+     * Choose between the main record type and the auxiliary record type according to <code>keySourceIndicators</code>.
+     *
+     * @param keySourceIndicators,
+     *            a list of integers, 0 means to choose <code>recordType</code> and 1
+     *            means to choose <code>metaRecordType</code>.
+     * @param index,
+     *            the offset to consider.
+     * @param recordType,
+     *            the main record type.
+     * @param metaRecordType
+     *            the auxiliary meta record type.
+     * @return the chosen record type.
+     */
+    public static ARecordType chooseSource(List<Integer> keySourceIndicators, int index, ARecordType recordType,
+            ARecordType metaRecordType) {
+        return keySourceIndicators.get(0) == 0 ? recordType : metaRecordType;
+    }
+}
diff --git a/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java b/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
index 997dccd..9f8d5d7 100644
--- a/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
+++ b/asterix-om/src/main/java/org/apache/asterix/om/functions/AsterixBuiltinFunctions.java
@@ -90,6 +90,7 @@
 import org.apache.asterix.om.typecomputer.impl.OptionalATemporalInstanceTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.OptionalATimeTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.OptionalAYearMonthDurationTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.OptionalOpenARecordTypeComputer;
 import org.apache.asterix.om.typecomputer.impl.OrderedListConstructorResultType;
 import org.apache.asterix.om.typecomputer.impl.OrderedListOfAInt32TypeComputer;
 import org.apache.asterix.om.typecomputer.impl.OrderedListOfAInt64TypeComputer;
@@ -723,6 +724,9 @@
     public static final FunctionIdentifier EXTERNAL_LOOKUP = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
             "external-lookup", FunctionIdentifier.VARARGS);
 
+    public static final FunctionIdentifier META = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "meta",
+            FunctionIdentifier.VARARGS);
+
     public static IFunctionInfo getAsterixFunctionInfo(FunctionIdentifier fid) {
         return registeredFunctions.get(fid);
     }
@@ -1028,6 +1032,9 @@
         addFunction(INTERVAL_CONSTRUCTOR_START_FROM_DATETIME, OptionalAIntervalTypeComputer.INSTANCE, true);
         addFunction(INTERVAL_CONSTRUCTOR_START_FROM_TIME, OptionalAIntervalTypeComputer.INSTANCE, true);
 
+        // meta() function
+        addFunction(META, OptionalOpenARecordTypeComputer.INSTANCE, true);
+
         addPrivateFunction(COLLECTION_TO_SEQUENCE, CollectionToSequenceTypeComputer.INSTANCE, true);
 
         // external lookup
diff --git a/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FieldAccessNestedResultType.java b/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FieldAccessNestedResultType.java
index f97e0ac..d8164b7 100644
--- a/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FieldAccessNestedResultType.java
+++ b/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/FieldAccessNestedResultType.java
@@ -18,7 +18,6 @@
  */
 package org.apache.asterix.om.typecomputer.impl;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -78,16 +77,12 @@
         } else {
             throw new AlgebricksException("Typing error: expecting a String, found " + ce + " instead.");
         }
-        try {
-            IAType subType = t0.getSubFieldType(fieldPath);
-            if (subType != null) {
-                return subType;
-            } else {
-                // Open field. Type can only be determined at runtime.
-                return BuiltinType.ANY;
-            }
-        } catch (IOException e) {
-            throw new AlgebricksException("FieldPath was invalid.");
+        IAType subType = t0.getSubFieldType(fieldPath);
+        if (subType != null) {
+            return subType;
+        } else {
+            // Open field. Type can only be determined at runtime.
+            return BuiltinType.ANY;
         }
     }
 
diff --git a/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OptionalOpenARecordTypeComputer.java b/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OptionalOpenARecordTypeComputer.java
new file mode 100644
index 0000000..b630f44
--- /dev/null
+++ b/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/OptionalOpenARecordTypeComputer.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 at
+ *
+ *   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 org.apache.asterix.om.typecomputer.impl;
+
+import org.apache.asterix.om.pointables.base.DefaultOpenFieldType;
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
+
+public class OptionalOpenARecordTypeComputer implements IResultTypeComputer {
+
+    public static final OptionalOpenARecordTypeComputer INSTANCE = new OptionalOpenARecordTypeComputer();
+
+    private OptionalOpenARecordTypeComputer() {
+    }
+
+    @Override
+    public IAType computeType(ILogicalExpression expression, IVariableTypeEnvironment env,
+            IMetadataProvider<?, ?> metadataProvider) throws AlgebricksException {
+        return AUnionType.createNullableType(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE, "OptionalOpenRecord");
+    }
+
+}
diff --git a/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java b/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
index c2eae36..91a67ba 100644
--- a/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
+++ b/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
@@ -19,7 +19,6 @@
 
 package org.apache.asterix.om.types;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -155,9 +154,9 @@
      * @param subFieldName
      *            The full pathname of the child
      * @return the type of the child
-     * @throws IOException
+     * @throws AsterixException
      */
-    public IAType getSubFieldType(List<String> subFieldName) throws IOException {
+    public IAType getSubFieldType(List<String> subFieldName) throws AsterixException {
         IAType subRecordType = getFieldType(subFieldName.get(0));
         for (int i = 1; i < subFieldName.size(); i++) {
             if (subRecordType == null) {
@@ -166,8 +165,8 @@
             if (subRecordType.getTypeTag().equals(ATypeTag.UNION)) {
                 //enforced SubType
                 subRecordType = ((AUnionType) subRecordType).getNullableType();
-                if (subRecordType.getTypeTag().serialize() != ATypeTag.RECORD.serialize()) {
-                    throw new IOException(
+                if (subRecordType.getTypeTag() != ATypeTag.RECORD) {
+                    throw new AsterixException(
                             "Field accessor is not defined for values of type " + subRecordType.getTypeTag());
                 }
 
diff --git a/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java b/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
index 75350f0..054f3e4 100644
--- a/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
+++ b/asterix-runtime/src/main/java/org/apache/asterix/runtime/formats/NonTaggedDataFormat.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.formats;
 
 import java.io.DataOutput;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -880,12 +879,8 @@
             ScalarFunctionCallExpression partitionFun = new ScalarFunctionCallExpression(finfoAccess,
                     new MutableObject<ILogicalExpression>(new VariableReferenceExpression(METADATA_DUMMY_VAR)),
                     new MutableObject<ILogicalExpression>(new ConstantExpression(new AsterixConstantValue(as))));
-            try {
-                return new Triple<IScalarEvaluatorFactory, ScalarFunctionCallExpression, IAType>(evalFactory,
-                        partitionFun, recType.getSubFieldType(fldName));
-            } catch (IOException e) {
-                throw new AlgebricksException(e);
-            }
+            return new Triple<IScalarEvaluatorFactory, ScalarFunctionCallExpression, IAType>(evalFactory, partitionFun,
+                    recType.getSubFieldType(fldName));
         }
         throw new AlgebricksException("Could not find field " + fldName + " in the schema.");
     }