QoreDB LogoQoreDB
Back to blog
ProduitTechnical journal

Result Snapshots in QoreDB: Freezing a State to Compare It Later

A query result is, by nature, ephemeral. You run a SELECT on a production table, you look at the grid, you close the tab, and the moment after, the rows have already changed. This volatility becomes a problem as soon as you want to reason over time: verifying that a migration…

Raphaël – Creator of QoreDBRaphaël – Creator of QoreDB
5 min readUpdated Jul 18, 2026
Result Snapshots in QoreDB: Freezing a State to Compare It Later

A query result is, by nature, ephemeral. You run a SELECT on a production table, you look at the grid, you close the tab, and the moment after, the rows have already changed. This volatility becomes a problem as soon as you want to reason over time: verifying that a migration produced the expected effect, comparing a state before and after a batch, or simply keeping a record of some odd behavior you observed yesterday.

QoreDB snapshots address this precise need: freezing a result at a given instant, storing it locally with its execution context, and being able to come back to it later. The mechanism is deliberately simple, with no server or external database, and designed for single-user desktop use.

What a snapshot captures exactly

A snapshot in QoreDB is not just a dump of rows. The Snapshot struct stores two things: the raw rows, and a SnapshotMeta object that fully describes the provenance. It contains a UUID v4 identifier, the name given by the user, an optional description, the source SQL query or table name, the source type (query or table), the connection name, the driver used, the targeted namespace, the complete list of columns with their types, the row count, and an ISO 8601 creation timestamp.

This wealth of metadata isn't free. Without it, a snapshot becomes an orphan file about which nothing is known anymore: which database, which query, when. By storing them explicitly, we guarantee that a snapshot remains self-describing even if the source connection disappears in the meantime.

One JSON file per snapshot, nothing else

The SnapshotStore is a filesystem-based store. Each snapshot becomes a single JSON file in the application's data directory, named after its UUID. No intermediate SQLite, no secondary index, no binary format. The list is obtained by reading each file in the directory, parsing its content, and sorting by descending creation date.

This choice is consistent with QoreDB's local-first philosophy. Snapshots live alongside the vault, the logs, and the interception cache, under the same root directory based on the bundle identifier com.qoredb.app. A user can back up their snapshots directory with a simple cp, transfer it to another machine, or delete it entirely without touching the rest of the configuration. JSON serialization keeps the content readable and grep-able, which simplifies debugging.

For desktop use, where the number of snapshots stays in the range of tens or hundreds, the cost of reading the files sequentially is negligible against the benefit in simplicity. A separate metadata database would add a possible point of desynchronization with no real gain.

Strict validation of the identifier

Every store operation (get, delete, rename, update_description) goes through a file_path method that starts by validating that the provided identifier really is a UUID. The validation uses uuid::Uuid::parse_str and rejects any identifier that doesn't match the format. A second check canonicalizes the resolved path and confirms that it stays inside the data directory.

This double check protects against path traversal attempts like ../../../etc/passwd. Since the identifier potentially comes from a Tauri call issued by the frontend, defense in depth is necessary even in a desktop app: a UI bug or a malicious plugin cannot reach files outside the intended directory. Three unit tests cover these cases in the module.

Comparing a snapshot with a current result

Capturing a state is only valuable if you can replay it. On the frontend side, the SnapshotManager lists all snapshots with their name, size, relative date, and source. Clicking on a snapshot reopens the DataGrid with exactly the captured columns and rows, without touching the database.

The integration with the Visual Data Diff gives the mechanism its real usefulness. A snapshot becomes a comparable source on the same footing as a current query or another snapshot. So you can freeze a state of production before a deployment, run the migration, then open the Visual Data Diff with the snapshot on one side and the fresh table on the other. Common columns are aligned; added, deleted, or modified rows are highlighted.

The diff doesn't depend on any database-side mechanism or on a transaction log. It compares two arbitrary QueryResult objects. This homogeneity also makes it possible to compare a PostgreSQL snapshot with a MongoDB query transformed by federation, as long as the aligned columns make sense.

The typical usage flow

In practice, creating a snapshot is done from the DataGrid via the SaveSnapshotDialog. The user gives a name (for example users-prod-before-migration-v2), an optional description, and confirms. The frontend calls the Tauri command save_snapshot with the complete result, the source, the type, and the connection metadata. The backend generates a UUID, serializes the object to JSON, writes the file, and returns the metadata enriched with its size.

Browsing happens through the Snapshots panel, accessible from the sidebar. Each entry displays a preview, an icon based on the source (query or table), the originating connection, and three actions: preview, compare in the Data Diff, delete. Renaming and updating the description are exposed from the context menu and applied atomically to the JSON file.

For sharing, a snapshot can be turned into a local share link or exported via the ShareExportDialog. The self-contained HTML export is particularly well suited: you get a single file with embedded data, built-in sorting and filtering, no external dependency, which you can send by email or archive in a ticket.

A deliberately narrow mechanism

The QoreDB snapshot doesn't try to be a database versioning system. It doesn't capture the schema, doesn't replicate constraints, doesn't maintain an automatic history. It saves exactly what was seen on screen, at the moment the user decided to. This narrowness is intentional: a predictable, self-contained mechanism that cleanly solves the case of the one-off reference point.

Combined with the Visual Data Diff, Virtual Relations, and Sandbox Mode, the snapshot forms one piece of a larger whole: giving the user the means to reason about their data over time, without depending on server-side features or external tools. When you work on a production system where the smallest change matters, being able to say before this, after that is a convenience that quickly becomes indispensable.

Newsletter

Stay updated on new releases

Subscribe to get product releases, new drivers notifications, and technical tutorials.

🎁 Bonus: Get our free SQL Performance Cheat Sheet — 9 pages, PostgreSQL / MySQL / SQLite (PDF)!
Share
Result Snapshots in QoreDB: Freezing a State to Compare It Later - Blog - QoreDB