From 01ea7e96b54b19e459877d83576f39e744b0836b Mon Sep 17 00:00:00 2001 From: Dehowe Feng <8065116+dehowef@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:22:37 -0800 Subject: [PATCH] Implement chained expression order of operations (#1402) Implements functionality for order of operations (parentheses) in chained operations. Added new nodes and functions to accomodate this feature. Added relevant regression tests for order of operations in chained expressions. Modified a previous regression test case that had its location reporting changed by this implementation. --- regress/expected/cypher_call.out | 4 +- regress/expected/expr.out | 129 ++++++++++++++++++++++++++++ regress/sql/expr.sql | 25 ++++++ src/backend/nodes/ag_nodes.c | 4 + src/backend/nodes/cypher_outfuncs.c | 22 +++++ src/backend/parser/cypher_expr.c | 47 ++++++++++ src/backend/parser/cypher_gram.y | 110 ++++++++++++++++++------ src/include/nodes/ag_nodes.h | 3 + src/include/nodes/cypher_nodes.h | 23 +++++ src/include/nodes/cypher_outfuncs.h | 4 + 10 files changed, 344 insertions(+), 27 deletions(-) diff --git a/regress/expected/cypher_call.out b/regress/expected/cypher_call.out index 8c5c5fca7..6980abe4b 100644 --- a/regress/expected/cypher_call.out +++ b/regress/expected/cypher_call.out @@ -84,8 +84,8 @@ HINT: No function matches the given name and argument types. You might need to /* CALL YIELD WHERE, should fail */ SELECT * FROM cypher('cypher_call', $$CALL sqrt(64) YIELD sqrt WHERE sqrt > 1$$) as (sqrt agtype); ERROR: Cannot use standalone CALL with WHERE -LINE 2: ...r('cypher_call', $$CALL sqrt(64) YIELD sqrt WHERE sqrt > 1$$... - ^ +LINE 2: SELECT * FROM cypher('cypher_call', $$CALL sqrt(64) YIELD sq... + ^ HINT: Instead use `CALL ... WITH * WHERE ... RETURN *` /* * subquery diff --git a/regress/expected/expr.out b/regress/expected/expr.out index 12b18d586..3a8ff6386 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -741,6 +741,135 @@ SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE NOT 35 < u.age + 1 <= {"id": 844424930131973, "label": "people", "properties": {"age": 15, "name": "David"}}::vertex (3 rows) +-- order of operations +-- expressions +SELECT * FROM cypher('chained', $$ RETURN 1 = 1 = 1 $$) AS (result agtype); + result +-------- + true +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN 1 = 2 = 1 $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN (1 = 1) = 1 $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN 1 = (1 = 1) $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN 1 = 1 = true $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN (1 = 1) = true $$) AS (result agtype); + result +-------- + true +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN true = ((1 = 1) = true) $$) AS (result agtype); + result +-------- + true +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN ((1 = 1) = 1) = 1 $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN (1 = (1 = 1)) = 1 $$) AS (result agtype); + result +-------- + false +(1 row) + +SELECT * FROM cypher('chained', $$ RETURN ((1 = (1 = 1)) = 1) = 1 $$) AS (result agtype); + result +-------- + false +(1 row) + +-- in clause +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE 35 = u.age = 35 RETURN u $$) AS (result agtype); + result +--------------------------------------------------------------------------------------------------- + {"id": 844424930131971, "label": "people", "properties": {"age": 35, "name": "Samantha"}}::vertex +(1 row) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (35 = u.age) = 35 RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (35 = 35) = u.age RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = u.age = u.age RETURN u $$) AS (result agtype); + result +--------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "people", "properties": {"age": 50, "name": "Jason"}}::vertex + {"id": 844424930131970, "label": "people", "properties": {"age": 25, "name": "Amy"}}::vertex + {"id": 844424930131971, "label": "people", "properties": {"age": 35, "name": "Samantha"}}::vertex + {"id": 844424930131972, "label": "people", "properties": {"age": 40, "name": "Mark"}}::vertex + {"id": 844424930131973, "label": "people", "properties": {"age": 15, "name": "David"}}::vertex +(5 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (u.age = u.age) = u.age RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = (u.age = u.age) RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (u.age = u.age) = (u.age = u.age) RETURN u $$) AS (result agtype); + result +--------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "people", "properties": {"age": 50, "name": "Jason"}}::vertex + {"id": 844424930131970, "label": "people", "properties": {"age": 25, "name": "Amy"}}::vertex + {"id": 844424930131971, "label": "people", "properties": {"age": 35, "name": "Samantha"}}::vertex + {"id": 844424930131972, "label": "people", "properties": {"age": 40, "name": "Mark"}}::vertex + {"id": 844424930131973, "label": "people", "properties": {"age": 15, "name": "David"}}::vertex +(5 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = (u.age = (u.age = u.age)) RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = 35 = ((u.age = u.age) = u.age) RETURN u $$) AS (result agtype); + result +-------- +(0 rows) + +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE ((u.age = u.age) = (u.age = u.age)) = (u.age = u.age) RETURN u $$) AS (result agtype); + result +--------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "people", "properties": {"age": 50, "name": "Jason"}}::vertex + {"id": 844424930131970, "label": "people", "properties": {"age": 25, "name": "Amy"}}::vertex + {"id": 844424930131971, "label": "people", "properties": {"age": 35, "name": "Samantha"}}::vertex + {"id": 844424930131972, "label": "people", "properties": {"age": 40, "name": "Mark"}}::vertex + {"id": 844424930131973, "label": "people", "properties": {"age": 15, "name": "David"}}::vertex +(5 rows) + -- -- Test transform logic for IS NULL & IS NOT NULL -- diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index aa1f38911..476bcceeb 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -310,6 +310,31 @@ SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE 35 < u.age + 1 <= 50 R -- should return 3 SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE NOT 35 < u.age + 1 <= 50 RETURN u $$) AS (result agtype); +-- order of operations +-- expressions +SELECT * FROM cypher('chained', $$ RETURN 1 = 1 = 1 $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN 1 = 2 = 1 $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN (1 = 1) = 1 $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN 1 = (1 = 1) $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN 1 = 1 = true $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN (1 = 1) = true $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN true = ((1 = 1) = true) $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN ((1 = 1) = 1) = 1 $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN (1 = (1 = 1)) = 1 $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ RETURN ((1 = (1 = 1)) = 1) = 1 $$) AS (result agtype); + +-- in clause +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE 35 = u.age = 35 RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (35 = u.age) = 35 RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (35 = 35) = u.age RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = u.age = u.age RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (u.age = u.age) = u.age RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = (u.age = u.age) RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE (u.age = u.age) = (u.age = u.age) RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = (u.age = (u.age = u.age)) RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE u.age = 35 = ((u.age = u.age) = u.age) RETURN u $$) AS (result agtype); +SELECT * FROM cypher('chained', $$ MATCH (u:people) WHERE ((u.age = u.age) = (u.age = u.age)) = (u.age = u.age) RETURN u $$) AS (result agtype); + -- -- Test transform logic for IS NULL & IS NOT NULL -- diff --git a/src/backend/nodes/ag_nodes.c b/src/backend/nodes/ag_nodes.c index 14715bd2e..dee460b34 100644 --- a/src/backend/nodes/ag_nodes.c +++ b/src/backend/nodes/ag_nodes.c @@ -48,6 +48,8 @@ const char *node_names[] = { "cypher_param", "cypher_map", "cypher_list", + "cypher_comparison_aexpr", + "cypher_comparison_boolexpr", "cypher_string_match", "cypher_typecast", "cypher_integer_const", @@ -111,6 +113,8 @@ const ExtensibleNodeMethods node_methods[] = { DEFINE_NODE_METHODS(cypher_param), DEFINE_NODE_METHODS(cypher_map), DEFINE_NODE_METHODS(cypher_list), + DEFINE_NODE_METHODS(cypher_comparison_aexpr), + DEFINE_NODE_METHODS(cypher_comparison_boolexpr), DEFINE_NODE_METHODS(cypher_string_match), DEFINE_NODE_METHODS(cypher_typecast), DEFINE_NODE_METHODS(cypher_integer_const), diff --git a/src/backend/nodes/cypher_outfuncs.c b/src/backend/nodes/cypher_outfuncs.c index 4053a399c..594c61514 100644 --- a/src/backend/nodes/cypher_outfuncs.c +++ b/src/backend/nodes/cypher_outfuncs.c @@ -259,6 +259,28 @@ void out_cypher_list(StringInfo str, const ExtensibleNode *node) WRITE_LOCATION_FIELD(location); } +// serialization function for the cypher_comparison_aexpr ExtensibleNode. +void out_cypher_comparison_aexpr(StringInfo str, const ExtensibleNode *node) +{ + DEFINE_AG_NODE(cypher_comparison_aexpr); + + WRITE_ENUM_FIELD(kind, A_Expr_Kind); + WRITE_NODE_FIELD(name); + WRITE_NODE_FIELD(lexpr); + WRITE_NODE_FIELD(rexpr); + WRITE_LOCATION_FIELD(location); +} + +// serialization function for the cypher_comparison_boolexpr ExtensibleNode. +void out_cypher_comparison_boolexpr(StringInfo str, const ExtensibleNode *node) +{ + DEFINE_AG_NODE(cypher_comparison_boolexpr); + + WRITE_ENUM_FIELD(boolop, BoolExprType); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + // serialization function for the cypher_string_match ExtensibleNode. void out_cypher_string_match(StringInfo str, const ExtensibleNode *node) { diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c index 372bff8c4..3a74f2b9b 100644 --- a/src/backend/parser/cypher_expr.c +++ b/src/backend/parser/cypher_expr.c @@ -72,7 +72,11 @@ static Node *transform_ColumnRef(cypher_parsestate *cpstate, ColumnRef *cref); static Node *transform_A_Indirection(cypher_parsestate *cpstate, A_Indirection *a_ind); static Node *transform_AEXPR_OP(cypher_parsestate *cpstate, A_Expr *a); +static Node *transform_cypher_comparison_aexpr_OP(cypher_parsestate *cpstate, + cypher_comparison_aexpr *a); static Node *transform_BoolExpr(cypher_parsestate *cpstate, BoolExpr *expr); +static Node *transform_cypher_comparison_boolexpr(cypher_parsestate *cpstate, + cypher_comparison_boolexpr *b); static Node *transform_cypher_bool_const(cypher_parsestate *cpstate, cypher_bool_const *bc); static Node *transform_cypher_integer_const(cypher_parsestate *cpstate, @@ -191,6 +195,12 @@ static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate, if (is_ag_node(expr, cypher_typecast)) return transform_cypher_typecast(cpstate, (cypher_typecast *)expr); + if (is_ag_node(expr, cypher_comparison_aexpr)) + return transform_cypher_comparison_aexpr_OP(cpstate, + (cypher_comparison_aexpr *)expr); + if (is_ag_node(expr, cypher_comparison_boolexpr)) + return transform_cypher_comparison_boolexpr(cpstate, + (cypher_comparison_boolexpr *)expr); ereport(ERROR, (errmsg_internal("unrecognized ExtensibleNode: %s", ((ExtensibleNode *)expr)->extnodename))); @@ -467,6 +477,25 @@ static Node *transform_AEXPR_OP(cypher_parsestate *cpstate, A_Expr *a) a->location); } +/* + * function for transforming cypher comparision A_Expr. Since this node is a + * wrapper to let us know when a comparison occurs in a chained comparison, + * we convert it to a regular A_expr and transform it. + */ +static Node *transform_cypher_comparison_aexpr_OP(cypher_parsestate *cpstate, + cypher_comparison_aexpr *a) +{ + A_Expr *n = makeNode(A_Expr); + n->kind = a->kind; + n->name = a->name; + n->lexpr = a->lexpr; + n->rexpr = a->rexpr; + n->location = a->location; + + return (Node *)transform_AEXPR_OP(cpstate, n); +} + + static Node *transform_AEXPR_IN(cypher_parsestate *cpstate, A_Expr *a) { Oid func_in_oid; @@ -525,6 +554,24 @@ static Node *transform_BoolExpr(cypher_parsestate *cpstate, BoolExpr *expr) return (Node *)makeBoolExpr(expr->boolop, args, expr->location); } +/* + * function for transforming cypher_comparison_boolexpr. Since this node is a + * wrapper to let us know when a comparison occurs in a chained comparison, + * we convert it to a PG BoolExpr and transform it. + */ +static Node *transform_cypher_comparison_boolexpr(cypher_parsestate *cpstate, + cypher_comparison_boolexpr *b) +{ + BoolExpr *n = makeNode(BoolExpr); + + n->boolop = b->boolop; + n->args = b->args; + n->location = b->location; + + return transform_BoolExpr(cpstate, n); +} + + static Node *transform_cypher_bool_const(cypher_parsestate *cpstate, cypher_bool_const *bc) { diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y index 9f9457cc1..b2810e064 100644 --- a/src/backend/parser/cypher_gram.y +++ b/src/backend/parser/cypher_gram.y @@ -205,6 +205,12 @@ static Node *make_or_expr(Node *lexpr, Node *rexpr, int location); static Node *make_and_expr(Node *lexpr, Node *rexpr, int location); static Node *make_xor_expr(Node *lexpr, Node *rexpr, int location); static Node *make_not_expr(Node *expr, int location); +static Node *make_comparison_and_expr(Node *lexpr, Node *rexpr, int location); +static Node *make_cypher_comparison_aexpr(A_Expr_Kind kind, char *name, + Node *lexpr, Node *rexpr, + int location); +static Node *make_cypher_comparison_boolexpr(BoolExprType boolop, List *args, + int location); // arithmetic operators static Node *do_negate(Node *n, int location); @@ -240,7 +246,7 @@ static cypher_relationship *build_VLE_relation(List *left_arg, int left_arg_location, int cr_location); // comparison -static bool is_A_Expr_a_comparison_operation(A_Expr *a); +static bool is_A_Expr_a_comparison_operation(cypher_comparison_aexpr *a); static Node *build_comparison_expression(Node *left_grammar_node, Node *right_grammar_node, char *opr_name, int location); @@ -1764,7 +1770,14 @@ expr_atom: } | '(' expr ')' { - $$ = $2; + Node *n = $2; + + if (is_ag_node(n, cypher_comparison_aexpr) || + is_ag_node(n, cypher_comparison_boolexpr)) + { + n = (Node *)node_to_agtype(n, "boolean", @2); + } + $$ = n; } | expr_case | expr_var @@ -2103,6 +2116,51 @@ static Node *make_not_expr(Node *expr, int location) return (Node *)makeBoolExpr(NOT_EXPR, list_make1(expr), location); } +/* + * chained expression comparison operators + */ + +static Node *make_cypher_comparison_aexpr(A_Expr_Kind kind, char *name, + Node *lexpr, Node *rexpr, int location) +{ + cypher_comparison_aexpr *a = make_ag_node(cypher_comparison_aexpr); + + a->kind = kind; + a->name = list_make1(makeString((char *) name)); + a->lexpr = lexpr; + a->rexpr = rexpr; + a->location = location; + return (Node *)a; +} + +static Node *make_cypher_comparison_boolexpr(BoolExprType boolop, List *args, int location) +{ + cypher_comparison_boolexpr *b = make_ag_node(cypher_comparison_boolexpr); + + b->boolop = boolop; + b->args = args; + b->location = location; + return (Node *)b; +} + +static Node *make_comparison_and_expr(Node *lexpr, Node *rexpr, int location) +{ + // flatten "a AND b AND c ..." to a single BoolExpr on sight + if (is_ag_node(lexpr, cypher_comparison_boolexpr)) + { + cypher_comparison_boolexpr *bexpr = (cypher_comparison_boolexpr *)lexpr; + + if (bexpr->boolop == AND_EXPR) + { + bexpr->args = lappend(bexpr->args, rexpr); + + return (Node *)bexpr; + } + } + + return (Node *)make_cypher_comparison_boolexpr(AND_EXPR, list_make2(lexpr, rexpr), location); +} + /* * arithmetic operators */ @@ -2515,7 +2573,7 @@ static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg, } /* check if A_Expr is a comparison expression */ -static bool is_A_Expr_a_comparison_operation(A_Expr *a) +static bool is_A_Expr_a_comparison_operation(cypher_comparison_aexpr *a) { Value *v = NULL; char *opr_name = NULL; @@ -2581,75 +2639,77 @@ static Node *build_comparison_expression(Node *left_grammar_node, /* * Case 1: - * If the previous expression is an A_Expr and it is also a + * If the left expression is an A_Expr and it is also a * comparison, then this is part of a chained comparison. In this * specific case, the second chained element. */ - if (IsA(left_grammar_node, A_Expr) && - is_A_Expr_a_comparison_operation((A_Expr *)left_grammar_node)) + if (is_ag_node(left_grammar_node, cypher_comparison_aexpr) && + is_A_Expr_a_comparison_operation((cypher_comparison_aexpr *)left_grammar_node)) { - A_Expr *aexpr = NULL; + cypher_comparison_aexpr *aexpr = NULL; Node *lexpr = NULL; Node *n = NULL; /* get the A_Expr on the left side */ - aexpr = (A_Expr *) left_grammar_node; + aexpr = (cypher_comparison_aexpr *)left_grammar_node; /* get its rexpr which will be our lexpr */ lexpr = aexpr->rexpr; /* build our comparison operator */ - n = (Node *)makeSimpleA_Expr(AEXPR_OP, opr_name, lexpr, + n = (Node *)make_cypher_comparison_aexpr(AEXPR_OP, opr_name, lexpr, right_grammar_node, location); /* now add it (AND) to the other comparison */ - result_expr = make_and_expr(left_grammar_node, n, location); + result_expr = make_comparison_and_expr(left_grammar_node, n, location); } /* * Case 2: - * If the previous expression is a boolean AND and its right most + * If the left expression is a boolean AND and its right most * expression is an A_Expr and a comparison, then this is part of * a chained comparison. In this specific case, the third and * beyond chained element. */ - if (IsA(left_grammar_node, BoolExpr) && - ((BoolExpr*)left_grammar_node)->boolop == AND_EXPR) + else if (is_ag_node(left_grammar_node, cypher_comparison_boolexpr) && + ((cypher_comparison_boolexpr*)left_grammar_node)->boolop == AND_EXPR) { - BoolExpr *bexpr = NULL; + cypher_comparison_boolexpr *bexpr = NULL; Node *last = NULL; /* cast the left to a boolean */ - bexpr = (BoolExpr *)left_grammar_node; + bexpr = (cypher_comparison_boolexpr *)left_grammar_node; /* extract the last node - ANDs are chained in a flat list */ last = llast(bexpr->args); /* is the last node an A_Expr and a comparison operator */ - if (IsA(last, A_Expr) && - is_A_Expr_a_comparison_operation((A_Expr *)last)) + if (is_ag_node(last, cypher_comparison_aexpr) && + is_A_Expr_a_comparison_operation((cypher_comparison_aexpr *)last)) { - A_Expr *aexpr = NULL; + cypher_comparison_aexpr *aexpr = NULL; Node *lexpr = NULL; Node *n = NULL; /* get the last expressions right expression */ - aexpr = (A_Expr *) last; + aexpr = (cypher_comparison_aexpr *) last; lexpr = aexpr->rexpr; /* make our comparison operator */ - n = (Node *)makeSimpleA_Expr(AEXPR_OP, opr_name, lexpr, + n = (Node *)make_cypher_comparison_aexpr(AEXPR_OP, opr_name, lexpr, right_grammar_node, location); /* now add it (AND) to the other comparisons */ - result_expr = make_and_expr(left_grammar_node, n, location); + result_expr = make_comparison_and_expr(left_grammar_node, n, location); } } + /* * Case 3: - * The previous expression isn't a chained comparison. So, treat - * it as a regular comparison expression. + * The left expression isn't a chained comparison. So, treat + * it as a regular comparison expression. This is usually an initial + * comparison expression. */ - if (result_expr == NULL) + else if (result_expr == NULL) { - result_expr = (Node *)makeSimpleA_Expr(AEXPR_OP, opr_name, + result_expr = (Node *)make_cypher_comparison_aexpr(AEXPR_OP, opr_name, left_grammar_node, right_grammar_node, location); } diff --git a/src/include/nodes/ag_nodes.h b/src/include/nodes/ag_nodes.h index e51a8de43..481c75b52 100644 --- a/src/include/nodes/ag_nodes.h +++ b/src/include/nodes/ag_nodes.h @@ -51,6 +51,9 @@ typedef enum ag_node_tag cypher_param_t, cypher_map_t, cypher_list_t, + // comparison expression + cypher_comparison_aexpr_t, + cypher_comparison_boolexpr_t, // string match cypher_string_match_t, // typecast diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h index 64318eb5d..b457148bb 100644 --- a/src/include/nodes/cypher_nodes.h +++ b/src/include/nodes/cypher_nodes.h @@ -239,6 +239,29 @@ typedef struct cypher_create_path char *var_name; } cypher_create_path; +/* + * comparison expressions + */ + +typedef struct cypher_comparison_aexpr +{ + ExtensibleNode extensible; + A_Expr_Kind kind; /* see above */ + List *name; /* possibly-qualified name of operator */ + Node *lexpr; /* left argument, or NULL if none */ + Node *rexpr; /* right argument, or NULL if none */ + int location; /* token location, or -1 if unknown */ +} cypher_comparison_aexpr; + +typedef struct cypher_comparison_boolexpr +{ + ExtensibleNode extensible; + BoolExprType boolop; + List *args; /* arguments to this expression */ + int location; /* token location, or -1 if unknown */ +} cypher_comparison_boolexpr; + + /* * procedure call */ diff --git a/src/include/nodes/cypher_outfuncs.h b/src/include/nodes/cypher_outfuncs.h index 836765bc9..4071b28db 100644 --- a/src/include/nodes/cypher_outfuncs.h +++ b/src/include/nodes/cypher_outfuncs.h @@ -53,6 +53,10 @@ void out_cypher_param(StringInfo str, const ExtensibleNode *node); void out_cypher_map(StringInfo str, const ExtensibleNode *node); void out_cypher_list(StringInfo str, const ExtensibleNode *node); +// comparison expression +void out_cypher_comparison_aexpr(StringInfo str, const ExtensibleNode *node); +void out_cypher_comparison_boolexpr(StringInfo str, const ExtensibleNode *node); + // string match void out_cypher_string_match(StringInfo str, const ExtensibleNode *node);