Implement predicate functions: all(), any(), none(), single() by gregfelice · Pull Request #2359 · apache/age

@gregfelice @claude

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>

@gregfelice @claude

… 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>

@gregfelice @claude

…ate.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>