fix(search): keep SQL placeholders aligned when QSL status + DXCC filters combine - #236
Merged
Merged
Conversation
…ters combine The contact-search route incremented a shared placeholder counter for every active filter, but the `qslStatus` branch adds a predicate (`confirmed = true`) without binding a value. That desynced the counter from the params array, so selecting a QSL status *and* a DXCC entity emitted a `$N` with no matching parameter — the COUNT query 500'd and the paginated query compared `dxcc` against the LIMIT value. Extract the WHERE-clause construction into a pure, server-import-free `@/lib/contact-search` builder that numbers placeholders off the params array as values are pushed, so predicate-only filters can never shift later ones. Adds unit tests covering the alignment regression and the full filter set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 24, 2026
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.
Problem
The advanced contact search (
GET /api/contacts/search, backing the/searchpage) breaks whenever an operator combines a QSL status filter with a DXCC entity filter.The route built its parameterized
WHEREclause with a singleparamCountcounter that was bumped once per active filter:But the
qslStatusbranch adds a SQL predicate (confirmed = true/(confirmed = false OR confirmed IS NULL)) without pushing a bound value. That desyncsparamCountfrom thequeryParamsarray. Becausedxccis applied afterqslStatus, selecting e.g. "Confirmed" and a DXCC entity produced a$Nplaceholder with no matching parameter:query(countSql, queryParams)) referenced$3while only 2 params were supplied → Postgresthere is no parameter $3→ the whole request 500s with "Internal server error".dxccplaceholder with theLIMITplaceholder, silently filteringdxcc = <limit value>(e.g.dxcc = 20) and returning wrong rows.The search UI sends both fields together, so any DX-focused search scoped by confirmation status was affected.
Solution
Extracted the WHERE-clause construction into a pure, server-import-free module
src/lib/contact-search.ts(same pattern as@/lib/grid/@/lib/bands, so it's unit-testable without a DB). Placeholders are now numbered off the length of theparamsarray as each value is pushed — a predicate-only filter simply doesn't push, so it can never shift a later filter's placeholder.The route now delegates to
buildContactSearchQuery(userId, filters), dropping ~55 lines of inline query-building. Behavior is otherwise identical (also added a small guard so a non-numericdxccis ignored rather than bound asNaN).Testing
tests/contact-search.spec.ts, 10 cases) covering each filter, theall/blank sentinels, the predicate-only QSL-status branch, and specifically the QSL-status + DXCC alignment regression (asserts every$Nin the clause has a matching param).npm run typecheck— cleannpm run lint— cleannpm run build— succeedsBackwards compatibility
No API surface change — same query params, same response shape. Purely a correctness fix plus test coverage.
Future follow-up
CLAUDE.md, this route's query params (gridLocator,startDate,endDate,qslStatus) are still camelCase — a known drifting surface deferred to a later snake_case sweep. Left untouched here to keep the change focused.🤖 Generated with Claude Code