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 43b40807f..e55285ac2 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -808,6 +808,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 6f7c58534..85065e060 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -328,6 +328,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 4e3e9d484..ac08164a8 100644 --- a/src/backend/parser/cypher_expr.c +++ b/src/backend/parser/cypher_expr.c @@ -73,7 +73,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, @@ -193,6 +197,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))); @@ -488,6 +498,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) { ParseState *pstate = (ParseState *)cpstate; @@ -660,6 +689,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 4f7fddf5d..1e3adb3ad 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); @@ -1776,7 +1782,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 @@ -2115,6 +2128,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 */ @@ -2535,7 +2593,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) { String *v = NULL; char *opr_name = NULL; @@ -2601,75 +2659,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 0b2f65349..c80c08218 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);