Restore
Put those tables back
A snapshot is taken after you approve and before the statement is forwarded, on a separate admin connection. Denied holds cost no snapshot. The statement runs only after the snapshot commits. If the snapshot fails, the statement does not run (TB004) unless you approve it again with an explicit no-snapshot acknowledgement.
$ tablebelt restore snap_0001
restoring public.users from snap_0001
ok public.users (48,218 rows)
note writes after snapshot are gone
Restore needs a TTY confirmation unless you pass --yes. --dry-run prints the plan and stops. --direct connects with the admin DSN when the proxy is down.
What comes back
Snapshotted tables return to their contents and structure at snapshot time. Writes to those tables after the snapshot are lost. Other tables are untouched. Objects that were not snapshotted are listed, not restored. Views dropped by CASCADE are in that list.
| Current state | What restore does |
|---|---|
| Table missing | Recreate from the manifest: sequences, table, rows, constraints, indexes, foreign keys, triggers, policies, RLS, ACL, owner, comments, then setval. |
| Same columns and types | In place: replace rows and re-add anything missing. |
| Columns missing, the rest match | In place, then ALTER TABLE ADD COLUMN for each missing column and load rows. Column order can differ. |
| Incompatible shape | Refused unless --replace. Drop the current table with no CASCADE, then recreate. Dependents must be listed, or pass --cascade. |
| DDL only snapshot | Re-apply RLS, policies, constraints and triggers. Rows stay. |
In-place replacement runs in one transaction. It drops inbound foreign keys, disables user triggers, truncates, copies rows back, and re-adds the foreign keys. If a child row written after the snapshot points at a parent that is gone, the constraint is left NOT VALID and the command prints the name. Sequences are set to the max of the current value and the captured value.
Before changing a table that still exists, restore takes a safety snapshot labelled as a pre-restore of that snapshot. Skip it with --no-safety-snapshot. If the table is over the size limit, --yes is required to proceed without that safety copy.
Consistency
- The snapshot sees committed data. Uncommitted changes in the agent's open transaction are not in it.
- Writes other sessions commit between the snapshot and execution (milliseconds to seconds) are not in it.
- Large objects referenced from
oidcolumns are not copied. Only the oid value is. - Generated columns store the base columns. Generated values are recomputed.
- If the agent's transaction holds
ACCESS EXCLUSIVEon the table, the snapshot hitslock_timeout(5 seconds) and the outcome issnapshot_failed. Retry outside that transaction.
DROP DATABASE
DROP DATABASE is held. v1 cannot snapshot a whole database, so will_snapshot is false and the reason is drop_database. Approval needs an explicit no-snapshot acknowledgement: tablebelt approve hold_0007 --no-snapshot, or the dashboard checkbox. The prompt says to take a host backup first. tablebelt init writes a deny-drop-database rule enabled by default, so a fresh install denies it outright.
Whole-database snapshots, partitioned tables, and foreign tables are not in this version. A plan with will_snapshot: false and reason partitioned_table or foreign_table also needs the no-snapshot acknowledgement. too_large means the hold is over snapshots.max_bytes_per_hold (default 1 GiB).
Retention
Default retention is 14 days and 5 GiB. An hourly loop, and a check before each new snapshot, deletes snapshots older than the retention, then oldest first until under the byte cap. It does not delete a snapshot younger than 24 hours unless a new snapshot would otherwise fail. Pro and Team do not change this. Snapshots never leave your database.
tablebelt snapshots prune --older-than 7d --keep 10