Releases and security
2.1.14 - 2026-06-06
Fix — runique (admin filters & search — PostgreSQL regression)
-
?placeholder incust_with_valuesbroke every admin filter and search on PostgreSQL (regression introduced by the 2.1.12 SQL-injection fix): to bind values safely, 2.1.12 replaced inline interpolation withExpr::cust_with_values(format!("CAST(col AS TEXT) = ?"), [val]). The?marker is only the bind placeholder on MySQL/SQLite — PostgreSQL uses$N. In sea-query'sCustomWithExprrenderer the placeholder is matched against the backend's own marker ($on Postgres), so an unrecognized?is emitted verbatim and the value is never bound. The condition collapsed toWHERE CAST(col AS TEXT) =immediately followed byLIMIT, producing asyntax error at or near "LIMIT"500 on any filtered list or search. Completely silent on MySQL/SQLite. Fixed: all raw-SQL conditions are rebuilt with typed sea-query expressions that emit the backend-correct placeholder automatically — equality viaExpr::col(Alias::new(col)).cast_as(Alias::new("TEXT")).eq(val), case-insensitive search viaExpr::expr(Func::lower(Expr::col(...).cast_as(Alias::new("TEXT")))).like(pattern)with the pattern lowered in Rust. Applied to the generatedlist_fncolumn filters, thesearch_cond!macro (bothall_columnsandor(...)arms), the bulk-upsert lookup and the m2m option query in the admin generator, and the builtin user / group / permission resources. Thesearch!macro was unaffected — it already builds conditions through typedColumnTraitmethods. -
Filter sidebar values sorted in Rust, descending: the
DISTINCTquery no longer carries theORDER BY CAST(col AS TEXT) DESCintroduced in 2.1.13 (it was entangled with the broken raw-SQL path). Distinct values are now sorted inside the generated closure (sort_by— numeric when both sides parse as integers, lexicographic otherwise), descending — highest version / id and most recent value first in each dropdown. The main list ordering is untouched (still driven by the clickable column headers).