Commit 84e2954
Add MERGE ON CREATE SET / ON MATCH SET support (#2347)
- Add MERGE ON CREATE SET / ON MATCH SET support
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.
- Move ExecInitExpr for ON CREATE/MATCH SET items from per-row
execution in apply_update_list() to plan initialization in
begin_cypher_merge(). Follows the established pattern used by
cypher_target_node (id_expr_state, prop_expr_state).
- Add prop_expr_state field to cypher_update_item with serialization
support in outfuncs/readfuncs/copyfuncs.
- apply_update_list() uses pre-initialized state when available,
falls back to per-row init for plain SET callers.
- Fix misleading comment: "ON MATCH SET" → "ON CREATE SET" for Case 1
first-run test.
- Add Case 1 second-run test that triggers ON MATCH SET with a
predecessor clause (MATCH ... MERGE ... ON MATCH SET).
- Add ON to safe_keywords in cypher_gram.y so that property keys
and labels named 'on' still work (e.g., n.on, MATCH (n:on)).
All other keywords added as tokens are also in safe_keywords.
- Add chained (non-terminal) MERGE regression tests exercising the
eager-buffering code path with ON CREATE SET and ON MATCH SET.
First run creates both nodes (ON CREATE SET fires), second run
matches both (ON MATCH SET fires).
- Move ExecStoreVirtualTuple before apply_update_list unconditionally in
Case 1 non-terminal and terminal MERGE paths, matching the pattern at
Case 3 (line 994). Ensures tts_nvalid is set for downstream ExecProject
even when ON CREATE SET is absent.
- Add resolve_merge_set_exprs() helper to deduplicate the prop_expr
resolution loops for ON MATCH SET and ON CREATE SET. Includes ereport
when target entry is missing (internal error, should never happen).
- Add regression test for ON keyword as label name, confirming backward
compatibility via safe_keywords grammar path.
- The four ExecStoreVirtualTuple calls in exec_cypher_merge were triggering
an Assert failure under --enable-cassert:
TRAP: failed Assert("TTS_EMPTY(slot)"), File: execTuples.c, Line: 1748
ExecStoreVirtualTuple (execTuples.c:1748) asserts that its target slot
is in the TTS_EMPTY state. In our MERGE executor, process_path writes
directly into the subquery's scan tuple slot -- which already holds the
subquery's output tuple and therefore is NOT empty. On a release build
the assertion compiles out and ExecStoreVirtualTuple just clears the flag
and sets tts_nvalid; on an --enable-cassert build the backend aborts and
takes down the regression run.
We only need the bookkeeping half of ExecStoreVirtualTuple (clear
TTS_FLAG_EMPTY and set tts_nvalid = natts) -- not the "store semantics"
that motivate the assertion. Add a small static helper
mark_scan_slot_valid() that does exactly the bookkeeping, and replace
the four call sites. Release-build behavior is byte-identical since
Assert() compiles to nothing; cassert-build behavior now matches release.
- Fix MERGE ON CREATE/MATCH SET crash when RHS references a bound variable
When MERGE has a previous clause (e.g. MATCH, UNWIND), transform_cypher_merge
takes the lateral-left-join path via transform_merge_make_lateral_join. That
helper called addRangeTableEntryForJoin with nscolumns=NULL, leaving the join
ParseNamespaceItem's p_nscolumns unset. For queries that did not subsequently
resolve a column reference against that nsitem (e.g. RETURN, which runs in a
fresh namespace built by handle_prev_clause), the NULL was harmless.
Our ON CREATE / ON MATCH SET transform runs in-line, before the MERGE query
becomes a subquery, so transform_cypher_set_item_list consulted the join's
nsitem directly. colNameToVar -> scanNSItemForColumn then dereferenced
p_nscolumns[attnum-1] = NULL[0] and the backend segfaulted on any ON SET
whose RHS referenced a bound variable.
Populate the join's p_nscolumns from res_colvars. The Var we end up
producing for a bound entity lives inside prop_expr, which is opaque to
the planner, so it is not rewritten to match the plan's output slots. At
ExecEvalScalarVar time only varattno is consulted, and scantuple's layout
mirrors the join's eref->colnames (via make_target_list_from_join). Use
the join rtindex and 1-based eref position so scantuple[varattno - 1]
resolves to the correct entity column at runtime; without this, Vars for
a (varno=l_rte) and b (varno=r_rte) with varattno=1 both hit
scantuple[0] and b.id evaluated to a.id.
Also initialise apply_update_list's new_property_value at its declaration.
All control paths reach the single alter_property_value call with the
variable set, but -Wmaybe-uninitialized fires at -O2 because the compiler
cannot prove remove_property == isnull when prop_expr is non-NULL.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>1 parent 6f520fe commit 84e2954
13 files changed
Lines changed: 788 additions & 28 deletions
File tree
- regress
- expected
- sql
- src
- backend
- executor
- nodes
- parser
- include
- executor
- nodes
- parser
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2001 | 2001 | | |
2002 | 2002 | | |
2003 | 2003 | | |
| 2004 | + | |
| 2005 | + | |
| 2006 | + | |
| 2007 | + | |
| 2008 | + | |
| 2009 | + | |
| 2010 | + | |
| 2011 | + | |
| 2012 | + | |
| 2013 | + | |
| 2014 | + | |
| 2015 | + | |
| 2016 | + | |
| 2017 | + | |
| 2018 | + | |
| 2019 | + | |
| 2020 | + | |
| 2021 | + | |
| 2022 | + | |
| 2023 | + | |
| 2024 | + | |
| 2025 | + | |
| 2026 | + | |
| 2027 | + | |
| 2028 | + | |
| 2029 | + | |
| 2030 | + | |
| 2031 | + | |
| 2032 | + | |
| 2033 | + | |
| 2034 | + | |
| 2035 | + | |
| 2036 | + | |
| 2037 | + | |
| 2038 | + | |
| 2039 | + | |
| 2040 | + | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
| 2046 | + | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
| 2053 | + | |
| 2054 | + | |
| 2055 | + | |
| 2056 | + | |
| 2057 | + | |
| 2058 | + | |
| 2059 | + | |
| 2060 | + | |
| 2061 | + | |
| 2062 | + | |
| 2063 | + | |
| 2064 | + | |
| 2065 | + | |
| 2066 | + | |
| 2067 | + | |
| 2068 | + | |
| 2069 | + | |
| 2070 | + | |
| 2071 | + | |
| 2072 | + | |
| 2073 | + | |
| 2074 | + | |
| 2075 | + | |
| 2076 | + | |
| 2077 | + | |
| 2078 | + | |
| 2079 | + | |
| 2080 | + | |
| 2081 | + | |
| 2082 | + | |
| 2083 | + | |
| 2084 | + | |
| 2085 | + | |
| 2086 | + | |
| 2087 | + | |
| 2088 | + | |
| 2089 | + | |
| 2090 | + | |
| 2091 | + | |
| 2092 | + | |
| 2093 | + | |
| 2094 | + | |
| 2095 | + | |
| 2096 | + | |
| 2097 | + | |
| 2098 | + | |
| 2099 | + | |
| 2100 | + | |
| 2101 | + | |
| 2102 | + | |
| 2103 | + | |
| 2104 | + | |
| 2105 | + | |
| 2106 | + | |
| 2107 | + | |
| 2108 | + | |
| 2109 | + | |
| 2110 | + | |
| 2111 | + | |
| 2112 | + | |
| 2113 | + | |
| 2114 | + | |
| 2115 | + | |
| 2116 | + | |
| 2117 | + | |
| 2118 | + | |
| 2119 | + | |
| 2120 | + | |
| 2121 | + | |
| 2122 | + | |
| 2123 | + | |
| 2124 | + | |
| 2125 | + | |
| 2126 | + | |
| 2127 | + | |
| 2128 | + | |
| 2129 | + | |
| 2130 | + | |
| 2131 | + | |
| 2132 | + | |
| 2133 | + | |
| 2134 | + | |
| 2135 | + | |
| 2136 | + | |
| 2137 | + | |
| 2138 | + | |
| 2139 | + | |
| 2140 | + | |
| 2141 | + | |
| 2142 | + | |
| 2143 | + | |
| 2144 | + | |
| 2145 | + | |
| 2146 | + | |
| 2147 | + | |
| 2148 | + | |
| 2149 | + | |
| 2150 | + | |
| 2151 | + | |
| 2152 | + | |
| 2153 | + | |
| 2154 | + | |
| 2155 | + | |
| 2156 | + | |
| 2157 | + | |
| 2158 | + | |
| 2159 | + | |
| 2160 | + | |
| 2161 | + | |
| 2162 | + | |
| 2163 | + | |
| 2164 | + | |
| 2165 | + | |
| 2166 | + | |
| 2167 | + | |
| 2168 | + | |
| 2169 | + | |
| 2170 | + | |
| 2171 | + | |
| 2172 | + | |
| 2173 | + | |
| 2174 | + | |
| 2175 | + | |
| 2176 | + | |
| 2177 | + | |
| 2178 | + | |
| 2179 | + | |
| 2180 | + | |
| 2181 | + | |
| 2182 | + | |
| 2183 | + | |
| 2184 | + | |
| 2185 | + | |
| 2186 | + | |
| 2187 | + | |
| 2188 | + | |
| 2189 | + | |
| 2190 | + | |
| 2191 | + | |
| 2192 | + | |
| 2193 | + | |
| 2194 | + | |
| 2195 | + | |
| 2196 | + | |
| 2197 | + | |
| 2198 | + | |
| 2199 | + | |
| 2200 | + | |
| 2201 | + | |
| 2202 | + | |
| 2203 | + | |
| 2204 | + | |
| 2205 | + | |
| 2206 | + | |
| 2207 | + | |
| 2208 | + | |
| 2209 | + | |
| 2210 | + | |
| 2211 | + | |
| 2212 | + | |
| 2213 | + | |
| 2214 | + | |
| 2215 | + | |
| 2216 | + | |
| 2217 | + | |
| 2218 | + | |
| 2219 | + | |
| 2220 | + | |
| 2221 | + | |
| 2222 | + | |
| 2223 | + | |
| 2224 | + | |
| 2225 | + | |
| 2226 | + | |
| 2227 | + | |
| 2228 | + | |
| 2229 | + | |
| 2230 | + | |
| 2231 | + | |
| 2232 | + | |
| 2233 | + | |
| 2234 | + | |
| 2235 | + | |
| 2236 | + | |
| 2237 | + | |
| 2238 | + | |
| 2239 | + | |
2004 | 2240 | | |
2005 | 2241 | | |
2006 | 2242 | | |
| 2243 | + | |
| 2244 | + | |
| 2245 | + | |
| 2246 | + | |
| 2247 | + | |
| 2248 | + | |
| 2249 | + | |
| 2250 | + | |
| 2251 | + | |
| 2252 | + | |
| 2253 | + | |
| 2254 | + | |
| 2255 | + | |
2007 | 2256 | | |
2008 | 2257 | | |
2009 | 2258 | | |
| |||
0 commit comments