Merge branch 'master' into release-10.3.z

This commit is contained in:
Bond-009
2019-07-06 23:08:52 +02:00
committed by GitHub
139 changed files with 5130 additions and 7634 deletions

View File

@@ -1,183 +1,141 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.Logging;
using SQLitePCL;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public abstract class BaseSqliteRepository : IDisposable
{
protected string DbFilePath { get; set; }
protected ReaderWriterLockSlim WriteLock;
protected ILogger Logger { get; private set; }
private bool _disposed = false;
protected BaseSqliteRepository(ILogger logger)
{
Logger = logger;
WriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
/// <summary>
/// Gets or sets the path to the DB file.
/// </summary>
/// <value>Path to the DB file.</value>
protected string DbFilePath { get; set; }
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
protected ILogger Logger { get; }
/// <summary>
/// Gets the default connection flags.
/// </summary>
/// <value>The default connection flags.</value>
protected virtual ConnectionFlags DefaultConnectionFlags => ConnectionFlags.NoMutex;
/// <summary>
/// Gets the transaction mode.
/// </summary>
/// <value>The transaction mode.</value>>
protected TransactionMode TransactionMode => TransactionMode.Deferred;
/// <summary>
/// Gets the transaction mode for read-only operations.
/// </summary>
/// <value>The transaction mode.</value>
protected TransactionMode ReadTransactionMode => TransactionMode.Deferred;
internal static int ThreadSafeMode { get; set; }
/// <summary>
/// Gets the cache size.
/// </summary>
/// <value>The cache size or null.</value>
protected virtual int? CacheSize => null;
static BaseSqliteRepository()
/// <summary>
/// Gets the journal mode.
/// </summary>
/// <value>The journal mode.</value>
protected virtual string JournalMode => "WAL";
/// <summary>
/// Gets the page size.
/// </summary>
/// <value>The page size or null.</value>
protected virtual int? PageSize => null;
/// <summary>
/// Gets the temp store mode.
/// </summary>
/// <value>The temp store mode.</value>
/// <see cref="TempStoreMode"/>
protected virtual TempStoreMode TempStore => TempStoreMode.Default;
/// <summary>
/// Gets the synchronous mode.
/// </summary>
/// <value>The synchronous mode or null.</value>
/// <see cref="SynchronousMode"/>
protected virtual SynchronousMode? Synchronous => null;
/// <summary>
/// Gets or sets the write lock.
/// </summary>
/// <value>The write lock.</value>
protected SemaphoreSlim WriteLock { get; set; } = new SemaphoreSlim(1, 1);
/// <summary>
/// Gets or sets the write connection.
/// </summary>
/// <value>The write connection.</value>
protected SQLiteDatabaseConnection WriteConnection { get; set; }
protected ManagedConnection GetConnection(bool _ = false)
{
SQLite3.EnableSharedCache = false;
int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0);
//CheckOk(rc);
rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD, 1);
//rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SINGLETHREAD, 1);
//rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SERIALIZED, 1);
//CheckOk(rc);
rc = raw.sqlite3_enable_shared_cache(1);
ThreadSafeMode = raw.sqlite3_threadsafe();
}
private static bool _versionLogged;
private string _defaultWal;
protected ManagedConnection _connection;
protected virtual bool EnableSingleConnection => true;
protected ManagedConnection CreateConnection(bool isReadOnly = false)
{
if (_connection != null)
WriteLock.Wait();
if (WriteConnection != null)
{
return _connection;
return new ManagedConnection(WriteConnection, WriteLock);
}
lock (WriteLock)
WriteConnection = SQLite3.Open(
DbFilePath,
DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
null);
if (CacheSize.HasValue)
{
if (!_versionLogged)
{
_versionLogged = true;
Logger.LogInformation("Sqlite version: " + SQLite3.Version);
Logger.LogInformation("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray()));
}
ConnectionFlags connectionFlags;
if (isReadOnly)
{
//Logger.LogInformation("Opening read connection");
//connectionFlags = ConnectionFlags.ReadOnly;
connectionFlags = ConnectionFlags.Create;
connectionFlags |= ConnectionFlags.ReadWrite;
}
else
{
//Logger.LogInformation("Opening write connection");
connectionFlags = ConnectionFlags.Create;
connectionFlags |= ConnectionFlags.ReadWrite;
}
if (EnableSingleConnection)
{
connectionFlags |= ConnectionFlags.PrivateCache;
}
else
{
connectionFlags |= ConnectionFlags.SharedCached;
}
connectionFlags |= ConnectionFlags.NoMutex;
var db = SQLite3.Open(DbFilePath, connectionFlags, null);
try
{
if (string.IsNullOrWhiteSpace(_defaultWal))
{
_defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
Logger.LogInformation("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
}
var queries = new List<string>
{
//"PRAGMA cache size=-10000"
//"PRAGMA read_uncommitted = true",
"PRAGMA synchronous=Normal"
};
if (CacheSize.HasValue)
{
queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
}
if (EnableTempStoreMemory)
{
queries.Add("PRAGMA temp_store = memory");
}
else
{
queries.Add("PRAGMA temp_store = file");
}
foreach (var query in queries)
{
db.Execute(query);
}
}
catch
{
using (db)
{
}
throw;
}
_connection = new ManagedConnection(db, false);
return _connection;
WriteConnection.Execute("PRAGMA cache_size=" + CacheSize.Value);
}
if (!string.IsNullOrWhiteSpace(JournalMode))
{
WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode);
}
if (Synchronous.HasValue)
{
WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
}
if (PageSize.HasValue)
{
WriteConnection.Execute("PRAGMA page_size=" + PageSize.Value);
}
WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore);
return new ManagedConnection(WriteConnection, WriteLock);
}
public IStatement PrepareStatement(ManagedConnection connection, string sql)
{
return connection.PrepareStatement(sql);
}
public IStatement PrepareStatementSafe(ManagedConnection connection, string sql)
{
return connection.PrepareStatement(sql);
}
=> connection.PrepareStatement(sql);
public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
{
return connection.PrepareStatement(sql);
}
=> connection.PrepareStatement(sql);
public IStatement PrepareStatementSafe(IDatabaseConnection connection, string sql)
{
return connection.PrepareStatement(sql);
}
public List<IStatement> PrepareAll(IDatabaseConnection connection, IEnumerable<string> sql)
{
return PrepareAllSafe(connection, sql);
}
public List<IStatement> PrepareAllSafe(IDatabaseConnection connection, IEnumerable<string> sql)
{
return sql.Select(connection.PrepareStatement).ToList();
}
public IEnumerable<IStatement> PrepareAll(IDatabaseConnection connection, IEnumerable<string> sql)
=> sql.Select(connection.PrepareStatement);
protected bool TableExists(ManagedConnection connection, string name)
{
@@ -199,105 +157,9 @@ namespace Emby.Server.Implementations.Data
}, ReadTransactionMode);
}
protected void RunDefaultInitialization(ManagedConnection db)
{
var queries = new List<string>
{
"PRAGMA journal_mode=WAL",
"PRAGMA page_size=4096",
"PRAGMA synchronous=Normal"
};
if (EnableTempStoreMemory)
{
queries.AddRange(new List<string>
{
"pragma default_temp_store = memory",
"pragma temp_store = memory"
});
}
else
{
queries.AddRange(new List<string>
{
"pragma temp_store = file"
});
}
// Configuration and pragmas can affect VACUUM so it needs to be last.
queries.Add("VACUUM");
db.ExecuteAll(string.Join(";", queries));
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
}
protected virtual bool EnableTempStoreMemory => false;
protected virtual int? CacheSize => null;
private bool _disposed;
protected void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name, "Object has been disposed and cannot be accessed.");
}
}
public void Dispose()
{
_disposed = true;
Dispose(true);
}
private readonly object _disposeLock = new object();
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
DisposeConnection();
}
}
private void DisposeConnection()
{
try
{
lock (_disposeLock)
{
using (WriteLock.Write())
{
if (_connection != null)
{
using (_connection)
{
_connection.Close();
}
_connection = null;
}
CloseConnection();
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error disposing database");
}
}
protected virtual void CloseConnection()
{
}
protected List<string> GetColumnNames(IDatabaseConnection connection, string table)
{
var list = new List<string>();
var columnNames = new List<string>();
foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
{
@@ -305,11 +167,13 @@ namespace Emby.Server.Implementations.Data
{
var name = row[1].ToString();
list.Add(name);
columnNames.Add(name);
}
}
// Configuration and pragmas can affect VACUUM so it needs to be last.
queries.Add("VACUUM");
return list;
return columnNames;
}
protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
@@ -321,61 +185,103 @@ namespace Emby.Server.Implementations.Data
connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");
}
protected void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name, "Object has been disposed and cannot be accessed.");
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (_disposed)
{
return;
}
if (dispose)
{
WriteLock.Wait();
try
{
WriteConnection.Dispose();
}
finally
{
WriteLock.Release();
}
WriteLock.Dispose();
}
WriteConnection = null;
WriteLock = null;
_disposed = true;
}
}
public static class ReaderWriterLockSlimExtensions
/// <summary>
/// The disk synchronization mode, controls how aggressively SQLite will write data
/// all the way out to physical storage.
/// </summary>
public enum SynchronousMode
{
private sealed class ReadLockToken : IDisposable
{
private ReaderWriterLockSlim _sync;
public ReadLockToken(ReaderWriterLockSlim sync)
{
_sync = sync;
sync.EnterReadLock();
}
public void Dispose()
{
if (_sync != null)
{
_sync.ExitReadLock();
_sync = null;
}
}
}
private sealed class WriteLockToken : IDisposable
{
private ReaderWriterLockSlim _sync;
public WriteLockToken(ReaderWriterLockSlim sync)
{
_sync = sync;
sync.EnterWriteLock();
}
public void Dispose()
{
if (_sync != null)
{
_sync.ExitWriteLock();
_sync = null;
}
}
}
/// <summary>
/// SQLite continues without syncing as soon as it has handed data off to the operating system
/// </summary>
Off = 0,
public static IDisposable Read(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
//{
// return new DummyToken();
//}
return new WriteLockToken(obj);
}
/// <summary>
/// SQLite database engine will still sync at the most critical moments
/// </summary>
Normal = 1,
public static IDisposable Write(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
//{
// return new DummyToken();
//}
return new WriteLockToken(obj);
}
/// <summary>
/// SQLite database engine will use the xSync method of the VFS
/// to ensure that all content is safely written to the disk surface prior to continuing.
/// </summary>
Full = 2,
/// <summary>
/// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal
/// is synced after that journal is unlinked to commit a transaction in DELETE mode.
/// </summary>
Extra = 3
}
/// <summary>
/// Storage mode used by temporary database files.
/// </summary>
public enum TempStoreMode
{
/// <summary>
/// The compile-time C preprocessor macro SQLITE_TEMP_STORE
/// is used to determine where temporary tables and indices are stored.
/// </summary>
Default = 0,
/// <summary>
/// Temporary tables and indices are stored in a file.
/// </summary>
File = 1,
/// <summary>
/// Temporary tables and indices are kept in as if they were pure in-memory databases memory.
/// </summary>
Memory = 2
}
}

