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

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


The following commit(s) were added to refs/heads/PG15 by this push:
     new 4765ee16 PR to merge master branch into PG15 branch (#1158)
4765ee16 is described below

commit 4765ee161633a52e61018fdbc3142eba8e20dbb7
Author: John Gemignani <[email protected]>
AuthorDate: Thu Aug 17 13:33:49 2023 -0700

    PR to merge master branch into PG15 branch (#1158)
    
    PR for moving the PG15 branch up to the current master branch.
    
    Contains all of the latest work in the master branch, which is currently
    at PostgreSQL version 15 at the time of this PR. When this PR is merged,
    PG15 will be the latest.
    
    Contains the following commits -
    
    * Added the toStringList() function (#1084)
    * Fix issue 1124: Segmentation fault when using specific tables (#1125)
    * Added the toStringList() function (#1128)
    * Fix issue 1000: Indexes created on WHERE (#1133)
---
 README.md                               |  12 +--
 RELEASE                                 |   2 +-
 age--1.3.0.sql                          |  17 +++-
 regress/expected/expr.out               | 139 ++++++++++++++++++++++++++++-
 regress/sql/expr.sql                    |  45 ++++++++++
 src/backend/parser/cypher_analyze.c     |  30 +++++++
 src/backend/parser/cypher_expr.c        |  11 ++-
 src/backend/parser/cypher_gram.y        |  33 ++++---
 src/backend/utils/adt/agtype.c          | 149 ++++++++++++++++++++++++++++----
 src/backend/utils/adt/agtype_util.c     |  81 +++++++++++++++++
 src/backend/utils/load/ag_load_edges.c  |   2 +
 src/backend/utils/load/ag_load_labels.c |   1 +
 src/backend/utils/load/age_load.c       |  52 ++++++++---
 src/include/utils/agtype.h              |   3 +
 14 files changed, 524 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md
index 821b968a..bb2a629b 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,8 @@
     <img 
src="https://img.shields.io/badge/Release-v1.3.0-FFA500?labelColor=gray&style=flat&link=https://github.com/apache/age/releases"/>
   </a>
   &nbsp;
-  <a href="https://www.postgresql.org/docs/14/index.html";>
-    <img src="https://img.shields.io/badge/Version-Postgresql 
15-00008B?labelColor=gray&style=flat&link=https://www.postgresql.org/docs/14/index.html"/>
+  <a href="https://www.postgresql.org/docs/15/index.html";>
+    <img src="https://img.shields.io/badge/Version-Postgresql 
15-00008B?labelColor=gray&style=flat&link=https://www.postgresql.org/docs/15/index.html"/>
   </a>
   &nbsp;
   <a href="https://github.com/apache/age/issues";>
@@ -131,8 +131,8 @@ Apache AGE is intended to be simple to install and run. It 
can be installed with
 &nbsp;Install PostgreSQL
 </h4>
 
-You will need to install an AGE compatible version of Postgres<a>, for now AGE 
supports Postgres 11, 12, 13 & 14. Supporting the latest versions is on AGE 
roadmap. 
-     
+You will need to install an AGE compatible version of Postgres<a>, for now AGE 
supports Postgres 11, 12, 13, 14 & 15. Supporting the latest versions is on AGE 
roadmap.
+
 <h4>
 &nbsp;Installation via Package Manager
 </h4>
@@ -149,7 +149,7 @@ sudo apt install postgresql
 &nbsp;Installation From Source Code
 </h4>
 
-You can <a href="https://www.postgresql.org/ftp/source/";> download the 
Postgres </a> source code and install your own instance of Postgres. You can 
read instructions on how to install from source code for different versions on 
the <a href="https://www.postgresql.org/docs/14/installation.html";>official 
Postgres Website.</a>
+You can <a href="https://www.postgresql.org/ftp/source/";> download the 
Postgres </a> source code and install your own instance of Postgres. You can 
read instructions on how to install from source code for different versions on 
the <a href="https://www.postgresql.org/docs/15/installation.html";>official 
Postgres Website.</a>
 
 
 
@@ -158,7 +158,7 @@ You can <a href="https://www.postgresql.org/ftp/source/";> 
download the Postgres
 
 Clone the <a href="https://github.com/apache/age";>github repository</a> or 
download the <a href="https://github.com/apache/age/releases";>download an 
official release.
 </a>
-Run the pg_config utility and check the version of PostgreSQL. Currently, only 
PostgreSQL versions 11, 12, 13 & 14 are supported. If you have any other 
version of Postgres, you will need to install PostgreSQL version 11, 12, 13, or 
14.
+Run the pg_config utility and check the version of PostgreSQL. Currently, only 
PostgreSQL versions 11, 12, 13, 14 & 15 are supported. If you have any other 
version of Postgres, you will need to install PostgreSQL version 11, 12, 13, 
14, or 15.
 <br>
     
 ```bash
diff --git a/RELEASE b/RELEASE
index 66da6387..2709a91c 100644
--- a/RELEASE
+++ b/RELEASE
@@ -19,7 +19,7 @@ Release Notes for Apache AGE release 0.0.0 for PG 15
 
 Apache AGE 0.0.0 - Release Notes
 
-NOTE: This is an initial release of PG14. There are no upgrade
+NOTE: This is an initial release of PG15. There are no upgrade
       scripts to this release.
 
 **************************************************************
diff --git a/age--1.3.0.sql b/age--1.3.0.sql
index 8bfa9a93..90097a7e 100644
--- a/age--1.3.0.sql
+++ b/age--1.3.0.sql
@@ -365,7 +365,7 @@ AS 'MODULE_PATHNAME';
 CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
 RETURNS cstring
 LANGUAGE c
-STABLE
+IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
@@ -3104,7 +3104,7 @@ AS 'MODULE_PATHNAME';
 CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, cstring, agtype)
 RETURNS agtype
 LANGUAGE c
-STABLE
+IMMUTABLE
 CALLED ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
@@ -3112,10 +3112,11 @@ AS 'MODULE_PATHNAME';
 --
 -- agtype - edge
 --
-CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid, 
cstring, agtype)
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              cstring, agtype)
 RETURNS agtype
 LANGUAGE c
-STABLE
+IMMUTABLE
 CALLED ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
@@ -3546,6 +3547,14 @@ RETURNS NULL ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION ag_catalog.age_tostringlist(variadic "any")
+RETURNS agtype
+LANGUAGE c
+IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 CREATE FUNCTION ag_catalog.age_size(variadic "any")
 RETURNS agtype
 LANGUAGE c
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 94c8d180..c4c16506 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -2917,9 +2917,9 @@ $$) AS (toFloatList agtype);
 SELECT * FROM cypher('expr', $$
     RETURN toFloatList(['1.9432', 8.6222, '9.4111212', 344.22])
 $$) AS (toFloatList agtype);
-               tofloatlist               
------------------------------------------
- [1.9432, 8.6222, 9.4111212, 344.220001]
+             tofloatlist             
+-------------------------------------
+ [1.9432, 8.6222, 9.4111212, 344.22]
 (1 row)
 
 SELECT * FROM cypher('expr', $$
@@ -3284,6 +3284,69 @@ ERROR:  function ag_catalog.age_tostring() does not exist
 LINE 1: SELECT * FROM cypher('expr', $$ RETURN toString() $$) AS (re...
                                                ^
 HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+-- toStringList() --
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([5, 10, 7.8, 9, 1.3]) 
+$$) AS (toStringList agtype);
+          tostringlist          
+--------------------------------
+ ["5", "10", "7.8", "9", "1.3"]
+(1 row)
+
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList(['test', 89, 'again', 7.1, 9]) 
+$$) AS (toStringList agtype);
+            tostringlist             
+-------------------------------------
+ ["test", "89", "again", "7.1", "9"]
+(1 row)
+
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([null, false, true, 'string']) 
+$$) AS (toStringList agtype);
+         tostringlist         
+------------------------------
+ [null, null, null, "string"]
+(1 row)
+
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([9.123456789, 5.123, 1.12345, 0.123123]) 
+$$) AS (toStringList agtype);
+                  tostringlist                   
+-------------------------------------------------
+ ["9.123456789", "5.123", "1.12345", "0.123123"]
+(1 row)
+
+-- should return null
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([null]) 
+$$) AS (toStringList agtype);
+ tostringlist 
+--------------
+ [null]
+(1 row)
+
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([true, false, true, true]) 
+$$) AS (toStringList agtype);
+       tostringlist       
+--------------------------
+ [null, null, null, null]
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([['a', b]]) 
+$$) AS (toStringList agtype);
+ERROR:  could not find rte for b
+LINE 2:     RETURN toStringList([['a', b]]) 
+                                       ^
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([test]) 
+$$) AS (toStringList agtype);
+ERROR:  could not find rte for test
+LINE 2:     RETURN toStringList([test]) 
+                                 ^
 --
 -- reverse(string)
 --
@@ -6913,9 +6976,79 @@ SELECT * FROM cypher('graph_395', $$ MATCH 
(p:Project)-[:Has]->(t:Task)-[:Assign
  {"pn": "Project B", "tasks": [{"tn": "Task C", "users": [{"id": 
1407374883553282, "label": "Person", "properties": {"age": 43, "name": 
"Bob"}}::vertex]}]}
 (2 rows)
 
+---
+--- Fix: Segmentation fault when using specific names for tables #1124
+---
+--- The following are just a few commands to test SQLValueFunction types
+---
+SELECT count(*) FROM CURRENT_ROLE;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM CURRENT_USER;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM USER;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM SESSION_USER;
+ count 
+-------
+     1
+(1 row)
+
+SELECT * FROM CURRENT_CATALOG;
+  current_catalog   
+--------------------
+ contrib_regression
+(1 row)
+
+SELECT * FROM CURRENT_SCHEMA;
+ current_schema 
+----------------
+ ag_catalog
+(1 row)
+
+SELECT * FROM create_graph('issue_1124');
+NOTICE:  graph "issue_1124" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+SELECT results, pg_typeof(user) FROM cypher('issue_1124', $$ CREATE (u) RETURN 
u $$) AS (results agtype), user;
+                            results                             | pg_typeof 
+----------------------------------------------------------------+-----------
+ {"id": 281474976710657, "label": "", "properties": {}}::vertex | name
+(1 row)
+
+SELECT results, pg_typeof(user) FROM cypher('issue_1124', $$ MATCH (u) RETURN 
u $$) AS (results agtype), user;
+                            results                             | pg_typeof 
+----------------------------------------------------------------+-----------
+ {"id": 281474976710657, "label": "", "properties": {}}::vertex | name
+(1 row)
+
 --
 -- Cleanup
 --
+SELECT * FROM drop_graph('issue_1124', true);
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to table issue_1124._ag_label_vertex
+drop cascades to table issue_1124._ag_label_edge
+NOTICE:  graph "issue_1124" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
 SELECT * FROM drop_graph('graph_395', true);
 NOTICE:  drop cascades to 7 other objects
 DETAIL:  drop cascades to table graph_395._ag_label_vertex
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index d4a7efbd..82421d9d 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1441,6 +1441,33 @@ SELECT * FROM age_toString(agtype_in(null));
 -- should fail
 SELECT * FROM age_toString();
 SELECT * FROM cypher('expr', $$ RETURN toString() $$) AS (results agtype);
+-- toStringList() --
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([5, 10, 7.8, 9, 1.3]) 
+$$) AS (toStringList agtype);
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList(['test', 89, 'again', 7.1, 9]) 
+$$) AS (toStringList agtype);
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([null, false, true, 'string']) 
+$$) AS (toStringList agtype);
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([9.123456789, 5.123, 1.12345, 0.123123]) 
+$$) AS (toStringList agtype);
+-- should return null
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([null]) 
+$$) AS (toStringList agtype);
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([true, false, true, true]) 
+$$) AS (toStringList agtype);
+-- should fail
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([['a', b]]) 
+$$) AS (toStringList agtype);
+SELECT * FROM cypher('expr', $$ 
+    RETURN toStringList([test]) 
+$$) AS (toStringList agtype);
 
 --
 -- reverse(string)
@@ -2810,9 +2837,27 @@ SELECT * FROM cypher('graph_395', $$ MATCH 
(p:Project)-[:Has]->(t:Task)-[:Assign
                                      WITH p, collect(task) AS tasks
                                      WITH {pn: p.name, tasks:tasks} AS project
                                      RETURN project $$) AS (p agtype);
+
+---
+--- Fix: Segmentation fault when using specific names for tables #1124
+---
+--- The following are just a few commands to test SQLValueFunction types
+---
+
+SELECT count(*) FROM CURRENT_ROLE;
+SELECT count(*) FROM CURRENT_USER;
+SELECT count(*) FROM USER;
+SELECT count(*) FROM SESSION_USER;
+SELECT * FROM CURRENT_CATALOG;
+SELECT * FROM CURRENT_SCHEMA;
+SELECT * FROM create_graph('issue_1124');
+SELECT results, pg_typeof(user) FROM cypher('issue_1124', $$ CREATE (u) RETURN 
u $$) AS (results agtype), user;
+SELECT results, pg_typeof(user) FROM cypher('issue_1124', $$ MATCH (u) RETURN 
u $$) AS (results agtype), user;
+
 --
 -- Cleanup
 --
+SELECT * FROM drop_graph('issue_1124', true);
 SELECT * FROM drop_graph('graph_395', true);
 SELECT * FROM drop_graph('chained', true);
 SELECT * FROM drop_graph('VLE', true);
diff --git a/src/backend/parser/cypher_analyze.c 
b/src/backend/parser/cypher_analyze.c
index a154960c..a481b315 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -167,6 +167,21 @@ static bool convert_cypher_walker(Node *node, ParseState 
*pstate)
                      parser_errposition(pstate, exprLocation((Node 
*)funcexpr))));
         }
 
+        /*
+         * From PG -
+         * SQLValueFunction - parameterless functions with special grammar
+         *                    productions.
+         *
+         * These are a special case that needs to be ignored.
+         *
+         * TODO: This likely needs to be done with XmlExpr types, and maybe
+         *       a few others too.
+         */
+        if (IsA(funcexpr, SQLValueFunction))
+        {
+            return false;
+        }
+
         return expression_tree_walker((Node *)funcexpr->args,
                                       convert_cypher_walker, pstate);
     }
@@ -296,6 +311,21 @@ static bool is_rte_cypher(RangeTblEntry *rte)
  */
 static bool is_func_cypher(FuncExpr *funcexpr)
 {
+    /*
+     * From PG -
+     * SQLValueFunction - parameterless functions with special grammar
+     *                    productions.
+     *
+     * These are a special case that needs to be ignored.
+     *
+     *  TODO: This likely needs to be done with XmlExpr types, and maybe
+     *        a few others too.
+     */
+    if (IsA(funcexpr, SQLValueFunction))
+    {
+        return false;
+    }
+
     return is_oid_ag_func(funcexpr->funcid, "cypher");
 }
 
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 646ac9e2..1f6c9469 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -249,11 +249,14 @@ static Node *transform_A_Const(cypher_parsestate 
*cpstate, A_Const *ac)
         d = boolean_to_agtype(boolVal(&ac->val));
         break;
     default:
-        if (ac->isnull) {
+        if (ac->isnull)
+        {
            is_null = true;
-       } else {
-           ereport(ERROR,
-                 (errmsg_internal("unrecognized node type: %d", 
nodeTag(&ac->val))));
+       }
+        else
+        {
+           ereport(ERROR, (errmsg_internal("unrecognized node type: %d",
+                                            nodeTag(&ac->val))));
            return NULL;
        }
     }
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 846e0201..1b3d9d4e 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -1521,10 +1521,12 @@ expr:
              * supported
              */
             else
+            {
                 ereport(ERROR,
                         (errcode(ERRCODE_SYNTAX_ERROR),
                          errmsg("invalid indirection syntax"),
                          ag_scanner_errposition(@1, scanner)));
+            }
         }
     | expr TYPECAST symbolic_name
         {
@@ -1841,7 +1843,7 @@ var_name:
             ereport(ERROR,
                 (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("%s is only for internal use", AGE_DEFAULT_PREFIX),
-                    ag_scanner_errposition(@1, scanner)));   
+                    ag_scanner_errposition(@1, scanner)));
         }
     }
     ;
@@ -2025,14 +2027,25 @@ static Node *do_negate(Node *n, int location)
 
 static void do_negate_float(Float *v)
 {
-    char *oldval = v->fval;
-
-       if (*oldval == '+')
-               oldval++;
-       if (*oldval == '-')
-               v->fval = oldval+1;     /* just strip the '-' */
-       else
-               v->fval = psprintf("-%s", oldval);
+    char *oldval = NULL;
+
+    Assert(v != NULL);
+    Assert(IsA(v, Float));
+
+    oldval = v->fval;
+
+    if (*oldval == '+')
+    {
+        oldval++;
+    }
+    if (*oldval == '-')
+    {
+        v->fval = oldval+1;    /* just strip the '-' */
+    }
+    else
+    {
+        v->fval = psprintf("-%s", oldval);
+    }
 }
 
 /*
@@ -2111,7 +2124,7 @@ static Node *make_bool_const(bool b, int location)
 static Node *make_null_const(int location)
 {
     A_Const *n = makeNode(A_Const);
-    
+
     n->isnull = true;
     n->location = location;
 
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 242a951e..fbfae7bb 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -31,6 +31,7 @@
 #include "postgres.h"
 
 #include <math.h>
+#include <float.h>
 
 #include "access/genam.h"
 #include "access/heapam.h"
@@ -5334,7 +5335,7 @@ Datum age_tofloat(PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1(age_tofloatlist);
 /*
- * toFloatList() converts a list of values and returns a list of floating 
point values. 
+ * toFloatList() converts a list of values and returns a list of floating 
point values.
  * If any values are not convertible to floating point they will be null in 
the list returned.
  */
 Datum age_tofloatlist(PG_FUNCTION_ARGS)
@@ -5347,7 +5348,7 @@ Datum age_tofloatlist(PG_FUNCTION_ARGS)
     int count;
     int i;
     bool is_valid = false;
-    float float_num;
+    float8 float_num;
     char buffer[64];
 
     /* check for null */
@@ -5356,17 +5357,22 @@ Datum age_tofloatlist(PG_FUNCTION_ARGS)
         PG_RETURN_NULL();
     }
     agt_arg = AG_GET_ARG_AGTYPE_P(0);
+
     /* check for an array */
     if (!AGT_ROOT_IS_ARRAY(agt_arg) || AGT_ROOT_IS_SCALAR(agt_arg))
+    {
         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                         errmsg("toFloatList() argument must resolve to a list 
or null")));
+    }
 
     count = AGT_ROOT_COUNT(agt_arg);
 
     /* if we have an empty list or only one element in the list, return null */
     if (count == 0)
+    {
         PG_RETURN_NULL();
-    
+    }
+
     /* clear the result structure */
     MemSet(&agis_result, 0, sizeof(agtype_in_state));
 
@@ -5390,15 +5396,15 @@ Datum age_tofloatlist(PG_FUNCTION_ARGS)
             {
                 float_elem.type = AGTV_FLOAT;
                 float_elem.val.float_value = float8in_internal_null(string, 
NULL, "double precision",
-                                            string, &is_valid); 
+                                            string, &is_valid);
                 agis_result.res = push_agtype_value(&agis_result.parse_state, 
WAGT_ELEM, &float_elem);
             }
-            else 
+            else
             {
                 float_elem.type = AGTV_NULL;
                 agis_result.res = push_agtype_value(&agis_result.parse_state, 
WAGT_ELEM, &float_elem);
             }
-            
+
             break;
 
         case AGTV_FLOAT:
@@ -5411,7 +5417,7 @@ Datum age_tofloatlist(PG_FUNCTION_ARGS)
             agis_result.res = push_agtype_value(&agis_result.parse_state, 
WAGT_ELEM, &float_elem);
 
             break;
-            
+
         default:
 
             float_elem.type = AGTV_NULL;
@@ -5526,17 +5532,17 @@ Datum age_tointeger(PG_FUNCTION_ARGS)
 
             /* convert it if it is a regular integer string */
             result = strtoi64(string, &endptr, 10);
-          
+
             /*
              * If it isn't an integer string, try converting it as a float
              * string.
              */
             result = float8in_internal_null(string, NULL, "double precision",
                                             string, &is_valid);
- 
+
             if (*endptr != '\0')
             {
-                float f;
+                float8 f;
 
                 f = float8in_internal_null(string, NULL, "double precision",
                                            string, &is_valid);
@@ -5546,7 +5552,9 @@ Datum age_tointeger(PG_FUNCTION_ARGS)
                  */
                 if (!is_valid || isnan(f) || isinf(f) ||
                     f < PG_INT64_MIN || f > (double)PG_INT64_MAX)
+                {
                     PG_RETURN_NULL();
+                }
 
                 result = (int64) f;
             }
@@ -5656,7 +5664,7 @@ Datum age_tointeger(PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1(age_tointegerlist);
 /*
- * toIntegerList() converts a list of values and returns a list of integers 
point values. 
+ * toIntegerList() converts a list of values and returns a list of integers 
point values.
  * If any values are not convertible to integer they will be null in the list 
returned.
  */
 Datum age_tointegerlist(PG_FUNCTION_ARGS)
@@ -5680,14 +5688,18 @@ Datum age_tointegerlist(PG_FUNCTION_ARGS)
     agt_arg = AG_GET_ARG_AGTYPE_P(0);
     /* check for an array */
     if (!AGT_ROOT_IS_ARRAY(agt_arg) || AGT_ROOT_IS_SCALAR(agt_arg))
+    {
         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                         errmsg("toIntegerList() argument must resolve to a 
list or null")));
+    }
 
     count = AGT_ROOT_COUNT(agt_arg);
 
     /* if we have an empty list or only one element in the list, return null */
     if (count == 0)
+    {
         PG_RETURN_NULL();
+    }
 
     /* clear the result structure */
     MemSet(&agis_result, 0, sizeof(agtype_in_state));
@@ -5710,13 +5722,12 @@ Datum age_tointegerlist(PG_FUNCTION_ARGS)
             string = elem->val.string.val;
             integer_elem.type = AGTV_INTEGER;
             integer_elem.val.int_value = atoi(string);
-            
+
             if (*string == '+' || *string == '-' || (*string >= '0' && *string 
<= '9'))
             {
                 is_float = 1;
                 while (*(++string))
                 {
-
                     if(!(*string >= '0' && *string <= '9'))
                     {
                         if(*string == '.' && is_float)
@@ -5729,7 +5740,7 @@ Datum age_tointegerlist(PG_FUNCTION_ARGS)
                             break;
                         }
                     }
-                }   
+                }
             }
             else
             {
@@ -5738,7 +5749,7 @@ Datum age_tointegerlist(PG_FUNCTION_ARGS)
             }
 
             agis_result.res = push_agtype_value(&agis_result.parse_state, 
WAGT_ELEM, &integer_elem);
-           
+
             break;
 
         case AGTV_FLOAT:
@@ -6200,6 +6211,114 @@ Datum age_tostring(PG_FUNCTION_ARGS)
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
 }
 
+PG_FUNCTION_INFO_V1(age_tostringlist);
+/*
+ * toStringList() converts a list of values and returns a list of String 
values.
+ * If any values are not convertible to string point they will be null in the 
list returned.
+ */
+Datum age_tostringlist(PG_FUNCTION_ARGS)
+{
+    agtype *agt_arg = NULL;
+    agtype_in_state agis_result;
+    agtype_value *elem;
+    agtype_value string_elem;
+    int count;
+    int i;
+    char buffer[64];
+
+    /* check for null */
+    if (PG_ARGISNULL(0))
+    {
+        PG_RETURN_NULL();
+    }
+    agt_arg = AG_GET_ARG_AGTYPE_P(0);
+    /* check for an array */
+    if (!AGT_ROOT_IS_ARRAY(agt_arg) || AGT_ROOT_IS_SCALAR(agt_arg))
+    {
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("toStringList() argument must resolve to a list 
or null")));
+    }
+
+    count = AGT_ROOT_COUNT(agt_arg);
+
+    /* if we have an empty list or only one element in the list, return null */
+    if (count == 0)
+    {
+        PG_RETURN_NULL();
+    }
+
+    /* clear the result structure */
+    MemSet(&agis_result, 0, sizeof(agtype_in_state));
+
+    /* push the beginning of the array */
+    agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                        WAGT_BEGIN_ARRAY, NULL);
+
+    /* iterate through the list */
+    for (i = 0; i < count; i++)
+    {
+        // TODO: check element's type, it's value, and convert it to string if 
possible.
+        elem = get_ith_agtype_value_from_container(&agt_arg->root, i);
+        string_elem.type = AGTV_STRING;
+
+        switch (elem->type)
+        {
+        case AGTV_STRING:
+
+            if(!elem)
+            {
+                string_elem.type = AGTV_NULL;
+
+                agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                                    WAGT_ELEM, &string_elem);
+            }
+
+            string_elem.val.string.val = elem->val.string.val;
+            string_elem.val.string.len = elem->val.string.len;
+
+            agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                                WAGT_ELEM, &string_elem);
+
+            break;
+
+        case AGTV_FLOAT:
+
+            sprintf(buffer, "%.*g", DBL_DIG, elem->val.float_value);
+            string_elem.val.string.val = pstrdup(buffer);
+            string_elem.val.string.len = strlen(buffer);
+
+            agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                                WAGT_ELEM, &string_elem);
+
+            break;
+
+        case AGTV_INTEGER:
+
+            sprintf(buffer, "%ld", elem->val.int_value);
+            string_elem.val.string.val = pstrdup(buffer);
+            string_elem.val.string.len = strlen(buffer);
+
+            agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                                WAGT_ELEM, &string_elem);
+
+            break;
+
+        default:
+
+            string_elem.type = AGTV_NULL;
+            agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                                WAGT_ELEM, &string_elem);
+
+            break;
+        }
+    }
+
+    agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                        WAGT_END_ARRAY, NULL);
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(agis_result.res));
+}
+
 static agtype_iterator *get_next_list_element(agtype_iterator *it,
                            agtype_container *agtc, agtype_value *elem)
 {
diff --git a/src/backend/utils/adt/agtype_util.c 
b/src/backend/utils/adt/agtype_util.c
index e7ba2142..0583bce6 100644
--- a/src/backend/utils/adt/agtype_util.c
+++ b/src/backend/utils/adt/agtype_util.c
@@ -2304,3 +2304,84 @@ char *agtype_value_type_to_string(enum agtype_value_type 
type)
 
     return NULL;
 }
+
+/*
+ * Deallocates the passed agtype_value recursively.
+ */
+void pfree_agtype_value(agtype_value* value)
+{
+    pfree_agtype_value_content(value);
+    pfree(value);
+}
+
+/*
+ * Helper function that recursively deallocates the contents 
+ * of the passed agtype_value only. It does not deallocate
+ * `value` itself.
+ */
+void pfree_agtype_value_content(agtype_value* value)
+{
+    int i;
+
+    // guards against stack overflow due to deeply nested agtype_value
+    check_stack_depth();
+
+    switch (value->type)
+    {
+        case AGTV_NUMERIC:
+            pfree(value->val.numeric);
+            break;
+
+        case AGTV_STRING:
+            /*
+             * The char pointer (val.string.val) is not free'd because
+             * it is not allocated by an agtype helper function.
+             */
+            break;
+
+        case AGTV_ARRAY:
+        case AGTV_PATH:
+            for (i = 0; i < value->val.array.num_elems; i++)
+            {
+                pfree_agtype_value_content(&value->val.array.elems[i]);
+            }
+            pfree(value->val.array.elems);
+            break;
+
+        case AGTV_OBJECT:
+        case AGTV_VERTEX:
+        case AGTV_EDGE:
+            for (i = 0; i < value->val.object.num_pairs; i++)
+            {
+                pfree_agtype_value_content(&value->val.object.pairs[i].key);
+                pfree_agtype_value_content(&value->val.object.pairs[i].value);
+            }
+            pfree(value->val.object.pairs);
+            break;
+
+        case AGTV_BINARY:
+            pfree(value->val.binary.data);
+            break;
+
+        case AGTV_NULL:
+        case AGTV_INTEGER:
+        case AGTV_FLOAT:
+        case AGTV_BOOL:
+            /*
+             * These are deallocated by the calling function.
+             */
+            break;
+
+        default:
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("unknown agtype")));
+            break;
+    }
+}
+
+void pfree_agtype_in_state(agtype_in_state* value)
+{
+    pfree_agtype_value(value->res);
+    free(value->parse_state);
+}
diff --git a/src/backend/utils/load/ag_load_edges.c 
b/src/backend/utils/load/ag_load_edges.c
index 60f15f03..5a07c403 100644
--- a/src/backend/utils/load/ag_load_edges.c
+++ b/src/backend/utils/load/ag_load_edges.c
@@ -105,6 +105,8 @@ void edge_row_cb(int delim __attribute__((unused)), void 
*data)
         insert_edge_simple(cr->graph_oid, cr->object_name,
                            object_graph_id, start_vertex_graph_id,
                            end_vertex_graph_id, props);
+
+        pfree(props);
     }
 
     for (i = 0; i < n_fields; ++i)
diff --git a/src/backend/utils/load/ag_load_labels.c 
b/src/backend/utils/load/ag_load_labels.c
index 27e50281..2f60b30e 100644
--- a/src/backend/utils/load/ag_load_labels.c
+++ b/src/backend/utils/load/ag_load_labels.c
@@ -137,6 +137,7 @@ void vertex_row_cb(int delim __attribute__((unused)), void 
*data)
                                         n_fields, label_id_int);
         insert_vertex_simple(cr->graph_oid, cr->object_name,
                              object_graph_id, props);
+        pfree(props);
     }
 
 
diff --git a/src/backend/utils/load/age_load.c 
b/src/backend/utils/load/age_load.c
index bc64db32..984ca124 100644
--- a/src/backend/utils/load/age_load.c
+++ b/src/backend/utils/load/age_load.c
@@ -39,6 +39,7 @@
 
 agtype *create_empty_agtype(void)
 {
+    agtype* out;
     agtype_in_state result;
 
     memset(&result, 0, sizeof(agtype_in_state));
@@ -47,12 +48,18 @@ agtype *create_empty_agtype(void)
                                    NULL);
     result.res = push_agtype_value(&result.parse_state, WAGT_END_OBJECT, NULL);
 
-    return agtype_value_to_agtype(result.res);
+    out = agtype_value_to_agtype(result.res);
+    pfree_agtype_in_state(&result);
+
+    return out;
 }
 
 agtype *create_agtype_from_list(char **header, char **fields, size_t 
fields_len,
                                 int64 vertex_id)
 {
+    agtype* out;
+    agtype_value* key_agtype;
+    agtype_value* value_agtype;
     agtype_in_state result;
     int i;
 
@@ -61,33 +68,50 @@ agtype *create_agtype_from_list(char **header, char 
**fields, size_t fields_len,
     result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_OBJECT,
                                    NULL);
 
+    key_agtype = string_to_agtype_value("__id__");
     result.res = push_agtype_value(&result.parse_state,
                                    WAGT_KEY,
-                                   string_to_agtype_value("__id__"));
+                                   key_agtype);
+
+    value_agtype = integer_to_agtype_value(vertex_id);
     result.res = push_agtype_value(&result.parse_state,
                                    WAGT_VALUE,
-                                   integer_to_agtype_value(vertex_id));
+                                   value_agtype);
+
+    pfree_agtype_value(key_agtype);
+    pfree_agtype_value(value_agtype);
 
     for (i = 0; i<fields_len; i++)
     {
+        key_agtype = string_to_agtype_value(header[i]);
         result.res = push_agtype_value(&result.parse_state,
                                        WAGT_KEY,
-                                       string_to_agtype_value(header[i]));
+                                       key_agtype);
+
+        value_agtype = string_to_agtype_value(fields[i]);
         result.res = push_agtype_value(&result.parse_state,
                                        WAGT_VALUE,
-                                       string_to_agtype_value(fields[i]));
+                                       value_agtype);
+
+        pfree_agtype_value(key_agtype);
+        pfree_agtype_value(value_agtype);
     }
 
     result.res = push_agtype_value(&result.parse_state,
                                    WAGT_END_OBJECT, NULL);
 
-    return agtype_value_to_agtype(result.res);
+    out = agtype_value_to_agtype(result.res);
+    pfree_agtype_in_state(&result);
+
+    return out;
 }
 
 agtype* create_agtype_from_list_i(char **header, char **fields,
                                   size_t fields_len, size_t start_index)
 {
-
+    agtype* out;
+    agtype_value* key_agtype;
+    agtype_value* value_agtype;
     agtype_in_state result;
     size_t i;
 
@@ -103,18 +127,26 @@ agtype* create_agtype_from_list_i(char **header, char 
**fields,
 
     for (i = start_index; i < fields_len; i++)
     {
+        key_agtype = string_to_agtype_value(header[i]);
         result.res = push_agtype_value(&result.parse_state,
                                        WAGT_KEY,
-                                       string_to_agtype_value(header[i]));
+                                       key_agtype);
+        value_agtype = string_to_agtype_value(fields[i]);
         result.res = push_agtype_value(&result.parse_state,
                                        WAGT_VALUE,
-                                       string_to_agtype_value(fields[i]));
+                                       value_agtype);
+
+        pfree_agtype_value(key_agtype);
+        pfree_agtype_value(value_agtype);
     }
 
     result.res = push_agtype_value(&result.parse_state,
                                    WAGT_END_OBJECT, NULL);
 
-    return agtype_value_to_agtype(result.res);
+    out = agtype_value_to_agtype(result.res);
+    pfree_agtype_in_state(&result);
+
+    return out;
 }
 
 void insert_edge_simple(Oid graph_oid, char *label_name, graphid edge_id,
diff --git a/src/include/utils/agtype.h b/src/include/utils/agtype.h
index d8975f16..436470ef 100644
--- a/src/include/utils/agtype.h
+++ b/src/include/utils/agtype.h
@@ -547,6 +547,9 @@ agtype_value *string_to_agtype_value(char *s);
 agtype_value *integer_to_agtype_value(int64 int_value);
 void add_agtype(Datum val, bool is_null, agtype_in_state *result, Oid val_type,
                 bool key_scalar);
+void pfree_agtype_value(agtype_value* value);
+void pfree_agtype_value_content(agtype_value* value);
+void pfree_agtype_in_state(agtype_in_state* value);
 
 /* Oid accessors for AGTYPE */
 Oid get_AGTYPEOID(void);


Reply via email to