Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions regress/expected/cypher_match.out
Original file line number Diff line number Diff line change
Expand Up @@ -3535,9 +3535,105 @@ SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:
Filter: ((agtype_access_operator(VARIADIC ARRAY[properties, '"school"'::agtype]) = '{"name": "XYZ College", "program": {"major": "Psyc", "degree": "BSc"}}'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"phone"'::agtype]) = '[123456789, 987654321, 456987123]'::agtype))
(2 rows)

---
--- tests for the additional variables added during node and edge transform
---
SELECT FROM create_graph('special_vars');
NOTICE: graph "special_vars" has been created
--
(1 row)

SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 1}) RETURN u $$) AS (u agtype);
u
-----------------------------------------------------------------------------
{"id": 844424930131969, "label": "Object", "properties": {"id": 1}}::vertex
(1 row)

SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 2}) RETURN u $$) AS (u agtype);
u
-----------------------------------------------------------------------------
{"id": 844424930131970, "label": "Object", "properties": {"id": 2}}::vertex
(1 row)

SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 3}) RETURN u $$) AS (u agtype);
u
-----------------------------------------------------------------------------
{"id": 844424930131971, "label": "Object", "properties": {"id": 3}}::vertex
(1 row)

SELECT * FROM cypher('special_vars', $$ MATCH (u) MATCH (v) CREATE(u)-[e:KNOWS {start: u_idc, end: v_idc}]->(v) RETURN e $$) AS (edge agtype);
edge
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 1125899906842625, "label": "KNOWS", "end_id": 844424930131969, "start_id": 844424930131969, "properties": {"end": 844424930131969, "start": 844424930131969}}::edge
{"id": 1125899906842626, "label": "KNOWS", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {"end": 844424930131970, "start": 844424930131969}}::edge
{"id": 1125899906842627, "label": "KNOWS", "end_id": 844424930131971, "start_id": 844424930131969, "properties": {"end": 844424930131971, "start": 844424930131969}}::edge
{"id": 1125899906842628, "label": "KNOWS", "end_id": 844424930131969, "start_id": 844424930131970, "properties": {"end": 844424930131969, "start": 844424930131970}}::edge
{"id": 1125899906842629, "label": "KNOWS", "end_id": 844424930131970, "start_id": 844424930131970, "properties": {"end": 844424930131970, "start": 844424930131970}}::edge
{"id": 1125899906842630, "label": "KNOWS", "end_id": 844424930131971, "start_id": 844424930131970, "properties": {"end": 844424930131971, "start": 844424930131970}}::edge
{"id": 1125899906842631, "label": "KNOWS", "end_id": 844424930131969, "start_id": 844424930131971, "properties": {"end": 844424930131969, "start": 844424930131971}}::edge
{"id": 1125899906842632, "label": "KNOWS", "end_id": 844424930131970, "start_id": 844424930131971, "properties": {"end": 844424930131970, "start": 844424930131971}}::edge
{"id": 1125899906842633, "label": "KNOWS", "end_id": 844424930131971, "start_id": 844424930131971, "properties": {"end": 844424930131971, "start": 844424930131971}}::edge
(9 rows)

SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->(v) RETURN e_idc, e_start_idc, e_end_idc, e_propertiesc $$) AS (e_idc agtype, e_start_idc agtype, e_end_idc agtype, e_propertiesc agtype);
e_idc | e_start_idc | e_end_idc | e_propertiesc
------------------+-----------------+-----------------+----------------------------------------------------
1125899906842628 | 844424930131970 | 844424930131969 | {"end": 844424930131969, "start": 844424930131970}
1125899906842625 | 844424930131969 | 844424930131969 | {"end": 844424930131969, "start": 844424930131969}
1125899906842631 | 844424930131971 | 844424930131969 | {"end": 844424930131969, "start": 844424930131971}
1125899906842629 | 844424930131970 | 844424930131970 | {"end": 844424930131970, "start": 844424930131970}
1125899906842626 | 844424930131969 | 844424930131970 | {"end": 844424930131970, "start": 844424930131969}
1125899906842632 | 844424930131971 | 844424930131970 | {"end": 844424930131970, "start": 844424930131971}
1125899906842627 | 844424930131969 | 844424930131971 | {"end": 844424930131971, "start": 844424930131969}
1125899906842633 | 844424930131971 | 844424930131971 | {"end": 844424930131971, "start": 844424930131971}
1125899906842630 | 844424930131970 | 844424930131971 | {"end": 844424930131971, "start": 844424930131970}
(9 rows)

SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->(v) RETURN u_idc, u_propertiesc, v_idc, v_propertiesc $$) AS (u_idc agtype, u_propertiesc agtype, v_idc agtype, v_propertiesc agtype);
u_idc | u_propertiesc | v_idc | v_propertiesc
-----------------+---------------+-----------------+---------------
844424930131970 | {"id": 2} | 844424930131969 | {"id": 1}
844424930131969 | {"id": 1} | 844424930131969 | {"id": 1}
844424930131971 | {"id": 3} | 844424930131969 | {"id": 1}
844424930131970 | {"id": 2} | 844424930131970 | {"id": 2}
844424930131969 | {"id": 1} | 844424930131970 | {"id": 2}
844424930131971 | {"id": 3} | 844424930131970 | {"id": 2}
844424930131969 | {"id": 1} | 844424930131971 | {"id": 3}
844424930131971 | {"id": 3} | 844424930131971 | {"id": 3}
844424930131970 | {"id": 2} | 844424930131971 | {"id": 3}
(9 rows)

SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->() RETURN count(*), u_idc ORDER BY count(*) DESC $$) AS (count agtype, u_idc agtype);
count | u_idc
-------+-----------------
3 | 844424930131969
3 | 844424930131970
3 | 844424930131971
(3 rows)

SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->() RETURN count(*), id(u) ORDER BY count(*) DESC $$) AS (count agtype, idu agtype);
count | idu
-------+-----------------
3 | 844424930131969
3 | 844424930131970
3 | 844424930131971
(3 rows)

--
-- Clean up
--
SELECT drop_graph('special_vars', true);
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table special_vars._ag_label_vertex
drop cascades to table special_vars._ag_label_edge
drop cascades to table special_vars."Object"
drop cascades to table special_vars."KNOWS"
NOTICE: graph "special_vars" has been dropped
drop_graph
------------

(1 row)

SELECT drop_graph('cypher_match', true);
NOTICE: drop cascades to 17 other objects
DETAIL: drop cascades to table cypher_match._ag_label_vertex
Expand Down
14 changes: 14 additions & 0 deletions regress/sql/cypher_match.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1437,9 +1437,23 @@ SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver', street: 30}}]->(y:Product) RETURN 0 $$) as (a agtype);
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'} },phone: [ 123456789, 987654321, 456987123 ]}) RETURN 0 $$) as (a agtype);

---
--- tests for the additional variables added during node and edge transform
---
SELECT FROM create_graph('special_vars');
SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 1}) RETURN u $$) AS (u agtype);
SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 2}) RETURN u $$) AS (u agtype);
SELECT * FROM cypher('special_vars', $$ CREATE (u:Object {id: 3}) RETURN u $$) AS (u agtype);
SELECT * FROM cypher('special_vars', $$ MATCH (u) MATCH (v) CREATE(u)-[e:KNOWS {start: u_idc, end: v_idc}]->(v) RETURN e $$) AS (edge agtype);
SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->(v) RETURN e_idc, e_start_idc, e_end_idc, e_propertiesc $$) AS (e_idc agtype, e_start_idc agtype, e_end_idc agtype, e_propertiesc agtype);
SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->(v) RETURN u_idc, u_propertiesc, v_idc, v_propertiesc $$) AS (u_idc agtype, u_propertiesc agtype, v_idc agtype, v_propertiesc agtype);
SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->() RETURN count(*), u_idc ORDER BY count(*) DESC $$) AS (count agtype, u_idc agtype);
SELECT * FROM cypher('special_vars', $$ MATCH (u)-[e]->() RETURN count(*), id(u) ORDER BY count(*) DESC $$) AS (count agtype, idu agtype);