View File

@@ -1,79 +1,78 @@
using System;
using System.Collections.Generic;
using System.Threading;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public class ManagedConnection : IDisposable
{
private SQLiteDatabaseConnection db;
private readonly bool _closeOnDispose;
private SQLiteDatabaseConnection _db;
private readonly SemaphoreSlim _writeLock;
private bool _disposed = false;
public ManagedConnection(SQLiteDatabaseConnection db, bool closeOnDispose)
public ManagedConnection(SQLiteDatabaseConnection db, SemaphoreSlim writeLock)
{
this.db = db;
_closeOnDispose = closeOnDispose;
_db = db;
_writeLock = writeLock;
}
public IStatement PrepareStatement(string sql)
{
return db.PrepareStatement(sql);
return _db.PrepareStatement(sql);
}
public IEnumerable<IStatement> PrepareAll(string sql)
{
return db.PrepareAll(sql);
return _db.PrepareAll(sql);
}
public void ExecuteAll(string sql)
{
db.ExecuteAll(sql);
_db.ExecuteAll(sql);
}
public void Execute(string sql, params object[] values)
{
db.Execute(sql, values);
_db.Execute(sql, values);
}
public void RunQueries(string[] sql)
{
db.RunQueries(sql);
_db.RunQueries(sql);
}
public void RunInTransaction(Action<IDatabaseConnection> action, TransactionMode mode)
{
db.RunInTransaction(action, mode);
_db.RunInTransaction(action, mode);
}
public T RunInTransaction<T>(Func<IDatabaseConnection, T> action, TransactionMode mode)
{
return db.RunInTransaction(action, mode);
return _db.RunInTransaction(action, mode);
}
public IEnumerable<IReadOnlyList<IResultSetValue>> Query(string sql)
{
return db.Query(sql);
return _db.Query(sql);
}
public IEnumerable<IReadOnlyList<IResultSetValue>> Query(string sql, params object[] values)
{
return db.Query(sql, values);
}
public void Close()
{
using (db)
{
}
return _db.Query(sql, values);
}
public void Dispose()
{
if (_closeOnDispose)
if (_disposed)
{
Close();
return;
}
_writeLock.Release();
_db = null; // Don't dispose it
_disposed = true;
}
}
}

