Восстановление данных из поврежденной базы данных SQLite
Оглавление
1. Восстановление (части) данных из поврежденной базы данных SQLite
Базы данных SQLite обладают значительной устойчивостью. Ошибки приложения и сбои питания обычно не приводят к потере данных в базе данных. Однако, повреждение базы данных SQLite возможно. Например, неисправности оборудования могут повредить файл базы данных, или вредоносный процесс может открыть базу данных и перезаписать части ее содержимого.
В случае повреждения файла базы данных иногда желательно попытаться извлечь как можно больше данных из файла. API восстановления разработан для этого.
1.1. Ограничения
Иногда возможно полностью восстановить поврежденную базу данных, но это скорее исключение. Обычно восстановленная база данных будет иметь дефекты:
Некоторые данные могут быть безвозвратно удалены и невосстановимы. Это может произойти, например, если вредоносный процесс перезапишет часть файла базы данных.
Предыдущие удаленные данные могут появиться вновь. Обычно, когда SQLite выполняет операцию DELETE, она не перезаписывает старые данные, а вместо этого запоминает, что место доступно для повторного использования при следующем INSERT. Если такие удаленные данные все еще присутствуют в файле во время попытки восстановления, они могут быть извлечены и «воскреснут».
Восстановленные данные могут быть изменены. Например, значение, хранящееся в определенной строке, может измениться с 48 на 49. Или оно может измениться с целого числа на строку или BLOB. Значение NULL может стать целым числом. Значение строки может стать BLOB. И так далее.
Ограничения могут быть недействительными после восстановления. Ограничения CHECK, ограничения FOREIGN KEY, ограничения UNIQUE, ограничения типа для строгих таблиц — любое из них может быть нарушено в восстановленной базе данных.
Данные могут быть перемещены из одной таблицы в другую.
API восстановления делает все возможное для восстановления базы данных, но результаты всегда под вопросом. Иногда (например, если повреждение ограничено индексами) восстановление идеально восстанавливает содержимое базы данных. Однако в других случаях восстановление будет неполным. Влияние этой неполноты зависит от приложения. База данных, содержащая список закладок, по-прежнему остается списком закладок и после восстановления. Некоторые закладки могут отсутствовать, быть добавлены или изменены после восстановления, но список изначально «размыт» и неточен, поэтому добавление некоторой неопределенности не приведет к критическим ошибкам в приложении. Но если учетная база данных повреждается и затем восстанавливается, баланс может быть нарушен.
Лучше всего рассматривать API восстановления как операцию по спасению. Восстановление извлечет как можно больше полезных данных из обломков старой базы данных, но некоторые части могут быть повреждены без возможности исправления, и перед возвращением восстановленной базы данных в работу необходимо выполнить некоторые доработки и тестирование.
2. Восстановление с помощью команды ".recover" в командной строке
Самый простой способ ручного восстановления поврежденной базы данных — использование интерфейса командной строки или «CLI» для SQLite. CLI — это программа под названием «sqlite3». Используйте ее для восстановления поврежденного файла базы данных, используя команду, подобную следующей:
sqlite3 corrupt.db .recover >data.sql
Это сгенерирует текст SQL в файле под названием «data.sql», который можно использовать для восстановления исходной базы данных:
sqlite3 recovered.db <data.sql
Опция ".recover" фактически является командой, которая выдается в CLI. Эта команда может принимать аргументы. Например, выполнив:
sqlite3 corruptdb ".recover --ignore-freelist" >data.sql
Обратите внимание, что команда ".recover" и ее аргументы должны быть заключены в кавычки. Поддерживаются следующие опции:
--ignore-freelist Игнорировать страницы базы данных, которые, по-видимому, являются частью свободной области. Обычно сканируется свободная область, и если она содержит страницы, которые, похоже, содержат данные, эти данные выводятся. Но если страница действительно находится в свободной области, это может означать, что ранее удаленная информация повторно вводится в базу данных.
--lost-and-found TABLE Если во время восстановления обнаруживаются данные, которые нельзя связать с определенной таблицей, они помещаются в таблицу «lost_and_found». Используйте эту опцию для изменения имени таблицы «lost_and_found» на «TABLE».
--no-rowids Если эта опция указана, значения rowid, которые не являются также значениями INTEGER PRIMARY KEY, не извлекаются из поврежденной базы данных.
3. Встраивание API восстановления в приложение
3.1. Файлы исходного кода
Если вы хотите добавить API восстановления в свое приложение, вам необходимо добавить некоторые файлы исходного кода в свой проект помимо обычных файлов «sqlite3.c» и «sqlite3.h». Вам понадобятся:
sqlite3recover.c Это основной файл исходного кода, реализующий API восстановления. sqlite3recover.h Это заголовочный файл, соответствующий sqlite3recover.h. dbdata.c Этот файл реализует две виртуальные таблицы под названием «sqlite_dbdata» и «sqlite_dbptr», необходимые для sqlite3recover.c.
Эти два файла исходного кода C необходимо связать с вашим приложением так же, как связывается «sqlite3.c». Заголовочный файл должен быть доступен компилятору при компиляции файлов C.
Кроме того, приложение, или точнее, связанный с ним sqlite3.c, необходимо скомпилировать со следующей опцией:
-DSQLITE_ENABLE_DBPAGE_VTAB
3.2. Как реализовать восстановление
Вот основные шаги, необходимые для восстановления содержимого из поврежденной базы данных:
Создайте обработчик sqlite3_recover, вызвав либо sqlite3_recover_init(), либо sqlite3_recover_init_sql(). Используйте sqlite3_recover_init() для хранения восстановленного содержимого в отдельной базе данных, а sqlite3_recover_init_sql() для генерации текста SQL, который восстановит базу данных.
Вызовите sqlite3_recover_config() ноль или более раз для установки опций для нового обработчика sqlite3_recovery.
Повторяйте вызов sqlite3_recover_step(), пока он не вернет значение, отличное от SQLITE_OK. Если он возвращает SQLITE_DONE, операция восстановления завершилась без ошибок. Если он возвращает какое-либо другое значение, отличное от SQLITE_OK, произошла ошибка. Интерфейс sqlite3_recover_run() также доступен как удобная оболочка, которая просто повторяет вызов sqlite3_recover_step(), пока он не вернет значение, отличное от SQLITE_DONE.
Получите код ошибки и сообщение об ошибке на английском языке, используя интерфейсы sqlite3_recover_errcode() и sqlite3_recover_errmsg() соответственно.
Вызовите sqlite3_recover_finish() для удаления объекта sqlite3_recover.
Подробное описание интерфейса приведено в комментариях в заголовочном файле sqlite3_recover.h.
3.3. Примеры реализации
Примеры использования расширения восстановления в самом SQLite можно найти по следующим ссылкам:
-
https://sqlite.org/src/info/30475c820dc5ab8a8?ln=999,1026
Пример расширения восстановления, найденный в утилите тестирования «fuzzcheck» в дереве SQLite.
-
https://sqlite.org/src/info/84bb08d8762920285f08f1c0?ln=7299,7361
Код, реализующий команду ".recover" в CLI.
SQLite is in the Public Domain.
https://sqlite.org/recovery.html