2022-07-05 22:20:24 +02:00
|
|
|
import 'package:drift/drift.dart';
|
|
|
|
import 'package:nc_photos/mobile/platform.dart'
|
|
|
|
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
|
|
|
|
|
|
|
part 'sqlite_table.g.dart';
|
|
|
|
|
|
|
|
class Servers extends Table {
|
|
|
|
IntColumn get rowId => integer().autoIncrement()();
|
|
|
|
TextColumn get address => text().unique()();
|
|
|
|
}
|
|
|
|
|
|
|
|
class Accounts extends Table {
|
|
|
|
IntColumn get rowId => integer().autoIncrement()();
|
|
|
|
IntColumn get server =>
|
|
|
|
integer().references(Servers, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
TextColumn get userId => text()();
|
|
|
|
|
|
|
|
@override
|
|
|
|
get uniqueKeys => [
|
|
|
|
{server, userId},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A file located on a server
|
|
|
|
class Files extends Table {
|
|
|
|
IntColumn get rowId => integer().autoIncrement()();
|
|
|
|
IntColumn get server =>
|
|
|
|
integer().references(Servers, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
IntColumn get fileId => integer()();
|
|
|
|
IntColumn get contentLength => integer().nullable()();
|
|
|
|
TextColumn get contentType => text().nullable()();
|
|
|
|
TextColumn get etag => text().nullable()();
|
|
|
|
DateTimeColumn get lastModified =>
|
|
|
|
dateTime().map(const _DateTimeConverter()).nullable()();
|
|
|
|
BoolColumn get isCollection => boolean().nullable()();
|
|
|
|
IntColumn get usedBytes => integer().nullable()();
|
|
|
|
BoolColumn get hasPreview => boolean().nullable()();
|
|
|
|
TextColumn get ownerId => text().nullable()();
|
2022-07-11 21:45:20 +02:00
|
|
|
TextColumn get ownerDisplayName => text().nullable()();
|
2022-07-05 22:20:24 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
get uniqueKeys => [
|
|
|
|
{server, fileId},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Account specific properties associated with a file
|
|
|
|
///
|
|
|
|
/// A file on a Nextcloud server can have more than 1 path when it's shared
|
|
|
|
class AccountFiles extends Table {
|
|
|
|
IntColumn get rowId => integer().autoIncrement()();
|
|
|
|
IntColumn get account =>
|
|
|
|
integer().references(Accounts, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
IntColumn get file =>
|
|
|
|
integer().references(Files, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
TextColumn get relativePath => text()();
|
|
|
|
BoolColumn get isFavorite => boolean().nullable()();
|
|
|
|
BoolColumn get isArchived => boolean().nullable()();
|
|
|
|
DateTimeColumn get overrideDateTime =>
|
|
|
|
dateTime().map(const _DateTimeConverter()).nullable()();
|
2022-07-13 22:23:41 +02:00
|
|
|
DateTimeColumn get bestDateTime =>
|
|
|
|
dateTime().map(const _DateTimeConverter())();
|
2022-07-05 22:20:24 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
get uniqueKeys => [
|
|
|
|
{account, file},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An image file
|
|
|
|
class Images extends Table {
|
|
|
|
// image data technically is identical between accounts, but the way it's
|
|
|
|
// stored in the server is account specific so we follow the server here
|
|
|
|
IntColumn get accountFile =>
|
|
|
|
integer().references(AccountFiles, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
DateTimeColumn get lastUpdated =>
|
|
|
|
dateTime().map(const _DateTimeConverter())();
|
|
|
|
TextColumn get fileEtag => text().nullable()();
|
|
|
|
IntColumn get width => integer().nullable()();
|
|
|
|
IntColumn get height => integer().nullable()();
|
|
|
|
TextColumn get exifRaw => text().nullable()();
|
|
|
|
|
|
|
|
// exif columns
|
|
|
|
DateTimeColumn get dateTimeOriginal =>
|
|
|
|
dateTime().map(const _DateTimeConverter()).nullable()();
|
|
|
|
|
|
|
|
@override
|
|
|
|
get primaryKey => {accountFile};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A file inside trashbin
|
|
|
|
@DataClassName("Trash")
|
|
|
|
class Trashes extends Table {
|
|
|
|
IntColumn get file =>
|
|
|
|
integer().references(Files, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
TextColumn get filename => text()();
|
|
|
|
TextColumn get originalLocation => text()();
|
|
|
|
DateTimeColumn get deletionTime =>
|
|
|
|
dateTime().map(const _DateTimeConverter())();
|
|
|
|
|
|
|
|
@override
|
|
|
|
get primaryKey => {file};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A file located under another dir (dir is also a file)
|
|
|
|
class DirFiles extends Table {
|
|
|
|
IntColumn get dir =>
|
|
|
|
integer().references(Files, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
IntColumn get child =>
|
|
|
|
integer().references(Files, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
|
|
|
|
@override
|
|
|
|
get primaryKey => {dir, child};
|
|
|
|
}
|
|
|
|
|
|
|
|
class Albums extends Table {
|
|
|
|
IntColumn get rowId => integer().autoIncrement()();
|
|
|
|
IntColumn get file => integer()
|
|
|
|
.references(Files, #rowId, onDelete: KeyAction.cascade)
|
|
|
|
.unique()();
|
|
|
|
IntColumn get version => integer()();
|
|
|
|
DateTimeColumn get lastUpdated =>
|
|
|
|
dateTime().map(const _DateTimeConverter())();
|
|
|
|
TextColumn get name => text()();
|
|
|
|
|
|
|
|
// provider
|
|
|
|
TextColumn get providerType => text()();
|
|
|
|
TextColumn get providerContent => text()();
|
|
|
|
|
|
|
|
// cover provider
|
|
|
|
TextColumn get coverProviderType => text()();
|
|
|
|
TextColumn get coverProviderContent => text()();
|
|
|
|
|
|
|
|
// sort provider
|
|
|
|
TextColumn get sortProviderType => text()();
|
|
|
|
TextColumn get sortProviderContent => text()();
|
|
|
|
}
|
|
|
|
|
|
|
|
class AlbumShares extends Table {
|
|
|
|
IntColumn get album =>
|
|
|
|
integer().references(Albums, #rowId, onDelete: KeyAction.cascade)();
|
|
|
|
TextColumn get userId => text()();
|
|
|
|
TextColumn get displayName => text().nullable()();
|
|
|
|
DateTimeColumn get sharedAt => dateTime().map(const _DateTimeConverter())();
|
|
|
|
|
|
|
|
@override
|
|
|
|
get primaryKey => {album, userId};
|
|
|
|
}
|
|
|
|
|
2022-07-21 12:28:44 +02:00
|
|
|
// remember to also update the truncate method after adding a new table
|
2022-07-05 22:20:24 +02:00
|
|
|
@DriftDatabase(
|
|
|
|
tables: [
|
|
|
|
Servers,
|
|
|
|
Accounts,
|
|
|
|
Files,
|
|
|
|
Images,
|
|
|
|
Trashes,
|
|
|
|
AccountFiles,
|
|
|
|
DirFiles,
|
|
|
|
Albums,
|
|
|
|
AlbumShares,
|
|
|
|
],
|
|
|
|
)
|
|
|
|
class SqliteDb extends _$SqliteDb {
|
|
|
|
SqliteDb({
|
|
|
|
QueryExecutor? executor,
|
|
|
|
}) : super(executor ?? platform.openSqliteConnection());
|
|
|
|
|
|
|
|
SqliteDb.connect(DatabaseConnection connection) : super.connect(connection);
|
|
|
|
|
|
|
|
@override
|
|
|
|
get schemaVersion => 1;
|
|
|
|
|
|
|
|
@override
|
|
|
|
get migration => MigrationStrategy(
|
|
|
|
onCreate: (m) async {
|
2022-07-26 07:25:02 +02:00
|
|
|
await customStatement("PRAGMA journal_mode=WAL;");
|
2022-07-05 22:20:24 +02:00
|
|
|
await m.createAll();
|
|
|
|
|
|
|
|
await m.createIndex(Index("files_server_index",
|
|
|
|
"CREATE INDEX files_server_index ON files(server);"));
|
|
|
|
await m.createIndex(Index("files_file_id_index",
|
|
|
|
"CREATE INDEX files_file_id_index ON files(file_id);"));
|
|
|
|
await m.createIndex(Index("files_content_type_index",
|
|
|
|
"CREATE INDEX files_content_type_index ON files(content_type);"));
|
|
|
|
|
|
|
|
await m.createIndex(Index("account_files_file_index",
|
|
|
|
"CREATE INDEX account_files_file_index ON account_files(file);"));
|
|
|
|
await m.createIndex(Index("account_files_relative_path_index",
|
|
|
|
"CREATE INDEX account_files_relative_path_index ON account_files(relative_path);"));
|
2022-07-13 22:23:41 +02:00
|
|
|
await m.createIndex(Index("account_files_best_date_time_index",
|
|
|
|
"CREATE INDEX account_files_best_date_time_index ON account_files(best_date_time);"));
|
2022-07-05 22:20:24 +02:00
|
|
|
|
|
|
|
await m.createIndex(Index("dir_files_dir_index",
|
|
|
|
"CREATE INDEX dir_files_dir_index ON dir_files(dir);"));
|
|
|
|
await m.createIndex(Index("dir_files_child_index",
|
|
|
|
"CREATE INDEX dir_files_child_index ON dir_files(child);"));
|
|
|
|
|
|
|
|
await m.createIndex(Index("album_shares_album_index",
|
|
|
|
"CREATE INDEX album_shares_album_index ON album_shares(album);"));
|
|
|
|
},
|
|
|
|
beforeOpen: (details) async {
|
2022-07-26 07:25:02 +02:00
|
|
|
await customStatement("PRAGMA foreign_keys = ON;");
|
2022-07-26 08:11:50 +02:00
|
|
|
// technically we have a platform side lock to ensure only one
|
|
|
|
// transaction is running in any isolates, but for some reason we are
|
|
|
|
// still seeing database is locked error in crashlytics, let see if
|
|
|
|
// this helps
|
|
|
|
await customStatement("PRAGMA busy_timeout = 5000;");
|
2022-07-05 22:20:24 +02:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class _DateTimeConverter extends TypeConverter<DateTime, DateTime> {
|
|
|
|
const _DateTimeConverter();
|
|
|
|
|
|
|
|
@override
|
|
|
|
DateTime? mapToDart(DateTime? fromDb) => fromDb?.toUtc();
|
|
|
|
|
|
|
|
@override
|
|
|
|
DateTime? mapToSql(DateTime? value) => value?.toUtc();
|
|
|
|
}
|