Row versions and archiving
A link stores two facts about its target: the row id and the target’s business-data version. This lets a record keep showing the exact supplier, product, address, or other linked data that it used, even after the shared row changes.
This matters most for global tables. One global row can be used by records in many accounting-period profiles, and those periods must not silently reinterpret historical records when shared master data is updated.
Revision and version are different
Section titled “Revision and version are different”Every managed row has two counters:
| Counter | Purpose | Changes when |
|---|---|---|
row_revision |
Detects concurrent edits | Any row update succeeds, including archiving |
version |
Identifies a business-data generation | ArchiveTableData snapshots the current generation |
Ordinary updates do not advance version. They change the current generation in place, subject to impact confirmation. Archiving freezes that generation and advances the live row to the next version.
Reading a linked row
Section titled “Reading a linked row”When a source row is saved, each link records the target’s current version in a hidden companion column. A later read resolves both that saved version and the target’s current state:
| Status | Meaning |
|---|---|
CURRENT |
The saved version is still the live target version |
SUPERSEDED |
The saved version is archived and a newer live version exists |
UNAVAILABLE |
The saved version cannot be loaded |
DELETED |
The target row is now soft-deleted |
RESTRICTED |
The caller cannot read the target table; only configured display fields are exposed |
For SUPERSEDED, the response contains the saved snapshot plus the newer live values and the fields that changed. GetTableDataVersion can also load one exact version of a row directly. A deleted row’s archived versions remain readable by version even though its live version is not returned as current data.
Check the impact before updating
Section titled “Check the impact before updating”GetTableUpdateImpact accepts a profile, table, and row id and returns the sorted set of profiles whose live rows currently reference that row’s current version.
For a profile-local target, this normally identifies its own profile. For a global target, it can identify every profile using that shared version. References from global source tables count as affecting every non-global profile because the global source is visible from all of them.
An ordinary PutTableData refuses a non-empty update when the current version is referenced. The refusal includes the affected profiles. To proceed deliberately, call PutTableDataConfirmed with the update and exactly that profile set in expected_affected_profiles.
The server recalculates the impact inside the update transaction. Confirmation succeeds only if the supplied set still matches, preventing a stale warning from authorizing an impact that changed meanwhile.
Archive before changing historical master data
Section titled “Archive before changing historical master data”Use ArchiveTableData with:
- the profile through which the table is being accessed;
- the table name and row id;
- the
expected_revisionreturned by the last read or write.
In one serializable transaction the server:
- locks the live, non-deleted row;
- verifies
expected_revision; - stores the complete row as JSON in
table_row_archives, together with its table definition, row id, version, source revision, archive time, and acting user; - reports the profiles currently using that version;
- increments the live row’s
version, which also advances itsrow_revision.
The archived (table definition, row id, version) is unique. Attempting to archive the same generation twice returns an already-exists error.
Existing links keep their stored version and therefore resolve the archived snapshot. New links read the advanced live version. After archiving, the live row can be edited without changing what older linked records display.
product row, version 1 ├── referenced by 2025 profile records └── archive ├── immutable snapshot: version 1 └── live product advances to version 2 └── new 2026 profile records reference version 2Deleting linked data
Section titled “Deleting linked data”Soft deletion is allowed only when no live source row still points at the target’s current version. If current references exist, deletion fails and asks for the row to be archived first.
After archiving, old links point at the archived generation rather than the new current version. The live row may then be deleted if nothing references that new version. Its archived generations remain available for historical link resolution.
Recommended period rollover
Section titled “Recommended period rollover”For master data shared between accounting-period profiles:
- query update impact before changing a row;
- if old period records must retain their values, archive the current version;
- update the newly advanced live version;
- let new-period records link to that version;
- use confirmed update only when changing all records that still reference the current generation is intentional.
Archiving is therefore a history boundary, not a backup mechanism. PostgreSQL backups protect the database as a whole; row archives preserve the business meaning of linked records inside the application.