Skip to content

persistence: conditional criteria with a modifier or chain silently match nothing, so conditional writes duplicate #865

Description

@aacruzgon

Summary

Conditional criteria that carry a modifier (identifier:exact=…, identifier:not=…) or a chain (organization.name=…) are accepted and silently match nothing. A conditional create or upsert then creates a duplicate, and a conditional delete answers 204 having deleted nothing. Discussion #28 models the sixteen modifiers as first-class search fragments and passes conditional criteria as typed SearchParameters.

Reproduction (batch_conformance harness, in-memory SQLite with spec parameters)

Seed one Patient with identifier http://example.org|12345, then batch PUT with each URL; every call answered 201 Created and the Patient count rose by one:

criteria result
Patient?identifier:exact=http://example.org|12345 201, duplicate
Patient?identifier:not=http://example.org|99999 201, duplicate
Patient?organization.name=Acme 201, duplicate

The resource endpoints take the same backend path (PUT /Patient?identifier:exact=…).

Cause

  • build_search_parameters (crates/persistence/src/backends/sqlite/storage.rs:2949-2977, backends/postgres/storage.rs:2881-2907) keeps the raw key as name and sets modifier: None, chain: vec![], components: vec![].
  • lookup_param_type misses for identifier:exact; crate::search::fallback_param_type (search/registry.rs:49-60) matches "identifier" exactly and falls through to SearchParamType::String.
  • The query builder interpolates the raw name: param_name = 'identifier:exact' (sqlite/search/query_builder.rs:459-464; postgres query_builder.rs:1054). No such index row exists, so zero rows and no error.

The ordinary search path parses name:modifier and chains before reaching the builder; the conditional path bypasses that parser.

Proposed fix

Parse conditional criteria with the same parser search uses (modifier, chain, components), or reject criteria the conditional path cannot evaluate with 400 rather than matching nothing. Tests per backend: :exact matching the seeded resource; a chain either resolving or being rejected; never a silent 201.

Related: #535 item 3 fixed the _source typing gap in the same fallbacks. Found while validating #511 (see PR #860).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions