SYSE.34:5 - Archetypal Grounding
SYSE.34:5.1 - Define a reversible address correspondence
In a constructed ParcelWorks example, one non-partitioned PostgreSQL 18 table has an immutable row ID and a non-null Unicode text value D, stored as delivery_address. Old applications read and replace that whole value. The new form edits the first display line L separately from the remaining display text T. It does not infer postal structure.
Split D at its first line-feed character, LF. If no LF exists, set L = D and T = null. Otherwise L is the text before that LF and T is all text after it, including any further line feeds. Reconstruct D as L when T is null, or L + LF + T otherwise. L must contain no LF. Preserve every character; do not normalize whitespace.
| Old D | New L | New T | Reconstructed meaning |
|---|---|---|---|
| 12 Oak St | 12 Oak St | null | No line separator was present. |
| 12 Oak St followed by LF | 12 Oak St | empty text | The trailing separator is preserved. |
| 12 Oak St, LF, North, LF, Depot | 12 Oak St | North, LF, Depot | Remaining lines stay in the tail. |
| Empty text | empty text | null | Empty but non-null old text remains representable. |
This correspondence makes old and new text mutually recoverable within the stated domain. Postal parsing, normalization or an additional field that cannot be joined back is outside it and reopens the recovery decision.
SYSE.34:5.2 - Keep one write authority during coexistence
The selected authority remains D. Old applications write D directly. The new application’s adapter validates L/T, joins them into D and writes D; it does not independently write derived columns.
A row-level BEFORE INSERT OR UPDATE trigger computes L/T from NEW.delivery_address and returns the modified row. The exact PostgreSQL 18 trigger behavior supplies same-transaction execution and returned-row semantics. This construction excludes other address-writing triggers, replicated writers that bypass this trigger and external side effects of these row updates. If those premises are not established, this mechanism is not yet qualified.
The authorized operator briefly fences new address writes and drains in-flight writers while installing the schema and trigger. Ordinary application roles must not bypass the mechanism or independently write the derived columns. The PostgreSQL 18 privilege rules require inspection of table grants, inherited rights, ownership and superuser access; revoking a column privilege does not cancel a broad table grant. Resume old writers only after the actual configuration satisfies those conditions.
SYSE.34:5.3 - Backfill current rows rather than cached values
Backfill operates in bounded Read Committed transactions over stable ID ranges. For each named ID, it performs the equivalent of:
UPDATE address
SET delivery_address = delivery_address
WHERE id = the_named_id;
Here the_named_id is a bound value for the immutable row identity, not literal executable SQL. The trigger derives from the row being updated. The procedure never writes a D/L/T tuple cached by an earlier SELECT.
Under PostgreSQL 18 Read Committed, an updater waiting on another updater proceeds against the qualifying updated row after that transaction commits. The immutable ID keeps this case’s selection stable. This is not a general guarantee for arbitrary multi-row predicates.
Advance the batch position only after commit. An aborted transaction retains neither its row changes nor an advanced position. If commit succeeded but its acknowledgement was lost, replay the range using current values. The case’s exclusion of other update side effects is necessary for that replay to remain safe.
| Constructed history | Result |
|---|---|
| Backfill derives A, then an old writer commits B. | The trigger derives B with that write; both representations describe B. |
| Old writer B holds the row while backfill waits. | Backfill acts on the committed current B and does not restore A. |
| Backfill commits A, acknowledgement is lost, old writer commits B, then the range is replayed. | Replay derives B from current D. |
| New writer submits L = PO Box 7 and T = North Depot. | The adapter writes their exact joined D; the trigger reconstructs the same pair. |
| A backfill transaction fails before commit. | Its derived-field changes are rolled back and the range remains unfinished. |
Enable L/T-dependent readers only after the scoped backfill and a correspondence check establish their required population. Continuing writes must preserve the same invariant. The table contains constructed histories, not a report of executed PostgreSQL qualification; deployment use requires exercising the actual schema, roles, concurrency and recovery arrangement.
SYSE.34:5.4 - State the return and its end
This release retains D, the trigger and both reader contracts. Subject to the application’s other compatibility conditions, the old executable can return while preserving legitimate address writes made through the new form. It is not necessary to undo those writes.
Contraction is separate. Before removing the old contract, fence and drain all old writers and compatibility adapters, select the new write authority and validate its consumers. Once a lossy transformation or incompatible new write has occurred, an old binary alone cannot restore the prior usable state.
Suppose a later contraction removes a trailing LF and retains neither the original D nor another copy of the absent-versus-empty tail distinction. Both 12 Oak St (T = null) and 12 Oak St followed by LF (T = empty text) then become 12 Oak St: whether a separator existed is lost, and returning the old executable cannot reconstruct it. An exact-return promise therefore requires retaining D or that lossless distinction before this change; accepting its removal requires the actual decision holder to authorize a narrower promise. After the information is gone, exact restoration needs an independently retained, qualified recovery source and is unavailable without one. A qualified forward repair must meet the actually accepted result; it is not an inverse obtainable from the transformed value alone.
A distinct fallback uses PostgreSQL 18 point-in-time recovery. It needs a suitable base backup and continuous required WAL archive; a logical dump is not a substitute for that mechanism. Restore the whole cluster into an appropriately isolated recovery arrangement, inspect its state and evaluate achieved loss/time. Choosing an earlier recovery point does not reconcile all later business events.
If an archive gap prevents that fallback, its recovery claim stops. A qualified forward repair may still be possible, and an independent non-data-changing artifact test can continue.
What changes in practice is that the team can explain which state remains usable after each change, why concurrent writes are preserved, and where “return to the old version” ceases to mean recovery.