Added support of typed indexes over open fields & indexes over nested fields

Open indexes requires user to provide a type along with a indexed field name.
This type would be enforced for all the indexed records, i.e. index cannot be created if in some records a field with provided name has a different type.
Index-specific rewrite rules match provided type with the inferred types of other arguments in join\select statements and trigger index rewrite.

Nested indexes use the same semantics as the regular indexes, with exception that field could be located arbitrarily deep inside nested structure

Change-Id: I53d00aba243ccf7cf79cf7d775dd305813d24f98
Reviewed-on: http://fulliautomatix.ics.uci.edu:8443/97
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Steven Jacobs <sjaco002@ucr.edu>
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CreateIndexStatement.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CreateIndexStatement.java
index 4355d4f..42d11c9 100644
--- a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CreateIndexStatement.java
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/CreateIndexStatement.java
@@ -17,19 +17,22 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import edu.uci.ics.asterix.aql.base.Expression;
 import edu.uci.ics.asterix.aql.base.Statement;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlExpressionVisitor;
 import edu.uci.ics.asterix.aql.expression.visitor.IAqlVisitorWithVoidReturn;
 import edu.uci.ics.asterix.common.config.DatasetConfig.IndexType;
 import edu.uci.ics.asterix.common.exceptions.AsterixException;
+import edu.uci.ics.hyracks.algebricks.common.utils.Pair;
 
 public class CreateIndexStatement implements Statement {
 
     private Identifier indexName;
     private Identifier dataverseName;
     private Identifier datasetName;
-    private List<String> fieldExprs = new ArrayList<String>();
+    private List<Pair<List<String>, TypeExpression>> fieldExprs = new ArrayList<Pair<List<String>, TypeExpression>>();
     private IndexType indexType = IndexType.BTREE;
+    private boolean enforced;
     private boolean ifNotExists;
 
     // Specific to NGram indexes.
@@ -70,12 +73,12 @@
         this.datasetName = datasetName;
     }
 
