Task: Fix optional<db_timestamp> → db_timestamp for all NOT NULL entity fields
Table of Contents
This page documents a task in the Unify entity timestamp handling story.
Goal
Change every optional<db_timestamp> that maps to a NOT NULL DB column to plain
db_timestamp. The optional wrapper let sqlgen silently skip populating the field
on a parse failure, which could produce a default-initialised db_timestamp (near-epoch)
that then fed through timestamp_to_timepoint to produce the "56 years ago" bug.
Nullable columns (completed_at, last_event_at, etc.) must stay as
optional<db_timestamp>.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Unify entity timestamp handling |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing — task closed. |
| Last touched | 2026-06-04 |
Acceptance
- Every
optional<db_timestamp> X = "..."(field with a default) isdb_timestamp X = "...". - Every
optional<db_timestamp> X;(no default, truly nullable) remainsoptional<db_timestamp> X;. - Mapper read sites that previously dereferenced
*v.Xfor the now-non-optional fields change tov.X(no dereference). - Null-check guards (
if (!v.X) throwpreceding the*v.X) are removed for non-optional fields. - Build green.
Plan
Identification rule
A field should become db_timestamp (not optional) if and only if it has a default
value in the struct declaration:
// NOT NULL → becomes db_timestamp: std::optional<db_timestamp> valid_from = "9999-12-31 23:59:59"; // Nullable → stays optional<db_timestamp>: std::optional<db_timestamp> completed_at; // no default
Script approach
Use a Python script on all *_entity.hpp files under projects/:
- Match
std::optional<db_timestamp> (\w+) = "...";→ replace withdb_timestamp \1 = "..."; - All other
optional<db_timestamp>(without default) → leave unchanged
Mapper read-side fixup
For each field that became non-optional, find the corresponding mapper and:
Remove any null-check guard:
// Remove this: if (!v.valid_from) throw std::logic_error("...");
Remove the dereference:
// Before: r.recorded_at = timestamp_to_timepoint(*v.valid_from); // After: r.recorded_at = timestamp_to_timepoint(v.valid_from);
Verification
After the change, grep for remaining optional<db_timestamp>. Every hit should be
a genuinely nullable field (no default in the struct). Any hit with a default is a
missed case.
grep -rn "optional<db_timestamp>" projects/ --include="*_entity.hpp" | grep -v build
PRs
| PR | Title |
|---|---|
| #1036 | [multi,compass] Timestamp unification mop-up; compass test logging toggle |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | valid_from should default to early epoch, not 9999 | badge_mapping_entity.hpp | Declined | Matches currency/badge_definition/host pattern; DB trigger owns the fields on insert. |
| 2 | Now field should be 'Nothing.' on DONE tasks | task_fix-mapper-helpers.org | Fixed in c54d298b1 |
Result
Bulk conversion (~100 entity files) landed in PR #1033 on branch
feature/unify-entity-timestamps. The mop-up on
feature/unify-entity-timestamps-mopup fixed the 7 remaining
optional<db_timestamp> fields whose DDL columns are NOT NULL:
badge_mapping_entity.hpp—valid_from=/=valid_to(with the standard9999-12-31 23:59:59defaults, currency pattern).database_info_entity.hpp,workflow_step_entity.hpp,workflow_instance_entity.hpp,workflow_batch_link_entity.hpp,report_input_bundle_entity.hpp—created_at(service_instance pattern, no struct default).
Read-side mappers dropped their null-guard + dereference
(database_info_mapper, workflow_step_mapper,
workflow_instance_mapper). Two write sites that relied on
nullopt → DB current_timestamp default now set the timestamp
explicitly via datetime::to_db_string(now())
(report_execution_handler, report_submit_handler).
The 11 remaining optional<db_timestamp> fields were verified
nullable in DDL — correctly optional. Full build green.