feat(query): uniform predicate negation — prefix ~ as a NOT wire node#318
Merged
Conversation
Prefix ~ negates any predicate node — leaf comparison or AND/OR compound — via QueryNode.__invert__, carried on the wire as a new recursive `not` node kind beside leaf/compound (QueryIR v6), and rendered in Rust as a faithful SQL NOT (...) over the rebuilt child (SeaQuery Cond::not). No per-operator negative forms are added: ~t.col.in_(ids) and ~t.col.like(p) close the pre-existing NOT IN / NOT LIKE gap (ADR-0008). - Two hand-authored golden vectors pin the not-over-leaf and not-over-compound wire shapes, asserted from the Python emitter and the Rust decoder; existing query vectors move to v6. - The truthiness guard (QueryNode.__bool__) now names ~ as the supported negation spelling. - The mutate traversal guard and join registration see through NOT, so negation can neither smuggle a join into update()/delete() nor drop a traversed child's join. - End-to-end result-set tests on both backends: ~ over every leaf operator kind, compounds, double negation, mixed &/| trees, NULL-row behavior (three-valued logic), order_by/limit composition, and NOT over a traversed leaf. Refs #310, #312, #313
- New "Negating Conditions" guide section: the one universal rule (~ negates any predicate; no per-operator negative forms), runnable lambda-style examples, and the central SQL three-valued-logic note with a real nullable-column model (Assignment + Annotated tabs) and the rendered NOT (...) SQL — negation excludes NULL rows, exactly like the existing != spelling. - Operators table gains the ~ / NOT row; the guardrails bullet quotes the updated truthiness message naming ~. - API reference states the universal ~ rule and links the NULL note. - not_in() removed from the roadmap and the guide's "Not Yet Supported" list — ~t.col.in_(ids) is the spelling. Refs #313
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements PRD #310 across both slices in one PR: the
~tracer bullet on leaf predicates (#312) and compounds, guard rails, and docs (#313).What
One universal negation rule: prefix
~negates any predicate node — leaf comparison or AND/OR compound.renders as a faithful
NOT (...)over the child:How
QueryNode.__invert__wraps any node in a NOT node; the truthiness guard (__bool__) now names~as the supported spelling. The mutate traversal guard and join registration see through NOT, so~can neither smuggle a join intoupdate()/delete()nor drop a traversed child's join.notnode kind besideleaf/compound—{"node_kind": "not", "child": {...}}, any child, any depth. QueryIR version bumps to v6 (unconditional, single supported version; the Rust gate rejects v5 with the actionable one-wheel message). No per-operator negative forms anywhere (ADR-0008).Cond::notover the rebuilt child. Double negation reaches Rust as two nestednotnodes; SeaQuery's negation toggle renders the even nesting as the plain child — semantically exact under three-valued logic (NOT NOT x ≡ xfor TRUE/FALSE/UNKNOWN alike).Testing
tests/test_negation.py), green on both backends:~over every leaf operator kind (==,!=, orderings,in_,like),~over AND/OR compounds, double negation,~mixed into&/|trees at multiple levels, NULL-row behavior under negation (three-valued semantics, matching!=), composition withorder_by()/limit(), and~over a relation-traversing leaf.~; NOT-wrapped traversal rejected onupdate()/delete().Docs
New "Negating Conditions" guide section with the central three-valued-logic note (real nullable-column model in Assignment + Annotated tabs, rendered SQL);
~/NOTrow in the operators table; API reference states the universal rule;not_in()removed from the roadmap. All examples lambda-style and runnable under the docs test harness.Closes #312
Closes #313