80 lines
3.6 KiB
Markdown
80 lines
3.6 KiB
Markdown
# База данных — схема и миграции
|
||
|
||
SQLite-база, путь задаётся в `config.ini` (`db/dbPath`). Управляется через `DatabaseManager` (singleton) + DAO-слой.
|
||
|
||
---
|
||
|
||
## Инициализация схемы
|
||
|
||
При каждом запуске приложения вызывается `DatabaseManager::initSchema()` (`database/DatabaseManager.cpp`).
|
||
Именно здесь происходят все миграции — до открытия главного окна.
|
||
|
||
При **обновлении из меню** приложение скачивает новый `.exe`, применяет его и перезапускается, поэтому `initSchema()` тоже выполняется заново на новой версии.
|
||
|
||
---
|
||
|
||
## Что применяется автоматически при обновлении
|
||
|
||
| Изменение | Применяется? | Как |
|
||
|-----------|-------------|-----|
|
||
| Новая таблица | Да | `CREATE TABLE IF NOT EXISTS` |
|
||
| Новая колонка | Да | `ALTER TABLE ... ADD COLUMN` |
|
||
| Удаление таблицы | Да | `DROP TABLE IF EXISTS` |
|
||
| Удаление колонки | Нет | SQLite не поддерживает |
|
||
| Переименование колонки | Нет | Нужна пересборка таблицы |
|
||
| Изменение типа колонки | Нет | Нужна пересборка таблицы |
|
||
| Изменение DEFAULT у существующей колонки | Нет | `CREATE TABLE IF NOT EXISTS` не выполняется повторно |
|
||
|
||
---
|
||
|
||
## Паттерн добавления колонки
|
||
|
||
```cpp
|
||
// 1. Добавить колонку в CREATE TABLE IF NOT EXISTS (для новых установок)
|
||
q.exec(R"(CREATE TABLE IF NOT EXISTS my_table (
|
||
...
|
||
new_col TEXT DEFAULT ''
|
||
))");
|
||
|
||
// 2. Добавить ALTER TABLE для существующих БД (ошибка "column already exists" игнорируется)
|
||
q.exec("ALTER TABLE my_table ADD COLUMN new_col TEXT DEFAULT ''");
|
||
```
|
||
|
||
Оба шага обязательны: `CREATE TABLE` покрывает свежие установки, `ALTER TABLE` — обновление существующей БД.
|
||
|
||
---
|
||
|
||
## Сложные миграции (изменение типа, переименование)
|
||
|
||
Для изменений, которые SQLite не поддерживает напрямую, используется ручная миграция с пересозданием таблицы и `PRAGMA user_version` для версионирования:
|
||
|
||
```cpp
|
||
QSqlQuery q(db);
|
||
q.exec("PRAGMA user_version");
|
||
q.next();
|
||
int version = q.value(0).toInt();
|
||
|
||
if (version < 2) {
|
||
// пересоздаём таблицу
|
||
q.exec("ALTER TABLE my_table RENAME TO my_table_old");
|
||
q.exec("CREATE TABLE my_table (...)");
|
||
q.exec("INSERT INTO my_table SELECT ... FROM my_table_old");
|
||
q.exec("DROP TABLE my_table_old");
|
||
q.exec("PRAGMA user_version = 2");
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Таблицы
|
||
|
||
| Таблица | Описание |
|
||
|---------|----------|
|
||
| `devices` | Android-устройства, подключённые по ADB |
|
||
| `bank_profiles` | Банковские профили (приложение + устройство) |
|
||
| `materials` | Карты/счета профиля |
|
||
| `transactions` | Транзакции по картам |
|
||
| `events` | Задачи из очереди API |
|
||
| `banks` | Справочник банков (alias, name, nspk_name) |
|
||
| `general_logs` | Общий лог приложения |
|
||
| `settings` | Настройки key-value | |