Merge commit '3e2623cf64827e891ecd8c6dea13d03f5599c6a5' into HEAD

Change-Id: I89091d8bf8575c73d26f497d5fbffe780adbedf9
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 53faff9..17c94bd 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -385,15 +385,15 @@
         hintCollector.clear();
         try {
             return parseFunction.parse();
+        } catch (SqlppParseException e) {
+            throw new CompilationException(ErrorCode.PARSE_ERROR, e.getSourceLocation(), LogRedactionUtil.userData(getMessage(e)));
+        } catch (ParseException e) {
+            throw new CompilationException(ErrorCode.PARSE_ERROR, LogRedactionUtil.userData(getMessage(e)));
         } catch (Error e) {
             // this is here as the JavaCharStream that's below the lexer sometimes throws Errors that are not handled
             // by the ANTLR-generated lexer or parser (e.g it does this for invalid backslash u + 4 hex digits escapes)
             final String msg = e.getClass().getSimpleName() + (e.getMessage() != null ? ": " + e.getMessage() : "");
             throw new CompilationException(ErrorCode.PARSE_ERROR, LogRedactionUtil.userData(msg));
-        } catch (SqlppParseException e) {
-            throw new CompilationException(ErrorCode.PARSE_ERROR, e.getSourceLocation(), LogRedactionUtil.userData(getMessage(e)));
-        } catch (ParseException e) {
-            throw new CompilationException(ErrorCode.PARSE_ERROR, LogRedactionUtil.userData(getMessage(e)));
         } finally {
             reportUnclaimedHints();
         }
@@ -478,13 +478,21 @@
     }
 
     private SqlppParseException createUnexpectedTokenError() {
-      return createUnexpectedTokenError(null);
+      return createUnexpectedTokenError(token, null);
+    }
+
+    private SqlppParseException createUnexpectedTokenError(Token t) {
+      return createUnexpectedTokenError(t, null);
     }
 
     private SqlppParseException createUnexpectedTokenError(String expected) {
-      String message = "Unexpected token: " + LogRedactionUtil.userData(token.image) +
+      return createUnexpectedTokenError(token, expected);
+    }
+
+    private SqlppParseException createUnexpectedTokenError(Token t, String expected) {
+      String message = "Unexpected token: " + LogRedactionUtil.userData(t.image) +
         (expected == null ? "" : ". Expected: " + LogRedactionUtil.userData(expected));
-      return new SqlppParseException(getSourceLocation(token), message);
+      return new SqlppParseException(getSourceLocation(t), message);
     }
 
     private boolean laToken(int idx, int kind) {
@@ -751,22 +759,19 @@
 {
   Pair<DataverseName,Identifier> nameComponents = null;
   boolean ifNotExists = false;
-  Pair<DataverseName,Identifier> typeComponents = null;
-  String adapterName = null;
-  Map<String,String> properties = null;
-  FunctionSignature appliedFunction = null;
+  TypeExpression typeExpr = null;
+  TypeExpression metaTypeExpr = null;
   Pair<List<Integer>, List<List<String>>> primaryKeyFields = null;
   String nodeGroupName = null;
   Map<String,String> hints = new HashMap<String,String>();
   DatasetDecl stmt = null;
   boolean autogenerated = false;
   Pair<Integer, List<String>> filterField = null;
-  Pair<DataverseName,Identifier> metaTypeComponents = new Pair<DataverseName, Identifier>(null, null);
   RecordConstructor withRecord = null;
 }
 {
   nameComponents = QualifiedName()
-  <LEFTPAREN> typeComponents = TypeName() <RIGHTPAREN>
+  typeExpr = DatasetTypeSpecification()
   (
     { String name; }
     <WITH>
@@ -777,7 +782,7 @@
                 "We can only support one additional associated field called \"meta\".");
         }
     }
-    <LEFTPAREN> metaTypeComponents = TypeName() <RIGHTPAREN>
+    metaTypeExpr = DatasetTypeSpecification()
   )?
   ifNotExists = IfNotExists()
   primaryKeyFields = PrimaryKey()
@@ -794,8 +799,7 @@
     InternalDetailsDecl idd = new InternalDetailsDecl(primaryKeyFields.second, primaryKeyFields.first, autogenerated,
       filterField == null? null : filterField.second);
     try {
-      stmt = new DatasetDecl(nameComponents.first, nameComponents.second, typeComponents.first, typeComponents.second,
-        metaTypeComponents.first, metaTypeComponents.second,
+      stmt = new DatasetDecl(nameComponents.first, nameComponents.second, typeExpr, metaTypeExpr,
         nodeGroupName != null ? new Identifier(nodeGroupName) : null, hints, DatasetType.INTERNAL, idd, withRecord,
         ifNotExists);
       return addSourceLocation(stmt, startStmtToken);
@@ -808,19 +812,18 @@
 DatasetDecl ExternalDatasetSpecification(Token startStmtToken) throws ParseException:
 {
   Pair<DataverseName,Identifier> nameComponents = null;
+  TypeExpression typeExpr = null;
   boolean ifNotExists = false;
-  Pair<DataverseName,Identifier> typeComponents = null;
   String adapterName = null;
   Map<String,String> properties = null;
   String nodeGroupName = null;
   Map<String,String> hints = new HashMap<String,String>();
   DatasetDecl stmt = null;
-  Pair<DataverseName,Identifier> metaTypeComponents = new Pair<DataverseName, Identifier>(null, null);
   RecordConstructor withRecord = null;
 }
 {
   nameComponents = QualifiedName()
-  <LEFTPAREN> typeComponents = TypeName() <RIGHTPAREN>
+  typeExpr = DatasetTypeSpecification()
   ifNotExists = IfNotExists()
   <USING> adapterName = AdapterName() properties = Configuration()
   ( <ON> nodeGroupName = Identifier() )?
@@ -831,8 +834,7 @@
     edd.setAdapter(adapterName);
     edd.setProperties(properties);
     try {
-      stmt = new DatasetDecl(nameComponents.first, nameComponents.second, typeComponents.first, typeComponents.second,
-        metaTypeComponents.first, metaTypeComponents.second,
+      stmt = new DatasetDecl(nameComponents.first, nameComponents.second, typeExpr, null,
         nodeGroupName != null? new Identifier(nodeGroupName): null, hints, DatasetType.EXTERNAL, edd, withRecord,
         ifNotExists);
       return addSourceLocation(stmt, startStmtToken);
@@ -842,6 +844,76 @@
   }
 }
 
+TypeExpression DatasetTypeSpecification() throws ParseException:
+{
+  TypeExpression typeExpr = null;
+}
+{
+  (
+    LOOKAHEAD(3) typeExpr = DatasetRecordTypeSpecification(true)
+    | typeExpr = DatasetReferenceTypeSpecification()
+  )
+  {
+    return typeExpr;
+  }
+}
+
+TypeExpression DatasetReferenceTypeSpecification() throws ParseException:
+{
+  TypeExpression typeExpr = null;
+}
+{
+  <LEFTPAREN> typeExpr = TypeReference() <RIGHTPAREN>
+  {
+    return typeExpr;
+  }
+}
+
+TypeExpression DatasetRecordTypeSpecification(boolean allowRecordKindModifier) throws ParseException:
+{
+  RecordTypeDefinition recordTypeDef = null;
+  RecordTypeDefinition.RecordKind recordKind = null;
+  Token recordKindToken = null;
+}
+{
+   <LEFTPAREN> recordTypeDef = DatasetRecordTypeDef() <RIGHTPAREN>
+   ( recordKind = RecordTypeKind() { recordKindToken = token; } <TYPE> )?
+   {
+     if (recordKind == null) {
+       recordKind = RecordTypeDefinition.RecordKind.CLOSED;
+     } else if (!allowRecordKindModifier) {
+       throw createUnexpectedTokenError(recordKindToken);
+     }
+     recordTypeDef.setRecordKind(recordKind);
+     return recordTypeDef;
+   }
+}
+
+RecordTypeDefinition DatasetRecordTypeDef() throws ParseException:
+{
+  RecordTypeDefinition recType = new RecordTypeDefinition();
+}
+{
+  DatasetRecordField(recType) ( <COMMA> DatasetRecordField(recType) )*
+  {
+    return recType;
+  }
+}
+
+void DatasetRecordField(RecordTypeDefinition recType) throws ParseException:
+{
+  String fieldName;
+  TypeExpression type = null;
+  boolean isUnknownable = true;
+}
+{
+  fieldName = Identifier()
+  type = TypeReference() ( <NOT> <NULL> { isUnknownable = false; } )?
+  {
+    recType.addField(fieldName, type, isUnknownable);
+  }
+}
+
 RefreshExternalDatasetStatement RefreshExternalDatasetStatement() throws ParseException:
 {
   Token startToken = null;
@@ -2077,15 +2149,28 @@
   }
 }
 
+RecordTypeDefinition.RecordKind RecordTypeKind() throws ParseException:
+{
+  RecordTypeDefinition.RecordKind recordKind = null;
+}
+{
+  (
+    <CLOSED> { recordKind = RecordTypeDefinition.RecordKind.CLOSED; }
+    | <OPEN> { recordKind = RecordTypeDefinition.RecordKind.OPEN; }
+  )
+  {
+    return recordKind;
+  }
+}
+
 RecordTypeDefinition RecordTypeDef() throws ParseException:
 {
   Token startToken = null;
   RecordTypeDefinition recType = new RecordTypeDefinition();
-  RecordTypeDefinition.RecordKind recordKind = null;
+  RecordTypeDefinition.RecordKind recordKind = RecordTypeDefinition.RecordKind.OPEN;
 }
 {
-  ( <CLOSED> { recordKind = RecordTypeDefinition.RecordKind.CLOSED; }
-    | <OPEN> { recordKind = RecordTypeDefinition.RecordKind.OPEN; } )?
+   ( recordKind = RecordTypeKind() )?
    <LEFTBRACE>
     {
       startToken = token;
@@ -2112,9 +2197,6 @@
     )?
    <RIGHTBRACE>
    {
-      if (recordKind == null) {
-        recordKind = RecordTypeDefinition.RecordKind.OPEN;
-      }
       recType.setRecordKind(recordKind);
       return addSourceLocation(recType, startToken);
    }