Commit 4f9db37
Implement predicate functions: all(), any(), none(), single() (apache#2359)
* Implement predicate functions: all(), any(), none(), single()
Implement the four openCypher predicate functions (issues apache#552, apache#553,
apache#555, apache#556) that test list elements against a predicate:
all(x IN list WHERE predicate) -- true if all elements match
any(x IN list WHERE predicate) -- true if at least one matches
none(x IN list WHERE predicate) -- true if no elements match
single(x IN list WHERE predicate) -- true if exactly one matches
Implementation approach:
- Add cypher_predicate_function node type with CPFK_ALL/ANY/NONE/SINGLE
kind enum, reusing the list comprehension's unnest-based transformation
- Grammar rules in expr_func_subexpr (alongside EXISTS, COALESCE, COUNT)
- Transform to efficient SQL sublinks:
all() -> NOT EXISTS (SELECT 1 FROM unnest WHERE NOT pred)
any() -> EXISTS (SELECT 1 FROM unnest WHERE pred)
none() -> NOT EXISTS (SELECT 1 FROM unnest WHERE pred)
single() -> (SELECT count(*) FROM unnest WHERE pred) = 1
- Three new keywords (ANY_P, NONE, SINGLE) added to safe_keywords for
backward compatibility as property keys and label names
- Shared extract_iter_variable_name() helper for variable validation
All 32 regression tests pass. New predicate_functions test covers basic
semantics, empty lists, graph data integration, boolean combinations,
nested predicates, and keyword backward compatibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address Copilot review: NULL semantics, iterator validation, single() perf, tests
- Rewrite predicate functions from EXISTS_SUBLINK to EXPR_SUBLINK with
aggregate-based CASE expressions (bool_or + IS TRUE/FALSE/NULL) to
preserve three-valued Cypher NULL semantics
- Add list_length check in extract_iter_variable_name() to reject
qualified names like x.y as iterator variables
- Add copy/read support for cypher_predicate_function ExtensibleNode
to prevent query rewriter crashes
- Use IS TRUE filtering in single() count (LIMIT 2 optimization
breaks correlated variable refs in graph contexts -- documented)
- Add 13 NULL regression tests: null list input, null elements,
null predicates for all four functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address Copilot round 2: NULL-list guard, single() comment, pg_aggregate.h
1. Add NULL-list guard for all predicate functions (all/any/none/single).
Wraps the result with CASE WHEN list IS NULL THEN NULL ELSE <result>
END in the grammar layer. This fixes single(x IN null WHERE ...)
returning false instead of NULL. The expr pointer is safely shared
between the NullTest and the predicate function node because AGE's
expression transformer creates new nodes without modifying the
parse tree in-place.
2. Fix single() block comment in transform_cypher_predicate_function:
described LIMIT 2 optimization but implementation uses plain
count(*). Updated comment to match actual implementation.
3. Keep #include "catalog/pg_aggregate.h" -- Copilot suggested removal
but AGGKIND_NORMAL macro requires it (build fails without it).
Regression test: predicate_functions OK.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address Copilot round 3: reuse extract_iter_variable_name for list comprehensions
- Refactor build_list_comprehension_node() to reuse the shared
extract_iter_variable_name() helper, so `var IN list` validation
is consistent between list comprehensions and predicate functions
(all/any/none/single). Qualified ColumnRefs like `x.y IN list`
are now rejected in list comprehensions the same way they are
in predicate functions.
- Update list_comprehension expected output for the normalized
lowercase "syntax error at or near IN" message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 15030a0 commit 4f9db37
17 files changed
Lines changed: 1272 additions & 18 deletions
File tree
- regress
- expected
- sql
- src
- backend
- nodes
- parser
- include
- nodes
- parser
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| 177 | + | |
177 | 178 | | |
178 | 179 | | |
179 | 180 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
577 | 577 | | |
578 | 578 | | |
579 | 579 | | |
580 | | - | |
| 580 | + | |
581 | 581 | | |
582 | 582 | | |
583 | 583 | | |
584 | | - | |
| 584 | + | |
585 | 585 | | |
586 | 586 | | |
587 | 587 | | |
| |||
0 commit comments