You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2021/03/17 23:18:17 UTC
[incubator-age] branch master updated: Add *, optional edges,
and VLE grammar components.
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 afee553 Add *, optional edges, and VLE grammar components.
afee553 is described below
commit afee55301f61d3bf8ede7ec96682e468e5861e38
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Tue Mar 16 10:46:35 2021 -0700
Add *, optional edges, and VLE grammar components.
Added * as a valid grammar item for the RETURN command. For example -
MATCH (u) RETURN *;
Added components to mask out our "hidden" variables and aliases.
Added the option to omit edges in the grammar. For example -
MATCH (u)--(v) RETURN u, v;
MATCH (u)-->(p)<--(v) RETURN u, p, v;
Added the components for Variable Length Edges (Relationships). However,
VLE commands are disallowed until implementation.
Added regression tests.
---
regress/expected/expr.out | 121 ++++++++++++++++++++++++++++++
regress/sql/expr.sql | 22 ++++++
src/backend/nodes/outfuncs.c | 2 +
src/backend/parser/cypher_clause.c | 43 +++++++++--
src/backend/parser/cypher_gram.y | 105 +++++++++++++++++++++++++-
src/backend/parser/cypher_item.c | 130 +++++++++++++++++++++++++++++++++
src/include/nodes/cypher_nodes.h | 1 +
src/include/parser/cypher_parse_node.h | 3 +-
8 files changed, 414 insertions(+), 13 deletions(-)
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 5266eeb..d0661c4 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -5023,9 +5023,130 @@ $$) AS (i agtype);
{"key": "value"}
(9 rows)
+-- RETURN * and (u)--(v) optional forms
+SELECT create_graph('opt_forms');
+NOTICE: graph "opt_forms" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT * FROM cypher('opt_forms', $$CREATE ({i:1})-[:KNOWS]->({i:2})<-[:KNOWS]-({i:3})$$)AS (result agtype);
+ result
+--------
+(0 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u) RETURN u$$) AS (result agtype);
+ result
+----------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"i": 3}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u) RETURN *$$) AS (result agtype);
+ result
+----------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"i": 3}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u)--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+ u | v
+---+---
+ 2 | 3
+ 3 | 2
+ 1 | 2
+ 2 | 1
+(4 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+ u | v
+---+---
+ 3 | 2
+ 1 | 2
+(2 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u)<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+ u | v
+---+---
+ 2 | 3
+ 2 | 1
+(2 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+ u | v
+---+---
+ 3 | 1
+ 1 | 3
+(2 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u) CREATE (u)-[:edge]->() RETURN *$$) AS (results agtype);
+ results
+----------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"i": 3}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN *$$) AS (col1 agtype, col2 agtype);
+ col1 | col2
+----------------------------------------------------------------------+----------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": 3}}::vertex | {"id": 281474976710657, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 1}}::vertex | {"id": 281474976710659, "label": "", "properties": {"i": 3}}::vertex
+(2 rows)
+
+-- VLE
+SELECT create_graph('VLE');
+NOTICE: graph "VLE" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+ERROR: variable length relationships are not supported
+LINE 1: SELECT * FROM cypher('VLE', $$MATCH (u)-[*]-(v) RETURN u, v$...
+ ^
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*0..1]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+ERROR: variable length relationships are not supported
+LINE 1: SELECT * FROM cypher('VLE', $$MATCH (u)-[*0..1]-(v) RETURN u...
+ ^
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*..1]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+ERROR: variable length relationships are not supported
+LINE 1: SELECT * FROM cypher('VLE', $$MATCH (u)-[*..1]-(v) RETURN u,...
+ ^
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*..5]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+ERROR: variable length relationships are not supported
+LINE 1: SELECT * FROM cypher('VLE', $$MATCH (u)-[*..5]-(v) RETURN u,...
+ ^
--
-- Cleanup
--
+SELECT * FROM drop_graph('VLE', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table "VLE"._ag_label_vertex
+drop cascades to table "VLE"._ag_label_edge
+NOTICE: graph "VLE" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT * FROM drop_graph('opt_forms', true);
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to table opt_forms._ag_label_vertex
+drop cascades to table opt_forms._ag_label_edge
+drop cascades to table opt_forms."KNOWS"
+drop cascades to table opt_forms.edge
+NOTICE: graph "opt_forms" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
SELECT * FROM drop_graph('type_coercion', true);
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table type_coercion._ag_label_vertex
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index dff0702..295c2b3 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -2085,9 +2085,31 @@ SELECT * FROM cypher('order_by', $$
ORDER BY u.i DESC
$$) AS (i agtype);
+-- RETURN * and (u)--(v) optional forms
+SELECT create_graph('opt_forms');
+SELECT * FROM cypher('opt_forms', $$CREATE ({i:1})-[:KNOWS]->({i:2})<-[:KNOWS]-({i:3})$$)AS (result agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u) RETURN u$$) AS (result agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u) RETURN *$$) AS (result agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u)--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u)<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u) CREATE (u)-[:edge]->() RETURN *$$) AS (results agtype);
+SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN *$$) AS (col1 agtype, col2 agtype);
+
+-- VLE
+SELECT create_graph('VLE');
+-- should fail
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*0..1]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*..1]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+SELECT * FROM cypher('VLE', $$MATCH (u)-[*..5]-(v) RETURN u, v$$) AS (u agtype, v agtype);
+
--
-- Cleanup
--
+SELECT * FROM drop_graph('VLE', true);
+SELECT * FROM drop_graph('opt_forms', true);
SELECT * FROM drop_graph('type_coercion', true);
SELECT * FROM drop_graph('order_by', true);
SELECT * FROM drop_graph('group_by', true);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 347f4fa..eb65ce7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -167,7 +167,9 @@ void out_cypher_relationship(StringInfo str, const ExtensibleNode *node)
write_string_field(name);
write_string_field(label);
write_node_field(props);
+ write_node_field(varlen);
write_enum_field(dir, cypher_rel_dir);
+ write_location_field(location);
}
/*
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 04cc006..f49379f 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -59,6 +59,25 @@
#include "utils/agtype.h"
#include "utils/graphid.h"
+/*
+ * Variable string names for makeTargetEntry. As they are going to be variable
+ * names that will be hidden from the user, we need to do our best to make sure
+ * they won't be picked by mistake. Additionally, their form needs to be easily
+ * determined as ours. For now, prefix them as follows -
+ *
+ * #define AGE_VARNAME_SOMETHING AGE_DEFAULT_VARNAME_PREFIX"something"
+ *
+ * We should probably make an automated variable generator, like for aliases,
+ * for this.
+ *
+ * Also, keep these here as nothing outside of this file needs to know these.
+ */
+#define AGE_VARNAME_CREATE_CLAUSE AGE_DEFAULT_VARNAME_PREFIX"create_clause"
+#define AGE_VARNAME_CREATE_NULL_VALUE AGE_DEFAULT_VARNAME_PREFIX"create_null_value"
+#define AGE_VARNAME_DELETE_CLAUSE AGE_DEFAULT_VARNAME_PREFIX"delete_clause"
+#define AGE_VARNAME_ID AGE_DEFAULT_VARNAME_PREFIX"id"
+#define AGE_VARNAME_SET_CLAUSE AGE_DEFAULT_VARNAME_PREFIX"set_clause"
+
enum transform_entity_type
{
ENT_VERTEX = 0x0,
@@ -348,7 +367,7 @@ static Query *transform_cypher_delete(cypher_parsestate *cpstate,
// Create the target entry
tle = makeTargetEntry(func_expr, pstate->p_next_resno++,
- "cypher_delete_clause", false);
+ AGE_VARNAME_DELETE_CLAUSE, false);
query->targetList = lappend(query->targetList, tle);
query->rtable = pstate->p_rtable;
@@ -480,7 +499,7 @@ static Query *transform_cypher_set(cypher_parsestate *cpstate,
// Create the target entry
tle = makeTargetEntry(func_expr, pstate->p_next_resno++,
- SET_CLAUSE_FUNCTION_NAME, false);
+ AGE_VARNAME_SET_CLAUSE, false);
query->targetList = lappend(query->targetList, tle);
query->rtable = pstate->p_rtable;
@@ -1295,11 +1314,11 @@ static char *get_next_default_alias(cypher_parsestate *cpstate)
sprintf(alias_num, "%d", cpstate->default_alias_num++);
- base_length = strlen(AG_DEFAULT_ALIAS_BASE);
+ base_length = strlen(AGE_DEFAULT_ALIAS_PREFIX);
alias = palloc0(sizeof(char) * (12 + base_length));
- strncat(alias, AG_DEFAULT_ALIAS_BASE, base_length);
+ strncat(alias, AGE_DEFAULT_ALIAS_PREFIX, base_length);
strncat(alias + base_length, alias_num, 12);
@@ -2051,6 +2070,14 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
TargetEntry *te;
Expr *expr;
+ if (rel->varlen != NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("variable length relationships are not supported"),
+ parser_errposition(pstate, rel->location)));
+ }
+
if (!rel->label)
rel->label = AG_DEFAULT_LABEL_EDGE;
else
@@ -2389,7 +2416,7 @@ static Query *transform_cypher_create(cypher_parsestate *cpstate,
null_const = makeNullConst(AGTYPEOID, -1, InvalidOid);
tle = makeTargetEntry((Expr *)null_const, pstate->p_next_resno++,
- "cypher_create_null_value", false);
+ AGE_VARNAME_CREATE_NULL_VALUE, false);
query->targetList = lappend(query->targetList, tle);
/*
@@ -2421,7 +2448,7 @@ static Query *transform_cypher_create(cypher_parsestate *cpstate,
// Create the target entry
tle = makeTargetEntry(func_expr, pstate->p_next_resno++,
- "cypher_create_clause", false);
+ AGE_VARNAME_CREATE_CLAUSE, false);
query->targetList = lappend(query->targetList, tle);
query->rtable = pstate->p_rtable;
@@ -2618,7 +2645,7 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
id = (Expr *)build_column_default(label_relation,
Anum_ag_label_edge_table_id);
- te = makeTargetEntry(id, InvalidAttrNumber, "id", false);
+ te = makeTargetEntry(id, InvalidAttrNumber, AGE_VARNAME_ID, false);
targetList = lappend(targetList, te);
rel->targetList = targetList;
@@ -2865,7 +2892,7 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
// id
id = cypher_create_id_default(cpstate, label_relation, ENT_VERTEX);
- te = makeTargetEntry((Expr *)id, InvalidAttrNumber, "id", false);
+ te = makeTargetEntry((Expr *)id, InvalidAttrNumber, AGE_VARNAME_ID, false);
rel->targetList = list_make1(te);
rel->id_var_no = -1;
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index debedc7..1db0fbd 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -104,8 +104,9 @@
%type <integer> order_opt
/* MATCH clause */
-%type <node> match
-
+%type <node> match cypher_varlen_opt cypher_range_opt cypher_range_idx
+ cypher_range_idx_opt
+%type <integer> Iconst
/* CREATE clause */
%type <node> create
@@ -291,6 +292,71 @@ updating_clause:
| delete
;
+cypher_varlen_opt:
+ '*' cypher_range_opt
+ {
+ A_Indices *n = (A_Indices *) $2;
+
+ if (n->lidx == NULL)
+ n->lidx = make_int_const(1, @2);
+
+ if (n->uidx != NULL)
+ {
+ A_Const *lidx = (A_Const *) n->lidx;
+ A_Const *uidx = (A_Const *) n->uidx;
+
+ if (lidx->val.val.ival > uidx->val.val.ival)
+ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range"),
+ ag_scanner_errposition(@2, scanner)));
+ }
+ $$ = (Node *) n;
+ }
+ | /* EMPTY */
+ {
+ $$ = NULL;
+ }
+ ;
+
+cypher_range_opt:
+ cypher_range_idx
+ {
+ A_Indices *n;
+
+ n = makeNode(A_Indices);
+ n->lidx = copyObject($1);
+ n->uidx = $1;
+ $$ = (Node *) n;
+ }
+ | cypher_range_idx_opt DOT_DOT cypher_range_idx_opt
+ {
+ A_Indices *n;
+
+ n = makeNode(A_Indices);
+ n->lidx = $1;
+ n->uidx = $3;
+ $$ = (Node *) n;
+ }
+ | /* EMPTY */
+ {
+ $$ = (Node *) makeNode(A_Indices);
+ }
+ ;
+
+cypher_range_idx:
+ Iconst
+ {
+ $$ = make_int_const($1, @1);
+ }
+ ;
+
+cypher_range_idx_opt:
+ cypher_range_idx
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+Iconst: INTEGER
+
/*
* RETURN and WITH clause
*/
@@ -361,6 +427,23 @@ return_item:
$$ = (Node *)n;
}
+ | '*'
+ {
+ ColumnRef *cr;
+ ResTarget *rt;
+
+ cr = makeNode(ColumnRef);
+ cr->fields = list_make1(makeNode(A_Star));
+ cr->location = @1;
+
+ rt = makeNode(ResTarget);
+ rt->name = NULL;
+ rt->indirection = NIL;
+ rt->val = (Node *)cr;
+ rt->location = @1;
+
+ $$ = (Node *)rt;
+ }
;
order_by_opt:
@@ -794,14 +877,28 @@ path_relationship:
;
path_relationship_body:
- '[' var_name_opt label_opt properties_opt ']'
+ '[' var_name_opt label_opt cypher_varlen_opt properties_opt ']'
{
cypher_relationship *n;
n = make_ag_node(cypher_relationship);
n->name = $2;
n->label = $3;
- n->props = $4;
+ n->varlen = $4;
+ n->props = $5;
+
+ $$ = (Node *)n;
+ }
+ |
+ /* empty */
+ {
+ cypher_relationship *n;
+
+ n = make_ag_node(cypher_relationship);
+ n->name = NULL;
+ n->label = NULL;
+ n->varlen = NULL;
+ n->props = NULL;
$$ = (Node *)n;
}
diff --git a/src/backend/parser/cypher_item.c b/src/backend/parser/cypher_item.c
index 9cfc1d7..5799bf4 100644
--- a/src/backend/parser/cypher_item.c
+++ b/src/backend/parser/cypher_item.c
@@ -26,12 +26,17 @@
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/cypher_expr.h"
#include "parser/cypher_item.h"
#include "parser/cypher_parse_node.h"
+static List *ExpandAllTables(ParseState *pstate, int location);
+static List *expand_rel_attrs(ParseState *pstate, RangeTblEntry *rte,
+ int rtindex, int sublevels_up, int location);
+
// see transformTargetEntry()
TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
Node *expr, ParseExprKind expr_kind,
@@ -57,12 +62,41 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
ListCell *li;
List *group_clause = NIL;
bool hasAgg = false;
+ bool expand_star;
+
+ expand_star = (expr_kind != EXPR_KIND_UPDATE_SOURCE);
foreach (li, item_list)
{
ResTarget *item = lfirst(li);
TargetEntry *te;
+ if (expand_star)
+ {
+ if (IsA(item->val, ColumnRef))
+ {
+ ColumnRef *cref = (ColumnRef *) item->val;
+
+ if (IsA(llast(cref->fields), A_Star))
+ {
+ ParseState *pstate = &cpstate->pstate;
+
+ /* we only allow a bare '*' */
+ if (list_length(cref->fields) != 1)
+ {
+ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("Invalid number of fields for *"),
+ parser_errposition(pstate,
+ cref->location)));
+ }
+
+ target_list = list_concat(target_list,
+ ExpandAllTables(pstate,
+ cref->location));
+ continue;
+ }
+ }
+ }
/* clear the exprHasAgg flag to check transform for an aggregate */
cpstate->exprHasAgg = false;
@@ -78,9 +112,13 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
* an aggregate in an expression
*/
if (!cpstate->exprHasAgg)
+ {
group_clause = lappend(group_clause, item->val);
+ }
else
+ {
hasAgg = true;
+ }
}
/*
@@ -89,7 +127,99 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
* will verify if it is valid.
*/
if (hasAgg)
+ {
*groupClause = group_clause;
+ }
return target_list;
}
+
+/*
+ * From PG's ExpandAllTables()
+ * Transforms '*' (in the target list) into a list of targetlist entries.
+ */
+static List *ExpandAllTables(ParseState *pstate, int location)
+{
+ List *target = NIL;
+ bool found_table = false;
+ ListCell *l;
+
+ foreach(l, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
+ RangeTblEntry *rte = nsitem->p_rte;
+
+ /* Ignore table-only items */
+ if (!nsitem->p_cols_visible)
+ continue;
+ /* Should not have any lateral-only items when parsing targetlist */
+ Assert(!nsitem->p_lateral_only);
+ /* Remember we found a p_cols_visible item */
+ found_table = true;
+
+ target = list_concat(target, expand_rel_attrs(pstate, rte,
+ RTERangeTablePosn(pstate,
+ rte,
+ NULL),
+ 0, location));
+ }
+
+ /* Check for "RETURN *;" */
+ if (!found_table)
+ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("RETURN * without a pattern is not valid"),
+ parser_errposition(pstate, location)));
+
+ return target;
+}
+
+/*
+ * From PG's expandRelAttrs
+ * Modified to exclude hidden variables and aliases in RETURN *
+ */
+static List *expand_rel_attrs(ParseState *pstate, RangeTblEntry *rte,
+ int rtindex, int sublevels_up, int location)
+{
+ List *names, *vars;
+ ListCell *name, *var;
+ List *te_list = NIL;
+ int var_prefix_len = strlen(AGE_DEFAULT_VARNAME_PREFIX);
+ int alias_prefix_len = strlen(AGE_DEFAULT_ALIAS_PREFIX);
+
+ expandRTE(rte, rtindex, sublevels_up, location, false, &names, &vars);
+
+ /*
+ * Require read access to the table. This is normally redundant with the
+ * markVarForSelectPriv calls below, but not if the table has zero
+ * columns.
+ */
+ rte->requiredPerms |= ACL_SELECT;
+
+ /* iterate through the variables */
+ forboth(name, names, var, vars)
+ {
+ char *label = strVal(lfirst(name));
+ Var *varnode = (Var *)lfirst(var);
+ TargetEntry *te;
+
+ /* 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 */
+ if (strncmp(AGE_DEFAULT_ALIAS_PREFIX, label, alias_prefix_len) == 0)
+ continue;
+
+ /* add this variable to the list */
+ te = makeTargetEntry((Expr *)varnode,
+ (AttrNumber)pstate->p_next_resno++, label, false);
+ te_list = lappend(te_list, te);
+
+ /* Require read access to each column */
+ markVarForSelectPriv(pstate, varnode, rte);
+ }
+
+ Assert(name == NULL && var == NULL); /* lists not the same length? */
+
+ return te_list;
+}
diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h
index c4ef763..99b7860 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -142,6 +142,7 @@ typedef struct cypher_relationship
char *name;
char *label;
Node *props; // map or parameter
+ Node *varlen; // variable length relationships (A_Indices)
cypher_rel_dir dir;
int location;
} cypher_relationship;
diff --git a/src/include/parser/cypher_parse_node.h b/src/include/parser/cypher_parse_node.h
index 5066c6c..dc7581b 100644
--- a/src/include/parser/cypher_parse_node.h
+++ b/src/include/parser/cypher_parse_node.h
@@ -23,7 +23,8 @@
#include "nodes/primnodes.h"
#include "parser/parse_node.h"
-#define AG_DEFAULT_ALIAS_BASE "_ag_default_alias_"
+#define AGE_DEFAULT_ALIAS_PREFIX "_age_default_alias_"
+#define AGE_DEFAULT_VARNAME_PREFIX "_age_varname_"
typedef struct cypher_parsestate
{