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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3c6c68e  Add the Cypher functions rTrim(), lTrim(), and trim()
3c6c68e is described below

commit 3c6c68eed53cbc8451088b226edfebf713142dd2
Author: John Gemignani <[email protected]>
AuthorDate: Thu Jul 30 17:31:14 2020 -0700

    Add the Cypher functions rTrim(), lTrim(), and trim()
    
    Added the Cypher functions rTrim(), lTrim(), and trim().
    
    Additionally fixed an issue with reverse().
    
    Added regression tests.
---
 age--0.2.0.sql                     |  24 +++
 regress/expected/cypher_create.out |  16 +-
 regress/expected/expr.out          | 134 ++++++++++++++++
 regress/sql/expr.sql               |  53 +++++++
 src/backend/parser/cypher_expr.c   |  12 +-
 src/backend/utils/adt/agtype.c     | 307 +++++++++++++++++++++++++++++++++++--
 6 files changed, 517 insertions(+), 29 deletions(-)

diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index d3d740b..e3faff5 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -934,6 +934,30 @@ RETURNS NULL ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION l_trim(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION r_trim(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION b_trim(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 --
 -- function for typecasting an agtype value to another agtype value
 --
diff --git a/regress/expected/cypher_create.out 
b/regress/expected/cypher_create.out
index 85003b0..d60bc70 100644
--- a/regress/expected/cypher_create.out
+++ b/regress/expected/cypher_create.out
@@ -375,14 +375,14 @@ SELECT * FROM cypher_create.e_var;
 SELECT * FROM ag_label;
        name       | graph | id | kind |            relation            
 ------------------+-------+----+------+--------------------------------
- _ag_label_vertex | 17013 |  1 | v    | cypher_create._ag_label_vertex
- _ag_label_edge   | 17013 |  2 | e    | cypher_create._ag_label_edge
- v                | 17013 |  3 | v    | cypher_create.v
- e                | 17013 |  4 | e    | cypher_create.e
- n_var            | 17013 |  5 | v    | cypher_create.n_var
- e_var            | 17013 |  6 | e    | cypher_create.e_var
- n_other_node     | 17013 |  7 | v    | cypher_create.n_other_node
- b_var            | 17013 |  8 | e    | cypher_create.b_var
+ _ag_label_vertex | 17019 |  1 | v    | cypher_create._ag_label_vertex
+ _ag_label_edge   | 17019 |  2 | e    | cypher_create._ag_label_edge
+ v                | 17019 |  3 | v    | cypher_create.v
+ e                | 17019 |  4 | e    | cypher_create.e
+ n_var            | 17019 |  5 | v    | cypher_create.n_var
+ e_var            | 17019 |  6 | e    | cypher_create.e_var
+ n_other_node     | 17019 |  7 | v    | cypher_create.n_other_node
+ b_var            | 17019 |  8 | e    | cypher_create.b_var
 (8 rows)
 
 --Validate every vertex has the correct label
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 56f6188..3c6be61 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -2255,6 +2255,140 @@ LINE 1: SELECT * FROM tolowercase();
                       ^
 HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
 --
+-- lTrim(), rTrim(), trim()
+--
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim("  string   ")
+$$) AS (results agtype);
+   results   
+-------------
+ "string   "
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim("  string   ")
+$$) AS (results agtype);
+  results   
+------------
+ "  string"
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN trim("  string   ")
+$$) AS (results agtype);
+ results  
+----------
+ "string"
+(1 row)
+
+SELECT * FROM l_trim('  string   ');
+   l_trim    
+-------------
+ "string   "
+(1 row)
+
+SELECT * FROM r_trim('  string   ');
+   r_trim   
+------------
+ "  string"
+(1 row)
+
+SELECT * FROM b_trim('  string   ');
+  b_trim  
+----------
+ "string"
+(1 row)
+
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN trim(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM l_trim(null);
+ l_trim 
+--------
+ 
+(1 row)
+
+SELECT * FROM r_trim(null);
+ r_trim 
+--------
+ 
+(1 row)
+
+SELECT * FROM b_trim(null);
+ b_trim 
+--------
+ 
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim(true)
+$$) AS (results agtype);
+ERROR:  lTrim() unsuppoted argument agtype 5
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim(true)
+$$) AS (results agtype);
+ERROR:  rTrim() unsuppoted argument agtype 5
+SELECT * FROM cypher('expr', $$
+    RETURN trim(true)
+$$) AS (results agtype);
+ERROR:  trim() unsuppoted argument agtype 5
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN trim()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM l_trim();
+ERROR:  function l_trim() does not exist
+LINE 1: SELECT * FROM l_trim();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM r_trim();
+ERROR:  function r_trim() does not exist
+LINE 1: SELECT * FROM r_trim();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM b_trim();
+ERROR:  function b_trim() does not exist
+LINE 1: SELECT * FROM b_trim();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 15a7000..0f205c5 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -988,6 +988,59 @@ SELECT * FROM touppercase();
 SELECT * FROM tolowercase();
 
 --
