Skip to content

Add MERGE ON CREATE SET / ON MATCH SET support (#1619)#2347

Open
gregfelice wants to merge 1 commit intoapache:masterfrom
gregfelice:feature_1619_merge_on_set
Open

Add MERGE ON CREATE SET / ON MATCH SET support (#1619)#2347
gregfelice wants to merge 1 commit intoapache:masterfrom
gregfelice:feature_1619_merge_on_set

Conversation

@gregfelice
Copy link
Contributor

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:

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.

Nodeson_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.

Transformcypher_clause.c transforms ON SET items and stores prop_expr for direct expression evaluation.

Executorapply_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:

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

  • All 31 existing regression tests pass (no regressions)
  • 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
  • Verified across all merge execution paths including non-terminal eager buffering (Fix chained MERGE not seeing sibling MERGE's changes (#1446) #2344)
  • Backward compatible — existing MERGE queries without ON SET clauses are unaffected

Closes #1619

Implements the openCypher-standard ON CREATE SET and ON MATCH SET
clauses for the MERGE statement. This allows conditional property
updates depending on whether MERGE created a new path or matched
an existing one:

  MERGE (n:Person {name: 'Alice'})
    ON CREATE SET n.created = timestamp()
    ON MATCH SET n.updated = timestamp()

Implementation spans parser, planner, and executor:

- Grammar: new merge_actions_opt/merge_actions/merge_action rules
  in cypher_gram.y, with ON keyword 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 serialized through
  copy/out/read funcs)
- Transform: cypher_clause.c transforms ON SET items and stores
  prop_expr for direct expression evaluation
- Executor: cypher_set.c extracts apply_update_list() from
  process_update_list(); cypher_merge.c calls it at all merge
  decision points (simple merge, terminal, non-terminal with
  eager buffering, and first-clause-with-followers paths)

Key design choice: prop_expr stores the Expr* directly in
cypher_update_item rather than using prop_position into the scan
tuple. The planner strips target list entries for SET expressions
that CustomScan doesn't need, making prop_position references
dangling. By storing the expression directly (only for MERGE ON
SET items), we evaluate it with ExecInitExpr/ExecEvalExpr
independent of the scan tuple layout.

Includes regression tests covering: basic ON CREATE SET, basic
ON MATCH SET, combined ON CREATE + ON MATCH, multiple SET items,
expression evaluation, interaction with WITH clause, and edge
property updates.

All 31 regression tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can we add support for ON CREATE SET and ON MATCH SET. (like neo4j)

1 participant