This is an automated email from the ASF dual-hosted git repository.

dehowef pushed a commit to branch PG16
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/PG16 by this push:
     new fd723b11 Add the command graph_stats and improve VLE messaging for 
load (#1750) (#1752)
fd723b11 is described below

commit fd723b11a15489927e36cf6b3fc6a3df90672324
Author: John Gemignani <[email protected]>
AuthorDate: Thu Apr 18 00:45:51 2024 -0700

    Add the command graph_stats and improve VLE messaging for load (#1750) 
(#1752)
    
    Added the command graph_stats. This command will force a reload of
    the specified graph. This is done to generate current statistics
    and dump any warning messages about the graph generated during a
    the load.
    
    Cleaned up some messaging from the VLE load process for duplicate
    vertices and edges.
    
    Added regression tests.
---
 age--1.5.0--y.y.y.sql                    |   8 ++
 regress/expected/age_global_graph.out    | 154 +++++++++++++++++++++++++++-
 regress/sql/age_global_graph.sql         |  34 ++++++
 sql/agtype_typecast.sql                  |   7 ++
 src/backend/utils/adt/age_global_graph.c | 171 ++++++++++++++++++++++++++-----
 5 files changed, 348 insertions(+), 26 deletions(-)

diff --git a/age--1.5.0--y.y.y.sql b/age--1.5.0--y.y.y.sql
index 0239d439..ef6a7727 100644
--- a/age--1.5.0--y.y.y.sql
+++ b/age--1.5.0--y.y.y.sql
@@ -98,3 +98,11 @@ RETURNS NULL ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+-- this is a new function for graph statistics
+CREATE FUNCTION ag_catalog.age_graph_stats(agtype)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
diff --git a/regress/expected/age_global_graph.out 
b/regress/expected/age_global_graph.out
index 73377b31..f2d9b059 100644
--- a/regress/expected/age_global_graph.out
+++ b/regress/expected/age_global_graph.out
@@ -216,12 +216,164 @@ SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN 
vertex_stats(a) $$) AS (r
  {"id": 1125899906842625, "label": "Person", "in_degree": 0, "out_degree": 0, 
"self_loops": 0}
 (2 rows)
 
+--
+-- graph_stats command
+--
+-- what's in the current graphs?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+                                  result                                  
+--------------------------------------------------------------------------
+ {"graph": "ag_graph_1", "num_loaded_edges": 0, "num_loaded_vertices": 3}
+(1 row)
+
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_2') $$) AS 
(result agtype);
+                                  result                                  
+--------------------------------------------------------------------------
+ {"graph": "ag_graph_2", "num_loaded_edges": 0, "num_loaded_vertices": 2}
+(1 row)
+
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_3') $$) AS 
(result agtype);
+                                  result                                  
+--------------------------------------------------------------------------
+ {"graph": "ag_graph_3", "num_loaded_edges": 0, "num_loaded_vertices": 1}
+(1 row)
+
+-- add some edges
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+ results 
+---------
+(0 rows)
+
+-- what is there now?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+                                  result                                   
+---------------------------------------------------------------------------
+ {"graph": "ag_graph_1", "num_loaded_edges": 4, "num_loaded_vertices": 11}
+(1 row)
+
+-- add some more
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) SET u.id = id(u)
+                                                        SET v.id = id(v)
+                                                        SET u.name = 'u'
+                                                        SET v.name = 'v'
+                                      RETURN u,v $$) AS (u agtype, v agtype);
+                                                u                              
                   |                                                v           
                                      
+--------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"id": 281474976710659, 
"name": "u"}}::vertex | {"id": 281474976710660, "label": "", "properties": 
{"id": 281474976710660, "name": "v"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"id": 281474976710661, 
"name": "u"}}::vertex | {"id": 281474976710662, "label": "", "properties": 
{"id": 281474976710662, "name": "v"}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"id": 281474976710663, 
"name": "u"}}::vertex | {"id": 281474976710664, "label": "", "properties": 
{"id": 281474976710664, "name": "v"}}::vertex
+ {"id": 281474976710665, "label": "", "properties": {"id": 281474976710665, 
"name": "u"}}::vertex | {"id": 281474976710666, "label": "", "properties": 
{"id": 281474976710666, "name": "v"}}::vertex
+(4 rows)
+
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) MERGE 
(v)-[:stalks]->(u) $$) AS (result agtype);
+ result 
+--------
+(0 rows)
+
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v $$) AS 
(u agtype, e agtype, v agtype);
+                                                u                              
                   |                                                            
  e                                                              |              
                                  v                                             
    