+-- lTrim(), rTrim(), trim()
+--
+
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim("  string   ")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim("  string   ")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN trim("  string   ")
+$$) AS (results agtype);
+SELECT * FROM l_trim('  string   ');
+SELECT * FROM r_trim('  string   ');
+SELECT * FROM b_trim('  string   ');
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN trim(null)
+$$) AS (results agtype);
+SELECT * FROM l_trim(null);
+SELECT * FROM r_trim(null);
+SELECT * FROM b_trim(null);
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim(true)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim(true)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN trim(true)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN lTrim()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN rTrim()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN trim()
+$$) AS (results agtype);
+
+SELECT * FROM l_trim();
+SELECT * FROM r_trim();
+SELECT * FROM b_trim();
+
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index a4a3661..250a4b4 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -56,10 +56,13 @@
 #define FUNC_TOINTEGER  {"toInteger",  "tointeger",  ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
 #define FUNC_TYPE       {"type",       "type",       AGTYPEOID, 0, 0, 
AGTYPEOID, 1, 1, false}
 #define FUNC_EXISTS     {"exists",     "exists_property", AGTYPEOID, 0, 0, 
BOOLOID, 1, 1, false}
-#define FUNC_TOSTRING   {"toString",  "tostring",    ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_TOSTRING   {"toString",   "tostring",   ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
 #define FUNC_REVERSE    {"reverse",    "reverse",    ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
-#define FUNC_TOUPPER    {"toUpper",    "touppercase",  ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
-#define FUNC_TOLOWER    {"toLower",    "tolowercase",  ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_TOUPPER    {"toUpper",    "touppercase", ANYOID,   0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_TOLOWER    {"toLower",    "tolowercase", ANYOID,   0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_LTRIM      {"lTrim",      "l_trim",     ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_RTRIM      {"rTrim",      "r_trim",     ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_BTRIM      {"trim",       "b_trim",     ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
 
 /* supported functions */
 #define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
@@ -67,7 +70,8 @@
                              FUNC_PROPERTIES, FUNC_SIZE, FUNC_STARTNODE, \
                              FUNC_TOINTEGER, FUNC_TOBOOLEAN, FUNC_TOFLOAT, \
                              FUNC_EXISTS, FUNC_TOSTRING, FUNC_REVERSE, \
-                             FUNC_TOUPPER, FUNC_TOLOWER}
+                             FUNC_TOUPPER, FUNC_TOLOWER, FUNC_LTRIM, \
+                             FUNC_RTRIM, FUNC_BTRIM}
 
 /* structure for supported function signatures */
 typedef struct function_signature
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 4f7816d..af63a75 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -4369,11 +4369,10 @@ Datum reverse(PG_FUNCTION_ARGS)
     bool *nulls;
     Oid *types;
     agtype_value agtv_result;
+    text *text_string = NULL;
     char *string = NULL;
-    char *result = NULL;
     int string_len;
     Oid type;
-    int i;
 
     /* extract argument values */
     nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
@@ -4394,14 +4393,13 @@ Datum reverse(PG_FUNCTION_ARGS)
     if (type != AGTYPEOID)
     {
         if (type == CSTRINGOID)
-            string = DatumGetCString(arg);
+            text_string = cstring_to_text(DatumGetCString(arg));
         else if (type == TEXTOID)
-            string = text_to_cstring(DatumGetTextPP(arg));
+            text_string = DatumGetTextPP(arg);
         else
             ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                             errmsg("reverse() unsuppoted argument type %d",
                                    type)));
