---
layout: article
title: Data Restore
description: How to restore a previous version of a Data Controller table
og_image:  https://docs.datacontroller.io/img/restore.png
---

# Data Restore

For tables that have [Audit Tracking](/dcc-tables/#audit_libds) enabled, it is possible to restore the data to an earlier state.

## How to restore data

Open the submit to be reverted (via the **History** tab, or the **Info / Versions** screen on the table viewer), and click the red **REVERT** button.

![](/img/restore.png)

This will generate a **new submission** containing all the reversal entries needed to bring the table back to its previous state.  This new submission **must then be approved** in the usual fashion - it goes through the same edit-stage-approve workflow as any other change.

This approach means that the audit history remains intact.  There is simply a new entry in the audit trail which reverts all the previous entries.

## How it works

Behind the scenes, rollback is not a silent undo.  It is a **first-class approval workflow** just like any other edit.  When you choose to restore a previous version, the backend reads the audit table and computes every difference between the current state and the version you want to go back to.

The process is driven by the open-source [`%mp_stripdiffs`](https://core.sasjs.io/mp__stripdiffs_8sas.html) macro.  It extracts all changes recorded in the audit table (the default is [`MPE_AUDIT`](/tables/mpe_audit/), or a custom table configured in `AUDIT_LIBDS`) from the selected version onwards, and left-joins them to the base table to build a staging dataset.  Changes are then applied in **reverse chronological order**:

- **Deleted rows** are re-inserted with their original values.
- **Modified rows** are reverted to their previous values.
- **Added rows** are marked for deletion with the `_____DELETE__THIS__RECORD_____` flag.

The computed differences are written to a new staging package in the approvals directory, complete with a CSV and a `macvars.sas` snapshot of the session context.  A new `LOAD_REF` is generated, and the package is submitted via the standard [`mpe_loader`](https://git.datacontroller.io/dc/dc/src/branch/main/sas/sasjs/macros/mpe_loader.sas) service.  This means the rollback itself is **reviewable and approvable** - nothing is applied silently.

Because the rollback creates a new approved changeset rather than silently rewinding history, the full audit trail is maintained: the reversion appears as a new load reference in `MPE_SUBMIT`, `MPE_REVIEW`, `MPE_DATALOADS`, and `MPE_AUDIT`, just like any other submission.

## Security and access

Not everyone can restore everything.  The [`mpe_checkrestore`](https://git.datacontroller.io/dc/dc/src/branch/main/sas/sasjs/macros/mpe_checkrestore.sas) macro enforces a strict access check before the restore service will run.

### Who can restore

- **Admins** - members of the admin group (configured in `MPE_CONFIG`) can restore any table that has audit tracking enabled.
- **Editors** - non-admin users must have `EDIT` access to the target table, and must **not** be subject to Column Level Security or Row Level Security restrictions.

### What is checked

1. **The load must exist in the audit table.**  Loads that were never applied, or tables without an audit table configured, cannot be restored.
2. **The user must have `EDIT` access.**  This is checked via `MPE_SECURITY`.
3. **CLS and RLS restrictions block non-admin users.**  If the user belongs to a group with active Column Level Security (`MPE_COLUMN_LEVEL_SECURITY`) or Row Level Security (`MPE_ROW_LEVEL_SECURITY`) rules scoped to `EDIT` or `ALL`, restore is denied.  Admins bypass this check.

!!! note
    The presence of CLS or RLS rules on a table does **not** prevent the table itself from being restorable.  It only prevents non-admin users who are subject to those restrictions from initiating the restore.  After a successful restore, the same CLS and RLS filters continue to apply when users view or edit the data.

If access is denied, the service aborts immediately with a clear reason - no opaque errors.

## Limitations

!!! warning
    Be aware of the following caveats before attempting a restore:

- **Audit table required.**  The table must have `AUDIT_LIBDS` configured in `MPE_TABLES` (it defaults to `MPE_AUDIT`).  Without this, there is no change history to roll back from.
- **Only Data Controller changes can be reversed.**  Changes made directly to the target table outside of Data Controller are not tracked in the audit table and cannot be reverted.
- **Supported load types.**  Restore is supported for `UPDATE` and `TXTEMPORAL` load types.  `REPLACE` loads do not maintain row-level audit history and cannot be restored.  `BITEMPORAL` loads maintain audit history but restore behaviour may be constrained by validity windows.
- **Model consistency.**  If the table structure has changed since the version being restored (for example, columns were added or removed), the restore will abort because the audit diffs no longer match the current schema.
- **SCD2 validity windows.**  For `TXTEMPORAL` tables, the restore works on the current snapshot only.  Historical validity windows are respected by the underlying [`%mp_stripdiffs`](https://core.sasjs.io/mp__stripdiffs_8sas.html) macro, but ensure your approvers understand that the reversion applies to the current state of the data.
- **No partial restore.**  Rolling back a version reverts **all** changes from that version onwards, not just selected records.  There is no way to cherry-pick individual rows from a previous version.

## Technical background

The restore process is implemented in the [`restore.sas`](https://git.datacontroller.io/dc/dc/src/branch/main/sas/sasjs/services/editors/restore.sas) service.  It delegates access control to [`mpe_checkrestore.sas`](https://git.datacontroller.io/dc/dc/src/branch/main/sas/sasjs/macros/mpe_checkrestore.sas), diff computation to [`%mp_stripdiffs`](https://core.sasjs.io/mp__stripdiffs_8sas.html), and approval workflow submission to [`%mpe_loader`](https://git.datacontroller.io/dc/dc/src/branch/main/sas/sasjs/macros/mpe_loader.sas).

Audit records are written by the open-source [`%mp_storediffs`](https://core.sasjs.io/mp__storediffs_8sas.html) macro during the normal approval workflow, which compares the pre-load snapshot with the applied changes and appends row-level entries to the audit table.  This is why restore is only possible for tables that were loaded through Data Controller's tracked loaders.

For a lighter overview of this feature, see the [feed post on datacontroller.io](https://datacontroller.io/rollback-data-changes/).
