The WHERE-generating helpers each emit a WHERE clause of a single operator kind: _selectWhere/_where is all equality, _selectWhereNull/_selectWhereNotNull is all IS (NOT) NULL, _selectWhereIn is a single IN. They can't be combined, so any query mixing operators has to concatenate raw SQL.
The most common casualty is soft-delete + tenant scoping: WHERE id =? AND deleted_at IS NULL, which no single builder can produce:
-- forced raw-SQL tail on essentially every by-id lookup:
_selectWhere @Model [[field| first_id |], [field| id |]] <> " AND deleted_at IS NULL"
_updateFieldsBy @Model setFields [field| first_id |] <> " AND id = ? AND deleted_at IS NULL"
This pattern appears ~15 times in a codebase i currently work on; the appended predicates are almost entirely IS NULL (soft-delete), plus a few OR and = ANY(array). Concrete gaps:
- No composition across operator kinds in a single WHERE (the = … AND … IS NULL case above).
- IS NULL / IN are SELECT-only — no update/delete equivalents.
- No !=/<>, comparison operators, OR, ANY, LIKE. _selectWhereIn is also Vector Text-only.
The result is that any non-trivial query uses hand-written SQL, which loses the [field| … |] type-safety the library provides, and silently, the builder returns a valid-but-incomplete query, so it's easy to forget the deleted_at IS NULL tail.
Request:
A composable condition type plus builders that accept it, added alongside the existing equality builders (so nothing current changes):
data Cond -- eq / ne / isNull / isNotNull / inList / gt / … , combined with (.&&.)/(.||.)
_selectWhereCond :: Entity e => [Cond] -> Query
_updateFieldsWhereCond :: Entity e => Vector Field -> [Cond] -> Query
_deleteWhereCond :: Entity e => [Cond] -> Query
_selectWhereCond @Model [ eq [field| first_id |], eq [field| id |], isNull [field| deleted_at |] ]
Happy to work on this if there's interest in this direction.
The WHERE-generating helpers each emit a WHERE clause of a single operator kind:
_selectWhere/_whereis all equality,_selectWhereNull/_selectWhereNotNullis all IS (NOT) NULL,_selectWhereInis a single IN. They can't be combined, so any query mixing operators has to concatenate raw SQL.The most common casualty is soft-delete + tenant scoping: WHERE id =? AND deleted_at IS NULL, which no single builder can produce:
This pattern appears ~15 times in a codebase i currently work on; the appended predicates are almost entirely IS NULL (soft-delete), plus a few OR and = ANY(array). Concrete gaps:
The result is that any non-trivial query uses hand-written SQL, which loses the [field| … |] type-safety the library provides, and silently, the builder returns a valid-but-incomplete query, so it's easy to forget the deleted_at IS NULL tail.
Request:
A composable condition type plus builders that accept it, added alongside the existing equality builders (so nothing current changes):
Happy to work on this if there's interest in this direction.