[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/cc.conf b/asterix-graphix/src/main/resources/cc.conf
new file mode 100644
index 0000000..8452471
--- /dev/null
+++ b/asterix-graphix/src/main/resources/cc.conf
@@ -0,0 +1,68 @@
+; 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.
+
+[nc/asterix_nc1]
+txn.log.dir=target/tmp/asterix_nc1/txnlog
+core.dump.dir=target/tmp/asterix_nc1/coredump
+iodevices=target/tmp/asterix_nc1/iodevice1,../asterix-server/target/tmp/asterix_nc1/iodevice2
+#jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006
+
+[nc/asterix_nc2]
+ncservice.port=9091
+nc.api.port=19005
+txn.log.dir=target/tmp/asterix_nc2/txnlog
+core.dump.dir=target/tmp/asterix_nc2/coredump
+iodevices=target/tmp/asterix_nc2/iodevice1,../asterix-server/target/tmp/asterix_nc2/iodevice2
+#jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5007
+
+[nc]
+address=127.0.0.1
+command=asterixnc
+app.class=org.apache.asterix.hyracks.bootstrap.NCApplication
+jvm.args=-Xmx4096m -Dnode.Resolver="org.apache.asterix.external.util.IdentitiyResolverFactory"
+storage.buffercache.pagesize=32KB
+storage.buffercache.size=128MB
+storage.memorycomponent.globalbudget=512MB
+storage.io.scheduler=greedy
+storage.filtered.memorycomponent.max.size=16MB
+
+[cc]
+address=127.0.0.1
+app.class=org.apache.asterix.hyracks.bootstrap.CCApplication
+heartbeat.period=2000
+heartbeat.max.misses=25
+
+[common]
+log.dir=logs/
+log.level=INFO
+compiler.framesize=32KB
+compiler.sortmemory=320KB
+compiler.groupmemory=160KB
+compiler.joinmemory=256KB
+compiler.textsearchmemory=160KB
+compiler.windowmemory=192KB
+compiler.sort.parallel=false
+compiler.internal.sanitycheck=true
+messaging.frame.size=4096
+messaging.frame.count=512
+
+[extension/org.apache.asterix.graphix.extension.GraphixQueryTranslatorExtension]
+enabled=true
+[extension/org.apache.asterix.graphix.extension.GraphixLangExtension]
+enabled=true
+[extension/org.apache.asterix.graphix.extension.GraphixMetadataExtension]
+enabled=true
\ No newline at end of file
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