View File

@@ -18,13 +18,13 @@ namespace Emby.Server.Implementations.Data
/// </summary>
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
protected IFileSystem FileSystem { get; private set; }
private readonly IFileSystem _fileSystem;
public SqliteDisplayPreferencesRepository(ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem)
: base(loggerFactory.CreateLogger(nameof(SqliteDisplayPreferencesRepository)))
{
_jsonSerializer = jsonSerializer;
FileSystem = fileSystem;
_fileSystem = fileSystem;
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
@@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Data
{
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
FileSystem.DeleteFile(DbFilePath);
_fileSystem.DeleteFile(DbFilePath);
InitializeInternal();
}
@@ -61,10 +61,8 @@ namespace Emby.Server.Implementations.Data
/// <returns>Task.</returns>
private void InitializeInternal()
{
using (var connection = CreateConnection())
using (var connection = GetConnection())
{
RunDefaultInitialization(connection);
string[] queries = {
"create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
@@ -98,15 +96,12 @@ namespace Emby.Server.Implementations.Data
cancellationToken.ThrowIfCancellationRequested();
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
{
SaveDisplayPreferences(displayPreferences, userId, client, db);
}, TransactionMode);
}
SaveDisplayPreferences(displayPreferences, userId, client, db);
}, TransactionMode);
}
}
@@ -142,18 +137,15 @@ namespace Emby.Server.Implementations.Data
cancellationToken.ThrowIfCancellationRequested();
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
foreach (var displayPreference in displayPreferences)
{
foreach (var displayPreference in displayPreferences)
{
SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
}
}, TransactionMode);
}
SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
}
}, TransactionMode);
}
}
@@ -174,27 +166,24 @@ namespace Emby.Server.Implementations.Data
var guidId = displayPreferencesId.GetMD5();
using (WriteLock.Read())
using (var connection = GetConnection(true))
{
using (var connection = CreateConnection(true))
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
{
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
{
statement.TryBind("@id", guidId.ToGuidBlob());
statement.TryBind("@userId", userId.ToGuidBlob());
statement.TryBind("@client", client);
statement.TryBind("@id", guidId.ToGuidBlob());
statement.TryBind("@userId", userId.ToGuidBlob());
statement.TryBind("@client", client);
foreach (var row in statement.ExecuteQuery())
{
return Get(row);
}
foreach (var row in statement.ExecuteQuery())
{
return Get(row);
}
return new DisplayPreferences
{
Id = guidId.ToString("N")
};
}
return new DisplayPreferences
{
Id = guidId.ToString("N")
};
}
}
@@ -208,18 +197,15 @@ namespace Emby.Server.Implementations.Data
{
var list = new List<DisplayPreferences>();
using (WriteLock.Read())
using (var connection = GetConnection(true))
{
using (var connection = CreateConnection(true))
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
{
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
{
statement.TryBind("@userId", userId.ToGuidBlob());
statement.TryBind("@userId", userId.ToGuidBlob());
foreach (var row in statement.ExecuteQuery())
{
list.Add(Get(row));
}
foreach (var row in statement.ExecuteQuery())
{
list.Add(Get(row));
}
}
}

View File

@@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Data
}
}
public static void Attach(ManagedConnection db, string path, string alias)
public static void Attach(SQLiteDatabaseConnection db, string path, string alias)
{
var commandText = string.Format("attach @path as {0};", alias);

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
@@ -33,14 +32,14 @@ namespace Emby.Server.Implementations.Data
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
public void Initialize(ReaderWriterLockSlim writeLock, ManagedConnection managedConnection, IUserManager userManager)
public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection)
{
_connection = managedConnection;
WriteLock.Dispose();
WriteLock = writeLock;
WriteLock = dbLock;
WriteConnection?.Dispose();
WriteConnection = dbConnection;
using (var connection = CreateConnection())
using (var connection = GetConnection())
{
var userDatasTableExists = TableExists(connection, "UserDatas");
var userDataTableExists = TableExists(connection, "userdata");
@@ -129,8 +128,6 @@ namespace Emby.Server.Implementations.Data
return list;
}
protected override bool EnableTempStoreMemory => true;
/// <summary>
/// Saves the user data.
/// </summary>
@@ -178,15 +175,12 @@ namespace Emby.Server.Implementations.Data
{
cancellationToken.ThrowIfCancellationRequested();
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
{
SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
}
SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
}
}
@@ -249,18 +243,15 @@ namespace Emby.Server.Implementations.Data
{
cancellationToken.ThrowIfCancellationRequested();
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
foreach (var userItemData in userDataList)
{
foreach (var userItemData in userDataList)
{
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
}, TransactionMode);
}
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
}, TransactionMode);
}
}
@@ -281,28 +272,26 @@ namespace Emby.Server.Implementations.Data
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
using (WriteLock.Read())
using (var connection = GetConnection(true))
{
using (var connection = CreateConnection(true))
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
statement.TryBind("@UserId", internalUserId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
{
statement.TryBind("@UserId", internalUserId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
{
return ReadRow(row);
}
return ReadRow(row);
}
return null;
}
return null;
}
}
@@ -335,18 +324,15 @@ namespace Emby.Server.Implementations.Data
var list = new List<UserItemData>();
using (WriteLock.Read())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
statement.TryBind("@UserId", internalUserId);
statement.TryBind("@UserId", internalUserId);
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
}
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
}
}
}
@@ -392,15 +378,5 @@ namespace Emby.Server.Implementations.Data
return userData;
}
protected override void Dispose(bool dispose)
{
// handled by library database
}
protected override void CloseConnection()
{
// handled by library database
}
}
}

