From dcf2fa0d651d9bca880b49a2e4782d9d4374e59f Mon Sep 17 00:00:00 2001 From: John Gemignani Date: Fri, 14 Jul 2023 17:33:03 -0700 Subject: [PATCH] Fix issue 395: ERROR: container is not an agtype array (#1039) Fixed issue 395 - ERROR: container is not an agtype array This was due to an incorrect order in checking for AGTV_NULL in the collect aggregate transfer function: age_collect_aggtransfn Added regression tests. --- regress/expected/expr.out | 75 ++++++++++++++++++++++++++++++++++ regress/sql/expr.sql | 35 ++++++++++++++++ src/backend/utils/adt/agtype.c | 17 ++++---- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/regress/expected/expr.out b/regress/expected/expr.out index 6c0266e90..52f5eb026 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -6908,9 +6908,84 @@ SELECT * FROM cypher('expr', $$MATCH (u) RETURN toString(pg_catalog.pg_typeof(u. "agtype" (7 rows) +-- issue: 395 aggregate function collect() incorrect container for operation +SELECT create_graph('graph_395'); +NOTICE: graph "graph_395" has been created + create_graph +-------------- + +(1 row) + +SELECT * FROM cypher('graph_395', $$ CREATE (n:Project {name: 'Project A'}), + (m:Project {name: 'Project B'}), + (a:Task {name: 'Task A', size: 10}), + (b:Task {name: 'Task B', size: 5}), + (c:Task {name: 'Task C', size: 7}), + (x:Person {name: 'John', age: 55}), + (y:Person {name: 'Bob', age: 43}), + (z:Person {name: 'Alice', age: 33}), + (n)-[:Has]->(a), + (n)-[:Has]->(b), + (m)-[:Has]->(c), + (a)-[:AssignedTo]->(x), + (b)-[:AssignedTo]->(y), + (c)-[:AssignedTo]->(y) $$) as (n agtype); + n +--- +(0 rows) + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + RETURN task $$) AS (p agtype); + p +----------------------------------------------------------------------------------------------------------------------------- + {"tn": "Task A", "users": [{"id": 1407374883553281, "label": "Person", "properties": {"age": 55, "name": "John"}}::vertex]} + {"tn": "Task B", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]} + {"tn": "Task C", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]} +(3 rows) + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + WITH p, collect(task) AS tasks + RETURN tasks $$) AS (p agtype); + p +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"tn": "Task A", "users": [{"id": 1407374883553281, "label": "Person", "properties": {"age": 55, "name": "John"}}::vertex]}, {"tn": "Task B", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]}] + [{"tn": "Task C", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]}] +(2 rows) + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + WITH p, collect(task) AS tasks + WITH {pn: p.name, tasks:tasks} AS project + RETURN project $$) AS (p agtype); + p +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {"pn": "Project A", "tasks": [{"tn": "Task A", "users": [{"id": 1407374883553281, "label": "Person", "properties": {"age": 55, "name": "John"}}::vertex]}, {"tn": "Task B", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]}]} + {"pn": "Project B", "tasks": [{"tn": "Task C", "users": [{"id": 1407374883553282, "label": "Person", "properties": {"age": 43, "name": "Bob"}}::vertex]}]} +(2 rows) + -- -- Cleanup -- +SELECT * FROM drop_graph('graph_395', true); +NOTICE: drop cascades to 7 other objects +DETAIL: drop cascades to table graph_395._ag_label_vertex +drop cascades to table graph_395._ag_label_edge +drop cascades to table graph_395."Project" +drop cascades to table graph_395."Task" +drop cascades to table graph_395."Person" +drop cascades to table graph_395."Has" +drop cascades to table graph_395."AssignedTo" +NOTICE: graph "graph_395" has been dropped + drop_graph +------------ + +(1 row) + SELECT * FROM drop_graph('chained', true); NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table chained._ag_label_vertex diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index d22484976..501286282 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -2802,9 +2802,44 @@ SELECT * FROM cypher('list',$$ MATCH p=(n:xyz)-[e]->() SET n.array=[0, 1, 2, 3, -- pg_typeof SELECT * FROM cypher('expr', $$MATCH (u) RETURN toString(pg_catalog.pg_typeof(u.id)) $$) AS (u agtype); +-- issue: 395 aggregate function collect() incorrect container for operation +SELECT create_graph('graph_395'); +SELECT * FROM cypher('graph_395', $$ CREATE (n:Project {name: 'Project A'}), + (m:Project {name: 'Project B'}), + (a:Task {name: 'Task A', size: 10}), + (b:Task {name: 'Task B', size: 5}), + (c:Task {name: 'Task C', size: 7}), + (x:Person {name: 'John', age: 55}), + (y:Person {name: 'Bob', age: 43}), + (z:Person {name: 'Alice', age: 33}), + (n)-[:Has]->(a), + (n)-[:Has]->(b), + (m)-[:Has]->(c), + (a)-[:AssignedTo]->(x), + (b)-[:AssignedTo]->(y), + (c)-[:AssignedTo]->(y) $$) as (n agtype); + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + RETURN task $$) AS (p agtype); + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + WITH p, collect(task) AS tasks + RETURN tasks $$) AS (p agtype); + +SELECT * FROM cypher('graph_395', $$ MATCH (p:Project)-[:Has]->(t:Task)-[:AssignedTo]->(u:Person) + WITH p, t, collect(u) AS users + WITH p, {tn: t.name, users: users} AS task + WITH p, collect(task) AS tasks + WITH {pn: p.name, tasks:tasks} AS project + RETURN project $$) AS (p agtype); -- -- Cleanup -- +SELECT * FROM drop_graph('graph_395', true); SELECT * FROM drop_graph('chained', true); SELECT * FROM drop_graph('VLE', true); SELECT * FROM drop_graph('case_statement', true); diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 736f41d22..359bbd794 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -9752,6 +9752,7 @@ Datum age_collect_aggtransfn(PG_FUNCTION_ARGS) /* create and initialize the state */ castate = palloc0(sizeof(agtype_in_state)); memset(castate, 0, sizeof(agtype_in_state)); + /* start the array */ castate->res = push_agtype_value(&castate->parse_state, WAGT_BEGIN_ARRAY, NULL); @@ -9781,23 +9782,25 @@ Datum age_collect_aggtransfn(PG_FUNCTION_ARGS) /* only add non null values */ if (nulls[0] == false) { + agtype_value *agtv_value = NULL; + /* we need to check for agtype null and skip it, if found */ if (types[0] == AGTYPEOID) { agtype *agt_arg; - agtype_value *agtv_value; /* get the agtype argument */ agt_arg = DATUM_GET_AGTYPE_P(args[0]); - agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, - 0); - /* add the arg if not agtype null */ - if (agtv_value->type != AGTV_NULL) + + /* get the scalar value */ + if (AGTYPE_CONTAINER_IS_SCALAR(&agt_arg->root)) { - add_agtype(args[0], nulls[0], castate, types[0], false); + agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); } } - else + + /* skip the arg if agtype null */ + if (agtv_value == NULL || agtv_value->type != AGTV_NULL) { add_agtype(args[0], nulls[0], castate, types[0], false); }