This is an automated email from the ASF dual-hosted git repository.
mtaha pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/PG12 by this push:
new b5deed4c Allow agtype_build_map to use AGTYPE keys (#1672)
b5deed4c is described below
commit b5deed4c97171c505999fa000708638b625851af
Author: John Gemignani <[email protected]>
AuthorDate: Wed Mar 13 23:26:00 2024 -0700
Allow agtype_build_map to use AGTYPE keys (#1672)
Added the ability for agtype_build_map to use AGTYPE keys.
In doing this, age_tostring needed to be broken up and rewritten
to handle UNKNOWNOID. This rewrite allows other functions to use
the logic in age_tostring without using age_tostring directly.
Additionally, rewrote age_tostring as a ("any") type function; it
used to be a (variadic "any"). The logic here can be used to re-
write other (variadic "any") functions that have a set number of
input parameters.
Added regression tests.
---
age--1.5.0--y.y.y.sql | 10 ++++
regress/expected/agtype.out | 44 ++++++++++++++-
regress/sql/agtype.sql | 16 ++++++
sql/age_scalar.sql | 2 +-
src/backend/utils/adt/agtype.c | 124 +++++++++++++++++++++++++++++++++--------
5 files changed, 170 insertions(+), 26 deletions(-)
diff --git a/age--1.5.0--y.y.y.sql b/age--1.5.0--y.y.y.sql
index 5258e2a9..3e05a6a0 100644
--- a/age--1.5.0--y.y.y.sql
+++ b/age--1.5.0--y.y.y.sql
@@ -107,3 +107,13 @@ DEFAULT FOR TYPE agtype USING gin AS
FUNCTION 6 ag_catalog.gin_triconsistent_agtype(internal, int2, agtype, int4,
internal, internal, internal),
STORAGE text;
+
+-- this function went from variadic "any" to just "any" type
+CREATE OR REPLACE FUNCTION ag_catalog.age_tostring("any")
+ RETURNS agtype
+ LANGUAGE c
+ IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index 66344c6e..b3750bcc 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -3139,7 +3139,7 @@ SELECT agtype_to_int4_array(NULL);
--
--Invalid Map Key (should fail)
SELECT agtype_build_map('[0]'::agtype, null);
-ERROR: key value must be scalar, not array, composite, or json
+ERROR: agtype_build_map_as_agtype_value only supports scalar arguments
--
-- Test agtype object/array access operators object.property,
object["property"], and array[element]
-- Note: At this point, object.property and object["property"] are equivalent.
@@ -3907,9 +3907,51 @@ SELECT ag_catalog.agtype_volatile_wrapper(32768::int2);
ERROR: smallint out of range
SELECT ag_catalog.agtype_volatile_wrapper(-32768::int2);
ERROR: smallint out of range
+--
+-- test that age_tostring can handle an UNKNOWNOID type
+--
+SELECT age_tostring('a');
+ age_tostring
+--------------
+ "a"
+(1 row)
+
+--
+-- test agtype_build_map_as_agtype_value via agtype_build_map
+--
+SELECT * FROM create_graph('agtype_build_map');
+NOTICE: graph "agtype_build_map" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT * FROM cypher('agtype_build_map', $$ RETURN
ag_catalog.agtype_build_map('1', '1', 2, 2, 3.14, 3.14, 'e', 2.71)
+ $$) AS (results agtype);
+ results
+---------------------------------------------
+ {"1": "1", "2": 2, "e": 2.71, "3.14": 3.14}
+(1 row)
+
+SELECT agtype_build_map('1', '1', 2, 2, 3.14, 3.14, 'e', 2.71);
+ agtype_build_map
+---------------------------------------------------------------
+ {"1": "1", "2": 2, "e": 2.71::numeric, "3.14": 3.14::numeric}
+(1 row)
+
--
-- Cleanup
--
+SELECT drop_graph('agtype_build_map', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table agtype_build_map._ag_label_vertex
+drop cascades to table agtype_build_map._ag_label_edge
+NOTICE: graph "agtype_build_map" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
DROP TABLE agtype_table;
--
-- End of AGTYPE data type regression tests
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index 4089d524..9e8d44e6 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -1098,9 +1098,25 @@ SELECT ag_catalog.agtype_volatile_wrapper(-32767::int2);
-- These should fail
SELECT ag_catalog.agtype_volatile_wrapper(32768::int2);
SELECT ag_catalog.agtype_volatile_wrapper(-32768::int2);
+
+--
+-- test that age_tostring can handle an UNKNOWNOID type
+--
+SELECT age_tostring('a');
+
+--
+-- test agtype_build_map_as_agtype_value via agtype_build_map
+--
+SELECT * FROM create_graph('agtype_build_map');
+SELECT * FROM cypher('agtype_build_map', $$ RETURN
ag_catalog.agtype_build_map('1', '1', 2, 2, 3.14, 3.14, 'e', 2.71)
+ $$) AS (results agtype);
+SELECT agtype_build_map('1', '1', 2, 2, 3.14, 3.14, 'e', 2.71);
+
--
-- Cleanup
--
+SELECT drop_graph('agtype_build_map', true);
+
DROP TABLE agtype_table;
--
diff --git a/sql/age_scalar.sql b/sql/age_scalar.sql
index 77f7689a..5014fbbe 100644
--- a/sql/age_scalar.sql
+++ b/sql/age_scalar.sql
@@ -149,7 +149,7 @@ RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';
-CREATE FUNCTION ag_catalog.age_tostring(variadic "any")
+CREATE FUNCTION ag_catalog.age_tostring("any")
RETURNS agtype
LANGUAGE c
IMMUTABLE
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index f1cfdcbe..db510cb7 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -175,6 +175,7 @@ static int extract_variadic_args_min(FunctionCallInfo
fcinfo,
int min_num_args);
static agtype_value* agtype_build_map_as_agtype_value(FunctionCallInfo fcinfo);
agtype_value *agtype_composite_to_agtype_value_binary(agtype *a);
+static agtype_value *tostring_helper(Datum arg, Oid type, char *msghdr);
/* global storage of OID for agtype and _agtype */
static Oid g_AGTYPEOID = InvalidOid;
@@ -2378,6 +2379,7 @@ static agtype_value*
agtype_build_map_as_agtype_value(FunctionCallInfo fcinfo)
result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_OBJECT,
NULL);
+ /* iterate through the arguments and build the object */
for (i = 0; i < nargs; i += 2)
{
/* process key */
@@ -2388,7 +2390,25 @@ static agtype_value*
agtype_build_map_as_agtype_value(FunctionCallInfo fcinfo)
errmsg("argument %d: key must not be null", i + 1)));
}
- add_agtype(args[i], false, &result, types[i], true);
+ /*
+ * If the key is agtype, we need to extract it as an agtype string and
+ * push the value.
+ */
+ if (types[i] == AGTYPEOID)
+ {
+ agtype_value *agtv = NULL;
+
+ agtv = tostring_helper(args[i], types[i],
+ "agtype_build_map_as_agtype_value");
+ result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
agtv);
+
+ /* free the agtype_value from tostring_helper */
+ pfree(agtv);
+ }
+ else
+ {
+ add_agtype(args[i], false, &result, types[i], true);
+ }
/* process value */
add_agtype(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
@@ -6709,16 +6729,12 @@ PG_FUNCTION_INFO_V1(age_tostring);
Datum age_tostring(PG_FUNCTION_ARGS)
{
int nargs;
- Datum *args;
Datum arg;
- bool *nulls;
- Oid *types;
- agtype_value agtv_result;
- char *string = NULL;
- Oid type;
+ Oid type = InvalidOid;
+ agtype *agt = NULL;
+ agtype_value *agtv = NULL;
- /* extract argument values */
- nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+ nargs = PG_NARGS();
/* check number of args */
if (nargs > 1)
@@ -6728,19 +6744,70 @@ Datum age_tostring(PG_FUNCTION_ARGS)
}
/* check for null */
- if (nargs < 0 || nulls[0])
+ if (nargs < 1 || PG_ARGISNULL(0))
{
PG_RETURN_NULL();
}
+ /* get the argument and type */
+ arg = PG_GETARG_DATUM(0);
+ type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+ /* verify that if the type is UNKNOWNOID it can be converted */
+ if (type == UNKNOWNOID && !get_fn_expr_arg_stable(fcinfo->flinfo, 0))
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("toString() UNKNOWNOID and not stable")));
+ }
+
/*
* toString() supports integer, float, numeric, text, cstring, boolean,
* regtype or the agtypes: integer, float, numeric, string, boolean input
*/
- arg = args[0];
- type = types[0];
+ agtv = tostring_helper(arg, type, "toString()");
- if (type != AGTYPEOID)
+ /* if we get a NULL back we need to return NULL */
+ if (agtv == NULL)
+ {
+ PG_RETURN_NULL();
+ }
+
+ /* convert to agtype and free the agtype_value */
+ agt = agtype_value_to_agtype(agtv);
+ pfree(agtv);
+
+ PG_RETURN_POINTER(agt);
+}
+
+/*
+ * Helper function to take any valid type and convert it to an agtype string.
+ * Returns NULL for NULL output.
+ */
+static agtype_value *tostring_helper(Datum arg, Oid type, char *msghdr)
+{
+ agtype_value *agtv_result = NULL;
+ char *string = NULL;
+
+ agtv_result = palloc0(sizeof(agtype_value));
+
+ /*
+ * toString() supports: unknown, integer, float, numeric, text, cstring,
+ * boolean, regtype or the agtypes: integer, float, numeric, string, and
+ * boolean input.
+ */
+
+ /*
+ * If the type is UNKNOWNOID convert it to a cstring. Prior to passing an
+ * UNKNOWNOID it should be verified to be stable.
+ */
+ if (type == UNKNOWNOID)
+ {
+ char *str = DatumGetPointer(arg);
+
+ string = pnstrdup(str, strlen(str));
+ }
+ /* if it is not an AGTYPEOID */
+ else if (type != AGTYPEOID)
{
if (type == INT2OID)
{
@@ -6787,11 +6854,12 @@ Datum age_tostring(PG_FUNCTION_ARGS)
else
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("toString() unsupported argument type %d",
- type)));
+ errmsg("%s unsupported argument type %d",
+ msghdr, type)));
}
}
- else
+ /* if it is an AGTYPEOID */
+ else if (type == AGTYPEOID)
{
agtype *agt_arg;
agtype_value *agtv_value;
@@ -6802,14 +6870,15 @@ Datum age_tostring(PG_FUNCTION_ARGS)
if (!AGT_ROOT_IS_SCALAR(agt_arg))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("toString() only supports scalar
arguments")));
+ errmsg("%s only supports scalar arguments",
+ msghdr)));
}
agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
if (agtv_value->type == AGTV_NULL)
{
- PG_RETURN_NULL();
+ return NULL;
}
else if (agtv_value->type == AGTV_INTEGER)
{
@@ -6838,17 +6907,24 @@ Datum age_tostring(PG_FUNCTION_ARGS)
else
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("toString() unsupported argument agtype %d",
- agtv_value->type)));
+ errmsg("%s unsupported argument agtype %d",
+ msghdr, agtv_value->type)));
}
}
+ /* it is an unknown type */
+ else
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%s unknown argument agtype %d",
+ msghdr, type)));
+ }
/* build the result */
- agtv_result.type = AGTV_STRING;
- agtv_result.val.string.val = string;
- agtv_result.val.string.len = strlen(string);
+ agtv_result->type = AGTV_STRING;
+ agtv_result->val.string.val = string;
+ agtv_result->val.string.len = strlen(string);
- PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+ return agtv_result;
}
PG_FUNCTION_INFO_V1(age_tostringlist);