// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using osu.Framework.Logging; using osu.Framework.Platform; using SQLite.Net; using SQLiteNetExtensions.Extensions; namespace osu.Game.Database { public abstract class DatabaseBackedStore { protected readonly Storage Storage; protected readonly SQLiteConnection Connection; protected virtual int StoreVersion => 1; protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null) { Storage = storage; Connection = connection; try { Prepare(); } catch (Exception e) { Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); Prepare(true); } checkMigrations(); } private void checkMigrations() { var storeName = GetType().Name; var reportedVersion = Connection.Table().FirstOrDefault(s => s.StoreName == storeName) ?? new StoreVersion { StoreName = storeName, Version = 0 }; if (reportedVersion.Version != StoreVersion) PerformMigration(reportedVersion.Version, reportedVersion.Version = StoreVersion); Connection.InsertOrReplace(reportedVersion); } protected virtual void PerformMigration(int currentVersion, int newVersion) { } /// /// Prepare this database for use. /// protected abstract void Prepare(bool reset = false); /// /// Reset this database to a default state. Undo all changes to database and storage backings. /// public void Reset() => Prepare(true); public TableQuery Query(Expression> filter = null) where T : class { checkType(typeof(T)); var query = Connection.Table(); if (filter != null) query = query.Where(filter); return query; } /// /// Query and populate results. /// /// An optional filter to refine results. /// public List QueryAndPopulate(Expression> filter = null) where T : class { checkType(typeof(T)); return Connection.GetAllWithChildren(filter, true); } /// /// Populate a database-backed item. /// /// /// Whether population should recurse beyond a single level. public void Populate(T item, bool recursive = true) { checkType(item.GetType()); Connection.GetChildren(item, recursive); } private void checkType(Type type) { if (!ValidTypes.Contains(type)) throw new InvalidOperationException($"The requested operation specified a type of {type}, which is invalid for this {nameof(DatabaseBackedStore)}."); } protected abstract Type[] ValidTypes { get; } } }