main
  1import 'dart:async';
  2
  3import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
  4import 'package:blood_pressure_app/model/storage/storage.dart';
  5import 'package:path/path.dart';
  6import 'package:sqflite/sqflite.dart';
  7
  8/// Database for storing settings and internal app state.
  9///
 10/// The table names ensured by this class are stored as constant public strings ending in Table.
 11@deprecated
 12class ConfigDB {
 13  ConfigDB._create();
 14  
 15  /// Name of the settings table.
 16  ///
 17  /// It is used to store json representations of [Settings] objects. The json representation is chosen to allow editing
 18  /// and editing fields without changing the database structure and because there is no use case where accessing
 19  /// individual fields for SQLs logic and matching is needed and the complexity of maintaining different settings
 20  /// formats (export) is not worth it. Disk space doesn't play a role, as in most cases there will be only one entry in
 21  /// the table.
 22  ///
 23  /// Format:
 24  /// `CREATE TABLE settings(profile_id INTEGER PRIMARY KEY, settings_json STRING)`
 25  static const String settingsTable = 'settings';
 26
 27  /// Name of the table for storing [ExportSettings] objects.
 28  ///
 29  /// Data is saved the same way as with [settingsTable].
 30  ///
 31  /// Format:
 32  /// `CREATE TABLE exportSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
 33  static const String exportSettingsTable = 'exportSettings';
 34
 35  /// Name of the table for storing [CsvExportSettings] objects.
 36  ///
 37  /// Data is saved the same way as with [settingsTable].
 38  ///
 39  /// Format:
 40  /// `CREATE TABLE exportCsvSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
 41  static const String exportCsvSettingsTable = 'exportCsvSettings';
 42
 43  /// Name of the table for storing [PdfExportSettings] objects.
 44  ///
 45  /// Data is saved the same way as with [settingsTable].
 46  ///
 47  /// Format:
 48  /// `CREATE TABLE exportPdfSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
 49  static const String exportPdfSettingsTable = 'exportPdfSettings';
 50
 51  /// Name of the table for storing time intervals to display.
 52  ///
 53  /// It is used to store json representations of [IntervalStorage] objects. Data is saved as fields, to save space
 54  /// on the disk and because no changes in the data format are expected. The field names are made to match the variable
 55  /// names in the class.
 56  ///
 57  /// Note that this table has 2 keys: profile_key to support multiple profiles and storage_id to support saving
 58  /// multiple entries per profile (e.g. export screen and main page).
 59  ///
 60  /// Format: `CREATE TABLE selectedIntervallStorage(profile_id INTEGER, storage_id INTEGER, stepSize INTEGER,`
 61  /// ` start INTEGER, end INTEGER, PRIMARY KEY(profile_id, storage_id))`
 62  static const String selectedIntervalStorageTable = 'selectedIntervallStorage';
 63
 64  /// Name of the exportStrings table. It is used to to update old columns.
 65  ///
 66  /// Format:
 67  /// `CREATE TABLE exportStrings(internalColumnName STRING PRIMARY KEY, columnTitle STRING, formatPattern STRING)`
 68  @Deprecated('removed after all usages are replaced by export_columns_store')
 69  static const String exportStringsTable = 'exportStrings';
 70
 71  /// Name of table of storing [ExportColumnsManager] objects
 72  ///
 73  /// Format:
 74  /// `CREATE TABLE exportColumns(profile_id INTEGER PRIMARY KEY, json STRING)`
 75  static const String exportColumnsTable = 'exportColumns';
 76  static const String _exportColumnsTableCreationString =
 77      'CREATE TABLE exportColumns(profile_id INTEGER PRIMARY KEY, json STRING)';
 78
 79  late final Database _database;
 80
 81  /// [dbPath] is the path to the folder the database is in. When [dbPath] is left empty the default database file is
 82  /// used. The [isFullPath] option tells the constructor not to add the default filename at the end of [dbPath].
 83  Future<bool> _asyncInit(String? dbPath, bool isFullPath) async {
 84    dbPath ??= await getDatabasesPath();
 85    if (dbPath != inMemoryDatabasePath && !isFullPath) {
 86      dbPath = join(dbPath, 'config.db');
 87    }
 88    bool dbTooOld = false;
 89    _database = await openDatabase(
 90      dbPath,
 91      onCreate: (_, __) => dbTooOld = true,
 92      onUpgrade: (Database db, int oldVersion, int newVersion) async {
 93        assert(newVersion == 3);
 94        if (oldVersion == 1) {
 95          dbTooOld = true;
 96        } else if (oldVersion == 2) {
 97          await db.execute(_exportColumnsTableCreationString);
 98        } else {
 99          assert(false, 'Unexpected version upgrade from $oldVersion to $newVersion.');
100        }
101      },
102      // When increasing the version an update procedure from every other possible version is needed
103      version: 3,
104      // In integration tests the file may be deleted which causes deadlocks.
105      singleInstance: false,
106    );
107    return dbTooOld;// TODO: test
108  }
109
110  /// Factory method that either opens an existing db file or creates a new one if no file is present.
111  ///
112  /// [dbPath] is the path to the folder the database is in. When [dbPath] is left empty the default database file is
113  /// used. The [isFullPath] option tells the constructor not to add the default filename at the end of [dbPath].
114  static Future<ConfigDB?> open({String? dbPath, bool isFullPath = false}) async {
115    final instance = ConfigDB._create();
116    if(await instance._asyncInit(dbPath, isFullPath)) {
117      return instance;
118    }
119    return null;
120  }
121
122  /// The database created by this class for getting and setting entries.
123  ///
124  /// Changes to the database structure should be done by altering the [_onDBCreate] and [_onDBUpgrade] methods of this
125  /// class to ensure smooth upgrades without the possibility of error.
126  Database get database => _database;
127}