+--------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"id": 281474976710660, 
"name": "v"}}::vertex | {"id": 1407374883553281, "label": "stalks", "end_id": 
281474976710659, "start_id": 281474976710660, "properties": {}}::edge | {"id": 
281474976710659, "label": "", "properties": {"id": 281474976710659, "name": 
"u"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"id": 281474976710659, 
"name": "u"}}::vertex | {"id": 1125899906842625, "label": "knows", "end_id": 
281474976710660, "start_id": 281474976710659, "properties": {}}::edge  | {"id": 
281474976710660, "label": "", "properties": {"id": 281474976710660, "name": 
"v"}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"id": 281474976710662, 
"name": "v"}}::vertex | {"id": 1407374883553282, "label": "stalks", "end_id": 
281474976710661, "start_id": 281474976710662, "properties": {}}::edge | {"id": 
281474976710661, "label": "", "properties": {"id": 281474976710661, "name": 
"u"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"id": 281474976710661, 
"name": "u"}}::vertex | {"id": 1125899906842626, "label": "knows", "end_id": 
281474976710662, "start_id": 281474976710661, "properties": {}}::edge  | {"id": 
281474976710662, "label": "", "properties": {"id": 281474976710662, "name": 
"v"}}::vertex
+ {"id": 281474976710664, "label": "", "properties": {"id": 281474976710664, 
"name": "v"}}::vertex | {"id": 1407374883553283, "label": "stalks", "end_id": 
281474976710663, "start_id": 281474976710664, "properties": {}}::edge | {"id": 
281474976710663, "label": "", "properties": {"id": 281474976710663, "name": 
"u"}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"id": 281474976710663, 
"name": "u"}}::vertex | {"id": 1125899906842627, "label": "knows", "end_id": 
281474976710664, "start_id": 281474976710663, "properties": {}}::edge  | {"id": 
281474976710664, "label": "", "properties": {"id": 281474976710664, "name": 
"v"}}::vertex
+ {"id": 281474976710666, "label": "", "properties": {"id": 281474976710666, 
"name": "v"}}::vertex | {"id": 1407374883553284, "label": "stalks", "end_id": 
281474976710665, "start_id": 281474976710666, "properties": {}}::edge | {"id": 
281474976710665, "label": "", "properties": {"id": 281474976710665, "name": 
"u"}}::vertex
+ {"id": 281474976710665, "label": "", "properties": {"id": 281474976710665, 
"name": "u"}}::vertex | {"id": 1125899906842628, "label": "knows", "end_id": 
281474976710666, "start_id": 281474976710665, "properties": {}}::edge  | {"id": 
281474976710666, "label": "", "properties": {"id": 281474976710666, "name": 
"v"}}::vertex
+(8 rows)
+
+-- what is there now?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+                                  result                                   
+---------------------------------------------------------------------------
+ {"graph": "ag_graph_1", "num_loaded_edges": 8, "num_loaded_vertices": 11}
+(1 row)
+
+-- remove some vertices
+SELECT * FROM ag_graph_1._ag_label_vertex;
+       id        |              properties              
+-----------------+--------------------------------------
+ 281474976710657 | {}
+ 281474976710658 | {}
+ 281474976710659 | {"id": 281474976710659, "name": "u"}
+ 281474976710660 | {"id": 281474976710660, "name": "v"}
+ 281474976710661 | {"id": 281474976710661, "name": "u"}
+ 281474976710662 | {"id": 281474976710662, "name": "v"}
+ 281474976710663 | {"id": 281474976710663, "name": "u"}
+ 281474976710664 | {"id": 281474976710664, "name": "v"}
+ 281474976710665 | {"id": 281474976710665, "name": "u"}
+ 281474976710666 | {"id": 281474976710666, "name": "v"}
+ 844424930131969 | {}
+(11 rows)
+
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710661';
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710662';
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710664';
+SELECT * FROM ag_graph_1._ag_label_vertex;
+       id        |              properties              
+-----------------+--------------------------------------
+ 281474976710657 | {}
+ 281474976710658 | {}
+ 281474976710659 | {"id": 281474976710659, "name": "u"}
+ 281474976710660 | {"id": 281474976710660, "name": "v"}
+ 281474976710663 | {"id": 281474976710663, "name": "u"}
+ 281474976710665 | {"id": 281474976710665, "name": "u"}
+ 281474976710666 | {"id": 281474976710666, "name": "v"}
+ 844424930131969 | {}
+(8 rows)
+
+SELECT * FROM ag_graph_1._ag_label_edge;
+        id        |    start_id     |     end_id      | properties 
+------------------+-----------------+-----------------+------------
+ 1125899906842625 | 281474976710659 | 281474976710660 | {}
+ 1125899906842626 | 281474976710661 | 281474976710662 | {}
+ 1125899906842627 | 281474976710663 | 281474976710664 | {}
+ 1125899906842628 | 281474976710665 | 281474976710666 | {}
+ 1407374883553281 | 281474976710660 | 281474976710659 | {}
+ 1407374883553282 | 281474976710662 | 281474976710661 | {}
+ 1407374883553283 | 281474976710664 | 281474976710663 | {}
+ 1407374883553284 | 281474976710666 | 281474976710665 | {}
+(8 rows)
+
+-- there should be warning messages
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+WARNING:  edge: [id: 1125899906842626, start: 281474976710661, end: 
281474976710662, label: knows] start and end vertices not found
+WARNING:  ignored malformed or dangling edge
+WARNING:  edge: [id: 1125899906842627, start: 281474976710663, end: 
281474976710664, label: knows] end vertex not found
+WARNING:  ignored malformed or dangling edge
+WARNING:  edge: [id: 1407374883553282, start: 281474976710662, end: 
281474976710661, label: stalks] start and end vertices not found
+WARNING:  ignored malformed or dangling edge
+WARNING:  edge: [id: 1407374883553283, start: 281474976710664, end: 
281474976710663, label: stalks] start vertex not found
+WARNING:  ignored malformed or dangling edge
+                                  result                                  
+--------------------------------------------------------------------------
+ {"graph": "ag_graph_1", "num_loaded_edges": 8, "num_loaded_vertices": 8}
+(1 row)
+
 --drop graphs
 SELECT * FROM drop_graph('ag_graph_1', true);