-        string_len = strlen(string);
     }
     else
     {
@@ -4421,30 +4419,32 @@ Datum reverse(PG_FUNCTION_ARGS)
         if (agtv_value->type == AGTV_NULL)
             PG_RETURN_NULL();
         if (agtv_value->type == AGTV_STRING)
-        {
-            string = agtv_value->val.string.val;
-            string_len = agtv_value->val.string.len;
-        }
+            text_string = cstring_to_text_with_len(agtv_value->val.string.val,
+                                                   agtv_value->val.string.len);
         else
             ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                             errmsg("reverse() unsuppoted argument agtype %d",
                                    agtv_value->type)));
     }
 
+    /*
+     * We need the string as a text string so that we can let PG deal with
+     * multibyte characters in reversing the string.
+     */
+    text_string = DatumGetTextPP(DirectFunctionCall1(text_reverse,
+                                                     
PointerGetDatum(text_string)));
+
+    /* convert it back to a cstring */
+    string = text_to_cstring(text_string);
+    string_len = strlen(string);
+
     /* if we have an empty string, return null */
     if (string_len == 0)
         PG_RETURN_NULL();
 
-    /* allocate the new string */
-    result = palloc(string_len);
-
-    /* reverse the string */
-    for (i = 0; i < string_len; i++)
-        result[i] = string[string_len - i - 1];
-
     /* build the result */
     agtv_result.type = AGTV_STRING;
-    agtv_result.val.string.val = result;
+    agtv_result.val.string.val = string;
     agtv_result.val.string.len = string_len;
 
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
@@ -4629,3 +4629,276 @@ Datum tolowercase(PG_FUNCTION_ARGS)
 
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
 }
