1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-22 20:12:56 +08:00

Move SQLite connections out of database classes; make abstract Database.

This commit is contained in:
Dean Herbert 2017-04-17 14:37:52 +09:00
parent ce12cc20bd
commit 83b083ce64
No known key found for this signature in database
GPG Key ID: 46D71BF4958ABB49
8 changed files with 105 additions and 77 deletions

View File

@ -26,7 +26,7 @@ namespace osu.Desktop.VisualTests.Tests
if (db == null) if (db == null)
{ {
storage = new TestStorage(@"TestCasePlaySongSelect"); storage = new TestStorage(@"TestCasePlaySongSelect");
db = new BeatmapDatabase(storage); db = new BeatmapDatabase(storage, storage.GetDatabase(@"client"));
var sets = new List<BeatmapSetInfo>(); var sets = new List<BeatmapSetInfo>();

View File

@ -4,7 +4,6 @@
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;

View File

@ -18,37 +18,19 @@ using SQLiteNetExtensions.Extensions;
namespace osu.Game.Database namespace osu.Game.Database
{ {
public class BeatmapDatabase public class BeatmapDatabase : Database
{ {
private SQLiteConnection connection { get; }
private readonly Storage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded; public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved; public event Action<BeatmapSetInfo> BeatmapSetRemoved;
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc; private BeatmapIPCChannel ipc;
public BeatmapDatabase(Storage storage, IIpcHost importHost = null) public BeatmapDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null) : base(storage, connection)
{ {
this.storage = storage;
if (importHost != null) if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this); ipc = new BeatmapIPCChannel(importHost, this);
if (connection == null)
{
try
{
connection = prepareConnection();
deletePending();
}
catch (Exception e)
{
Logger.Error(e, @"Failed to initialise the beatmap database! Trying again with a clean database...");
storage.DeleteDatabase(@"beatmaps");
connection = prepareConnection();
}
}
} }
private void deletePending() private void deletePending()
@ -57,20 +39,20 @@ namespace osu.Game.Database
{ {
try try
{ {
storage.Delete(b.Path); Storage.Delete(b.Path);
GetChildren(b, true); GetChildren(b, true);
foreach (var i in b.Beatmaps) foreach (var i in b.Beatmaps)
{ {
if (i.Metadata != null) connection.Delete(i.Metadata); if (i.Metadata != null) Connection.Delete(i.Metadata);
if (i.Difficulty != null) connection.Delete(i.Difficulty); if (i.Difficulty != null) Connection.Delete(i.Difficulty);
connection.Delete(i); Connection.Delete(i);
} }
if (b.Metadata != null) connection.Delete(b.Metadata); if (b.Metadata != null) Connection.Delete(b.Metadata);
connection.Delete(b); Connection.Delete(b);
} }
catch (Exception e) catch (Exception e)
{ {
@ -80,41 +62,31 @@ namespace osu.Game.Database
//this is required because sqlite migrations don't work, initially inserting nulls into this field. //this is required because sqlite migrations don't work, initially inserting nulls into this field.
//see https://github.com/praeclarum/sqlite-net/issues/326 //see https://github.com/praeclarum/sqlite-net/issues/326
connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL"); Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL");
} }
private SQLiteConnection prepareConnection() protected override void Prepare()
{ {
var conn = storage.GetDatabase(@"beatmaps"); Connection.CreateTable<BeatmapMetadata>();
Connection.CreateTable<BeatmapDifficulty>();
Connection.CreateTable<BeatmapSetInfo>();
Connection.CreateTable<BeatmapInfo>();
try deletePending();
{
conn.CreateTable<BeatmapMetadata>();
conn.CreateTable<BeatmapDifficulty>();
conn.CreateTable<BeatmapSetInfo>();
conn.CreateTable<BeatmapInfo>();
}
catch
{
conn.Close();
throw;
} }
return conn; public override void Reset()
}
public void Reset()
{ {
foreach (var setInfo in Query<BeatmapSetInfo>()) foreach (var setInfo in Query<BeatmapSetInfo>())
{ {
if (storage.Exists(setInfo.Path)) if (Storage.Exists(setInfo.Path))
storage.Delete(setInfo.Path); Storage.Delete(setInfo.Path);
} }
connection.DeleteAll<BeatmapMetadata>(); Connection.DeleteAll<BeatmapMetadata>();
connection.DeleteAll<BeatmapDifficulty>(); Connection.DeleteAll<BeatmapDifficulty>();
connection.DeleteAll<BeatmapSetInfo>(); Connection.DeleteAll<BeatmapSetInfo>();
connection.DeleteAll<BeatmapInfo>(); Connection.DeleteAll<BeatmapInfo>();
} }
/// <summary> /// <summary>
@ -174,7 +146,7 @@ namespace osu.Game.Database
BeatmapMetadata metadata; BeatmapMetadata metadata;
using (var reader = ArchiveReader.GetReader(storage, path)) using (var reader = ArchiveReader.GetReader(Storage, path))
{ {
using (var stream = new StreamReader(reader.GetStream(reader.BeatmapFilenames[0]))) using (var stream = new StreamReader(reader.GetStream(reader.BeatmapFilenames[0])))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
@ -182,18 +154,18 @@ namespace osu.Game.Database
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
{ {
using (var input = storage.GetStream(path)) using (var input = Storage.GetStream(path))
{ {
hash = input.GetMd5Hash(); hash = input.GetMd5Hash();
input.Seek(0, SeekOrigin.Begin); input.Seek(0, SeekOrigin.Begin);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
if (!storage.Exists(path)) if (!Storage.Exists(path))
using (var output = storage.GetStream(path, FileAccess.Write)) using (var output = Storage.GetStream(path, FileAccess.Write))
input.CopyTo(output); input.CopyTo(output);
} }
} }
var existing = connection.Table<BeatmapSetInfo>().FirstOrDefault(b => b.Hash == hash); var existing = Connection.Table<BeatmapSetInfo>().FirstOrDefault(b => b.Hash == hash);
if (existing != null) if (existing != null)
{ {
@ -216,7 +188,7 @@ namespace osu.Game.Database
Metadata = metadata Metadata = metadata
}; };
using (var archive = ArchiveReader.GetReader(storage, path)) using (var archive = ArchiveReader.GetReader(Storage, path))
{ {
string[] mapNames = archive.BeatmapFilenames; string[] mapNames = archive.BeatmapFilenames;
foreach (var name in mapNames) foreach (var name in mapNames)
@ -248,17 +220,17 @@ namespace osu.Game.Database
public void Import(IEnumerable<BeatmapSetInfo> beatmapSets) public void Import(IEnumerable<BeatmapSetInfo> beatmapSets)
{ {
lock (connection) lock (Connection)
{ {
connection.BeginTransaction(); Connection.BeginTransaction();
foreach (var s in beatmapSets) foreach (var s in beatmapSets)
{ {
connection.InsertWithChildren(s, true); Connection.InsertWithChildren(s, true);
BeatmapSetAdded?.Invoke(s); BeatmapSetAdded?.Invoke(s);
} }
connection.Commit(); Connection.Commit();
} }
} }
@ -275,7 +247,7 @@ namespace osu.Game.Database
if (string.IsNullOrEmpty(beatmapSet.Path)) if (string.IsNullOrEmpty(beatmapSet.Path))
return null; return null;
return ArchiveReader.GetReader(storage, beatmapSet.Path); return ArchiveReader.GetReader(Storage, beatmapSet.Path);
} }
public BeatmapSetInfo GetBeatmapSet(int id) public BeatmapSetInfo GetBeatmapSet(int id)
@ -305,25 +277,25 @@ namespace osu.Game.Database
public TableQuery<T> Query<T>() where T : class public TableQuery<T> Query<T>() where T : class
{ {
return connection.Table<T>(); return Connection.Table<T>();
} }
public T GetWithChildren<T>(object id) where T : class public T GetWithChildren<T>(object id) where T : class
{ {
return connection.GetWithChildren<T>(id); return Connection.GetWithChildren<T>(id);
} }
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true) public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true)
where T : class where T : class
{ {
return connection.GetAllWithChildren(filter, recursive); return Connection.GetAllWithChildren(filter, recursive);
} }
public T GetChildren<T>(T item, bool recursive = false) public T GetChildren<T>(T item, bool recursive = false)
{ {
if (item == null) return default(T); if (item == null) return default(T);
connection.GetChildren(item, recursive); Connection.GetChildren(item, recursive);
return item; return item;
} }
@ -339,11 +311,11 @@ namespace osu.Game.Database
if (validTypes.All(t => t != typeof(T))) if (validTypes.All(t => t != typeof(T)))
throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T)); throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T));
if (cascade) if (cascade)
connection.UpdateWithChildren(record); Connection.UpdateWithChildren(record);
else else
connection.Update(record); Connection.Update(record);
} }
public bool Exists(BeatmapSetInfo beatmapSet) => storage.Exists(beatmapSet.Path); public bool Exists(BeatmapSetInfo beatmapSet) => Storage.Exists(beatmapSet.Path);
} }
} }