--
-- Clean up
--
SELECT drop_graph('special_vars', true);
SELECT drop_graph('cypher_match', true);
SELECT drop_graph('test_retrieve_var', true);
SELECT drop_graph('test_enable_containment', true);
Expand Down
86 changes: 86 additions & 0 deletions src/backend/parser/cypher_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ static bool isa_special_VLE_case(cypher_path *path);
static ParseNamespaceItem *find_pnsi(cypher_parsestate *cpstate, char *varname);
static bool has_list_comp_or_subquery(Node *expr, void *context);

static List **add_additional_variables(ParseState *pstate,
ParseNamespaceItem *pnsi,
List **target_list, char *name,
List *colnames, List *suffixes);
/*
* transform a cypher_clause
*/
Expand Down Expand Up @@ -5193,6 +5197,34 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
*target_list = lappend(*target_list, te);
}

/*
* Add in our additional variables tied to this edge var -
*
* - edge id column var
* - edge properties column var
* - edge start_id
* - edge end_id
*/
if (rel->name != NULL)
{
List *suffixes = NULL;
List *colnames = NULL;

suffixes = list_make4(EDGE_ID_COLUMN_SUFFIX,
EDGE_PROPERTIES_COLUMN_SUFFIX,
EDGE_START_ID_COLUMN_SUFFIX,
EDGE_END_ID_COLUMN_SUFFIX);

colnames = list_make4(AG_EDGE_COLNAME_ID,
AG_EDGE_COLNAME_PROPERTIES,
AG_EDGE_COLNAME_START_ID,
AG_EDGE_COLNAME_END_ID);

target_list = add_additional_variables(pstate, pnsi, target_list,
rel->name, colnames, suffixes);
}


return (Expr *)expr;
}

Expand Down Expand Up @@ -5479,9 +5511,63 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
te = makeTargetEntry(expr, resno, node->name, false);
*target_list = lappend(*target_list, te);

/*
* Add in our additional variables tied to this node var -
*
* - vertex id column var
* - vertex properties column var
*/
if (node->name != NULL)
{
List *suffixes = NULL;
List *colnames = NULL;

suffixes = list_make2(VERTEX_ID_COLUMN_SUFFIX,
VERTEX_PROPERTIES_COLUMN_SUFFIX);

colnames = list_make2(AG_VERTEX_COLNAME_ID,
AG_VERTEX_COLNAME_PROPERTIES);

target_list = add_additional_variables(pstate, pnsi, target_list,
node->name, colnames, suffixes);
}

return expr;
}

/* helper function to add additional variables to a node or edge */
static List **add_additional_variables(ParseState *pstate,
ParseNamespaceItem *pnsi,
List **target_list, char *name,
List *colnames, List *suffixes)
{
ListCell *slc = NULL;
ListCell *clc = NULL;

forboth (slc, suffixes, clc, colnames)
{
TargetEntry *te = NULL;
Node *column = NULL;
char *varname = NULL;
char *suffix = (char *)lfirst(slc);
char *colname = (char *)lfirst(clc);
int varnamelen = 0;
int resno = -1;


varnamelen = strlen(name) + strlen(suffix) +1;
varname = palloc0(varnamelen);
strcpy(varname, name);
strcat(varname, suffix);
column = scanNSItemForColumn(pstate, pnsi, 0, colname, -1);
resno = pstate->p_next_resno++;
te = makeTargetEntry((Expr*)column, resno, varname, false);
*target_list = lappend(*target_list, te);
}

return target_list;
}

static Node *make_edge_expr(cypher_parsestate *cpstate,
ParseNamespaceItem *pnsi)
{
Expand Down
58 changes: 56 additions & 2 deletions src/backend/parser/cypher_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "parser/cypher_expr.h"
#include "parser/cypher_item.h"
#include "parser/cypher_transform_entity.h"

static List *ExpandAllTables(ParseState *pstate, int location);
static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
Expand Down Expand Up @@ -168,6 +169,43 @@ static List *ExpandAllTables(ParseState *pstate, int location)
return target;
}