View File

@@ -40,10 +40,8 @@ namespace Emby.Server.Implementations.Data
/// <returns>Task.</returns>
public void Initialize()
{
using (var connection = CreateConnection())
using (var connection = GetConnection())
{
RunDefaultInitialization(connection);
var localUsersTableExists = TableExists(connection, "LocalUsersv2");
connection.RunQueries(new[] {
@@ -56,7 +54,7 @@ namespace Emby.Server.Implementations.Data
TryMigrateToLocalUsersTable(connection);
}
RemoveEmptyPasswordHashes();
RemoveEmptyPasswordHashes(connection);
}
}
@@ -75,9 +73,9 @@ namespace Emby.Server.Implementations.Data
}
}
private void RemoveEmptyPasswordHashes()
private void RemoveEmptyPasswordHashes(ManagedConnection connection)
{
foreach (var user in RetrieveAllUsers())
foreach (var user in RetrieveAllUsers(connection))
{
// If the user password is the sha1 hash of the empty string, remove it
if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
@@ -89,22 +87,16 @@ namespace Emby.Server.Implementations.Data
user.Password = null;
var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write())
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}, TransactionMode);
}
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}, TransactionMode);
}
}
/// <summary>
@@ -119,31 +111,28 @@ namespace Emby.Server.Implementations.Data
var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
{
using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
{
statement.TryBind("@guid", user.Id.ToGuidBlob());
statement.TryBind("@data", serialized);
statement.TryBind("@guid", user.Id.ToGuidBlob());
statement.TryBind("@data", serialized);
statement.MoveNext();
}
statement.MoveNext();
}
var createdUser = GetUser(user.Id, false);
var createdUser = GetUser(user.Id, connection);
if (createdUser == null)
{
throw new ApplicationException("created user should never be null");
}
if (createdUser == null)
{
throw new ApplicationException("created user should never be null");
}
user.InternalId = createdUser.InternalId;
user.InternalId = createdUser.InternalId;
}, TransactionMode);
}
}, TransactionMode);
}
}
@@ -156,39 +145,30 @@ namespace Emby.Server.Implementations.Data
var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized);
statement.MoveNext();
}
}, TransactionMode);
}
}, TransactionMode);
}
}
private User GetUser(Guid guid, bool openLock)
private User GetUser(Guid guid, ManagedConnection connection)
{
using (openLock ? WriteLock.Read() : null)
using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
{
using (var connection = CreateConnection(true))
{
using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
{
statement.TryBind("@guid", guid);
statement.TryBind("@guid", guid);
foreach (var row in statement.ExecuteQuery())
{
return GetUser(row);
}
}
foreach (var row in statement.ExecuteQuery())
{
return GetUser(row);
}
}
@@ -216,20 +196,22 @@ namespace Emby.Server.Implementations.Data
/// <returns>IEnumerable{User}.</returns>
public List<User> RetrieveAllUsers()
{
var list = new List<User>();
using (WriteLock.Read())
using (var connection = GetConnection(true))
{
using (var connection = CreateConnection(true))
{
foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
{
list.Add(GetUser(row));
}
}
return new List<User>(RetrieveAllUsers(connection));
}
}
return list;
/// <summary>
/// Retrieve all users from the database
/// </summary>
/// <returns>IEnumerable{User}.</returns>
private IEnumerable<User> RetrieveAllUsers(ManagedConnection connection)
{
foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
{
yield return GetUser(row);
}
}
/// <summary>
@@ -245,19 +227,16 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(user));
}
using (WriteLock.Write())
using (var connection = GetConnection())
{
using (var connection = CreateConnection())
connection.RunInTransaction(db =>
{
connection.RunInTransaction(db =>
using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
{
using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
{
statement.TryBind("@id", user.InternalId);
statement.MoveNext();
}
}, TransactionMode);
}
statement.TryBind("@id", user.InternalId);
statement.MoveNext();
}
}, TransactionMode);
}
}
}