Skip to content

fix(search): normalize sqlite date comparisons across precisions - #463

Open
angela-helios wants to merge 2 commits into
mainfrom
fix/456-sqlite-date-precision
Open

fix(search): normalize sqlite date comparisons across precisions#463
angela-helios wants to merge 2 commits into
mainfrom
fix/456-sqlite-date-precision

Conversation

@angela-helios

Copy link
Copy Markdown
Contributor

Closes #456

Root cause: value_date stores the resource's own precision (1995-10-02, 2016-01-23T13:07:42-04:00) while query bounds were full datetimes — and SQLite compares TEXT, where '1995-10-02' < '1995-10-02T00:00:00' (the shorter string sorts first). Every boundary misbehaved exactly as the issue table shows, and full-precision eq built the unsatisfiable >= X AND < X.

One shared date_condition() now serves all three date-comparison sites — the parameter handler, the chain builder (which compared with a raw text = regardless of precision), and the _filter parser: both sides go through datetime() (normalizes partial dates, folds timezone offsets to UTC), and partial-precision upper bounds are derived in SQL ('+1 day') so one bind parameter serves both ends. That single-parameter shape also fixes a latent numbering collision: multi-value date eq consumed two ?N slots while the caller advanced the offset by one.

Verified

Scope note

SQLite only. Postgres parses dates into real timestamps on a separate path (parse_date_value) and needs no change; the Elasticsearch and Mongo date handlers are unverified — the #448 backend legs will tell, and findings there get their own issues.

Stored value_date keeps the resource's own precision ('1995-10-02',
'2016-01-23T13:07:42-04:00') while search bounds were full datetimes, and
SQLite compares TEXT — lexicographically '1995-10-02' sorts before
'1995-10-02T00:00:00', so a day never fell inside its own range: eq
matched nothing, ge excluded the named day, le/lt leaked the next
midnight in. Full-precision values built the impossible range
'>= X AND < X'.

One shared date_condition() now serves the parameter handler, the chain
builder (previously a raw text '=' regardless of precision), and the
_filter parser: both sides go through datetime(), which normalizes
partial dates and folds timezone offsets to UTC, and the upper bound of
a partial value is derived in SQL ('+1 day') so a single bind parameter
serves both ends — which also fixes the latent multi-value numbering
collision where eq consumed two parameter slots while the caller
advanced by one.

Postgres parses dates into real timestamps on its own path; the ES and
Mongo handlers are unverified and covered by the #448 backend legs.

Closes #456
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.31818% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...stence/src/backends/sqlite/search/filter_parser.rs 83.72% 7 Missing ⚠️
.../backends/sqlite/search/parameter_handlers/date.rs 98.23% 2 Missing ⚠️
...stence/src/backends/sqlite/search/chain_builder.rs 95.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

The codecov patch gap was real: the ne/sa/eb arms, the full-precision
single-bound comparisons, the ap windows at finer precisions, the
aliased-column pass-through, the chained-terminal delegate, and the
_filter date branch had no direct coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

search: day-precision date comparisons are off at the boundaries (eq matches nothing; ge excludes the day itself; lt/le include the next midnight)

2 participants