-NOTICE:  drop cascades to 3 other objects
+NOTICE:  drop cascades to 5 other objects
 DETAIL:  drop cascades to table ag_graph_1._ag_label_vertex
 drop cascades to table ag_graph_1._ag_label_edge
 drop cascades to table ag_graph_1.vertex1
+drop cascades to table ag_graph_1.knows
+drop cascades to table ag_graph_1.stalks
 NOTICE:  graph "ag_graph_1" has been dropped
  drop_graph 
 ------------
diff --git a/regress/sql/age_global_graph.sql b/regress/sql/age_global_graph.sql
index badc0b5b..acd95fbf 100644
--- a/regress/sql/age_global_graph.sql
+++ b/regress/sql/age_global_graph.sql
@@ -90,6 +90,40 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN 
vertex_stats(n) $$) AS (r
 --should return 1 vertice and 1 label
 SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN vertex_stats(a) $$) AS 
(result agtype);
 
+--
+-- graph_stats command
+--
+-- what's in the current graphs?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_2') $$) AS 
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_3') $$) AS 
(result agtype);
+-- add some edges
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+SELECT * FROM cypher('ag_graph_1', $$ CREATE ()-[:knows]->() $$) AS (results 
agtype);
+-- what is there now?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+-- add some more
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) SET u.id = id(u)
+                                                        SET v.id = id(v)
+                                                        SET u.name = 'u'
+                                                        SET v.name = 'v'
+                                      RETURN u,v $$) AS (u agtype, v agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) MERGE 
(v)-[:stalks]->(u) $$) AS (result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v $$) AS 
(u agtype, e agtype, v agtype);
+-- what is there now?
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+-- remove some vertices
+SELECT * FROM ag_graph_1._ag_label_vertex;
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710661';
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710662';
+DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710664';
+SELECT * FROM ag_graph_1._ag_label_vertex;
+SELECT * FROM ag_graph_1._ag_label_edge;
+-- there should be warning messages
+SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS 
(result agtype);
+
 --drop graphs
 
 SELECT * FROM drop_graph('ag_graph_1', true);