View File

@ -0,0 +1,44 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Logging;
using osu.Framework.Platform;
using SQLite.Net;
namespace osu.Game.Database
{
public abstract class Database
{
protected SQLiteConnection Connection { get; }
protected Storage Storage { get; }
protected Database(Storage storage, SQLiteConnection connection)
{
Storage = storage;
Connection = connection;
try
{
Prepare();
}
catch (Exception e)
{
Logger.Error(e, @"Failed to initialise the beatmap database! Trying again with a clean database...");
storage.DeleteDatabase(@"beatmaps");
Reset();
Prepare();
}
}
/// <summary>
/// Prepare this database for use.
/// </summary>
protected abstract void Prepare();
/// <summary>
/// Reset this database to a default state. Undo all changes to database and storage backings.
/// </summary>
public abstract void Reset();
}
}

View File

@ -10,10 +10,11 @@ using osu.Game.IPC;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Scoring; using osu.Game.Modes.Scoring;
using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA;
using SQLite.Net;
namespace osu.Game.Database namespace osu.Game.Database
{ {
public class ScoreDatabase public class ScoreDatabase : Database
{ {
private readonly Storage storage; private readonly Storage storage;
private readonly BeatmapDatabase beatmaps; private readonly BeatmapDatabase beatmaps;
@ -23,7 +24,7 @@ namespace osu.Game.Database
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ScoreIPCChannel ipc; private ScoreIPCChannel ipc;
public ScoreDatabase(Storage storage, IIpcHost importHost = null, BeatmapDatabase beatmaps = null) public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapDatabase beatmaps = null) : base(storage, connection)
{ {
this.storage = storage; this.storage = storage;
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
@ -39,7 +40,7 @@ namespace osu.Game.Database
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
using (SerializationReader sr = new SerializationReader(s)) using (SerializationReader sr = new SerializationReader(s))
{ {
var ruleset = RulesetCollection.GetRuleset((int)sr.ReadByte()); var ruleset = RulesetCollection.GetRuleset(sr.ReadByte());
score = ruleset.CreateScoreProcessor().CreateScore(); score = ruleset.CreateScoreProcessor().CreateScore();
/* score.Pass = true;*/ /* score.Pass = true;*/
@ -107,5 +108,13 @@ namespace osu.Game.Database
return score; return score;
} }
protected override void Prepare()
{
}
public override void Reset()
{
}
} }
} }

View File

@ -18,6 +18,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Processing; using osu.Game.Graphics.Processing;
using osu.Game.Online.API; using osu.Game.Online.API;
using SQLite.Net;
namespace osu.Game namespace osu.Game
{ {
@ -80,8 +81,11 @@ namespace osu.Game
{ {
Dependencies.Cache(this); Dependencies.Cache(this);
Dependencies.Cache(LocalConfig); Dependencies.Cache(LocalConfig);
Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, Host));
Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, Host, BeatmapDatabase)); SQLiteConnection connection = Host.Storage.GetDatabase(@"client");
Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, connection, Host));
Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, connection, Host, BeatmapDatabase));
Dependencies.Cache(new OsuColour()); Dependencies.Cache(new OsuColour());
//this completely overrides the framework default. will need to change once we make a proper FontStore. //this completely overrides the framework default. will need to change once we make a proper FontStore.

View File

@ -78,6 +78,7 @@
<Compile Include="Beatmaps\Timing\TimeSignatures.cs" /> <Compile Include="Beatmaps\Timing\TimeSignatures.cs" />
<Compile Include="Beatmaps\Timing\TimingInfo.cs" /> <Compile Include="Beatmaps\Timing\TimingInfo.cs" />
<Compile Include="Database\BeatmapMetrics.cs" /> <Compile Include="Database\BeatmapMetrics.cs" />
<Compile Include="Database\Database.cs" />
<Compile Include="Database\ScoreDatabase.cs" /> <Compile Include="Database\ScoreDatabase.cs" />
<Compile Include="Graphics\Backgrounds\Triangles.cs" /> <Compile Include="Graphics\Backgrounds\Triangles.cs" />
<Compile Include="Graphics\Cursor\CursorTrail.cs" /> <Compile Include="Graphics\Cursor\CursorTrail.cs" />