/*
* Function takes the name of the passed var and then checks it against all of
* the potential var suffixes. If one is found it returns the root/entity name.
*/
static char *get_parent_entity_name(char *varname)
{
List *suffixes = NULL;
ListCell *slc = NULL;
int vlen = strlen(varname);

/* list of current suffixes */
suffixes = list_make5(VERTEX_ID_COLUMN_SUFFIX,
VERTEX_PROPERTIES_COLUMN_SUFFIX,
EDGE_ID_COLUMN_SUFFIX,
EDGE_PROPERTIES_COLUMN_SUFFIX,
EDGE_START_ID_COLUMN_SUFFIX);
suffixes = lappend(suffixes, EDGE_END_ID_COLUMN_SUFFIX);

/*
* Check each suffix against the input varname. If a match is found,
* return the potential entity name.
*/
foreach (slc, suffixes)
{
char *suffix = (char*) lfirst(slc);
int slen = strlen(suffix);

if (strncmp(&varname[vlen-slen], suffix, slen) == 0)
{
return strndup(varname, vlen-slen);
}
}

/* nothing found, return null */
return NULL;
}

/*
* From PG's expandNSItemAttrs
* Modified to exclude hidden variables and aliases in RETURN *
Expand All @@ -176,6 +214,7 @@ static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
int sublevels_up, bool require_col_privs,
int location)
{
cypher_parsestate *cpstate = (cypher_parsestate*)pstate;
RangeTblEntry *rte = pnsi->p_rte;
RTEPermissionInfo *perminfo = pnsi->p_perminfo;
List *names, *vars;
Expand All @@ -187,7 +226,7 @@ static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
vars = expandNSItemVars(pstate, pnsi, sublevels_up, location, &names);

/*
* Require read access to the table. This is normally redundant with the
* Require read access to the table. This is normally redundant with the
* markVarForSelectPriv calls below, but not if the table has zero
* columns.
*/
Expand All @@ -203,14 +242,29 @@ static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
char *label = strVal(lfirst(name));
Var *varnode = (Var *)lfirst(var);
TargetEntry *te;
char *entity = NULL;

/* we want to skip our "hidden" variables */
if (strncmp(AGE_DEFAULT_VARNAME_PREFIX, label, var_prefix_len) == 0)
{
continue;
}

/* we want to skip out "hidden" aliases */
/* we want to skip our "hidden" aliases */
if (strncmp(AGE_DEFAULT_ALIAS_PREFIX, label, alias_prefix_len) == 0)
{
continue;
}

/*
* If we find an potential entity based off of this var and it exists,
* we need to skip it.
*/
entity = get_parent_entity_name(label);
if (entity != NULL && find_variable(cpstate, entity) != NULL)
{
continue;
}

/* add this variable to the list */
te = makeTargetEntry((Expr *)varnode,
Expand Down
5 changes: 3 additions & 2 deletions src/backend/utils/adt/agtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -12058,11 +12058,12 @@ Datum agtype_volatile_wrapper(PG_FUNCTION_ARGS)
agtv_result.type = AGTV_BOOL;
agtv_result.val.boolean = DatumGetBool(arg);
}
else if (type == INT2OID || type == INT4OID || type == INT8OID)
else if (type == INT2OID || type == INT4OID || type == INT8OID ||
type == GRAPHIDOID)
{
agtv_result.type = AGTV_INTEGER;

if (type == INT8OID)
if (type == INT8OID || GRAPHIDOID)
{
agtv_result.val.int_value = DatumGetInt64(arg);
}
Expand Down
12 changes: 12 additions & 0 deletions src/include/parser/cypher_parse_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@
#define AGE_DEFAULT_ALIAS_PREFIX AGE_DEFAULT_PREFIX"alias_"
#define AGE_DEFAULT_VARNAME_PREFIX AGE_DEFAULT_PREFIX"varname_"

/*
* Every vertex or edge creation, that is labeled, will additionally add
* variables to point directly to their respective columns. Below are the
* suffixes used.
*/
#define VERTEX_ID_COLUMN_SUFFIX "_idc"
#define VERTEX_PROPERTIES_COLUMN_SUFFIX "_propertiesc"
#define EDGE_ID_COLUMN_SUFFIX "_idc"
#define EDGE_START_ID_COLUMN_SUFFIX "_start_idc"
#define EDGE_END_ID_COLUMN_SUFFIX "_end_idc"
#define EDGE_PROPERTIES_COLUMN_SUFFIX "_propertiesc"

typedef struct cypher_parsestate
{
ParseState pstate;
Expand Down