Status: AcceptedArea: BackendDate: 2026-08-12
Context
SQLAlchemy hybrid properties often expand to comparisons such aspayment_lock_acquired_at <= cutoff or deleted_at IS NOT NULL. Wrapping such an expression
in .is_(True) or .is_(False) produces an outer IS TRUE or IS FALSE node; PostgreSQL’s
limited predicate implication logic may then fail to see the underlying indexable condition,
particularly when matching a partial index, and choose a sequential scan instead.
Decision
In SQL predicate positions, write a boolean expression directly for truth and negate it with~expression or sqlalchemy.not_(expression) for falsehood. Do not use .is_(True) or
.is_(False) unless SQL identity semantics are intentionally required.
expression.is_not(True) means “false or unknown” and therefore includes NULL, while
~expression means strict falsehood and excludes NULL. The rule does not apply to
.is_(None) / .is_not(None), which express SQL null checks.
Consequences
- PostgreSQL can see comparisons and null checks inside hybrid properties, making ordinary and partial indexes available to the planner.
- Predicate code consistently uses
expressionfor true and~expressionfor false. - Every replacement must preserve the intended handling of
NULL;IS NOT TRUE, projections, constraints, and other contexts whereUNKNOWNis distinct require explicit review. - We accept that identity tests remain in exceptional cases and should make the required null semantics evident from their context or a short comment.

