commit
e722801f80
|
@ -15,14 +15,14 @@ namespace Emby.Server.Implementations.Activity
|
|||
{
|
||||
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
|
||||
{
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
protected IFileSystem FileSystem { get; private set; }
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
|
||||
: base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
|
||||
{
|
||||
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
|
||||
FileSystem = fileSystem;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
|
@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
{
|
||||
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
|
||||
|
||||
FileSystem.DeleteFile(DbFilePath);
|
||||
_fileSystem.DeleteFile(DbFilePath);
|
||||
|
||||
InitializeInternal();
|
||||
}
|
||||
|
@ -43,10 +43,8 @@ namespace Emby.Server.Implementations.Activity
|
|||
|
||||
private void InitializeInternal()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
RunDefaultInitialization(connection);
|
||||
|
||||
connection.RunQueries(new[]
|
||||
{
|
||||
"create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
|
||||
|
@ -85,8 +83,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
throw new ArgumentNullException(nameof(entry));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = CreateConnection())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
|
@ -124,8 +121,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
throw new ArgumentNullException(nameof(entry));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = CreateConnection())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
|
@ -159,8 +155,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
|
||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
||||
{
|
||||
using (WriteLock.Read())
|
||||
using (var connection = CreateConnection(true))
|
||||
using (var connection = GetConnection(true))
|
||||
{
|
||||
var commandText = BaseActivitySelectText;
|
||||
var whereClauses = new List<string>();
|
||||
|
@ -218,7 +213,7 @@ namespace Emby.Server.Implementations.Activity
|
|||
var list = new List<ActivityLogEntry>();
|
||||
var result = new QueryResult<ActivityLogEntry>();
|
||||
|
||||
var statements = PrepareAllSafe(db, statementTexts).ToList();
|
||||
var statements = PrepareAll(db, statementTexts).ToList();
|
||||
|
||||
using (var statement = statements[0])
|
||||
{
|
||||
|
|
|
@ -754,10 +754,6 @@ namespace Emby.Server.Implementations
|
|||
UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
|
||||
serviceCollection.AddSingleton(UserDataManager);
|
||||
|
||||
UserRepository = GetUserRepository();
|
||||
// This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
|
||||
serviceCollection.AddSingleton(UserRepository);
|
||||
|
||||
var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager);
|
||||
serviceCollection.AddSingleton<IDisplayPreferencesRepository>(displayPreferencesRepo);
|
||||
|
||||
|
@ -767,6 +763,8 @@ namespace Emby.Server.Implementations
|
|||
AuthenticationRepository = GetAuthenticationRepository();
|
||||
serviceCollection.AddSingleton(AuthenticationRepository);
|
||||
|
||||
UserRepository = GetUserRepository();
|
||||
|
||||
UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
|
||||
serviceCollection.AddSingleton(UserManager);
|
||||
|
||||
|
@ -807,7 +805,6 @@ namespace Emby.Server.Implementations
|
|||
serviceCollection.AddSingleton(TVSeriesManager);
|
||||
|
||||
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
|
||||
|
||||
serviceCollection.AddSingleton(DeviceManager);
|
||||
|
||||
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
|
||||
|
@ -1893,8 +1890,12 @@ namespace Emby.Server.Implementations
|
|||
Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
UserRepository.Dispose();
|
||||
}
|
||||
|
||||
UserRepository = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,103 +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"
|
||||
});
|
||||
}
|
||||
|
||||
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 + ")"))
|
||||
{
|
||||
|
@ -303,11 +167,11 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
var name = row[1].ToString();
|
||||
|
||||
list.Add(name);
|
||||
columnNames.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
|
||||
|
@ -319,61 +183,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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,9 +222,8 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
public void Initialize()
|
||||
{
|
||||
_users = LoadUsers();
|
||||
|
||||
var users = Users.ToList();
|
||||
var users = LoadUsers();
|
||||
_users = users.ToArray();
|
||||
|
||||
// If there are no local users with admin rights, make them all admins
|
||||
if (!users.Any(i => i.Policy.IsAdministrator))
|
||||
|
@ -555,35 +554,36 @@ namespace Emby.Server.Implementations.Library
|
|||
/// Loads the users from the repository
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{User}.</returns>
|
||||
private User[] LoadUsers()
|
||||
private List<User> LoadUsers()
|
||||
{
|
||||
var users = UserRepository.RetrieveAllUsers();
|
||||
|
||||
// There always has to be at least one user.
|
||||
if (users.Count == 0)
|
||||
if (users.Count != 0)
|
||||
{
|
||||
var defaultName = Environment.UserName;
|
||||
if (string.IsNullOrWhiteSpace(defaultName))
|
||||
{
|
||||
defaultName = "MyJellyfinUser";
|
||||
}
|
||||
var name = MakeValidUsername(defaultName);
|
||||
|
||||
var user = InstantiateNewUser(name);
|
||||
|
||||
user.DateLastSaved = DateTime.UtcNow;
|
||||
|
||||
UserRepository.CreateUser(user);
|
||||
|
||||
users.Add(user);
|
||||
|
||||
user.Policy.IsAdministrator = true;
|
||||
user.Policy.EnableContentDeletion = true;
|
||||
user.Policy.EnableRemoteControlOfOtherUsers = true;
|
||||
UpdateUserPolicy(user, user.Policy, false);
|
||||
return users;
|
||||
}
|
||||
|
||||
return users.ToArray();
|
||||
var defaultName = Environment.UserName;
|
||||
if (string.IsNullOrWhiteSpace(defaultName))
|
||||
{
|
||||
defaultName = "MyJellyfinUser";
|
||||
}
|
||||
|
||||
var name = MakeValidUsername(defaultName);
|
||||
|
||||
var user = InstantiateNewUser(name);
|
||||
|
||||
user.DateLastSaved = DateTime.UtcNow;
|
||||
|
||||
UserRepository.CreateUser(user);
|
||||
|
||||
user.Policy.IsAdministrator = true;
|
||||
user.Policy.EnableContentDeletion = true;
|
||||
user.Policy.EnableRemoteControlOfOtherUsers = true;
|
||||
UpdateUserPolicy(user, user.Policy, false);
|
||||
|
||||
return new List<User> { user };
|
||||
}
|
||||
|
||||
public UserDto GetUserDto(User user, string remoteEndPoint = null)
|
||||
|
|
|
@ -23,10 +23,8 @@ namespace Emby.Server.Implementations.Security
|
|||
|
||||
public void Initialize()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
RunDefaultInitialization(connection);
|
||||
|
||||
var tableNewlyCreated = !TableExists(connection, "Tokens");
|
||||
|
||||
string[] queries = {
|
||||
|
@ -87,31 +85,28 @@ namespace Emby.Server.Implementations.Security
|
|||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)"))
|
||||
{
|
||||
using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)"))
|
||||
{
|
||||
statement.TryBind("@AccessToken", info.AccessToken);
|
||||
statement.TryBind("@AccessToken", info.AccessToken);
|
||||
|
||||
statement.TryBind("@DeviceId", info.DeviceId);
|
||||
statement.TryBind("@AppName", info.AppName);
|
||||
statement.TryBind("@AppVersion", info.AppVersion);
|
||||
statement.TryBind("@DeviceName", info.DeviceName);
|
||||
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
|
||||
statement.TryBind("@UserName", info.UserName);
|
||||
statement.TryBind("@IsActive", true);
|
||||
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
|
||||
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
|
||||
statement.TryBind("@DeviceId", info.DeviceId);
|
||||
statement.TryBind("@AppName", info.AppName);
|
||||
statement.TryBind("@AppVersion", info.AppVersion);
|
||||
statement.TryBind("@DeviceName", info.DeviceName);
|
||||
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
|
||||
statement.TryBind("@UserName", info.UserName);
|
||||
statement.TryBind("@IsActive", true);
|
||||
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
|
||||
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
|
||||
|
||||
statement.MoveNext();
|
||||
}
|
||||
statement.MoveNext();
|
||||
}
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,31 +117,28 @@ namespace Emby.Server.Implementations.Security
|
|||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id"))
|
||||
{
|
||||
using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id"))
|
||||
{
|
||||
statement.TryBind("@Id", info.Id);
|
||||
statement.TryBind("@Id", info.Id);
|
||||
|
||||
statement.TryBind("@AccessToken", info.AccessToken);
|
||||
statement.TryBind("@AccessToken", info.AccessToken);
|
||||
|
||||
statement.TryBind("@DeviceId", info.DeviceId);
|
||||
statement.TryBind("@AppName", info.AppName);
|
||||
statement.TryBind("@AppVersion", info.AppVersion);
|
||||
statement.TryBind("@DeviceName", info.DeviceName);
|
||||
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
|
||||
statement.TryBind("@UserName", info.UserName);
|
||||
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
|
||||
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
|
||||
statement.TryBind("@DeviceId", info.DeviceId);
|
||||
statement.TryBind("@AppName", info.AppName);
|
||||
statement.TryBind("@AppVersion", info.AppVersion);
|
||||
statement.TryBind("@DeviceName", info.DeviceName);
|
||||
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
|
||||
statement.TryBind("@UserName", info.UserName);
|
||||
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
|
||||
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
|
||||
|
||||
statement.MoveNext();
|
||||
}
|
||||
}, TransactionMode);
|
||||
}
|
||||
statement.MoveNext();
|
||||
}
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,20 +149,17 @@ namespace Emby.Server.Implementations.Security
|
|||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id"))
|
||||
{
|
||||
using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id"))
|
||||
{
|
||||
statement.TryBind("@Id", info.Id);
|
||||
statement.TryBind("@Id", info.Id);
|
||||
|
||||
statement.MoveNext();
|
||||
}
|
||||
}, TransactionMode);
|
||||
}
|
||||
statement.MoveNext();
|
||||
}
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,45 +246,42 @@ namespace Emby.Server.Implementations.Security
|
|||
|
||||
var list = new List<AuthenticationInfo>();
|
||||
|
||||
using (WriteLock.Read())
|
||||
using (var connection = GetConnection(true))
|
||||
{
|
||||
using (var connection = CreateConnection(true))
|
||||
return connection.RunInTransaction(db =>
|
||||
{
|
||||
return connection.RunInTransaction(db =>
|
||||
var result = new QueryResult<AuthenticationInfo>();
|
||||
|
||||
var statementTexts = new List<string>();
|
||||
statementTexts.Add(commandText);
|
||||
statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
|
||||
|
||||
var statements = PrepareAll(db, statementTexts)
|
||||
.ToList();
|
||||
|
||||
using (var statement = statements[0])
|
||||
{
|
||||
var result = new QueryResult<AuthenticationInfo>();
|
||||
BindAuthenticationQueryParams(query, statement);
|
||||
|
||||
var statementTexts = new List<string>();
|
||||
statementTexts.Add(commandText);
|
||||
statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
|
||||
|
||||
var statements = PrepareAllSafe(db, statementTexts)
|
||||
.ToList();
|
||||
|
||||
using (var statement = statements[0])
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
BindAuthenticationQueryParams(query, statement);
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
list.Add(Get(row));
|
||||
}
|
||||
|
||||
using (var totalCountStatement = statements[1])
|
||||
{
|
||||
BindAuthenticationQueryParams(query, totalCountStatement);
|
||||
|
||||
result.TotalRecordCount = totalCountStatement.ExecuteQuery()
|
||||
.SelectScalarInt()
|
||||
.First();
|
||||
}
|
||||
list.Add(Get(row));
|
||||
}
|
||||
|
||||
result.Items = list.ToArray();
|
||||
return result;
|
||||
using (var totalCountStatement = statements[1])
|
||||
{
|
||||
BindAuthenticationQueryParams(query, totalCountStatement);
|
||||
|
||||
}, ReadTransactionMode);
|
||||
}
|
||||
result.TotalRecordCount = totalCountStatement.ExecuteQuery()
|
||||
.SelectScalarInt()
|
||||
.First();
|
||||
}
|
||||
}
|
||||
|
||||
result.Items = list.ToArray();
|
||||
return result;
|
||||
|
||||
}, ReadTransactionMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,31 +344,28 @@ namespace Emby.Server.Implementations.Security
|
|||
|
||||
public DeviceOptions GetDeviceOptions(string deviceId)
|
||||
{
|
||||
using (WriteLock.Read())
|
||||
using (var connection = GetConnection(true))
|
||||
{
|
||||
using (var connection = CreateConnection(true))
|
||||
return connection.RunInTransaction(db =>
|
||||
{
|
||||
return connection.RunInTransaction(db =>
|
||||
using (var statement = base.PrepareStatement(db, "select CustomName from Devices where Id=@DeviceId"))
|
||||
{
|
||||
using (var statement = PrepareStatementSafe(db, "select CustomName from Devices where Id=@DeviceId"))
|
||||
statement.TryBind("@DeviceId", deviceId);
|
||||
|
||||
var result = new DeviceOptions();
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
{
|
||||
statement.TryBind("@DeviceId", deviceId);
|
||||
|
||||
var result = new DeviceOptions();
|
||||
|
||||
foreach (var row in statement.ExecuteQuery())
|
||||
if (row[0].SQLiteType != SQLiteType.Null)
|
||||
{
|
||||
if (row[0].SQLiteType != SQLiteType.Null)
|
||||
{
|
||||
result.CustomName = row[0].ToString();
|
||||
}
|
||||
result.CustomName = row[0].ToString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}, ReadTransactionMode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}, ReadTransactionMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,30 +376,27 @@ namespace Emby.Server.Implementations.Security
|
|||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
using (WriteLock.Write())
|
||||
using (var connection = GetConnection())
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
connection.RunInTransaction(db =>
|
||||
{
|
||||
connection.RunInTransaction(db =>
|
||||
using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))"))
|
||||
{
|
||||
using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))"))
|
||||
statement.TryBind("@Id", deviceId);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.CustomName))
|
||||
{
|
||||
statement.TryBind("@Id", deviceId);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.CustomName))
|
||||
{
|
||||
statement.TryBindNull("@CustomName");
|
||||
}
|
||||
else
|
||||
{
|
||||
statement.TryBind("@CustomName", options.CustomName);
|
||||
}
|
||||
|
||||
statement.MoveNext();
|
||||
statement.TryBindNull("@CustomName");
|
||||
}
|
||||
else
|
||||
{
|
||||
statement.TryBind("@CustomName", options.CustomName);
|
||||
}
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
statement.MoveNext();
|
||||
}
|
||||
|
||||
}, TransactionMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.AspNetCore;
|
||||
using SQLitePCL;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Jellyfin.Server
|
||||
|
@ -126,7 +127,11 @@ namespace Jellyfin.Server
|
|||
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
|
||||
#pragma warning restore CA5359
|
||||
|
||||
SQLitePCL.Batteries_V2.Init();
|
||||
Batteries_V2.Init();
|
||||
if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
|
||||
{
|
||||
Console.WriteLine("WARN: Failed to enable shared cache for SQLite");
|
||||
}
|
||||
|
||||
using (var appHost = new CoreAppHost(
|
||||
appPaths,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Events;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
|
|
Loading…
Reference in New Issue
Block a user