[Age label inheritance] - VLE edge inheritance: regression tests (#789)

* Added inheritance to create_vlabel function

* Only two parameters to create_vlabel function, inheritance works as -> child_name:parent_name

* create_vlabel receives 3 arguments, the third is a list of parent labels

* Elements in the array are fetched properly

* Error when merging multiple inherited definitions fixed, but still needs to fix the testing

* Changed how create_vlabel handles if there is a parent name in the array

* Added some checking with the is_inheriting variable to see if the label is inheriting

* Added inheritance to create_elabel

* Changed create_label function calls

* changes to match new needed arguments (boolean is_inheriting).

* Tests for edge and vertex label inheritance added for the cypher_create.sql file

* Regression test for VLE edge inheritance

* VLE finds child of child edge

* cypher_vle test passed.

---------

Co-authored-by: Marco A S <aureliojuniorcmrj@hotmail.com>
diff --git a/regress/expected/cypher_vle.out b/regress/expected/cypher_vle.out
index 5f8f922..79876a3 100644
--- a/regress/expected/cypher_vle.out
+++ b/regress/expected/cypher_vle.out
@@ -762,6 +762,242 @@
 (1 row)
 
 --
+-- Test VLE for edge inheritance
+--
+SELECT create_graph('vle_inheritance_graph');
+NOTICE:  graph "vle_inheritance_graph" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+SELECT create_vlabel('vle_inheritance_graph', 'Head');
+NOTICE:  VLabel "Head" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+SELECT create_vlabel('vle_inheritance_graph', 'Tail');
+NOTICE:  VLabel "Tail" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+SELECT create_vlabel('vle_inheritance_graph', 'Node');
+NOTICE:  VLabel "Node" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+SELECT create_elabel('vle_inheritance_graph', 'PARENT_EDGE_A');
+NOTICE:  ELabel "PARENT_EDGE_A" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT create_elabel('vle_inheritance_graph', 'PARENT_EDGE_B');
+NOTICE:  ELabel "PARENT_EDGE_B" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_A', ARRAY['PARENT_EDGE_A']);
+NOTICE:  ELabel CHILD_EDGE_A will inherit from PARENT_EDGE_A
+NOTICE:  merging column "id" with inherited definition
+NOTICE:  merging column "start_id" with inherited definition
+NOTICE:  merging column "end_id" with inherited definition
+NOTICE:  merging column "properties" with inherited definition
+NOTICE:  ELabel "CHILD_EDGE_A" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_B', ARRAY['PARENT_EDGE_B']);
+NOTICE:  ELabel CHILD_EDGE_B will inherit from PARENT_EDGE_B
+NOTICE:  merging column "id" with inherited definition
+NOTICE:  merging column "start_id" with inherited definition
+NOTICE:  merging column "end_id" with inherited definition
+NOTICE:  merging column "properties" with inherited definition
+NOTICE:  ELabel "CHILD_EDGE_B" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_C', ARRAY['CHILD_EDGE_B']);
+NOTICE:  ELabel CHILD_EDGE_C will inherit from CHILD_EDGE_B
+NOTICE:  merging column "id" with inherited definition
+NOTICE:  merging column "start_id" with inherited definition
+NOTICE:  merging column "end_id" with inherited definition
+NOTICE:  merging column "properties" with inherited definition
+NOTICE:  ELabel "CHILD_EDGE_C" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    CREATE (:Head {id: 1})-[:PARENT_EDGE_A]->(:Node {id: 2})-[:CHILD_EDGE_A]->(:Node {id: 3})-[:PARENT_EDGE_A]->(:Tail {id: 4}),
+            (:Head {id: 5})-[:PARENT_EDGE_B]->(:Node {id: 6})-[:CHILD_EDGE_B]->(:Node {id: 7})-[:PARENT_EDGE_B]->(:Tail {id: 8}),
+            (:Head {id: 9})-[:CHILD_EDGE_C]->(:Tail {id: 10})
+    $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--
+-- VLE with the PARENT_EDGEs
+--
+-- should find 6 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 1    | 2
+ 1    | 3
+ 1    | 4
+ 2    | 3
+ 2    | 4
+ 3    | 4
+(6 rows)
+
+-- should find 3 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 1    | 2
+ 2    | 3
+ 3    | 4
+(3 rows)
+
+-- should find 2 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*2]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 1    | 3
+ 2    | 4
+(2 rows)
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*3]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 1    | 4
+(1 row)
+
+-- should find 7 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 5    | 6
+ 5    | 7
+ 5    | 8
+ 9    | 10
+ 6    | 7
+ 6    | 8
+ 7    | 8
+(7 rows)
+
+-- should find 4 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 5    | 6
+ 9    | 10
+ 6    | 7
+ 7    | 8
+(4 rows)
+
+-- should find 2 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*2]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 5    | 7
+ 6    | 8
+(2 rows)
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*3]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 5    | 8
+(1 row)
+
+--
+-- VLE with the CHILD_EDGEs
+--
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_A*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 2    | 3
+(1 row)
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_A*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 2    | 3
+(1 row)
+
+-- should find 2 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_B*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 9    | 10
+ 6    | 7
+(2 rows)
+
+-- should find 2 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_B*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+ a_id | b_id 
+------+------
+ 9    | 10
+ 6    | 7
+(2 rows)
+
+--
 -- Clean up
 --
 DROP TABLE start_and_end_points;
