Skip to content
Merged
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
8 changes: 6 additions & 2 deletions src/backend/executor/query_dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -2069,8 +2069,10 @@ static bool set_values_need_pipeline(cypher_query *query)
cypher_set_item *it = (cypher_set_item *)items->items[i];
if (!it || !it->property || it->property->type == AST_NODE_LABEL_EXPR) continue;
if (!set_value_plain(it->expr)) return true;
/* `SET a += {k: a.s}` reads the node it writes */
if (it->is_merge && it->expr && it->expr->type == AST_NODE_MAP) {
/* `SET a += {k: a.s}` reads the node it writes; `SET a = {k:
* a.s}` too, and bulk SET cleared a before it read the value
* (GQLITE-T-0479). Bulk SET stores only literal map values. */
if (it->expr && it->expr->type == AST_NODE_MAP) {
ast_list *a = ((cypher_map *)it->expr)->pairs;
for (int k = 0; a && k < a->count; k++) {
ast_node *pv = ((cypher_map_pair *)a->items[k])->value;
Expand Down Expand Up @@ -5174,6 +5176,8 @@ static bool rw_expr_bound(ast_node *expr, const rw_row *row)
}
case AST_NODE_NOT_EXPR: return rw_expr_bound(((cypher_not_expr*)expr)->expr, row);
case AST_NODE_NULL_CHECK: return rw_expr_bound(((cypher_null_check*)expr)->expr, row);
/* `n:A` in a SET value was left to SET, which refused it (GQLITE-T-0479) */
case AST_NODE_LABEL_EXPR: return rw_expr_bound(((cypher_label_expr*)expr)->expr, row);
case AST_NODE_CASE_EXPR: {
cypher_case_expr *c = (cypher_case_expr*)expr;
if (!rw_expr_bound(c->operand, row) || !rw_expr_bound(c->else_expr, row)) return false;
Expand Down
31 changes: 31 additions & 0 deletions tests/functional/109_set_map_case.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- ========================================================================
-- Test 109: SET n = map that reads n, and SET values with label tests
-- (GQLITE-T-0479)
-- ========================================================================
-- PURPOSE: `SET n = {id: n.id}` cleared n and then read n.id, and bulk SET
-- dropped any map value that was not a literal: n lost id. A SET
-- value with a label test (`CASE WHEN n:A ...`, `n:A`) was refused
-- ("SET value must be a literal ...").
-- NOTE: Assertions are hard: _assert has CHECK (ok = 1), so under
-- `sqlite3 -bail` any failed expectation aborts the run.
-- ========================================================================

.load ./build/graphqlite

SELECT '=== Test 109: SET = map and label tests ===' as test_section;

CREATE TEMP TABLE _assert(name TEXT, ok INTEGER CHECK (ok = 1));

SELECT cypher('CREATE (:A109 {id: 1, v: 5, s: ''x''}), (:B109 {id: 2, v: 6})') as setup;

SELECT cypher('MATCH (n:A109) SET n = {id: n.id, q: n.v + 1, l: [n.s]}') as w1;
INSERT INTO _assert SELECT 'SET = map reads n before it clears n',
json(cypher('MATCH (n:A109) RETURN properties(n) AS p'))
= json('[{"p":{"id":1,"q":6,"l":["x"]}}]');

SELECT cypher('MATCH (n) WHERE n:A109 OR n:B109 SET n.k = CASE WHEN n:A109 THEN ''a'' ELSE ''o'' END, n.t = n:B109') as w2;
INSERT INTO _assert SELECT 'label tests in SET values',
json(cypher('MATCH (n) WHERE n:A109 OR n:B109 RETURN n.id AS id, n.k AS k, n.t AS t ORDER BY id'))
= json('[{"id":1,"k":"a","t":false},{"id":2,"k":"o","t":true}]');

SELECT 'Test 109 passed: ' || count(*) || ' assertions' AS result FROM _assert;
1 change: 0 additions & 1 deletion tests/metamorphic/known.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[
{"relation": ".", "kind": "error", "error": "invalid JSON result", "task": "GQLITE-T-0484"},
{"relation": "set_forms/replace", "kind": "mismatch", "task": "GQLITE-T-0479"},
{"relation": "^expr_context/call/", "kind": "error", "error": "is not defined", "task": "GQLITE-T-0487"},
{"relation": "^expr_context/comp/", "kind": "mismatch", "task": "GQLITE-T-0480"},
{"relation": "^expr_context/(unwind|unwind_var|union)/", "kind": "mismatch", "task": "GQLITE-T-0481"},
Expand Down
Loading