-    public List<String> getFieldExprs() {
+    public List<Pair<List<String>, TypeExpression>> getFieldExprs() {
         return fieldExprs;
     }
 
-    public void addFieldExpr(String fe) {
-        this.fieldExprs.add(fe);
+    public void addFieldExprPair(Pair<List<String>, TypeExpression> fp) {
+        this.fieldExprs.add(fp);
     }
 
     public IndexType getIndexType() {
@@ -86,6 +89,14 @@
         this.indexType = indexType;
     }
 
+    public boolean isEnforced() {
+        return enforced;
+    }
+
+    public void setEnforced(boolean isEnforced) {
+        this.enforced = isEnforced;
+    }
+
     public void setIfNotExists(boolean ifNotExists) {
         this.ifNotExists = ifNotExists;
     }
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/FeedDetailsDecl.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/FeedDetailsDecl.java
index 552bafd..4a3661b 100644
--- a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/FeedDetailsDecl.java
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/FeedDetailsDecl.java
@@ -25,8 +25,8 @@
     private final FunctionSignature functionSignature;
 
     public FeedDetailsDecl(String adapterFactoryClassname, Map<String, String> configuration,
-            FunctionSignature signature, Identifier nodeGroupName, List<String> partitioningExpr,
-            String compactionPolicy, Map<String, String> compactionPolicyProperties, String filterField) {
+            FunctionSignature signature, Identifier nodeGroupName, List<List<String>> partitioningExpr,
+            String compactionPolicy, Map<String, String> compactionPolicyProperties, List<String> filterField) {
         super(nodeGroupName, partitioningExpr, false, compactionPolicy, compactionPolicyProperties, filterField);
         this.adapterFactoryClassname = adapterFactoryClassname;
         this.configuration = configuration;
diff --git a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/InternalDetailsDecl.java b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/InternalDetailsDecl.java
index fc71a8f..8fe482e 100644
--- a/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/InternalDetailsDecl.java
+++ b/asterix-aql/src/main/java/edu/uci/ics/asterix/aql/expression/InternalDetailsDecl.java
@@ -21,14 +21,14 @@
 
 public class InternalDetailsDecl implements IDatasetDetailsDecl {
     private final Identifier nodegroupName;
-    private final List<String> partitioningExprs;
+    private final List<List<String>> partitioningExprs;
     private final boolean autogenerated;
     private final String compactionPolicy;
     private final Map<String, String> compactionPolicyProperties;
-    private final String filterField;
+    private final List<String> filterField;
 
-    public InternalDetailsDecl(Identifier nodeGroupName, List<String> partitioningExpr, boolean autogenerated,
-            String compactionPolicy, Map<String, String> compactionPolicyProperties, String filterField) {
+    public InternalDetailsDecl(Identifier nodeGroupName, List<List<String>> partitioningExpr, boolean autogenerated,
+            String compactionPolicy, Map<String, String> compactionPolicyProperties, List<String> filterField) {
         this.nodegroupName = nodeGroupName == null ? new Identifier(MetadataConstants.METADATA_DEFAULT_NODEGROUP_NAME)
                 : nodeGroupName;
         this.partitioningExprs = partitioningExpr;
@@ -38,7 +38,7 @@
         this.filterField = filterField;
     }
 
-    public List<String> getPartitioningExprs() {
+    public List<List<String>> getPartitioningExprs() {
         return partitioningExprs;
     }
 
@@ -61,7 +61,7 @@
         return compactionPolicyProperties;
     }
 
-    public String getFilterField() {
+    public List<String> getFilterField() {
         return filterField;
     }
 
diff --git a/asterix-aql/src/main/javacc/AQL.jj b/asterix-aql/src/main/javacc/AQL.jj
index e85b56b..7ed63b1 100644
--- a/asterix-aql/src/main/javacc/AQL.jj
+++ b/asterix-aql/src/main/javacc/AQL.jj
@@ -320,13 +320,13 @@
   Map<String,String> properties = null;
   Map<String,String> compactionPolicyProperties = null;
   FunctionSignature appliedFunction = null;
-  List<String> primaryKeyFields = null;
+  List<List<String>> primaryKeyFields = null;
   String nodeGroupName = null;
   Map<String,String> hints = new HashMap<String,String>();
   DatasetDecl dsetDecl = null;
   boolean autogenerated = false;
   String compactionPolicy = null;
-  String filterField = null;
+  List<String> filterField = null;
 }
 {
   (
@@ -361,7 +361,7 @@
     ("on" nodeGroupName = Identifier() )?
     ( "hints" hints = Properties() )?
     ( "using" "compaction" "policy" compactionPolicy = CompactionPolicy() (compactionPolicyProperties = Configuration())? )?
-    ( "with filter on" filterField = FilterField() )?
+    ( "with filter on" filterField = NestedField() )?
       {
         InternalDetailsDecl idd = new InternalDetailsDecl(nodeGroupName != null
                                                             ? new Identifier(nodeGroupName)
@@ -425,24 +425,25 @@
 {
   CreateIndexStatement cis = new CreateIndexStatement();
   String indexName = null;
-  String fieldExpr = null;
   boolean ifNotExists = false;
   Pair<Identifier,Identifier> nameComponents = null;
+  Pair<List<String>, TypeExpression> fieldPair = null;
   IndexParams indexType = null;
+  boolean enforced = false;
 }
 {
   "index" indexName = Identifier()
   ifNotExists = IfNotExists()
   "on" nameComponents = QualifiedName()
-  <LEFTPAREN> ( fieldExpr = Identifier()
+  <LEFTPAREN> ( fieldPair = OpenField()
     {
-      cis.addFieldExpr(fieldExpr);
+      cis.addFieldExprPair(fieldPair);
     }
-  ) (<COMMA> fieldExpr = Identifier()
+  ) (<COMMA> fieldPair = OpenField()
     {
-      cis.addFieldExpr(fieldExpr);
+      cis.addFieldExprPair(fieldPair);
     }
-  )* <RIGHTPAREN> ( "type" indexType = IndexType() )?
+  )* <RIGHTPAREN> ( "type" indexType = IndexType() )? ( "enforced" { enforced = true; } )?
     {
       cis.setIndexName(new Identifier(indexName));
       cis.setIfNotExists(ifNotExists);
@@ -452,6 +453,7 @@
         cis.setIndexType(indexType.type);
         cis.setGramLength(indexType.gramLength);
       }
+      cis.setEnforced(enforced);
       return cis;
     }
 }
@@ -668,17 +670,17 @@
     }
 }
 
-List<String> PrimaryKey() throws ParseException:
+List<List<String>> PrimaryKey() throws ParseException:
 {
-  String tmp = null;
-  List<String> primaryKeyFields = new ArrayList<String>();
+  List<String> tmp = null;
+  List<List<String>> primaryKeyFields = new ArrayList<List<String>>();
 }
 {
-  "primary" "key" tmp = Identifier()
+  "primary" "key" tmp = NestedField()
     {
       primaryKeyFields.add(tmp);
     }
-  ( <COMMA> tmp = Identifier()
+  ( <COMMA> tmp = NestedField()
     {
       primaryKeyFields.add(tmp);
     }
@@ -1016,6 +1018,21 @@
     }
 }
 
+TypeExpression IndexedTypeExpr() throws ParseException:
+{
+  TypeExpression typeExpr = null;
+}
+{
+  (
+      typeExpr = TypeReference()
+    | typeExpr = OrderedListTypeDef()
+    | typeExpr = UnorderedListTypeDef()
+  )
+  {
+    return typeExpr;
+  }
+}
+
 TypeExpression TypeExpr() throws ParseException:
 {
   TypeExpression typeExpr = null;
@@ -1205,6 +1222,42 @@
     }
 }
 
+Pair<List<String>, TypeExpression> OpenField() throws ParseException:
+{
+  TypeExpression fieldType = null;
+  List<String> fieldList = null;
+}
+{
+  fieldList = NestedField()
+  ( <COLON> fieldType =  IndexedTypeExpr() )?
+  {
+    return new Pair<List<String>, TypeExpression>(fieldList, fieldType);
+  }
+}
+
+List<String> NestedField() throws ParseException:
+{
+  List<String> exprList = new ArrayList<String>();
+  String lit = null;
+}
+{
+  lit = Identifier()
+  {
+    exprList.add(lit);
+  }
+  (<DOT>
+    lit = Identifier()
+    {
+      exprList.add(lit);
+    }
+  )*
+  {
+    return exprList;
+  }
+}
+
+
+
 String StringLiteral() throws ParseException:
 {
 }