@@ -782,6 +1018,24 @@
  
 (1 row)
 
+SELECT drop_graph('vle_inheritance_graph', true)
 --
 -- End
 --
+NOTICE:  drop cascades to 10 other objects
+DETAIL:  drop cascades to table vle_inheritance_graph._ag_label_vertex
+drop cascades to table vle_inheritance_graph._ag_label_edge
+drop cascades to table vle_inheritance_graph."Head"
+drop cascades to table vle_inheritance_graph."Tail"
+drop cascades to table vle_inheritance_graph."Node"
+drop cascades to table vle_inheritance_graph."PARENT_EDGE_A"
+drop cascades to table vle_inheritance_graph."PARENT_EDGE_B"
+drop cascades to table vle_inheritance_graph."CHILD_EDGE_A"
+drop cascades to table vle_inheritance_graph."CHILD_EDGE_B"
+drop cascades to table vle_inheritance_graph."CHILD_EDGE_C"
+NOTICE:  graph "vle_inheritance_graph" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
diff --git a/regress/sql/cypher_vle.sql b/regress/sql/cypher_vle.sql
index 468135e..5dc5d8f 100644
--- a/regress/sql/cypher_vle.sql
+++ b/regress/sql/cypher_vle.sql
@@ -290,6 +290,108 @@
 
 SELECT drop_graph('mygraph', true);
 
+
+--
+-- Test VLE for edge inheritance
+--
+
+SELECT create_graph('vle_inheritance_graph');
+SELECT create_vlabel('vle_inheritance_graph', 'Head');
+SELECT create_vlabel('vle_inheritance_graph', 'Tail');
+SELECT create_vlabel('vle_inheritance_graph', 'Node');
+SELECT create_elabel('vle_inheritance_graph', 'PARENT_EDGE_A');
+SELECT create_elabel('vle_inheritance_graph', 'PARENT_EDGE_B');
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_A', ARRAY['PARENT_EDGE_A']);
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_B', ARRAY['PARENT_EDGE_B']);
+SELECT create_elabel('vle_inheritance_graph', 'CHILD_EDGE_C', ARRAY['CHILD_EDGE_B']);
+
+
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    CREATE (:Head {id: 1})-[:PARENT_EDGE_A]->(:Node {id: 2})-[:CHILD_EDGE_A]->(:Node {id: 3})-[:PARENT_EDGE_A]->(:Tail {id: 4}),
+            (:Head {id: 5})-[:PARENT_EDGE_B]->(:Node {id: 6})-[:CHILD_EDGE_B]->(:Node {id: 7})-[:PARENT_EDGE_B]->(:Tail {id: 8}),
+            (:Head {id: 9})-[:CHILD_EDGE_C]->(:Tail {id: 10})
+    $$) AS (a agtype);
+
+--
+-- VLE with the PARENT_EDGEs
+--
+
+-- should find 6 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 3 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 2 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*2]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_A*3]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 7 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 4 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 2 rows
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*2]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:PARENT_EDGE_B*3]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+--
+-- VLE with the CHILD_EDGEs
+--
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_A*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 1 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_A*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 2 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_B*]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
+-- should find 2 row
+SELECT * FROM cypher('vle_inheritance_graph', $$
+    MATCH (a)-[:CHILD_EDGE_B*1]->(b)
+    RETURN a.id, b.id
+$$) AS (a_id agtype, b_id agtype);
+
 --
 -- Clean up
 --
