Summary
The Cypher SET executor (apply_update_list in src/backend/executor/cypher_set.c) creates a fresh ResultRelInfo per updated entity via create_entity_result_rel_info() inside the per-row loop.
Now that SET computes stored generated columns (#2458, for #2450), ExecComputeStoredGenerated() lazily initializes ri_GeneratedExprsU on that ResultRelInfo. Because a new ResultRelInfo is built per row, a multi-row SET on a label with a stored generated column rebuilds the generated-column expression state on every row and retains it in the executor query context until query end.
Impact
- Only affects
SET on labels that actually have a GENERATED ALWAYS ... STORED column (the compute is guarded by has_generated_stored), so there is no regression for the common case.
- For that case, expression-state setup is O(rows) instead of O(labels), with corresponding memory held for the duration of the query.
Proposed fix
Cache/reuse the ResultRelInfo per label (relation OID) for the lifetime of apply_update_list, mirroring the existing relation / index-OID / RLS caching already done there. That lets ri_GeneratedExprsU (and index/constraint setup) initialize once per relation instead of once per updated row.
Notes
Split out from the #2458 review (thanks @MuhammadTahaNaveed) to keep that bugfix focused. Not a correctness issue — a performance/memory improvement for multi-row SET on generated-column labels.
Summary
The Cypher
SETexecutor (apply_update_listinsrc/backend/executor/cypher_set.c) creates a freshResultRelInfoper updated entity viacreate_entity_result_rel_info()inside the per-row loop.Now that
SETcomputes stored generated columns (#2458, for #2450),ExecComputeStoredGenerated()lazily initializesri_GeneratedExprsUon thatResultRelInfo. Because a newResultRelInfois built per row, a multi-rowSETon a label with a stored generated column rebuilds the generated-column expression state on every row and retains it in the executor query context until query end.Impact
SETon labels that actually have aGENERATED ALWAYS ... STOREDcolumn (the compute is guarded byhas_generated_stored), so there is no regression for the common case.Proposed fix
Cache/reuse the
ResultRelInfoper label (relation OID) for the lifetime ofapply_update_list, mirroring the existing relation / index-OID / RLS caching already done there. That letsri_GeneratedExprsU(and index/constraint setup) initialize once per relation instead of once per updated row.Notes
Split out from the #2458 review (thanks @MuhammadTahaNaveed) to keep that bugfix focused. Not a correctness issue — a performance/memory improvement for multi-row
SETon generated-column labels.