diff --git a/sql/agtype_typecast.sql b/sql/agtype_typecast.sql
index 5326182f..aa551407 100644
--- a/sql/agtype_typecast.sql
+++ b/sql/agtype_typecast.sql
@@ -196,6 +196,13 @@ CREATE FUNCTION ag_catalog.age_vertex_stats(agtype, agtype)
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION ag_catalog.age_graph_stats(agtype)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 CREATE FUNCTION ag_catalog.age_delete_global_graphs(agtype)
     RETURNS boolean
     LANGUAGE c
diff --git a/src/backend/utils/adt/age_global_graph.c 
b/src/backend/utils/adt/age_global_graph.c
index 9967a78f..d4a02242 100644
--- a/src/backend/utils/adt/age_global_graph.c
+++ b/src/backend/utils/adt/age_global_graph.c
@@ -96,9 +96,9 @@ static void load_edge_hashtable(GRAPH_global_context *ggctx);
 static void freeze_GRAPH_global_hashtables(GRAPH_global_context *ggctx);
 static List *get_ag_labels_names(Snapshot snapshot, Oid graph_oid,
                                  char label_type);
-static bool insert_edge(GRAPH_global_context *ggctx, graphid edge_id,
-                        Datum edge_properties, graphid start_vertex_id,
-                        graphid end_vertex_id, Oid edge_label_table_oid);
+static bool insert_edge_entry(GRAPH_global_context *ggctx, graphid edge_id,
+                              Datum edge_properties, graphid start_vertex_id,
+                              graphid end_vertex_id, Oid edge_label_table_oid);
 static bool insert_vertex_edge(GRAPH_global_context *ggctx,
                                graphid start_vertex_id, graphid end_vertex_id,
                                graphid edge_id, char *edge_label_name);
