Skip to content

Relax value-presence constraint per model (Observation/Measurement/Metadata) - #33

Merged
nicoloesch merged 2 commits into
mainfrom
25-value-mixin
Aug 3, 2026
Merged

Relax value-presence constraint per model (Observation/Measurement/Metadata)#33
nicoloesch merged 2 commits into
mainfrom
25-value-mixin

Conversation

@nicoloesch

@nicoloesch nicoloesch commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Breaking Change

The old shared ValueMixin.__table_args__ had a subtle bug:

  • It reused a single CheckConstraint object across Measurement, Observation, and Metadata
  • Reusing the same object does not mean only one table got the constraint at the DB level
  • SQLAlchemy adds the shared object to each table's own constraint collection independently
  • Result: all three tables physically received the check constraint, not just one
  • All three share the same name, frozen from the object's first binding during import: ck_measurement_ck_value_present

Verified directly, not assumed: built all three models against a fresh database, then inserted a value-less row into each of measurement, observation, and metadata. Each raised IntegrityError: CHECK constraint failed: ck_measurement_ck_value_present.

Check your own database before dropping anything, since names/behavior may vary depending on load history.

PostgreSQL

-- Find every table carrying the constraint — expect up to three rows:
-- measurement, observation, metadata.
SELECT conname, conrelid::regclass FROM pg_constraint WHERE conname LIKE '%value_present%';

-- Drop from each table returned above, e.g.:
ALTER TABLE measurement DROP CONSTRAINT ck_measurement_ck_value_present;
ALTER TABLE observation DROP CONSTRAINT ck_measurement_ck_value_present;
ALTER TABLE metadata DROP CONSTRAINT ck_measurement_ck_value_present;

SQLite (probably not super relevant)

-- SQLite equivalent of above (no pg_constraint catalog):
SELECT name, sql FROM sqlite_master WHERE type='table' AND sql LIKE '%value_present%';

-- SQLite has no DROP CONSTRAINT. For each table returned above, the
-- standard workaround is a table rebuild:
--   1. CREATE TABLE <table>_new (... same columns, no CHECK ...);
--   2. INSERT INTO <table>_new SELECT * FROM <table>;
--   3. DROP TABLE <table>;
--   4. ALTER TABLE <table>_new RENAME TO <table>;
--   5. Recreate <table>'s indexes, as they are dropped with the old table.

@nicoloesch
nicoloesch requested a review from gkennos July 10, 2026 05:46
@nicoloesch nicoloesch added the breaking Incompatible API change. MAJOR: x+1.y.z label Aug 3, 2026
@nicoloesch
nicoloesch merged commit 97ad7e2 into main Aug 3, 2026
5 of 6 checks passed
@nicoloesch
nicoloesch deleted the 25-value-mixin branch August 3, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Incompatible API change. MAJOR: x+1.y.z

Projects

None yet

Development

Successfully merging this pull request may close these issues.

omop-alchemy currently enforces a shared ValueMixin rule requiring at least one of value_as_number or value_as_concept_id to be set

2 participants