@@ -298,6 +400,8 @@
 
 SELECT drop_graph('cypher_vle', true);
 
+SELECT drop_graph('vle_inheritance_graph', true)
+
 --
 -- End
 --
diff --git a/src/backend/utils/adt/age_global_graph.c b/src/backend/utils/adt/age_global_graph.c
index 6f7562a..ef5f84b 100644
--- a/src/backend/utils/adt/age_global_graph.c
+++ b/src/backend/utils/adt/age_global_graph.c
@@ -490,11 +490,14 @@
     /* get the specific graph OID and namespace (schema) OID */
     graph_oid = ggctx->graph_oid;
     graph_namespace_oid = get_namespace_oid(ggctx->graph_name, false);
+
     /* get the active snapshot */
     snapshot = GetActiveSnapshot();
+
     /* get the names of all of the edge label tables */
     edge_label_names = get_ag_labels_names(snapshot, graph_oid,
                                            LABEL_TYPE_EDGE);
+
     /* go through all edge label tables in list */
     foreach (lc, edge_label_names)
     {
@@ -507,14 +510,18 @@
 
         /* get the edge label name */
         edge_label_name = lfirst(lc);
+
         /* get the edge label name's OID */
         edge_label_table_oid = get_relname_relid(edge_label_name,
                                                  graph_namespace_oid);
+
         /* open the relation (table) and begin the scan */
         graph_edge_label = heap_open(edge_label_table_oid, ShareLock);
         scan_desc = heap_beginscan(graph_edge_label, snapshot, 0, NULL);
+
         /* get the tupdesc - we don't need to release this one */
         tupdesc = RelationGetDescr(graph_edge_label);
+
         /* bail if the number of columns differs */
         if (tupdesc->natts != 4)
         {
@@ -990,60 +997,34 @@
     return ee->end_vertex_id;
 }
 
-List* getChildren(GRAPH_global_context *ggctx)
+/* Function to return all the child edges from the given parent edge */
+List* get_child_edges(GRAPH_global_context *ggctx, char* edge_label_name)
 {
-    
-    Oid graph_oid;
     Oid graph_namespace_oid;
-    Snapshot snapshot;
-    List *edge_label_names = NIL;
-    ListCell *lc;
-    List *children = NIL;
+    Oid edge_label_table_oid;
+    List *child_edges_oid = NIL;
+
+    /* return the empty list */
+    if (edge_label_name == NULL)
+    {
+        return child_edges_oid;
+    }
 
     /* get the specific graph OID and namespace (schema) OID */
-    graph_oid = ggctx->graph_oid;
     graph_namespace_oid = get_namespace_oid(ggctx->graph_name, false);
-    /* get the active snapshot */
-    snapshot = GetActiveSnapshot();
-    /* get the names of all of the edge label tables */
-    edge_label_names = get_ag_labels_names(snapshot, graph_oid,
-                                           LABEL_TYPE_EDGE);
-    /* go through all edge label tables in list */
-    foreach (lc, edge_label_names)
+
+    /* get the edge label name's OID */
+    edge_label_table_oid = get_relname_relid(edge_label_name,
+                                            graph_namespace_oid);
+
+    /* verify if the current edge has child edges */
+    if(has_subclass(edge_label_table_oid))
     {
-        Relation graph_edge_label;
-        HeapScanDesc scan_desc;
-        HeapTuple tuple;
-        char *edge_label_name;
-        Oid edge_label_table_oid;
-        TupleDesc tupdesc;
-        
-        
-        /* get the edge label name */
-        edge_label_name = lfirst(lc);
-
-        /* get the edge label name's OID */
-        edge_label_table_oid = get_relname_relid(edge_label_name,
-                                                 graph_namespace_oid);
-
-        List *child_edges_oid_temp = NIL;
-       
-        if( has_subclass(edge_label_table_oid))
-        {
-            child_edges_oid_temp = find_inheritance_children(edge_label_table_oid, NoLock);
-            
-            //loop through the child edges
-            ListCell *lc1;
-            foreach (lc1, child_edges_oid_temp)
-            {
-                Oid child_edge_oid = lfirst_oid(lc1);
-                children = lappend_oid(children, child_edge_oid);
-            }
-        }
-        
+        /* get the edge's child labels OIDs */
+        child_edges_oid = find_all_inheritors(edge_label_table_oid, NoLock, NULL);
     }
-    
-    return children;
+        
+    return child_edges_oid;
 }
 
 /* PostgreSQL SQL facing functions */
diff --git a/src/backend/utils/adt/age_vle.c b/src/backend/utils/adt/age_vle.c
index b123d26..7feaf69 100644
--- a/src/backend/utils/adt/age_vle.c
+++ b/src/backend/utils/adt/age_vle.c
@@ -335,7 +335,8 @@
     int num_edge_property_constraints = 0;
     int num_edge_properties = 0;
     List *child_oid_list = NIL;
-    bool isChild = false;
+    ListCell *lc;
+    bool is_child = false;
     GRAPH_global_context *ggctx = NULL;
 
     /*get the graph global context */
@@ -363,11 +364,14 @@
 
     /* get the edge label name from the oid */
     edge_label_name = get_rel_name(get_edge_entry_label_table_oid(ee));
+
     /* get our edge's properties */
     edge_property = DATUM_GET_AGTYPE_P(get_edge_entry_properties(ee));
+
     /* get the containers */
     agtc_edge_property_constraint = &vlelctx->edge_property_constraint->root;
     agtc_edge_property = &edge_property->root;
+
     /* get the number of properties in the edge to be matched */
     num_edge_properties = AGTYPE_CONTAINER_SIZE(agtc_edge_property);
 
@@ -381,18 +385,18 @@
         return false;
     }
 