@@ -237,39 +237,57 @@ static List *get_ag_labels_names(Snapshot snapshot, Oid 
graph_oid,
  * Helper function to insert one edge/edge->vertex, key/value pair, in the
  * current GRAPH global edge hashtable.
  */
-static bool insert_edge(GRAPH_global_context *ggctx, graphid edge_id,
-                        Datum edge_properties, graphid start_vertex_id,
-                        graphid end_vertex_id, Oid edge_label_table_oid)
+static bool insert_edge_entry(GRAPH_global_context *ggctx, graphid edge_id,
+                              Datum edge_properties, graphid start_vertex_id,
+                              graphid end_vertex_id, Oid edge_label_table_oid)
 {
-    edge_entry *value = NULL;
+    edge_entry *ee = NULL;
     bool found = false;
 
     /* search for the edge */
-    value = (edge_entry *)hash_search(ggctx->edge_hashtable, (void *)&edge_id,
+    ee = (edge_entry *)hash_search(ggctx->edge_hashtable, (void *)&edge_id,
                                       HASH_ENTER, &found);
+
+    /* if the hash enter returned is NULL, error out */
+    if (ee == NULL)
+    {
+        elog(ERROR, "insert_edge_entry: hash table returned NULL for ee");
+    }
+
     /*
      * If we found the key, either we have a duplicate, or we made a mistake 
and
      * inserted it already. Either way, this isn't good so don't insert it and
-     * return false. Likewise, if the value returned is NULL, don't do 
anything,
-     * just return false. This way the caller can decide what to do.
+     * return false.
      */
-    if (found || value == NULL)
+    if (found)
     {
+        ereport(WARNING,
+                (errcode(ERRCODE_DATA_EXCEPTION),
+                 errmsg("edge: [id: %ld, start: %ld, end: %ld, label oid: %d] 
%s",
+                        edge_id, start_vertex_id, end_vertex_id,
+                        edge_label_table_oid, "duplicate edge found")));
+
+        ereport(WARNING,
+                (errcode(ERRCODE_DATA_EXCEPTION),
+                 errmsg("previous edge: [id: %ld, start: %ld, end: %ld, label 
oid: %d]",
+                        ee->edge_id, ee->start_vertex_id, ee->end_vertex_id,
+                        ee->edge_label_table_oid)));
+
         return false;
     }
 
     /* not sure if we really need to zero out the entry, as we set everything 
*/
-    MemSet(value, 0, sizeof(edge_entry));
+    MemSet(ee, 0, sizeof(edge_entry));
 
     /*
      * Set the edge id - this is important as this is the hash key value used
      * for hash function collisions.
      */
-    value->edge_id = edge_id;
-    value->edge_properties = edge_properties;
-    value->start_vertex_id = start_vertex_id;
-    value->end_vertex_id = end_vertex_id;
-    value->edge_label_table_oid = edge_label_table_oid;
+    ee->edge_id = edge_id;
+    ee->edge_properties = edge_properties;
+    ee->start_vertex_id = start_vertex_id;
+    ee->end_vertex_id = end_vertex_id;
+    ee->edge_label_table_oid = edge_label_table_oid;
 
     /* increment the number of loaded edges */
     ggctx->num_loaded_edges++;
@@ -292,9 +310,26 @@ static bool insert_vertex_entry(GRAPH_global_context 
*ggctx, graphid vertex_id,
     ve = (vertex_entry *)hash_search(ggctx->vertex_hashtable,
                                      (void *)&vertex_id, HASH_ENTER, &found);
 
-    /* we should never have duplicates, return false */
+    /* if the hash enter returned is NULL, error out */
+    if (ve == NULL)
+    {
+        elog(ERROR, "insert_vertex_entry: hash table returned NULL for ve");
+    }
+
+    /* we should never have duplicates, warn and return false */
     if (found)
     {
+        ereport(WARNING,
+                (errcode(ERRCODE_DATA_EXCEPTION),
+                 errmsg("vertex: [id: %ld, label oid: %d] %s",
+                        vertex_id, vertex_label_table_oid,
+                        "duplicate vertex found")));
+
+        ereport(WARNING,
+                (errcode(ERRCODE_DATA_EXCEPTION),
+                 errmsg("previous vertex: [id: %ld, label oid: %d]",
+                        ve->vertex_id, ve->vertex_label_table_oid)));
+
         return false;
     }
 
@@ -482,10 +517,12 @@ static void load_vertex_hashtable(GRAPH_global_context 
*ggctx)
                                            vertex_label_table_oid,
                                            vertex_properties);
 
-            /* this insert must not fail, it means there is a duplicate */
+            /* warn if there is a duplicate */
             if (!inserted)
             {
-                 elog(ERROR, "insert_vertex_entry: failed due to duplicate");
+                 ereport(WARNING,
+                         (errcode(ERRCODE_DATA_EXCEPTION),
+                          errmsg("ignored duplicate vertex")));
             }
         }
 
@@ -598,14 +635,17 @@ static void load_edge_hashtable(GRAPH_global_context 
*ggctx)
             edge_properties = datumCopy(edge_properties, false, -1);
 
             /* insert edge into edge hashtable */
-            inserted = insert_edge(ggctx, edge_id, edge_properties,
-                                   edge_vertex_start_id, edge_vertex_end_id,
-                                   edge_label_table_oid);
+            inserted = insert_edge_entry(ggctx, edge_id, edge_properties,
+                                         edge_vertex_start_id,
+                                         edge_vertex_end_id,
+                                         edge_label_table_oid);
 
-            /* this insert must not fail */
+            /* warn if there is a duplicate */
             if (!inserted)
             {
-                 elog(ERROR, "insert_edge: failed to insert");
+                 ereport(WARNING,
+                         (errcode(ERRCODE_DATA_EXCEPTION),
+                          errmsg("ignored duplicate edge")));
             }
 
             /* insert the edge into the start and end vertices edge lists */
@@ -1193,3 +1233,84 @@ Datum age_vertex_stats(PG_FUNCTION_ARGS)
 
     PG_RETURN_POINTER(agtype_value_to_agtype(result.res));
 }
+
+/* PG wrapper function for age_graph_stats */
+PG_FUNCTION_INFO_V1(age_graph_stats);
+
+Datum age_graph_stats(PG_FUNCTION_ARGS)
+{
+    GRAPH_global_context *ggctx = NULL;
+    agtype_value *agtv_temp = NULL;
+    agtype_value agtv_integer;
+    agtype_in_state result;
+    char *graph_name = NULL;
+    Oid graph_oid = InvalidOid;
+
+    /* the graph name is required, but this generally isn't user supplied */
+    if (PG_ARGISNULL(0))
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("graph_stats: graph name cannot be NULL")));
+    }
+
+    /* get the graph name */
+    agtv_temp = get_agtype_value("graph_stats", AG_GET_ARG_AGTYPE_P(0),
+                                 AGTV_STRING, true);
+
+    graph_name = pnstrdup(agtv_temp->val.string.val,
+                          agtv_temp->val.string.len);
+
+    /*
+     * Remove any context for this graph. This is done to allow graph_stats to
+     * show any load issues.
+     */
+    delete_specific_GRAPH_global_contexts(graph_name);
+
+    /* get the graph oid */
+    graph_oid = get_graph_oid(graph_name);
+
+    /*
+     * Create or retrieve the GRAPH global context for this graph. This 
function
+     * will also purge off invalidated contexts.
+     */
+    ggctx = manage_GRAPH_global_contexts(graph_name, graph_oid);
+
+    /* free the graph name */
+    pfree(graph_name);
+
+    /* zero the state */
+    memset(&result, 0, sizeof(agtype_in_state));
+
+    /* start the object */
+    result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_OBJECT,
+                                   NULL);
+    /* store the graph name */
+    result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
+                                   string_to_agtype_value("graph"));
+    result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);
+
+    /* set up an integer for returning values */
+    agtv_temp = &agtv_integer;
+    agtv_temp->type = AGTV_INTEGER;
+    agtv_temp->val.int_value = 0;
+
+    /* get and store num_loaded_vertices */
+    agtv_temp->val.int_value = ggctx->num_loaded_vertices;
+    result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
+                                   
string_to_agtype_value("num_loaded_vertices"));
+    result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);
+
+    /* get and store num_loaded_edges */
+    agtv_temp->val.int_value = ggctx->num_loaded_edges;
+    result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
+                                   string_to_agtype_value("num_loaded_edges"));
+    result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);
+
+    /* close the object */
+    result.res = push_agtype_value(&result.parse_state, WAGT_END_OBJECT, NULL);
+
+    result.res->type = AGTV_OBJECT;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(result.res));
+}

Reply via email to