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));
+}