-    /* get the children list of the current VLE_local_context*/
-    child_oid_list = getChildren(ggctx);
-    ListCell *lc;
 
-    /* check if child exists or not*/
+    /* get the children list of the current VLE_local_context edge label name */
+    child_oid_list = get_child_edges(ggctx, vlelctx->edge_label_name);
+
+    /* check if child exists or not */
     foreach(lc, child_oid_list)
     {
         Oid child_oid = lfirst_oid(lc);
         char *child_name = get_rel_name(child_oid);
         if (strcmp(edge_label_name, child_name) == 0)
         {
-            isChild = true;
+            is_child = true;
         }
     }
 
@@ -400,7 +404,7 @@
      * Check for a label constraint. If the label name is NULL, there isn't one.
      */
     if (vlelctx->edge_label_name != NULL &&
-        strcmp(vlelctx->edge_label_name, edge_label_name) != 0  && !isChild) 
+        strcmp(vlelctx->edge_label_name, edge_label_name) != 0  && !is_child) 
     {
         return false;
     }
diff --git a/src/include/utils/age_global_graph.h b/src/include/utils/age_global_graph.h
index 7801141..46b14cc 100644
--- a/src/include/utils/age_global_graph.h
+++ b/src/include/utils/age_global_graph.h
@@ -60,5 +60,5 @@
 Datum get_edge_entry_properties(edge_entry *ee);
 graphid get_edge_entry_start_vertex_id(edge_entry *ee);
 graphid get_edge_entry_end_vertex_id(edge_entry *ee);
-List* getChildren(GRAPH_global_context *ggctx);
+List* get_child_edges(GRAPH_global_context *ggctx, char* edge_label_name);
 #endif