[ASTERIXDB-2979][MTD][GRAPH] Implement CREATE / DROP GRAPH

Initial commit. This supports CREATE GRAPH, DROP GRAPH, and prevents
dropping views / functions / datasets / synonyms / dataverses that a
graph depends on.

Change-Id: Ibaf4dc7066b85d8ea3b58c6b90fd83af3a700506
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/14644
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ian Maxon <imaxon@uci.edu>
diff --git a/asterix-graphix/src/main/resources/lang-extension/lang.txt b/asterix-graphix/src/main/resources/lang-extension/lang.txt
new file mode 100644
index 0000000..6d215f0
--- /dev/null
+++ b/asterix-graphix/src/main/resources/lang-extension/lang.txt
@@ -0,0 +1,354 @@
+//
+// 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.
+//
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+import org.apache.asterix.graphix.lang.expression.GraphConstructor;
+import org.apache.asterix.graphix.lang.statement.CreateGraphStatement;
+import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
+import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
+import org.apache.asterix.lang.sqlpp.parser.ParseException;
+import org.apache.asterix.lang.sqlpp.parser.SqlppParseException;
+import org.apache.asterix.lang.sqlpp.parser.Token;
+
+@new_at_the_class_def
+public GraphElementDecl parseGraphElementBody(GraphElementIdentifier identifier) throws CompilationException {
+    return parseImpl(new ParseFunction<GraphElementDecl>() {
+        @Override
+        public GraphElementDecl parse() throws ParseException {
+            DataverseName dataverse = defaultDataverse;
+            defaultDataverse = identifier.getGraphIdentifier().getDataverseName();
+
+            // We borrow the ViewBody production, where we have a SelectExpression or a VariableRef.
+            createNewScope();
+            Expression elementBodyExpr = GraphixParser.this.ViewBody();
+            removeCurrentScope();
+
+            defaultDataverse = dataverse;
+            return new GraphElementDecl(identifier, elementBodyExpr);
+        }
+    });
+}
+
+@merge
+Statement CreateStatement() throws ParseException:
+{
+  // merge area 1
+  before:
+  after:
+}
+{
+  (
+    // merge area 2
+    before:
+    after:    | stmt = CreateGraphStatement(startToken, false)
+  )
+  {
+    // merge area 3
+  }
+}
+
+@merge
+Statement CreateOrReplaceStatement(Token startStmtToken) throws ParseException:
+{
+  // merge area 1
+  before:
+  after:
+}
+{
+  (
+    // merge area 2
+    before:
+    after:    | stmt = CreateGraphStatement(startStmtToken, true)
+  )
+  {
+    // merge area 3
+  }
+}
+
+@merge
+Statement DropStatement() throws ParseException:
+{
+  // merge area 1
+  before:
+  after:
+}
+{
+  (
+    // merge area 2
+    before:
+    after:    | stmt = DropGraphStatement(startToken)
+  )
+  {
+    // merge area 3
+  }
+}
+
+@new
+CreateGraphStatement CreateGraphStatement(Token startStmtToken, boolean orReplace) throws ParseException:
+{
+  CreateGraphStatement stmt = null;
+}
+{
+  <GRAPH> stmt = CreateGraphSpecification(startStmtToken, orReplace)
+  {
+    return stmt;
+  }
+}
+
+@new
+CreateGraphStatement CreateGraphSpecification(Token startStmtToken, boolean orReplace) throws ParseException:
+{
+  Pair<DataverseName, Identifier> nameComponents = null;
+  GraphConstructor graphConstructor = null;
+  boolean ifNotExists = false;
+}
+{
+  nameComponents = QualifiedName()
+  ifNotExists = IfNotExists()
+  {
+    if (orReplace && ifNotExists) {
+      throw new SqlppParseException(getSourceLocation(startStmtToken), "Unexpected IF NOT EXISTS");
+    }
+  }
+  <AS> graphConstructor = GraphConstructor(token)
+  {
+    CreateGraphStatement stmt = new CreateGraphStatement(nameComponents.first, nameComponents.second.getValue(),
+        orReplace, ifNotExists, graphConstructor);
+    return addSourceLocation(stmt, startStmtToken);
+  }
+}
+
+@new
+GraphDropStatement DropGraphStatement(Token startStmtToken) throws ParseException:
+{
+   GraphDropStatement stmt = null;
+}
+{
+  <GRAPH> stmt = DropGraphSpecification(startStmtToken)
+  {
+    return stmt;
+  }
+}
+
+@new
+GraphDropStatement DropGraphSpecification(Token startStmtToken) throws ParseException:
+{
+  Pair<DataverseName, Identifier> pairId = null;
+  boolean ifExists = false;
+}
+{
+  pairId = QualifiedName() ifExists = IfExists()
+  {
+    GraphDropStatement stmt = new GraphDropStatement(pairId.first, pairId.second.getValue(), ifExists);
+    return addSourceLocation(stmt, startStmtToken);
+  }
+}
+
+@new
+Pair<List<Integer>, List<List<String>>> KeyFields() throws ParseException:
+{
+  Pair<List<Integer>, List<List<String>>> keyFields = null;
+}
+{
+  // This is essentially an alias for the production PrimaryKeyFields.
+  keyFields = PrimaryKeyFields()
+  {
+    return keyFields;
+  }
+}
+
+@new
+GraphConstructor GraphConstructor(Token startStmtToken) throws ParseException:
+{
+  List<GraphConstructor.VertexElement> vertexElements = new ArrayList<GraphConstructor.VertexElement>();
+  List<GraphConstructor.EdgeElement> edgeElements = new ArrayList<GraphConstructor.EdgeElement>();
+  GraphConstructor.VertexElement vertexElement = null;
+  GraphConstructor.EdgeElement edgeElement = null;
+}
+{
+  vertexElement = GraphVertexSpecification(startStmtToken) { vertexElements.add(vertexElement); }
+  ( <COMMA>
+    (
+      ( vertexElement = GraphVertexSpecification(token) { vertexElements.add(vertexElement); } )
+      | ( edgeElement = GraphEdgeSpecification(token) { edgeElements.add(edgeElement); } )
+    )
+  )*
+  {
+    GraphConstructor graphConstructor = new GraphConstructor(vertexElements, edgeElements);
+    return addSourceLocation(graphConstructor, startStmtToken);
+  }
+}
+
+@new
+GraphConstructor.VertexElement GraphVertexSpecification(Token startStmtToken) throws ParseException:
+{
+  Pair<List<Integer>, List<List<String>>> primaryKeyFields;
+  Token beginPos = null, endPos = null;
+  Expression vertexDefinitionExpr;
+  String vertexName;
+}
+{
+  <VERTEX>
+  vertexName = GraphVertexDefinitionPattern()
+  <PRIMARY> <KEY> <LEFTPAREN> primaryKeyFields = KeyFields() <RIGHTPAREN>
+  <AS>
+  {
+    beginPos = token;
+    createNewScope();
+  }
+  (
+    vertexDefinitionExpr = ViewBody()
+    | <LEFTPAREN> vertexDefinitionExpr = ViewBody() <RIGHTPAREN>
+  )
+  {
+    endPos = token;
+    String vDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
+    removeCurrentScope();
+    GraphConstructor.VertexElement vertexElement = new GraphConstructor.VertexElement(vertexName,
+      primaryKeyFields.second, primaryKeyFields.first, vertexDefinitionExpr, vDef);
+    return addSourceLocation(vertexElement, startStmtToken);
+  }
+}
+
+@new
+String GraphVertexDefinitionPattern() throws ParseException:
+{
+  String vertexName;
+}
+{
+  <LEFTPAREN> <COLON> vertexName = Identifier() <RIGHTPAREN>
+  {
+    return vertexName;
+  }
+}
+
+@new
+GraphConstructor.EdgeElement GraphEdgeSpecification(Token startStmtToken) throws ParseException:
+{
+  Pair<Triple<String, String, String>, Boolean> edgeDefinitionPattern;
+  Pair<List<Integer>, List<List<String>>> keyFields;
+  Token beginPos = null, endPos = null;
+  Expression edgeDefinitionExpr = null;
+
+  List<Integer> destinationKeySourceIndicators = null;
+  List<Integer> sourceKeySourceIndicators = null;
+  List<Integer> primaryKeySourceIndicators = null;
+  List<List<String>> destinationKeyFields = null;
+  List<List<String>> sourceKeyFields = null;
+  List<List<String>> primaryKeyFields = null;
+}
+{
+  <EDGE>
+  edgeDefinitionPattern = GraphEdgeDefinitionPattern()
+  (
+    (
+      <PRIMARY> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+      {
+        primaryKeyFields = keyFields.second;
+        primaryKeySourceIndicators = keyFields.first;
+      }
+      <SOURCE> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+      {
+        sourceKeyFields = keyFields.second;
+        sourceKeySourceIndicators = keyFields.first;
+      }
+      <DESTINATION> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+      {
+        destinationKeyFields = keyFields.second;
+        destinationKeySourceIndicators = keyFields.first;
+      }
+      <AS>
+      {
+        beginPos = token;
+        createNewScope();
+      }
+      (
+        edgeDefinitionExpr = ViewBody()
+        | <LEFTPAREN> edgeDefinitionExpr = ViewBody() <RIGHTPAREN>
+      )
+    )
+    |
+    (
+      <DESTINATION> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+      {
+        destinationKeyFields = keyFields.second;
+        destinationKeySourceIndicators = keyFields.first;
+      }
+    )
+  )
+  {
+    String destinationLabel, edgeLabel, sourceLabel;
+    if (edgeDefinitionPattern.second) { // isDirectedLeft
+      sourceLabel = edgeDefinitionPattern.first.third;
+      edgeLabel = edgeDefinitionPattern.first.second;
+      destinationLabel = edgeDefinitionPattern.first.first;
+    } else {
+      sourceLabel = edgeDefinitionPattern.first.first;
+      edgeLabel = edgeDefinitionPattern.first.second;
+      destinationLabel = edgeDefinitionPattern.first.third;
+    }
+
+    String eDef = null;
+    if (edgeDefinitionExpr != null) {
+      endPos = token;
+      eDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
+      removeCurrentScope();
+    }
+
+    GraphConstructor.EdgeElement edgeElement = new GraphConstructor.EdgeElement(edgeLabel, destinationLabel,
+        sourceLabel, primaryKeyFields, primaryKeySourceIndicators, destinationKeyFields, destinationKeySourceIndicators,
+        sourceKeyFields, sourceKeySourceIndicators, edgeDefinitionExpr, eDef);
+    return addSourceLocation(edgeElement, startStmtToken);
+  }
+}
+
+@new
+Pair<Triple<String, String, String>, Boolean> GraphEdgeDefinitionPattern() throws ParseException:
+{
+  String leftVertexName, edgeName, rightVertexName;
+  boolean isDirectedLeft;
+}
+{
+  leftVertexName = GraphVertexDefinitionPattern()
+  ( <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> <GT> { isDirectedLeft = false; }
+  | <LT> <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> { isDirectedLeft = true; } )
+  rightVertexName = GraphVertexDefinitionPattern()
+  {
+    Triple<String, String, String> t = new Triple<String, String, String>(leftVertexName, edgeName, rightVertexName);
+    return new Pair<Triple<String, String, String>, Boolean>(t, isDirectedLeft);
+  }
+}
+
+@new
+<DEFAULT,IN_DBL_BRACE>
+TOKEN [IGNORE_CASE]:
+{
+  <DESTINATION: "destination">
+  | <EDGE: "edge">
+  | <GRAPH: "graph">
+  | <SOURCE: "source">
+  | <VERTEX: "vertex">
+}
+
+@new_at_the_end
+<DEFAULT,IN_DBL_BRACE>
+TOKEN :
+{
+  <BAR: "|">
+}
\ No newline at end of file