Emits a filter as ->filter({row | <predicate>}) (PLAN §8.4):

  • a negative operator is not over its positive one (!($row.c == 'x'));
  • a Not over a group is pushed down to the leaves (De Morgan), because the engine's NOT (… OR …) would drop NULL rows; a double negation cancels;
  • a Not over a comparison takes its negated operator, or is not(…) for one without (!($row.c > 5); <= would drop NULL rows);
  • a negation keeps NULL rows (D4): on a column the input schema marks nullable it is $row.c->isEmpty() || !(…), because the engine keeps them only for columns it types [0..1] itself, and it does not after an outer join (PLAN Appendix B). IsNotEmpty is the one negation that drops them;
  • And and Or are binary, folded left; a group of one rule is that rule;
  • values are typed literals of the column's type; enumeration values are written EnumPath.VALUE.

The filter must be valid for its input schema.