gregfelice opened a new pull request, #2347:
URL: https://github.com/apache/age/pull/2347
## Summary
Implements the openCypher-standard `ON CREATE SET` and `ON MATCH SET`
clauses for the MERGE statement, resolving #1619. This allows conditional
property updates depending on whether MERGE created a new path or matched an
existing one:
```cypher
MERGE (n:Person {name: 'Alice'})
ON CREATE SET n.created = timestamp()
ON MATCH SET n.updated = timestamp()
```
## Design
The implementation spans parser, planner, and executor:
**Parser** — New grammar rules (`merge_actions_opt`, `merge_actions`,
`merge_action`) in `cypher_gram.y`. The `ON` keyword is added to
`cypher_kwlist.h`.
**Nodes** — `on_match` / `on_create` lists on `cypher_merge`, corresponding
`on_match_set_info` / `on_create_set_info` on `cypher_merge_information`, and
`prop_expr` on `cypher_update_item`. All fields serialized through
copy/out/read funcs.
**Transform** — `cypher_clause.c` transforms ON SET items and stores
`prop_expr` for direct expression evaluation.
**Executor** — `apply_update_list()` is extracted from
`process_update_list()` in `cypher_set.c` as reusable SET logic.
`cypher_merge.c` calls it at all merge decision points:
- Simple merge (MERGE as only clause)
- Terminal non-first clause
- Non-terminal with eager buffering (compatible with #2344)
- First-clause-with-followers (Case 3 sub-cases)
### Why prop_expr?
The PostgreSQL planner strips target list entries for SET expressions that
the CustomScan doesn't reference. This makes `prop_position` references into
the scan tuple dangling. The solution: store the `Expr*` directly in
`cypher_update_item->prop_expr` and evaluate it with `ExecInitExpr` /
`ExecEvalExpr`, independent of scan tuple layout. This is only done for MERGE
ON SET items — regular SET continues to use `prop_position` unchanged.
## Files changed (12)
| File | Changes |
|------|---------|
| `src/include/parser/cypher_kwlist.h` | Added `ON` keyword |
| `src/backend/parser/cypher_gram.y` | Grammar rules for merge actions |
| `src/include/nodes/cypher_nodes.h` | Node struct fields for
on_match/on_create |
| `src/backend/nodes/cypher_copyfuncs.c` | Serialization for new fields |
| `src/backend/nodes/cypher_outfuncs.c` | Serialization for new fields |
| `src/backend/nodes/cypher_readfuncs.c` | Deserialization for new fields |
| `src/backend/parser/cypher_clause.c` | Transform ON MATCH/CREATE SET items
|
| `src/include/executor/cypher_utils.h` | State fields + `apply_update_list`
declaration |
| `src/backend/executor/cypher_set.c` | Extracted `apply_update_list()` from
`process_update_list()` |
| `src/backend/executor/cypher_merge.c` | Wired `apply_update_list` at all
merge decision points |
| `regress/sql/cypher_merge.sql` | Regression tests |
| `regress/expected/cypher_merge.out` | Expected output |
## Test plan
- [x] All 31 existing regression tests pass (no regressions)
- [x] New tests cover: basic ON CREATE SET, basic ON MATCH SET, combined ON
CREATE + ON MATCH, multiple SET items in a single clause, expression
evaluation, interaction with WITH clause, edge property updates
- [x] Verified across all merge execution paths including non-terminal eager
buffering (#2344)
- [x] Backward compatible — existing MERGE queries without ON SET clauses
are unaffected
Closes #1619
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]