+
+PG_FUNCTION_INFO_V1(r_trim);
+
+Datum r_trim(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    Datum arg;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    text *text_string = NULL;
+    char *string = NULL;
+    int string_len;
+    Oid type;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs > 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("rTrim() only supports one argument")));
+
+    /* check for null */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /* rTrim() supports text, cstring, or the agtype string input */
+    arg = args[0];
+    type = types[0];
+
+    if (type != AGTYPEOID)
+    {
+        if (type == CSTRINGOID)
+            text_string = cstring_to_text(DatumGetCString(arg));
+        else if (type == TEXTOID)
+            text_string = DatumGetTextPP(arg);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("rTrim() unsuppoted argument type %d",
+                                   type)));
+    }
+    else
+    {
+        agtype *agt_arg;
+        agtype_value *agtv_value;
+
+        /* get the agtype argument */
+        agt_arg = DATUM_GET_AGTYPE_P(arg);
+
+        if (!AGT_ROOT_IS_SCALAR(agt_arg))
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("rTrim() only supports scalar arguments")));
+
+        agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+        /* check for agtype null */
+        if (agtv_value->type == AGTV_NULL)
+            PG_RETURN_NULL();
+        if (agtv_value->type == AGTV_STRING)
+            text_string = cstring_to_text_with_len(agtv_value->val.string.val,
+                                                   agtv_value->val.string.len);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("rTrim() unsuppoted argument agtype %d",
+                                   agtv_value->type)));
+    }
+
+    /*
+     * We need the string as a text string so that we can let PG deal with
+     * multibyte characters in trimming the string.
+     */
+    text_string = DatumGetTextPP(DirectFunctionCall1(rtrim1,
+                                                     
PointerGetDatum(text_string)));
+
+    /* convert it back to a cstring */
+    string = text_to_cstring(text_string);
+    string_len = strlen(string);
+
+    /* if we have an empty string, return null */
+    if (string_len == 0)
+        PG_RETURN_NULL();
+
+    /* build the result */
+    agtv_result.type = AGTV_STRING;
+    agtv_result.val.string.val = string;
+    agtv_result.val.string.len = string_len;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(l_trim);
+
+Datum l_trim(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    Datum arg;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    text *text_string = NULL;
+    char *string = NULL;
+    int string_len;
+    Oid type;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs > 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("lTrim() only supports one argument")));
+
+    /* check for null */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /* rTrim() supports text, cstring, or the agtype string input */
+    arg = args[0];
+    type = types[0];
+
+    if (type != AGTYPEOID)
+    {
+        if (type == CSTRINGOID)
+            text_string = cstring_to_text(DatumGetCString(arg));
+        else if (type == TEXTOID)
+            text_string = DatumGetTextPP(arg);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("lTrim() unsuppoted argument type %d",
+                                   type)));
+    }
+    else
+    {
+        agtype *agt_arg;
+        agtype_value *agtv_value;
+
+        /* get the agtype argument */
+        agt_arg = DATUM_GET_AGTYPE_P(arg);
+
+        if (!AGT_ROOT_IS_SCALAR(agt_arg))
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("lTrim() only supports scalar arguments")));
+
+        agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+        /* check for agtype null */
+        if (agtv_value->type == AGTV_NULL)
+            PG_RETURN_NULL();
+        if (agtv_value->type == AGTV_STRING)
+            text_string = cstring_to_text_with_len(agtv_value->val.string.val,
+                                                   agtv_value->val.string.len);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("lTrim() unsuppoted argument agtype %d",
+                                   agtv_value->type)));
+    }
+
+    /*
+     * We need the string as a text string so that we can let PG deal with
+     * multibyte characters in trimming the string.
+     */
+    text_string = DatumGetTextPP(DirectFunctionCall1(ltrim1,
+                                                     
PointerGetDatum(text_string)));
+
+    /* convert it back to a cstring */
+    string = text_to_cstring(text_string);
+    string_len = strlen(string);
+
+    /* if we have an empty string, return null */
+    if (string_len == 0)
+        PG_RETURN_NULL();
+
+    /* build the result */
+    agtv_result.type = AGTV_STRING;
+    agtv_result.val.string.val = string;
+    agtv_result.val.string.len = string_len;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(b_trim);
+
+Datum b_trim(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    Datum arg;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    text *text_string = NULL;
+    char *string = NULL;
+    int string_len;
+    Oid type;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs > 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("trim() only supports one argument")));
+
+    /* check for null */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /* trim() supports text, cstring, or the agtype string input */
+    arg = args[0];
+    type = types[0];
+
+    if (type != AGTYPEOID)
+    {
+        if (type == CSTRINGOID)
+            text_string = cstring_to_text(DatumGetCString(arg));
+        else if (type == TEXTOID)
+            text_string = DatumGetTextPP(arg);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("trim() unsuppoted argument type %d",
+                                   type)));
+    }
+    else
+    {
+        agtype *agt_arg;
+        agtype_value *agtv_value;
+
+        /* get the agtype argument */
+        agt_arg = DATUM_GET_AGTYPE_P(arg);
+
+        if (!AGT_ROOT_IS_SCALAR(agt_arg))
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("trim() only supports scalar arguments")));
+
+        agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+        /* check for agtype null */
+        if (agtv_value->type == AGTV_NULL)
+            PG_RETURN_NULL();
+        if (agtv_value->type == AGTV_STRING)
+            text_string = cstring_to_text_with_len(agtv_value->val.string.val,
+                                                   agtv_value->val.string.len);
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("trim() unsuppoted argument agtype %d",
+                                   agtv_value->type)));
+    }
+
+    /*
+     * We need the string as a text string so that we can let PG deal with
+     * multibyte characters in trimming the string.
+     */
+    text_string = DatumGetTextPP(DirectFunctionCall1(btrim1,
+                                                     
PointerGetDatum(text_string)));
+
+    /* convert it back to a cstring */
+    string = text_to_cstring(text_string);
+    string_len = strlen(string);
+
+    /* if we have an empty string, return null */
+    if (string_len == 0)
+        PG_RETURN_NULL();
+
+    /* build the result */
+    agtv_result.type = AGTV_STRING;
+    agtv_result.val.string.val = string;
+    agtv_result.val.string.len = string_len;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}

Reply via email to