“We deleted it” is not the same as “it is not in the file”.
“We deleted the customer record” and “the customer record is not in the file” are
different claims. On this machine, a table of 600 rows deleted with secure_delete=OFF
leaves 761 copies of its marker strings in the file; with the default
secure_delete=ON it leaves none. The same statement produced two different files
depending on a compile-time flag nobody in the room remembers setting.
Where the bytes stay
| Mode | Markers left in the file | Where |
|---|---|---|
secure_delete=OFF | 761 | live pages (deleted cells still there) + all free pages |
secure_delete=FAST | 600 | live pages wiped, whole pages that became free are untouched |
secure_delete=ON | 0 | — |
Under WAL the picture is worse than that table suggests: with FAST, a commit that
deleted half the rows left 50 marker occurrences in the -wal, and deleting
everything still left about 45% of them. Only ON reached zero. So “we run
secure_delete” is not an answer without the mode and the journal setting.
The check people actually run
$ grep -c customer299@example.com deleted_off.db the deleted customer is still on disk
A test you can run in ten seconds, on a file you are about to hand to someone else: a copy of a
production database, a support bundle, a device image, a dataset for a partner. If the strings you
deleted are still findable, the file is a disclosure — and the fact that
SELECT count(*) returns 0 is not a defence.
What a clean file looks like
backup_clean.db (52.0 KB) page_size=4096 journal_mode=WAL header_pages=13 file_pages=13 tail=0 pages freelist=1 change_counter=2 valid_for=2 · FREELIST_ZEROED: 1 free page(s) hold no readable text: the writer ran with secure_delete=ON => OK
Limits of this check, stated
- It counts printable strings in free pages and past the declared end of the file. It does not reconstruct rows, so it cannot say “these are the deleted rows” — only “deleted content is recoverable here”.
- Absence of readable strings is evidence, not proof: a value can be compressed or split across pages and still be there.
- The verdict is per file. Two servers running the “same” build with different flags produce different files, which is exactly why this is worth measuring instead of assuming.
Check one .db file (max 20 MB; add the -wal if you have it)
Read-only byte inspection: page counts, WAL commit, freelist residue. The file is deleted before the answer is written, and nothing about its content is logged.