From 7cc4c92dada8777382f3546c830ab6492528d613 Mon Sep 17 00:00:00 2001 From: John Gemignani Date: Thu, 4 Jan 2024 16:13:00 -0800 Subject: [PATCH] Add checks for array functions to recognize and decode VPC (#1064) (#1490) * Add checks for array functions to recognize and decode VPC - Added for array functions size, head, last, isEmpty, reverse, agtype_in_operator and agtype_access_slice. - Fixed a bug where object input to reverse would terminate the server instead of erroring out. - Added regression tests. * Fix issue with in operator tranformation - We need to build a function call here if the rexpr is already tranformed. It can be already tranformed cypher_list as columnref. Resolved - Conflicts: src/backend/parser/cypher_expr.c Co-authored-by: Muhammad Taha Naveed --- regress/expected/expr.out | 349 ++++++++++++++++++++++++++- regress/sql/expr.sql | 158 +++++++++++++ src/backend/parser/cypher_expr.c | 32 ++- src/backend/utils/adt/agtype.c | 391 ++++++++++++++++++++++++------- src/include/utils/agtype.h | 2 + 5 files changed, 830 insertions(+), 102 deletions(-) diff --git a/regress/expected/expr.out b/regress/expected/expr.out index b4bf90c63..6c90666b7 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -323,13 +323,9 @@ $$RETURN 1 IN [[null]]$$) AS r(c boolean); SELECT * FROM cypher('expr', $$RETURN null IN 'str' $$) AS r(c boolean); ERROR: object of IN must be a list -LINE 2: $$RETURN null IN 'str' $$) AS r(c boolean); - ^ SELECT * FROM cypher('expr', $$RETURN 'str' IN 'str' $$) AS r(c boolean); ERROR: object of IN must be a list -LINE 2: $$RETURN 'str' IN 'str' $$) AS r(c boolean); - ^ -- list access SELECT * FROM cypher('expr', $$RETURN [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10][0]$$) AS r(c agtype); @@ -2793,6 +2789,16 @@ ERROR: function ag_catalog.age_size() does not exist LINE 2: RETURN size() ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array[0]) = 0 + RETURN vle_array +$$) AS (vle_array agtype); +ERROR: size() unsupported argument +SELECT * FROM cypher('expr', $$ + RETURN size({id: 0, status:'it_will_fail'}) +$$) AS (size agtype); +ERROR: size() unsupported argument -- head() of an array SELECT * FROM cypher('expr', $$ RETURN head([1, 2, 3, 4, 5]) @@ -2827,6 +2833,14 @@ $$) AS (head agtype); (1 row) +SELECT * FROM cypher('expr', $$ + RETURN head([null, null]) +$$) AS (head agtype); + head +------ + +(1 row) + -- should fail SELECT * FROM cypher('expr', $$ RETURN head(1234567890) @@ -2839,6 +2853,15 @@ ERROR: function ag_catalog.age_head() does not exist LINE 2: RETURN head() ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN head(vle_array[0]) +$$) AS (head agtype); +ERROR: head() argument must resolve to a list or null +SELECT * FROM cypher('expr', $$ + RETURN head({id: 0, status:'it_will_fail'}) +$$) AS (head agtype); +ERROR: head() argument must resolve to a list or null -- last() SELECT * FROM cypher('expr', $$ RETURN last([1, 2, 3, 4, 5]) @@ -2873,6 +2896,14 @@ $$) AS (last agtype); (1 row) +SELECT * FROM cypher('expr', $$ + RETURN last([null, null]) +$$) AS (last agtype); + last +------ + +(1 row) + -- should fail SELECT * FROM cypher('expr', $$ RETURN last(1234567890) @@ -2885,6 +2916,15 @@ ERROR: function ag_catalog.age_last() does not exist LINE 2: RETURN last() ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN last(vle_array[0]) +$$) AS (last agtype); +ERROR: last() argument must resolve to a list or null +SELECT * FROM cypher('expr', $$ + RETURN last({id: 0, status:'it_will_fail'}) +$$) AS (last agtype); +ERROR: last() argument must resolve to a list or null -- properties() SELECT * FROM cypher('expr', $$ MATCH (v) RETURN properties(v) @@ -3750,6 +3790,15 @@ ERROR: function age_reverse() does not exist LINE 1: SELECT * FROM age_reverse(); ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + MATCH (v) + RETURN reverse(v) +$$) AS (results agtype); +ERROR: reverse() unsupported argument agtype 6 +SELECT * FROM cypher('expr', $$ + RETURN reverse({}) +$$) AS (results agtype); +ERROR: reverse() unsupported argument agtype -- -- toUpper() and toLower() -- @@ -7665,6 +7714,298 @@ 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) +-- +-- issue 1044 - array functions not recognizing vpc +-- +-- size +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 0 + RETURN vle_array +$$) AS (vle_array agtype); + vle_array +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 1 + RETURN vle_array +$$) AS (vle_array agtype); + vle_array +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(2 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 2 + RETURN vle_array +$$) AS (vle_array agtype); + vle_array +----------- +(0 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) = size(vle_array) + RETURN vle_array +$$) AS (vle_array agtype); + vle_array +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +-- head +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN head(vle_array) +$$) AS (head agtype); + head +--------------------------------------------------------------------------------------------------------------------------- + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = vle_array[0] + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = vle_array[size(vle_array) - size(vle_array)] + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = head([vle_array[0]]) + RETURN vle_array LIMIT 1 +$$) AS (head agtype); + head +----------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(1 row) + +-- last +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN last(vle_array) +$$) AS (head agtype); + head +--------------------------------------------------------------------------------------------------------------------------- + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge + {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge + {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = vle_array[0] + RETURN vle_array +$$) AS (head agtype); + head +----------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(4 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = vle_array[size(vle_array)-1] + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = head([vle_array[size(vle_array)-1]]) + RETURN vle_array LIMIT 1 +$$) AS (head agtype); + head +----------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(1 row) + +-- isEmpty +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array) + RETURN vle_array +$$) AS (head agtype); + head +------ +(0 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array) = false + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array[0..0]) + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty([vle_array[3]]) = false + RETURN vle_array +$$) AS (head agtype); + head +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] +(6 rows) + +-- reverse +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN reverse(vle_array) +$$) as (u agtype); + u +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(6 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE reverse(vle_array)[0] = last(vle_array) + RETURN reverse(vle_array) +$$) as (u agtype); + u +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge, {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(6 rows) + +-- IN operator +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0] IN vle_array + RETURN vle_array +$$) AS (a agtype); + a +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(3 rows) + +-- access slice +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0..1] = [vle_array[0]] + RETURN vle_array +$$) AS (a agtype); + a +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge] + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] + [{"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(3 rows) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[1..2] = [last(vle_array)] + RETURN vle_array +$$) AS (a agtype); + a +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge, {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge] +(1 row) + +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0..1] = [vle_array[0], vle_array[1]] + RETURN vle_array +$$) AS (a agtype); + a +--- +(0 rows) + --- --- Fix: Segmentation fault when using specific names for tables #1124 --- diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index ecf5abf91..a9cfc185b 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -1221,6 +1221,14 @@ $$) AS (size agtype); SELECT * FROM cypher('expr', $$ RETURN size() $$) AS (size agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array[0]) = 0 + RETURN vle_array +$$) AS (vle_array agtype); +SELECT * FROM cypher('expr', $$ + RETURN size({id: 0, status:'it_will_fail'}) +$$) AS (size agtype); -- head() of an array SELECT * FROM cypher('expr', $$ RETURN head([1, 2, 3, 4, 5]) @@ -1235,6 +1243,9 @@ $$) AS (head agtype); SELECT * FROM cypher('expr', $$ RETURN head(null) $$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + RETURN head([null, null]) +$$) AS (head agtype); -- should fail SELECT * FROM cypher('expr', $$ RETURN head(1234567890) @@ -1242,6 +1253,13 @@ $$) AS (head agtype); SELECT * FROM cypher('expr', $$ RETURN head() $$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN head(vle_array[0]) +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + RETURN head({id: 0, status:'it_will_fail'}) +$$) AS (head agtype); -- last() SELECT * FROM cypher('expr', $$ RETURN last([1, 2, 3, 4, 5]) @@ -1256,6 +1274,9 @@ $$) AS (last agtype); SELECT * FROM cypher('expr', $$ RETURN last(null) $$) AS (last agtype); +SELECT * FROM cypher('expr', $$ + RETURN last([null, null]) +$$) AS (last agtype); -- should fail SELECT * FROM cypher('expr', $$ RETURN last(1234567890) @@ -1263,6 +1284,13 @@ $$) AS (last agtype); SELECT * FROM cypher('expr', $$ RETURN last() $$) AS (last agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN last(vle_array[0]) +$$) AS (last agtype); +SELECT * FROM cypher('expr', $$ + RETURN last({id: 0, status:'it_will_fail'}) +$$) AS (last agtype); -- properties() SELECT * FROM cypher('expr', $$ MATCH (v) RETURN properties(v) @@ -1617,6 +1645,13 @@ SELECT * FROM cypher('expr', $$ RETURN reverse() $$) AS (results agtype); SELECT * FROM age_reverse(); +SELECT * FROM cypher('expr', $$ + MATCH (v) + RETURN reverse(v) +$$) AS (results agtype); +SELECT * FROM cypher('expr', $$ + RETURN reverse({}) +$$) AS (results agtype); -- -- toUpper() and toLower() @@ -3157,7 +3192,130 @@ 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); +-- +-- issue 1044 - array functions not recognizing vpc +-- + +-- size +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 0 + RETURN vle_array +$$) AS (vle_array agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 1 + RETURN vle_array +$$) AS (vle_array agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) > 2 + RETURN vle_array +$$) AS (vle_array agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE size(vle_array) = size(vle_array) + RETURN vle_array +$$) AS (vle_array agtype); +-- head +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN head(vle_array) +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = vle_array[0] + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = vle_array[size(vle_array) - size(vle_array)] + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE head(vle_array) = head([vle_array[0]]) + RETURN vle_array LIMIT 1 +$$) AS (head agtype); + +-- last +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN last(vle_array) +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = vle_array[0] + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = vle_array[size(vle_array)-1] + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE last(vle_array) = head([vle_array[size(vle_array)-1]]) + RETURN vle_array LIMIT 1 +$$) AS (head agtype); + +-- isEmpty +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array) + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array) = false + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty(vle_array[0..0]) + RETURN vle_array +$$) AS (head agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE isEmpty([vle_array[3]]) = false + RETURN vle_array +$$) AS (head agtype); + +-- reverse +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + RETURN reverse(vle_array) +$$) as (u agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]-() + WHERE reverse(vle_array)[0] = last(vle_array) + RETURN reverse(vle_array) +$$) as (u agtype); + +-- IN operator +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0] IN vle_array + RETURN vle_array +$$) AS (a agtype); + +-- access slice +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0..1] = [vle_array[0]] + RETURN vle_array +$$) AS (a agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[1..2] = [last(vle_array)] + RETURN vle_array +$$) AS (a agtype); +SELECT * FROM cypher('expr', $$ + MATCH ()-[vle_array *]->() + WHERE vle_array[0..1] = [vle_array[0], vle_array[1]] + RETURN vle_array +$$) AS (a agtype); --- --- Fix: Segmentation fault when using specific names for tables #1124 --- diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c index 4fed75379..24f452c67 100644 --- a/src/backend/parser/cypher_expr.c +++ b/src/backend/parser/cypher_expr.c @@ -505,21 +505,29 @@ static Node *transform_AEXPR_IN(cypher_parsestate *cpstate, A_Expr *a) bool useOr; ListCell *l; - /* Check for null arguments in the list to return NULL*/ if (!is_ag_node(a->rexpr, cypher_list)) { - if (nodeTag(a->rexpr) == T_A_Const) - { - A_Const *r_a_const = (A_Const*)a->rexpr; - if (r_a_const->val.type == T_Null) - { - return (Node *)makeConst(AGTYPEOID, -1, InvalidOid, -1, - (Datum)NULL, true, false); - } - } + /* + * We need to build a function call here if the rexpr is already + * tranformed. It can be already tranformed cypher_list as columnref. + */ + Oid func_in_oid; + FuncExpr *func_in_expr; + List *args = NIL; + + args = lappend(args, transform_cypher_expr_recurse(cpstate, a->rexpr)); + args = lappend(args, transform_cypher_expr_recurse(cpstate, a->lexpr)); + + /* get the agtype_in_operator function */ + func_in_oid = get_ag_func_oid("agtype_in_operator", 2, AGTYPEOID, + AGTYPEOID); + + func_in_expr = makeFuncExpr(func_in_oid, AGTYPEOID, args, InvalidOid, + InvalidOid, COERCE_EXPLICIT_CALL); + + func_in_expr->location = exprLocation(a->lexpr); - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("object of IN must be a list"))); + return (Node *)func_in_expr; } Assert(is_ag_node(a->rexpr, cypher_list)); diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 2bdcf3691..9909b9680 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -3923,7 +3923,8 @@ Datum agtype_access_slice(PG_FUNCTION_ARGS) agtype_value *lidx_value = NULL; agtype_value *uidx_value = NULL; agtype_in_state result; - agtype *array = NULL; + agtype *agt_array = NULL; + agtype_value *agtv_array = NULL; int64 upper_index = 0; int64 lower_index = 0; uint32 array_size = 0; @@ -3943,15 +3944,26 @@ Datum agtype_access_slice(PG_FUNCTION_ARGS) } /* get the array parameter and verify that it is a list */ - array = AG_GET_ARG_AGTYPE_P(0); - if (!AGT_ROOT_IS_ARRAY(array) || AGT_ROOT_IS_SCALAR(array)) + agt_array = AG_GET_ARG_AGTYPE_P(0); + + if ((!AGT_ROOT_IS_ARRAY(agt_array) && !AGT_ROOT_IS_VPC(agt_array)) || AGT_ROOT_IS_SCALAR(agt_array)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("slice must access a list"))); } - /* get its size */ - array_size = AGT_ROOT_COUNT(array); + /* If we have a vpc, decode it and get AGTV_ARRAY agtype_value */ + if (AGT_ROOT_IS_VPC(agt_array)) + { + agtv_array = agtv_materialize_vle_edges(agt_array); + + /* get the size of array */ + array_size = agtv_array->val.array.num_elems; + } + else + { + array_size = AGT_ROOT_COUNT(agt_array); + } /* if we don't have a lower bound, make it 0 */ if (PG_ARGISNULL(1)) @@ -4044,11 +4056,23 @@ Datum agtype_access_slice(PG_FUNCTION_ARGS) result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_ARRAY, NULL); - /* get array elements */ - for (i = lower_index; i < upper_index; i++) + /* if we have agtype_value, we need to iterate through the array */ + if (agtv_array) { - result.res = push_agtype_value(&result.parse_state, WAGT_ELEM, - get_ith_agtype_value_from_container(&array->root, i)); + for (i = lower_index; i < upper_index; i++) + { + result.res = push_agtype_value(&result.parse_state, WAGT_ELEM, + &agtv_array->val.array.elems[i]); + } + } + else + { + /* get array elements from agtype_container */ + for (i = lower_index; i < upper_index; i++) + { + result.res = push_agtype_value(&result.parse_state, WAGT_ELEM, + get_ith_agtype_value_from_container(&agt_array->root, i)); + } } result.res = push_agtype_value(&result.parse_state, WAGT_END_ARRAY, NULL); @@ -4062,78 +4086,145 @@ PG_FUNCTION_INFO_V1(agtype_in_operator); */ Datum agtype_in_operator(PG_FUNCTION_ARGS) { - agtype *agt_array, *agt_item; + agtype *agt_arg, *agt_item; agtype_iterator *it_array, *it_item; - agtype_value agtv_item, agtv_elem; + agtype_value *agtv_arg, agtv_item, agtv_elem; uint32 array_size = 0; bool result = false; uint32 i = 0; /* return null if the array is null */ if (PG_ARGISNULL(0)) + { PG_RETURN_NULL(); + } /* get the array parameter and verify that it is a list */ - agt_array = AG_GET_ARG_AGTYPE_P(0); - if (!AGT_ROOT_IS_ARRAY(agt_array)) - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("object of IN must be a list"))); + agt_arg = AG_GET_ARG_AGTYPE_P(0); - /* init array iterator */ - it_array = agtype_iterator_init(&agt_array->root); - /* open array container */ - agtype_iterator_next(&it_array, &agtv_elem, false); - /* check for an array scalar value */ - if (agtv_elem.type == AGTV_ARRAY && agtv_elem.val.array.raw_scalar) + if ((!AGT_ROOT_IS_ARRAY(agt_arg) && !AGT_ROOT_IS_VPC(agt_arg)) || AGT_ROOT_IS_SCALAR(agt_arg)) { - agtype_iterator_next(&it_array, &agtv_elem, false); - /* check for AGTYPE NULL */ - if (agtv_elem.type == AGTV_NULL) - PG_RETURN_NULL(); - /* if it is a scalar, but not AGTV_NULL, error out */ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("object of IN must be a list"))); } + /* If we have vpc as arg, get the agtype_value AGTV_ARRAY of edges */ + if (AGT_ROOT_IS_VPC(agt_arg)) + { + agtv_arg = agtv_materialize_vle_edges(agt_arg); + array_size = agtv_arg->val.array.num_elems; + + /* return null if the item to find is null */ + if (PG_ARGISNULL(1)) + { + PG_RETURN_NULL(); + } + /* get the item to search for */ + agt_item = AG_GET_ARG_AGTYPE_P(1); - array_size = AGT_ROOT_COUNT(agt_array); + /* init item iterator */ + it_item = agtype_iterator_init(&agt_item->root); - /* return null if the item to find is null */ - if (PG_ARGISNULL(1)) - PG_RETURN_NULL(); - /* get the item to search for */ - agt_item = AG_GET_ARG_AGTYPE_P(1); + /* get value of item */ + agtype_iterator_next(&it_item, &agtv_item, false); + if (agtv_item.type == AGTV_ARRAY && agtv_item.val.array.raw_scalar) + { + agtype_iterator_next(&it_item, &agtv_item, false); + /* check for AGTYPE NULL */ + if (agtv_item.type == AGTV_NULL) + { + PG_RETURN_NULL(); + } + } - /* init item iterator */ - it_item = agtype_iterator_init(&agt_item->root); + /* iterate through the array, but stop if we find it */ + for (i = 0; i < array_size && !result; i++) + { + agtv_elem = agtv_arg->val.array.elems[i]; - /* get value of item */ - agtype_iterator_next(&it_item, &agtv_item, false); - if (agtv_item.type == AGTV_ARRAY && agtv_item.val.array.raw_scalar) + /* if both are containers, compare containers */ + if (!IS_A_AGTYPE_SCALAR(&agtv_item) && !IS_A_AGTYPE_SCALAR(&agtv_elem)) + { + result = (compare_agtype_containers_orderability( + &agt_item->root, agtv_elem.val.binary.data) == 0); + } + /* if both are scalars and of the same type, compare scalars */ + else if (IS_A_AGTYPE_SCALAR(&agtv_item) && + IS_A_AGTYPE_SCALAR(&agtv_elem) && + agtv_item.type == agtv_elem.type) + { + result = (compare_agtype_scalar_values(&agtv_item, &agtv_elem) == + 0); + } + } + } + /* Else we need to iterate agtype_container */ + else { - agtype_iterator_next(&it_item, &agtv_item, false); - /* check for AGTYPE NULL */ - if (agtv_item.type == AGTV_NULL) + /* init array iterator */ + it_array = agtype_iterator_init(&agt_arg->root); + /* open array container */ + agtype_iterator_next(&it_array, &agtv_elem, false); + /* check for an array scalar value */ + if (agtv_elem.type == AGTV_ARRAY && agtv_elem.val.array.raw_scalar) + { + agtype_iterator_next(&it_array, &agtv_elem, false); + /* check for AGTYPE NULL */ + if (agtv_elem.type == AGTV_NULL) + { + PG_RETURN_NULL(); + } + /* if it is a scalar, but not AGTV_NULL, error out */ + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("object of IN must be a list"))); + } + + array_size = AGT_ROOT_COUNT(agt_arg); + + /* return null if the item to find is null */ + if (PG_ARGISNULL(1)) + { PG_RETURN_NULL(); - } + } + /* get the item to search for */ + agt_item = AG_GET_ARG_AGTYPE_P(1); - /* iterate through the array, but stop if we find it */ - for (i = 0; i < array_size && !result; i++) - { - /* get next element */ - agtype_iterator_next(&it_array, &agtv_elem, true); - /* if both are containers, compare containers */ - if (!IS_A_AGTYPE_SCALAR(&agtv_item) && !IS_A_AGTYPE_SCALAR(&agtv_elem)) + /* init item iterator */ + it_item = agtype_iterator_init(&agt_item->root); + + /* get value of item */ + agtype_iterator_next(&it_item, &agtv_item, false); + if (agtv_item.type == AGTV_ARRAY && agtv_item.val.array.raw_scalar) { - result = (compare_agtype_containers_orderability( - &agt_item->root, agtv_elem.val.binary.data) == 0); + agtype_iterator_next(&it_item, &agtv_item, false); + /* check for AGTYPE NULL */ + if (agtv_item.type == AGTV_NULL) + { + PG_RETURN_NULL(); + } } - /* if both are scalars and of the same type, compare scalars */ - else if (IS_A_AGTYPE_SCALAR(&agtv_item) && - IS_A_AGTYPE_SCALAR(&agtv_elem) && - agtv_item.type == agtv_elem.type) - result = (compare_agtype_scalar_values(&agtv_item, &agtv_elem) == - 0); + + /* iterate through the array, but stop if we find it */ + for (i = 0; i < array_size && !result; i++) + { + /* get next element */ + agtype_iterator_next(&it_array, &agtv_elem, true); + /* if both are containers, compare containers */ + if (!IS_A_AGTYPE_SCALAR(&agtv_item) && !IS_A_AGTYPE_SCALAR(&agtv_elem)) + { + result = (compare_agtype_containers_orderability( + &agt_item->root, agtv_elem.val.binary.data) == 0); + } + /* if both are scalars and of the same type, compare scalars */ + else if (IS_A_AGTYPE_SCALAR(&agtv_item) && + IS_A_AGTYPE_SCALAR(&agtv_elem) && + agtv_item.type == agtv_elem.type) + { + result = (compare_agtype_scalar_values(&agtv_item, &agtv_elem) == + 0); + } + } } + return boolean_to_agtype(result); } @@ -5261,31 +5352,58 @@ PG_FUNCTION_INFO_V1(age_head); Datum age_head(PG_FUNCTION_ARGS) { agtype *agt_arg = NULL; + agtype_value *agtv_arg = NULL; agtype_value *agtv_result = NULL; - int count; /* 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)) + if ((!AGT_ROOT_IS_ARRAY(agt_arg) && !AGT_ROOT_IS_VPC(agt_arg)) || AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("head() argument must resolve to a list or null"))); + } - count = AGT_ROOT_COUNT(agt_arg); + /* + * If we have a vpc, materialize the edges to get AGTV_ARRAY + * agtype_value, process it and return the result. + */ + if (AGT_ROOT_IS_VPC(agt_arg)) + { + agtv_arg = agtv_materialize_vle_edges(agt_arg); - /* if we have an empty list, return a null */ - if (count == 0) - PG_RETURN_NULL(); + /* if we have an empty list, return a null */ + if (agtv_arg->val.array.num_elems == 0) + { + PG_RETURN_NULL(); + } + + /* get the first element of the array */ + agtv_result = &agtv_arg->val.array.elems[0]; + } + else + { + /* if we have an empty list, return a null */ + if (AGT_ROOT_COUNT(agt_arg) == 0) + { + PG_RETURN_NULL(); + } - /* get the first element of the array */ - agtv_result = get_ith_agtype_value_from_container(&agt_arg->root, 0); + /* get the first element of the array */ + agtv_result = get_ith_agtype_value_from_container(&agt_arg->root, 0); + } /* if it is AGTV_NULL, return null */ if (agtv_result->type == AGTV_NULL) + { PG_RETURN_NULL(); + } PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result)); } @@ -5295,31 +5413,63 @@ PG_FUNCTION_INFO_V1(age_last); Datum age_last(PG_FUNCTION_ARGS) { agtype *agt_arg = NULL; + agtype_value *agtv_arg = NULL; agtype_value *agtv_result = NULL; - int count; + int size; /* 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)) + if ((!AGT_ROOT_IS_ARRAY(agt_arg) && !AGT_ROOT_IS_VPC(agt_arg)) || AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("last() argument must resolve to a list or null"))); + } - count = AGT_ROOT_COUNT(agt_arg); + /* + * If we have a vpc, materialize the edges to get AGTV_ARRAY + * agtype_value, process it and return the result. + */ + if (AGT_ROOT_IS_VPC(agt_arg)) + { + agtv_arg = agtv_materialize_vle_edges(agt_arg); - /* if we have an empty list, return null */ - if (count == 0) - PG_RETURN_NULL(); + size = agtv_arg->val.array.num_elems; - /* get the last element of the array */ - agtv_result = get_ith_agtype_value_from_container(&agt_arg->root, count -1); + /* if we have an empty list, return a null */ + if (size == 0) + { + PG_RETURN_NULL(); + } + + /* get the first element of the array */ + agtv_result = &agtv_arg->val.array.elems[size-1]; + } + else + { + size = AGT_ROOT_COUNT(agt_arg); + + /* if we have an empty list, return a null */ + if (size == 0) + { + PG_RETURN_NULL(); + } + + /* get the first element of the array */ + agtv_result = get_ith_agtype_value_from_container(&agt_arg->root, size-1); + } /* if it is AGTV_NULL, return null */ if (agtv_result->type == AGTV_NULL) + { PG_RETURN_NULL(); + } PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result)); } @@ -6254,12 +6404,16 @@ Datum age_size(PG_FUNCTION_ARGS) /* check number of args */ if (nargs > 1) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("size() only supports one argument"))); + } /* check for null */ if (nargs < 0 || nulls[0]) + { PG_RETURN_NULL(); + } /* * size() supports cstring, text, or the agtype string or list input @@ -6280,31 +6434,45 @@ Datum age_size(PG_FUNCTION_ARGS) else if (type == AGTYPEOID) { 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)) { - agtype_value *agtv_value; - agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); if (agtv_value->type == AGTV_STRING) + { result = agtv_value->val.string.len; + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("size() unsupported argument"))); + } + } + else if (AGT_ROOT_IS_VPC(agt_arg)) + { + agtv_value = agtv_materialize_vle_edges(agt_arg); + result = agtv_value->val.array.num_elems; } else if (AGT_ROOT_IS_ARRAY(agt_arg)) + { result = AGT_ROOT_COUNT(agt_arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("size() unsupported argument"))); + } } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("size() unsupported argument"))); + } /* build the result */ agtv_result.type = AGTV_INTEGER; @@ -6428,14 +6596,13 @@ Datum age_isempty(PG_FUNCTION_ARGS) else if (type == AGTYPEOID) { 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)) { - agtype_value *agtv_value; - agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); if (agtv_value->type == AGTV_STRING) @@ -6448,6 +6615,11 @@ Datum age_isempty(PG_FUNCTION_ARGS) errmsg("isEmpty() unsupported argument, expected a List, Map, or String"))); } } + else if (AGT_ROOT_IS_VPC(agt_arg)) + { + agtv_value = agtv_materialize_vle_edges(agt_arg); + result = agtv_value->val.array.num_elems; + } else if (AGT_ROOT_IS_ARRAY(agt_arg)) { result = AGT_ROOT_COUNT(agt_arg); @@ -6730,12 +6902,16 @@ Datum age_reverse(PG_FUNCTION_ARGS) /* check number of args */ if (nargs > 1) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("reverse() only supports one argument"))); + } /* check for null */ if (nargs < 0 || nulls[0]) + { PG_RETURN_NULL(); + } /* reverse() supports text, cstring, or the agtype string input */ arg = args[0]; @@ -6744,18 +6920,25 @@ Datum age_reverse(PG_FUNCTION_ARGS) 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("reverse() unsupported argument type %d", type))); + } } else { agtype *agt_arg = NULL; agtype_value *agtv_value = NULL; + agtype_in_state result; agtype_parse_state *parse_state = NULL; agtype_value elem = {0}; agtype_iterator *it = NULL; @@ -6767,7 +6950,28 @@ Datum age_reverse(PG_FUNCTION_ARGS) /* get the agtype argument */ agt_arg = DATUM_GET_AGTYPE_P(arg); - if (!AGT_ROOT_IS_SCALAR(agt_arg)) + if (AGT_ROOT_IS_SCALAR(agt_arg)) + { + 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("reverse() unsupported argument agtype %d", + agtv_value->type))); + } + } + else if (AGT_ROOT_IS_ARRAY(agt_arg)) { agtv_value = push_agtype_value(&parse_state, WAGT_BEGIN_ARRAY, NULL); @@ -6798,19 +7002,32 @@ Datum age_reverse(PG_FUNCTION_ARGS) PG_RETURN_POINTER(agtype_value_to_agtype(agtv_value)); } + else if (AGT_ROOT_IS_VPC(agt_arg)) + { + elems = agtv_materialize_vle_edges(agt_arg); + num_elems = elems->val.array.num_elems; - agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); + /* build our result array */ + memset(&result, 0, sizeof(agtype_in_state)); - /* 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); + result.res = push_agtype_value(&result.parse_state, + WAGT_BEGIN_ARRAY, NULL); + + for (i = num_elems-1; i >= 0; i--) + { + result.res = push_agtype_value(&result.parse_state, WAGT_ELEM, + &elems->val.array.elems[i]); + } + + result.res = push_agtype_value(&result.parse_state, WAGT_END_ARRAY, NULL); + + PG_RETURN_POINTER(agtype_value_to_agtype(result.res)); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("reverse() unsupported argument agtype %d", - agtv_value->type))); + errmsg("reverse() unsupported argument agtype"))); + } } /* @@ -6826,7 +7043,9 @@ Datum age_reverse(PG_FUNCTION_ARGS) /* if we have an empty string, return null */ if (string_len == 0) + { PG_RETURN_NULL(); + } /* build the result */ agtv_result.type = AGTV_STRING; diff --git a/src/include/utils/agtype.h b/src/include/utils/agtype.h index 75712dd70..d4c88b98c 100644 --- a/src/include/utils/agtype.h +++ b/src/include/utils/agtype.h @@ -294,6 +294,8 @@ typedef struct ((*(uint32 *)VARDATA(agtp_) & AGT_FBINARY) != 0) #define AGT_ROOT_BINARY_FLAGS(agtp_) \ (*(uint32 *)VARDATA(agtp_) & AGT_FBINARY_MASK) +#define AGT_ROOT_IS_VPC(agtp_) \ + (AGT_ROOT_IS_BINARY(agtp_) && (AGT_ROOT_BINARY_FLAGS(agtp_) == AGT_FBINARY_TYPE_VLE_PATH)) /* values for the AGTYPE header field to denote the stored data type */ #define AGT_HEADER_INTEGER 0x00000000