aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Persistence
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/Persistence')
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs24
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs64
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs263
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs35
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs434
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs730
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs47
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs204
10 files changed, 1185 insertions, 649 deletions
diff --git a/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs b/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs
index 395907844..eec5b4b76 100644
--- a/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs
@@ -9,13 +9,35 @@ namespace MediaBrowser.Server.Implementations.Persistence
public abstract class BaseSqliteRepository : IDisposable
{
protected readonly SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1);
+ protected readonly IDbConnector DbConnector;
protected ILogger Logger;
- protected BaseSqliteRepository(ILogManager logManager)
+ protected string DbFilePath { get; set; }
+
+ protected BaseSqliteRepository(ILogManager logManager, IDbConnector dbConnector)
{
+ DbConnector = dbConnector;
Logger = logManager.GetLogger(GetType().Name);
}
+ protected virtual bool EnableConnectionPooling
+ {
+ get { return true; }
+ }
+
+ protected virtual async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
+ {
+ var connection = await DbConnector.Connect(DbFilePath, false, true).ConfigureAwait(false);
+
+ connection.RunQueries(new[]
+ {
+ "pragma temp_store = memory"
+
+ }, Logger);
+
+ return connection;
+ }
+
private bool _disposed;
protected void CheckDisposed()
{
@@ -84,6 +106,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
- protected abstract void CloseConnection();
+ protected virtual void CloseConnection()
+ {
+
+ }
}
-}
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
index 2a2f9a09d..b11a3e496 100644
--- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Server.Implementations.ScheduledTasks;
@@ -145,7 +146,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
- IsCurrentSchema = false
+ IsCurrentSchema = false,
+ ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
});
var numComplete = 0;
@@ -236,14 +238,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
// These have their own cleanup routines
ExcludeItemTypes = new[]
{
- typeof(Person).Name,
- typeof(Genre).Name,
- typeof(MusicGenre).Name,
- typeof(GameGenre).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(Channel).Name,
- typeof(AggregateFolder).Name,
+ typeof(Person).Name,
+ typeof(Genre).Name,
+ typeof(MusicGenre).Name,
+ typeof(GameGenre).Name,
+ typeof(Studio).Name,
+ typeof(Year).Name,
+ typeof(Channel).Name,
+ typeof(AggregateFolder).Name,
typeof(CollectionFolder).Name
}
});
@@ -313,8 +315,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
+ return new ITaskTrigger[]
+ {
new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
};
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs b/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs
index cac9fe983..596cf8407 100644
--- a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs
@@ -5,6 +5,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
public interface IDbConnector
{
- Task<IDbConnection> Connect(string dbPath);
+ Task<IDbConnection> Connect(string dbPath, bool isReadOnly, bool enablePooling = false, int? cacheSize = null);
}
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs
index 948e99cb8..1d9be2e0d 100644
--- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs
@@ -28,6 +28,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
AddNalColumn();
AddIsAvcColumn();
AddTitleColumn();
+ AddTimeBaseColumn();
+ AddCodecTimeBaseColumn();
}
private void AddIsAvcColumn()
@@ -61,6 +63,68 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.RunQueries(new[] { builder.ToString() }, _logger);
}
+ private void AddTimeBaseColumn()
+ {
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = "PRAGMA table_info(mediastreams)";
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ {
+ while (reader.Read())
+ {
+ if (!reader.IsDBNull(1))
+ {
+ var name = reader.GetString(1);
+
+ if (string.Equals(name, "TimeBase", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ var builder = new StringBuilder();
+
+ builder.AppendLine("alter table mediastreams");
+ builder.AppendLine("add column TimeBase TEXT");
+
+ _connection.RunQueries(new[] { builder.ToString() }, _logger);
+ }
+
+ private void AddCodecTimeBaseColumn()
+ {
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = "PRAGMA table_info(mediastreams)";
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ {
+ while (reader.Read())
+ {
+ if (!reader.IsDBNull(1))
+ {
+ var name = reader.GetString(1);
+
+ if (string.Equals(name, "CodecTimeBase", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ var builder = new StringBuilder();
+
+ builder.AppendLine("alter table mediastreams");
+ builder.AppendLine("add column CodecTimeBase TEXT");
+
+ _connection.RunQueries(new[] { builder.ToString() }, _logger);
+ }
+
private void AddTitleColumn()
{
using (var cmd = _connection.CreateCommand())
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
index 6077cfdba..40970dbe4 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
@@ -18,12 +18,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
- private IDbConnection _connection;
-
- public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths) : base(logManager)
+ public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IDbConnector dbConnector)
+ : base(logManager, dbConnector)
{
_jsonSerializer = jsonSerializer;
- _appPaths = appPaths;
+ DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
/// <summary>
@@ -44,32 +43,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
- /// The _app paths
- /// </summary>
- private readonly IApplicationPaths _appPaths;
-
- /// <summary>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public async Task Initialize(IDbConnector dbConnector)
+ public async Task Initialize()
{
- var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db");
-
- _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
-
- string[] queries = {
+ using (var connection = await CreateConnection().ConfigureAwait(false))
+ {
+ string[] queries = {
"create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)",
- "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)",
-
- //pragmas
- "pragma temp_store = memory",
-
- "pragma shrink_memory"
+ "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
};
- _connection.RunQueries(queries, Logger);
+ connection.RunQueries(queries, Logger);
+ }
}
/// <summary>
@@ -96,58 +84,57 @@ namespace MediaBrowser.Server.Implementations.Persistence
var serialized = _jsonSerializer.SerializeToBytes(displayPreferences);
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (var connection = await CreateConnection().ConfigureAwait(false))
{
- transaction = _connection.BeginTransaction();
+ IDbTransaction transaction = null;
- using (var cmd = _connection.CreateCommand())
+ try
{
- cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
+ transaction = connection.BeginTransaction();
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreferences.Id);
- cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@3", DbType.String).Value = client;
- cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
- cmd.Transaction = transaction;
+ cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreferences.Id);
+ cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
+ cmd.Parameters.Add(cmd, "@3", DbType.String).Value = client;
+ cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
- cmd.ExecuteNonQuery();
- }
+ cmd.Transaction = transaction;
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
+ cmd.ExecuteNonQuery();
+ }
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
{
- transaction.Rollback();
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
}
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save display preferences:", e);
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save display preferences:", e);
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
+ throw;
}
-
- throw;
- }
- finally
- {
- if (transaction != null)
+ finally
{
- transaction.Dispose();
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
}
-
- WriteLock.Release();
}
}
@@ -168,64 +155,63 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (var connection = await CreateConnection().ConfigureAwait(false))
{
- transaction = _connection.BeginTransaction();
+ IDbTransaction transaction = null;
- foreach (var displayPreference in displayPreferences)
+ try
{
+ transaction = connection.BeginTransaction();
- var serialized = _jsonSerializer.SerializeToBytes(displayPreference);
-
- using (var cmd = _connection.CreateCommand())
+ foreach (var displayPreference in displayPreferences)
{
- cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreference.Id);
- cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@3", DbType.String).Value = displayPreference.Client;
- cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
+ var serialized = _jsonSerializer.SerializeToBytes(displayPreference);
- cmd.Transaction = transaction;
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
- cmd.ExecuteNonQuery();
+ cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreference.Id);
+ cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
+ cmd.Parameters.Add(cmd, "@3", DbType.String).Value = displayPreference.Client;
+ cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
+
+ cmd.Transaction = transaction;
+
+ cmd.ExecuteNonQuery();
+ }
}
- }
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
{
- transaction.Rollback();
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
}
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save display preferences:", e);
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save display preferences:", e);
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
+ throw;
}
-
- throw;
- }
- finally
- {
- if (transaction != null)
+ finally
{
- transaction.Dispose();
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
}
-
- WriteLock.Release();
}
}
@@ -246,28 +232,33 @@ namespace MediaBrowser.Server.Implementations.Persistence
var guidId = displayPreferencesId.GetMD5();
- var cmd = _connection.CreateCommand();
- cmd.CommandText = "select data from userdisplaypreferences where id = @id and userId=@userId and client=@client";
-
- cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = guidId;
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@client", DbType.String).Value = client;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ using (var connection = CreateConnection(true).Result)
{
- if (reader.Read())
+ using (var cmd = connection.CreateCommand())
{
- using (var stream = reader.GetMemoryStream(0))
+ cmd.CommandText = "select data from userdisplaypreferences where id = @id and userId=@userId and client=@client";
+
+ cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = guidId;
+ cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
+ cmd.Parameters.Add(cmd, "@client", DbType.String).Value = client;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
{
- return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
+ if (reader.Read())
+ {
+ using (var stream = reader.GetMemoryStream(0))
+ {
+ return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
+ }
+ }
}
+
+ return new DisplayPreferences
+ {
+ Id = guidId.ToString("N")
+ };
}
}
-
- return new DisplayPreferences
- {
- Id = guidId.ToString("N")
- };
}
/// <summary>
@@ -278,36 +269,30 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <exception cref="System.ArgumentNullException">item</exception>
public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
{
+ var list = new List<DisplayPreferences>();
- var cmd = _connection.CreateCommand();
- cmd.CommandText = "select data from userdisplaypreferences where userId=@userId";
-
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (var connection = CreateConnection(true).Result)
{
- while (reader.Read())
+ using (var cmd = connection.CreateCommand())
{
- using (var stream = reader.GetMemoryStream(0))
+ cmd.CommandText = "select data from userdisplaypreferences where userId=@userId";
+
+ cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
- yield return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
+ while (reader.Read())
+ {
+ using (var stream = reader.GetMemoryStream(0))
+ {
+ list.Add(_jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream));
+ }
+ }
}
}
}
- }
-
- protected override void CloseConnection()
- {
- if (_connection != null)
- {
- if (_connection.IsOpen())
- {
- _connection.Close();
- }
- _connection.Dispose();
- _connection = null;
- }
+ return list;
}
public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
index dd2f15cfd..d5b582da5 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
@@ -19,43 +19,40 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <summary>
/// Connects to db.
/// </summary>
- /// <param name="dbPath">The db path.</param>
- /// <param name="logger">The logger.</param>
- /// <returns>Task{IDbConnection}.</returns>
- /// <exception cref="System.ArgumentNullException">dbPath</exception>
- public static async Task<IDbConnection> ConnectToDb(string dbPath, ILogger logger)
+ public static async Task<IDbConnection> ConnectToDb(string dbPath, bool isReadOnly, bool enablePooling, int? cacheSize, ILogger logger)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
- logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath);
+ SQLiteConnection.SetMemoryStatus(false);
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
- CacheSize = 2000,
+ CacheSize = cacheSize ?? 2000,
SyncMode = SynchronizationModes.Normal,
DataSource = dbPath,
- JournalMode = SQLiteJournalModeEnum.Wal
+ JournalMode = SQLiteJournalModeEnum.Wal,
+
+ // This is causing crashing under linux
+ Pooling = enablePooling && Environment.OSVersion.Platform == PlatformID.Win32NT,
+ ReadOnly = isReadOnly
};
- var connection = new SQLiteConnection(connectionstr.ConnectionString);
+ var connectionString = connectionstr.ConnectionString;
+
+ if (!enablePooling)
+ {
+ logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, connectionString);
+ }
+
+ var connection = new SQLiteConnection(connectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
}
-
- public static void BindFunction(this SQLiteConnection connection, SQLiteFunction function)
- {
- var attributes = function.GetType().GetCustomAttributes(typeof(SQLiteFunctionAttribute), true).Cast<SQLiteFunctionAttribute>().ToArray();
- if (attributes.Length == 0)
- {
- throw new InvalidOperationException("SQLiteFunction doesn't have SQLiteFunctionAttribute");
- }
- connection.BindFunction(attributes[0], function);
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs
index 037776997..7a5e00090 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs
@@ -16,74 +16,29 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
public class SqliteFileOrganizationRepository : BaseSqliteRepository, IFileOrganizationRepository, IDisposable
{
- private IDbConnection _connection;
-
- private readonly IServerApplicationPaths _appPaths;
-
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private IDbCommand _saveResultCommand;
- private IDbCommand _deleteResultCommand;
- private IDbCommand _deleteAllCommand;
-
- public SqliteFileOrganizationRepository(ILogManager logManager, IServerApplicationPaths appPaths) : base(logManager)
+ public SqliteFileOrganizationRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector connector) : base(logManager, connector)
{
- _appPaths = appPaths;
+ DbFilePath = Path.Combine(appPaths.DataPath, "fileorganization.db");
}
/// <summary>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public async Task Initialize(IDbConnector dbConnector)
+ public async Task Initialize()
{
- var dbFile = Path.Combine(_appPaths.DataPath, "fileorganization.db");
-
- _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
-
- string[] queries = {
+ using (var connection = await CreateConnection().ConfigureAwait(false))
+ {
+ string[] queries = {
"create table if not exists FileOrganizerResults (ResultId GUID PRIMARY KEY, OriginalPath TEXT, TargetPath TEXT, FileLength INT, OrganizationDate datetime, Status TEXT, OrganizationType TEXT, StatusMessage TEXT, ExtractedName TEXT, ExtractedYear int null, ExtractedSeasonNumber int null, ExtractedEpisodeNumber int null, ExtractedEndingEpisodeNumber, DuplicatePaths TEXT int null)",
- "create index if not exists idx_FileOrganizerResults on FileOrganizerResults(ResultId)",
-
- //pragmas
- "pragma temp_store = memory",
-
- "pragma shrink_memory"
+ "create index if not exists idx_FileOrganizerResults on FileOrganizerResults(ResultId)"
};
- _connection.RunQueries(queries, Logger);
-
- PrepareStatements();
- }
-
- private void PrepareStatements()
- {
- _saveResultCommand = _connection.CreateCommand();
- _saveResultCommand.CommandText = "replace into FileOrganizerResults (ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths) values (@ResultId, @OriginalPath, @TargetPath, @FileLength, @OrganizationDate, @Status, @OrganizationType, @StatusMessage, @ExtractedName, @ExtractedYear, @ExtractedSeasonNumber, @ExtractedEpisodeNumber, @ExtractedEndingEpisodeNumber, @DuplicatePaths)";
-
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ResultId");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@OriginalPath");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@TargetPath");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@FileLength");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@OrganizationDate");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@Status");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@OrganizationType");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@StatusMessage");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ExtractedName");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ExtractedYear");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ExtractedSeasonNumber");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ExtractedEpisodeNumber");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@ExtractedEndingEpisodeNumber");
- _saveResultCommand.Parameters.Add(_saveResultCommand, "@DuplicatePaths");
-
- _deleteResultCommand = _connection.CreateCommand();
- _deleteResultCommand.CommandText = "delete from FileOrganizerResults where ResultId = @ResultId";
-
- _deleteResultCommand.Parameters.Add(_saveResultCommand, "@ResultId");
-
- _deleteAllCommand = _connection.CreateCommand();
- _deleteAllCommand.CommandText = "delete from FileOrganizerResults";
+ connection.RunQueries(queries, Logger);
+ }
}
public async Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
@@ -95,65 +50,84 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = _connection.BeginTransaction();
-
- var index = 0;
-
- _saveResultCommand.GetParameter(index++).Value = new Guid(result.Id);
- _saveResultCommand.GetParameter(index++).Value = result.OriginalPath;
- _saveResultCommand.GetParameter(index++).Value = result.TargetPath;
- _saveResultCommand.GetParameter(index++).Value = result.FileSize;
- _saveResultCommand.GetParameter(index++).Value = result.Date;
- _saveResultCommand.GetParameter(index++).Value = result.Status.ToString();
- _saveResultCommand.GetParameter(index++).Value = result.Type.ToString();
- _saveResultCommand.GetParameter(index++).Value = result.StatusMessage;
- _saveResultCommand.GetParameter(index++).Value = result.ExtractedName;
- _saveResultCommand.GetParameter(index++).Value = result.ExtractedYear;
- _saveResultCommand.GetParameter(index++).Value = result.ExtractedSeasonNumber;
- _saveResultCommand.GetParameter(index++).Value = result.ExtractedEpisodeNumber;
- _saveResultCommand.GetParameter(index++).Value = result.ExtractedEndingEpisodeNumber;
- _saveResultCommand.GetParameter(index).Value = string.Join("|", result.DuplicatePaths.ToArray());
-
- _saveResultCommand.Transaction = transaction;
-
- _saveResultCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
+ using (var connection = await CreateConnection().ConfigureAwait(false))
{
- if (transaction != null)
+ using (var saveResultCommand = connection.CreateCommand())
{
- transaction.Rollback();
- }
+ saveResultCommand.CommandText = "replace into FileOrganizerResults (ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths) values (@ResultId, @OriginalPath, @TargetPath, @FileLength, @OrganizationDate, @Status, @OrganizationType, @StatusMessage, @ExtractedName, @ExtractedYear, @ExtractedSeasonNumber, @ExtractedEpisodeNumber, @ExtractedEndingEpisodeNumber, @DuplicatePaths)";
+
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ResultId");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@OriginalPath");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@TargetPath");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@FileLength");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@OrganizationDate");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@Status");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@OrganizationType");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@StatusMessage");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedName");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedYear");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedSeasonNumber");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedEpisodeNumber");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedEndingEpisodeNumber");
+ saveResultCommand.Parameters.Add(saveResultCommand, "@DuplicatePaths");
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = connection.BeginTransaction();
+
+ var index = 0;
+
+ saveResultCommand.GetParameter(index++).Value = new Guid(result.Id);
+ saveResultCommand.GetParameter(index++).Value = result.OriginalPath;
+ saveResultCommand.GetParameter(index++).Value = result.TargetPath;
+ saveResultCommand.GetParameter(index++).Value = result.FileSize;
+ saveResultCommand.GetParameter(index++).Value = result.Date;
+ saveResultCommand.GetParameter(index++).Value = result.Status.ToString();
+ saveResultCommand.GetParameter(index++).Value = result.Type.ToString();
+ saveResultCommand.GetParameter(index++).Value = result.StatusMessage;
+ saveResultCommand.GetParameter(index++).Value = result.ExtractedName;
+ saveResultCommand.GetParameter(index++).Value = result.ExtractedYear;
+ saveResultCommand.GetParameter(index++).Value = result.ExtractedSeasonNumber;
+ saveResultCommand.GetParameter(index++).Value = result.ExtractedEpisodeNumber;
+ saveResultCommand.GetParameter(index++).Value = result.ExtractedEndingEpisodeNumber;
+ saveResultCommand.GetParameter(index).Value = string.Join("|", result.DuplicatePaths.ToArray());
+
+ saveResultCommand.Transaction = transaction;
+
+ saveResultCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save FileOrganizationResult:", e);
+ throw;
+ }
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save FileOrganizationResult:", e);
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+ }
}
-
- WriteLock.Release();
}
}
@@ -164,100 +138,110 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("id");
}
- await WriteLock.WaitAsync().ConfigureAwait(false);
+ using (var connection = await CreateConnection().ConfigureAwait(false))
+ {
+ using (var deleteResultCommand = connection.CreateCommand())
+ {
+ deleteResultCommand.CommandText = "delete from FileOrganizerResults where ResultId = @ResultId";
- IDbTransaction transaction = null;
+ deleteResultCommand.Parameters.Add(deleteResultCommand, "@ResultId");
- try
- {
- transaction = _connection.BeginTransaction();
+ IDbTransaction transaction = null;
- _deleteResultCommand.GetParameter(0).Value = new Guid(id);
+ try
+ {
+ transaction = connection.BeginTransaction();
- _deleteResultCommand.Transaction = transaction;
+ deleteResultCommand.GetParameter(0).Value = new Guid(id);
- _deleteResultCommand.ExecuteNonQuery();
+ deleteResultCommand.Transaction = transaction;
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ deleteResultCommand.ExecuteNonQuery();
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete FileOrganizationResult:", e);
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ throw;
+ }
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to delete FileOrganizationResult:", e);
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- WriteLock.Release();
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+ }
+ }
}
}
public async Task DeleteAll()
{
- await WriteLock.WaitAsync().ConfigureAwait(false);
+ using (var connection = await CreateConnection().ConfigureAwait(false))
+ {
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "delete from FileOrganizerResults";
- IDbTransaction transaction = null;
+ IDbTransaction transaction = null;
- try
- {
- transaction = _connection.BeginTransaction();
-
- _deleteAllCommand.Transaction = transaction;
+ try
+ {
+ transaction = connection.BeginTransaction();
- _deleteAllCommand.ExecuteNonQuery();
+ cmd.Transaction = transaction;
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ cmd.ExecuteNonQuery();
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete results", e);
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ throw;
+ }
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to delete results", e);
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- WriteLock.Release();
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+ }
+ }
}
}
-
+
public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
{
if (query == null)
@@ -265,46 +249,49 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("query");
}
- using (var cmd = _connection.CreateCommand())
+ using (var connection = CreateConnection(true).Result)
{
- cmd.CommandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
-
- if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
+ using (var cmd = connection.CreateCommand())
{
- cmd.CommandText += string.Format(" WHERE ResultId NOT IN (SELECT ResultId FROM FileOrganizerResults ORDER BY OrganizationDate desc LIMIT {0})",
- query.StartIndex.Value.ToString(_usCulture));
- }
+ cmd.CommandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
- cmd.CommandText += " ORDER BY OrganizationDate desc";
+ if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
+ {
+ cmd.CommandText += string.Format(" WHERE ResultId NOT IN (SELECT ResultId FROM FileOrganizerResults ORDER BY OrganizationDate desc LIMIT {0})",
+ query.StartIndex.Value.ToString(_usCulture));
+ }
- if (query.Limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
+ cmd.CommandText += " ORDER BY OrganizationDate desc";
+
+ if (query.Limit.HasValue)
+ {
+ cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
- cmd.CommandText += "; select count (ResultId) from FileOrganizerResults";
+ cmd.CommandText += "; select count (ResultId) from FileOrganizerResults";
- var list = new List<FileOrganizationResult>();
- var count = 0;
+ var list = new List<FileOrganizationResult>();
+ var count = 0;
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
- list.Add(GetResult(reader));
+ while (reader.Read())
+ {
+ list.Add(GetResult(reader));
+ }
+
+ if (reader.NextResult() && reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
}
- if (reader.NextResult() && reader.Read())
+ return new QueryResult<FileOrganizationResult>()
{
- count = reader.GetInt32(0);
- }
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
}
-
- return new QueryResult<FileOrganizationResult>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
}
}
@@ -315,24 +302,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("id");
}
- var guid = new Guid(id);
-
- using (var cmd = _connection.CreateCommand())
+ using (var connection = CreateConnection(true).Result)
{
- cmd.CommandText = "select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@Id";
+ var guid = new Guid(id);
- cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ using (var cmd = connection.CreateCommand())
{
- if (reader.Read())
+ cmd.CommandText = "select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@Id";
+
+ cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
{
- return GetResult(reader);
+ if (reader.Read())
+ {
+ return GetResult(reader);
+ }
}
}
- }
- return null;
+ return null;
+ }
}
public FileOrganizationResult GetResult(IDataReader reader)
@@ -414,19 +404,5 @@ namespace MediaBrowser.Server.Implementations.Persistence
return result;
}
-
- protected override void CloseConnection()
- {
- if (_connection != null)
- {
- if (_connection.IsOpen())
- {
- _connection.Close();
- }
-
- _connection.Dispose();
- _connection = null;
- }
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index 460a67ca7..6d067e345 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Server.Implementations.Persistence
@@ -94,13 +95,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
private IDbCommand _updateInheritedRatingCommand;
private IDbCommand _updateInheritedTagsCommand;
- public const int LatestSchemaVersion = 89;
+ public const int LatestSchemaVersion = 96;
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
- public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager)
- : base(logManager)
+ public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager, IDbConnector connector)
+ : base(logManager, connector)
{
if (config == null)
{
@@ -115,61 +116,72 @@ namespace MediaBrowser.Server.Implementations.Persistence
_jsonSerializer = jsonSerializer;
_criticReviewsPath = Path.Combine(_config.ApplicationPaths.DataPath, "critic-reviews");
+ DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
}
private const string ChaptersTableName = "Chapters2";
+ protected override async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
+ {
+ var connection = await DbConnector.Connect(DbFilePath, false, false, 10000).ConfigureAwait(false);
+
+ connection.RunQueries(new[]
+ {
+ "pragma temp_store = memory",
+ "pragma default_temp_store = memory",
+ "PRAGMA locking_mode=EXCLUSIVE"
+
+ }, Logger);
+
+ return connection;
+ }
+
/// <summary>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public async Task Initialize(IDbConnector dbConnector)
+ public async Task Initialize(SqliteUserDataRepository userDataRepo)
{
- var dbFile = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
-
- _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
+ _connection = await CreateConnection(false).ConfigureAwait(false);
var createMediaStreamsTableCommand
- = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
string[] queries = {
"create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
- "create index if not exists idx_TypedBaseItems2 on TypedBaseItems(Type,Guid)",
"create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
"create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
"create index if not exists idx_AncestorIds2 on AncestorIds(AncestorIdText)",
- "create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT, PRIMARY KEY (ItemId, UserDataKey))",
- "create index if not exists idx_UserDataKeys1 on UserDataKeys(ItemId)",
+ "create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT Priority INT, PRIMARY KEY (ItemId, UserDataKey))",
- "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)",
- "create index if not exists idx_ItemValues on ItemValues(ItemId)",
+ "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
+ //"create index if not exists idx_ItemValues on ItemValues(ItemId)",
"create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)",
"create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT, PRIMARY KEY (ItemId, Name))",
- "create index if not exists Idx_ProviderIds on ProviderIds(ItemId)",
+ // covering index
+ "create index if not exists Idx_ProviderIds1 on ProviderIds(ItemId,Name,Value)",
"create table if not exists Images (ItemId GUID NOT NULL, Path TEXT NOT NULL, ImageType INT NOT NULL, DateModified DATETIME, IsPlaceHolder BIT NOT NULL, SortOrder INT)",
"create index if not exists idx_Images on Images(ItemId)",
"create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
- "create index if not exists idxPeopleItemId on People(ItemId)",
+
+ "drop index if exists idxPeopleItemId",
+ "create index if not exists idxPeopleItemId1 on People(ItemId,ListOrder)",
"create index if not exists idxPeopleName on People(Name)",
"create table if not exists "+ChaptersTableName+" (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))",
- "create index if not exists idx_"+ChaptersTableName+"1 on "+ChaptersTableName+"(ItemId)",
createMediaStreamsTableCommand,
- "create index if not exists idx_mediastreams1 on mediastreams(ItemId)",
- //pragmas
- "pragma temp_store = memory",
+ "create index if not exists idx_mediastreams1 on mediastreams(ItemId)",
- "pragma shrink_memory"
};
_connection.RunQueries(queries, Logger);
@@ -254,16 +266,60 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.AddColumn(Logger, "TypedBaseItems", "Album", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "IsVirtualItem", "BIT");
_connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
+ _connection.AddColumn(Logger, "TypedBaseItems", "UserDataKey", "Text");
_connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
+ _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
string[] postQueries =
- {
+
+ {
+ // obsolete
+ "drop index if exists idx_TypedBaseItems",
+ "drop index if exists idx_mediastreams",
+ "drop index if exists idx_"+ChaptersTableName,
+ "drop index if exists idx_UserDataKeys1",
+ "drop index if exists idx_UserDataKeys2",
+ "drop index if exists idx_TypeTopParentId3",
+ "drop index if exists idx_TypeTopParentId2",
+ "drop index if exists idx_TypeTopParentId4",
+ "drop index if exists idx_Type",
+ "drop index if exists idx_TypeTopParentId",
+ "drop index if exists idx_GuidType",
+ "drop index if exists idx_TopParentId",
+ "drop index if exists idx_TypeTopParentId6",
+ "drop index if exists idx_ItemValues2",
+ "drop index if exists Idx_ProviderIds",
+
"create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
- "create index if not exists idx_Type on TypedBaseItems(Type)",
- "create index if not exists idx_TopParentId on TypedBaseItems(TopParentId)",
- "create index if not exists idx_TypeTopParentId on TypedBaseItems(Type,TopParentId)"
- };
+ "create index if not exists idx_GuidTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,Type,IsFolder,IsVirtualItem)",
+ //"create index if not exists idx_GuidMediaTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,MediaType,IsFolder,IsVirtualItem)",
+ "create index if not exists idx_CleanNameType on TypedBaseItems(CleanName,Type)",
+
+ // covering index
+ "create index if not exists idx_TopParentIdGuid on TypedBaseItems(TopParentId,Guid)",
+
+ // live tv programs
+ "create index if not exists idx_TypeTopParentIdStartDate on TypedBaseItems(Type,TopParentId,StartDate)",
+
+ // used by movie suggestions
+ "create index if not exists idx_TypeTopParentIdGroup on TypedBaseItems(Type,TopParentId,PresentationUniqueKey)",
+ "create index if not exists idx_TypeTopParentId5 on TypedBaseItems(TopParentId,IsVirtualItem)",
+
+ // latest items
+ "create index if not exists idx_TypeTopParentId9 on TypedBaseItems(TopParentId,Type,IsVirtualItem,PresentationUniqueKey,DateCreated)",
+ "create index if not exists idx_TypeTopParentId8 on TypedBaseItems(TopParentId,IsFolder,IsVirtualItem,PresentationUniqueKey,DateCreated)",
+
+ // resume
+ "create index if not exists idx_TypeTopParentId7 on TypedBaseItems(TopParentId,MediaType,IsVirtualItem,PresentationUniqueKey)",
+
+ // items by name
+ "create index if not exists idx_ItemValues3 on ItemValues(ItemId,Type,CleanValue)",
+ "create index if not exists idx_ItemValues4 on ItemValues(ItemId,Type,Value,CleanValue)",
+
+ // covering index
+ "create index if not exists idx_UserDataKeys3 on UserDataKeys(ItemId,Priority,UserDataKey)"
+ };
_connection.RunQueries(postQueries, Logger);
@@ -272,6 +328,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
new MediaStreamColumns(_connection, Logger).AddColumns();
DataExtensions.Attach(_connection, Path.Combine(_config.ApplicationPaths.DataPath, "userdata_v2.db"), "UserDataDb");
+ await userDataRepo.Initialize(_connection).ConfigureAwait(false);
+ //await Vacuum(_connection).ConfigureAwait(false);
}
private readonly string[] _retriveItemColumns =
@@ -368,7 +426,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"Comment",
"NalLengthSize",
"IsAvc",
- "Title"
+ "Title",
+ "TimeBase",
+ "CodecTimeBase"
};
/// <summary>
@@ -451,7 +511,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"DateLastMediaAdded",
"Album",
"IsVirtualItem",
- "SeriesName"
+ "SeriesName",
+ "UserDataKey"
};
_saveItemCommand = _connection.CreateCommand();
_saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
@@ -554,10 +615,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
_deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
_saveItemValuesCommand = _connection.CreateCommand();
- _saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value) values (@ItemId, @Type, @Value)";
+ _saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)";
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@ItemId");
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Type");
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
+ _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@CleanValue");
// provider ids
_deleteProviderIdsCommand = _connection.CreateCommand();
@@ -867,15 +929,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.GetParameter(index++).Value = item.Album;
- var season = item as Season;
- if (season != null && season.IsVirtualItem.HasValue)
- {
- _saveItemCommand.GetParameter(index++).Value = season.IsVirtualItem.Value;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ _saveItemCommand.GetParameter(index++).Value = item.IsVirtualItem || (!item.IsFolder && item.LocationType == LocationType.Virtual);
var hasSeries = item as IHasSeries;
if (hasSeries != null)
@@ -887,6 +941,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.GetParameter(index++).Value = null;
}
+ _saveItemCommand.GetParameter(index++).Value = item.GetUserDataKeys().FirstOrDefault();
+
_saveItemCommand.Transaction = transaction;
_saveItemCommand.ExecuteNonQuery();
@@ -899,7 +955,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
UpdateImages(item.Id, item.ImageInfos, transaction);
UpdateProviderIds(item.Id, item.ProviderIds, transaction);
- UpdateItemValues(item.Id, GetItemValues(item), transaction);
+ UpdateItemValues(item.Id, GetItemValuesToSave(item), transaction);
}
transaction.Commit();
@@ -1304,10 +1360,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.CriticRatingSummary = reader.GetString(57);
}
- var season = item as Season;
- if (season != null && !reader.IsDBNull(58))
+ if (!reader.IsDBNull(58))
{
- season.IsVirtualItem = reader.GetBoolean(58);
+ item.IsVirtualItem = reader.GetBoolean(58);
}
return item;
@@ -1459,7 +1514,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// or
/// cancellationToken
/// </exception>
- public async Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken)
+ public async Task SaveChapters(Guid id, List<ChapterInfo> chapters, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1686,10 +1741,42 @@ namespace MediaBrowser.Server.Implementations.Persistence
return string.Empty;
}
- return " left join UserDataDb.UserData on (select UserDataKey from UserDataKeys where ItemId=Guid order by Priority LIMIT 1)=UserDataDb.UserData.Key";
+ if (_config.Configuration.SchemaVersion >= 96)
+ {
+ return " left join UserDataDb.UserData on UserDataKey=UserDataDb.UserData.Key And (UserId=@UserId)";
+ }
+
+ return " left join UserDataDb.UserData on (select UserDataKey from UserDataKeys where ItemId=Guid order by Priority LIMIT 1)=UserDataDb.UserData.Key And (UserId=@UserId)";
+ }
+
+ private string GetGroupBy(InternalItemsQuery query)
+ {
+ var groups = new List<string>();
+
+ if (!string.IsNullOrWhiteSpace(query.GroupByAncestorOfType))
+ {
+ groups.Add("(Select PresentationUniqueKey from TypedBaseItems B where B.Type = 'MediaBrowser.Controller.Entities.TV.Series' And B.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))");
+ }
+
+ if (EnableGroupByPresentationUniqueKey(query))
+ {
+ groups.Add("PresentationUniqueKey");
+ }
+
+ if (groups.Count > 0)
+ {
+ return " Group by " + string.Join(",", groups.ToArray());
+ }
+
+ return string.Empty;
}
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
+ private string GetFromText()
+ {
+ return " from TypedBaseItems A";
+ }
+
+ public List<BaseItem> GetItemList(InternalItemsQuery query)
{
if (query == null)
{
@@ -1700,9 +1787,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
var now = DateTime.UtcNow;
+ var list = new List<BaseItem>();
+
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + " from TypedBaseItems";
+ cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + GetFromText();
cmd.CommandText += GetJoinUserDataText(query);
if (EnableJoinUserData(query))
@@ -1718,22 +1807,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- cmd.CommandText += " Group by PresentationUniqueKey";
- }
+ cmd.CommandText += GetGroupBy(query);
cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
- var limit = query.Limit ?? int.MaxValue;
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.StartIndex.HasValue)
+ if (offset > 0)
{
- cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
}
@@ -1746,11 +1835,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
var item = GetItem(reader);
if (item != null)
{
- yield return item;
+ list.Add(item);
}
}
}
}
+
+ return list;
}
private void LogQueryTime(string methodName, IDbCommand cmd, DateTime startDate)
@@ -1760,7 +1851,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
var slowThreshold = 1000;
#if DEBUG
- slowThreshold = 100;
+ slowThreshold = 50;
#endif
if (elapsed >= slowThreshold)
@@ -1788,11 +1879,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
+ if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
+ {
+ var list = GetItemList(query);
+ return new QueryResult<BaseItem>
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = list.Count
+ };
+ }
+
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + " from TypedBaseItems";
+ cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + GetFromText();
cmd.CommandText += GetJoinUserDataText(query);
if (EnableJoinUserData(query))
@@ -1812,32 +1913,41 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- cmd.CommandText += " Group by PresentationUniqueKey";
- }
+ cmd.CommandText += GetGroupBy(query);
cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
- var limit = query.Limit ?? int.MaxValue;
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.StartIndex.HasValue)
+ if (offset > 0)
{
- cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
}
+ cmd.CommandText += ";";
+
+ var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+
+ if (isReturningZeroItems)
+ {
+ cmd.CommandText = "";
+ }
+
if (EnableGroupByPresentationUniqueKey(query))
{
- cmd.CommandText += "; select count (distinct PresentationUniqueKey) from TypedBaseItems";
+ cmd.CommandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
}
else
{
- cmd.CommandText += "; select count (guid) from TypedBaseItems";
+ cmd.CommandText += " select count (guid)" + GetFromText();
}
cmd.CommandText += GetJoinUserDataText(query);
@@ -1850,18 +1960,28 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
LogQueryTime("GetItems", cmd, now);
- while (reader.Read())
+ if (isReturningZeroItems)
{
- var item = GetItem(reader);
- if (item != null)
+ if (reader.Read())
{
- list.Add(item);
+ count = reader.GetInt32(0);
}
}
-
- if (reader.NextResult() && reader.Read())
+ else
{
- count = reader.GetInt32(0);
+ while (reader.Read())
+ {
+ var item = GetItem(reader);
+ if (item != null)
+ {
+ list.Add(item);
+ }
+ }
+
+ if (reader.NextResult() && reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
}
}
@@ -1881,11 +2001,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
if (query.User != null)
{
- query.SortBy = new[] { "SimilarityScore", "IsPlayed", "Random" };
+ query.SortBy = new[] { "SimilarityScore", ItemSortBy.IsPlayed, ItemSortBy.Random };
}
else
{
- query.SortBy = new[] { "SimilarityScore", "Random" };
+ query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
}
query.SortOrder = SortOrder.Descending;
}
@@ -1900,7 +2020,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
return " ORDER BY " + string.Join(",", query.SortBy.Select(i =>
{
- var columnMap = MapOrderByField(i);
+ var columnMap = MapOrderByField(i, query);
var columnAscending = isAscending;
if (columnMap.Item2)
{
@@ -1913,7 +2033,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}).ToArray());
}
- private Tuple<string, bool> MapOrderByField(string name)
+ private Tuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
@@ -1972,6 +2092,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
return new Tuple<string, bool>("(select value from itemvalues where ItemId=Guid and Type=3 LIMIT 1)", false);
}
+ if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
+ {
+ return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
+ }
return new Tuple<string, bool>(name, false);
}
@@ -1989,7 +2113,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + " from TypedBaseItems";
+ cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + GetFromText();
cmd.CommandText += GetJoinUserDataText(query);
if (EnableJoinUserData(query))
@@ -2005,22 +2129,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- cmd.CommandText += " Group by PresentationUniqueKey";
- }
+ cmd.CommandText += GetGroupBy(query);
cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
- var limit = query.Limit ?? int.MaxValue;
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.StartIndex.HasValue)
+ if (offset > 0)
{
- cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
}
@@ -2065,22 +2189,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- cmd.CommandText += " Group by PresentationUniqueKey";
- }
+ cmd.CommandText += GetGroupBy(query);
cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
- var limit = query.Limit ?? int.MaxValue;
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.StartIndex.HasValue)
+ if (offset > 0)
{
- cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
}
@@ -2128,11 +2252,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
+ if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
+ {
+ var list = GetItemIdsList(query);
+ return new QueryResult<Guid>
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = list.Count
+ };
+ }
+
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + " from TypedBaseItems";
+ cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + GetFromText();
var whereClauses = GetWhereClauses(query, cmd);
cmd.CommandText += GetJoinUserDataText(query);
@@ -2148,32 +2282,32 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- cmd.CommandText += " Group by PresentationUniqueKey";
- }
+ cmd.CommandText += GetGroupBy(query);
cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue || query.StartIndex.HasValue)
{
- var limit = query.Limit ?? int.MaxValue;
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (query.StartIndex.HasValue)
+ if (offset > 0)
{
- cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
}
if (EnableGroupByPresentationUniqueKey(query))
{
- cmd.CommandText += "; select count (distinct PresentationUniqueKey) from TypedBaseItems";
+ cmd.CommandText += "; select count (distinct PresentationUniqueKey)" + GetFromText();
}
else
{
- cmd.CommandText += "; select count (guid) from TypedBaseItems";
+ cmd.CommandText += "; select count (guid)" + GetFromText();
}
cmd.CommandText += GetJoinUserDataText(query);
@@ -2205,13 +2339,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
- private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd)
+ private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, string paramSuffix = "")
{
var whereClauses = new List<string>();
if (EnableJoinUserData(query))
{
- whereClauses.Add("(UserId is null or UserId=@UserId)");
+ //whereClauses.Add("(UserId is null or UserId=@UserId)");
}
if (query.IsCurrentSchema.HasValue)
{
@@ -2281,8 +2415,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
if (includeTypes.Length == 1)
{
- whereClauses.Add("type=@type");
- cmd.Parameters.Add(cmd, "@type", DbType.String).Value = includeTypes[0];
+ whereClauses.Add("type=@type" + paramSuffix);
+ cmd.Parameters.Add(cmd, "@type" + paramSuffix, DbType.String).Value = includeTypes[0];
}
else if (includeTypes.Length > 1)
{
@@ -2365,6 +2499,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
cmd.Parameters.Add(cmd, "@ParentIndexNumber", DbType.Int32).Value = query.ParentIndexNumber.Value;
}
+ if (query.ParentIndexNumberNotEquals.HasValue)
+ {
+ whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
+ cmd.Parameters.Add(cmd, "@ParentIndexNumberNotEquals", DbType.Int32).Value = query.ParentIndexNumberNotEquals.Value;
+ }
if (query.MinEndDate.HasValue)
{
whereClauses.Add("EndDate>=@MinEndDate");
@@ -2581,8 +2720,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var artist in query.ArtistNames)
{
- clauses.Add("@ArtistName" + index + " in (select value from itemvalues where ItemId=Guid and Type <= 1)");
- cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist;
+ clauses.Add("@ArtistName" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)");
+ cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist.RemoveDiacritics();
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2595,8 +2734,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var item in query.Genres)
{
- clauses.Add("@Genre" + index + " in (select value from itemvalues where ItemId=Guid and Type=2)");
- cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item;
+ clauses.Add("@Genre" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
+ cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item.RemoveDiacritics();
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2609,8 +2748,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var item in query.Tags)
{
- clauses.Add("@Tag" + index + " in (select value from itemvalues where ItemId=Guid and Type=4)");
- cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item;
+ clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
+ cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item.RemoveDiacritics();
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2623,8 +2762,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var item in query.Studios)
{
- clauses.Add("@Studio" + index + " in (select value from itemvalues where ItemId=Guid and Type=3)");
- cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item;
+ clauses.Add("@Studio" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
+ cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item.RemoveDiacritics();
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2637,8 +2776,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var item in query.Keywords)
{
- clauses.Add("@Keyword" + index + " in (select value from itemvalues where ItemId=Guid and Type=5)");
- cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item;
+ clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)");
+ cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item.RemoveDiacritics();
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2717,8 +2856,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.LocationTypes.Length == 1)
{
- whereClauses.Add("LocationType=@LocationType");
- cmd.Parameters.Add(cmd, "@LocationType", DbType.String).Value = query.LocationTypes[0].ToString();
+ if (query.LocationTypes[0] == LocationType.Virtual && _config.Configuration.SchemaVersion >= 90)
+ {
+ query.IsVirtualItem = true;
+ }
+ else
+ {
+ whereClauses.Add("LocationType=@LocationType");
+ cmd.Parameters.Add(cmd, "@LocationType", DbType.String).Value = query.LocationTypes[0].ToString();
+ }
}
else if (query.LocationTypes.Length > 1)
{
@@ -2728,8 +2874,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if (query.ExcludeLocationTypes.Length == 1)
{
- whereClauses.Add("LocationType<>@ExcludeLocationTypes");
- cmd.Parameters.Add(cmd, "@ExcludeLocationTypes", DbType.String).Value = query.ExcludeLocationTypes[0].ToString();
+ if (query.ExcludeLocationTypes[0] == LocationType.Virtual && _config.Configuration.SchemaVersion >= 90)
+ {
+ query.IsVirtualItem = false;
+ }
+ else
+ {
+ whereClauses.Add("LocationType<>@ExcludeLocationTypes");
+ cmd.Parameters.Add(cmd, "@ExcludeLocationTypes", DbType.String).Value = query.ExcludeLocationTypes[0].ToString();
+ }
}
else if (query.ExcludeLocationTypes.Length > 1)
{
@@ -2737,6 +2890,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("LocationType not in (" + val + ")");
}
+ if (query.IsVirtualItem.HasValue)
+ {
+ whereClauses.Add("IsVirtualItem=@IsVirtualItem");
+ cmd.Parameters.Add(cmd, "@IsVirtualItem", DbType.Boolean).Value = query.IsVirtualItem.Value;
+ }
if (query.MediaTypes.Length == 1)
{
whereClauses.Add("MediaType=@MediaTypes");
@@ -2748,6 +2906,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("MediaType in (" + val + ")");
}
+ if (query.ItemIds.Length > 0)
+ {
+ var excludeIds = new List<string>();
+
+ var index = 0;
+ foreach (var id in query.ItemIds)
+ {
+ excludeIds.Add("Guid = @IncludeId" + index);
+ cmd.Parameters.Add(cmd, "@IncludeId" + index, DbType.Guid).Value = new Guid(id);
+ index++;
+ }
+
+ whereClauses.Add(string.Join(" OR ", excludeIds.ToArray()));
+ }
if (query.ExcludeItemIds.Length > 0)
{
var excludeIds = new List<string>();
@@ -3414,7 +3586,294 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
- private List<Tuple<int, string>> GetItemValues(BaseItem item)
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 0, typeof(MusicArtist).FullName);
+ }
+
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 1, typeof(MusicArtist).FullName);
+ }
+
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 3, typeof(Studio).FullName);
+ }
+
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 2, typeof(Genre).FullName);
+ }
+
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 2, typeof(GameGenre).FullName);
+ }
+
+ public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+ {
+ return GetItemValues(query, 2, typeof(MusicGenre).FullName);
+ }
+
+ private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int itemValueType, string returnType)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ if (!query.Limit.HasValue)
+ {
+ query.EnableTotalRecordCount = false;
+ }
+
+ CheckDisposed();
+
+ var now = DateTime.UtcNow;
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ var itemCountColumns = new List<Tuple<string, string>>();
+
+ var typesToCount = query.IncludeItemTypes.ToList();
+
+ if (typesToCount.Count == 0)
+ {
+ //typesToCount.Add("Item");
+ }
+
+ foreach (var type in typesToCount)
+ {
+ var itemCountColumnQuery = "Select Count(Value) from ItemValues where ItemValues.CleanValue=CleanName AND Type=@ItemValueType AND ItemId in (";
+ itemCountColumnQuery += "select guid" + GetFromText();
+
+ var typeSubQuery = new InternalItemsQuery(query.User)
+ {
+ ExcludeItemTypes = query.ExcludeItemTypes,
+ MediaTypes = query.MediaTypes,
+ AncestorIds = query.AncestorIds,
+ ExcludeItemIds = query.ExcludeItemIds,
+ ItemIds = query.ItemIds,
+ TopParentIds = query.TopParentIds,
+ ParentId = query.ParentId,
+ IsPlayed = query.IsPlayed
+ };
+ if (string.Equals(type, "Item", StringComparison.OrdinalIgnoreCase))
+ {
+ typeSubQuery.IncludeItemTypes = query.IncludeItemTypes;
+ }
+ else
+ {
+ typeSubQuery.IncludeItemTypes = new[] { type };
+ }
+ var whereClauses = GetWhereClauses(typeSubQuery, cmd, type);
+
+ var typeWhereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ itemCountColumnQuery += typeWhereText;
+
+ itemCountColumnQuery += ")";
+
+ var columnName = type + "Count";
+
+ itemCountColumns.Add(new Tuple<string, string>(columnName, "(" + itemCountColumnQuery + ") as " + columnName));
+ }
+
+ var columns = _retriveItemColumns.ToList();
+ columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
+
+ cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray(), cmd)) + GetFromText();
+ cmd.CommandText += GetJoinUserDataText(query);
+
+ var innerQuery = new InternalItemsQuery(query.User)
+ {
+ ExcludeItemTypes = query.ExcludeItemTypes,
+ IncludeItemTypes = query.IncludeItemTypes,
+ MediaTypes = query.MediaTypes,
+ AncestorIds = query.AncestorIds,
+ ExcludeItemIds = query.ExcludeItemIds,
+ ItemIds = query.ItemIds,
+ TopParentIds = query.TopParentIds,
+ ParentId = query.ParentId,
+ IsPlayed = query.IsPlayed
+ };
+
+ var innerWhereClauses = GetWhereClauses(innerQuery, cmd);
+
+ var innerWhereText = innerWhereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", innerWhereClauses.ToArray());
+
+ var whereText = " where Type=@SelectType";
+ whereText += " And CleanName In (Select CleanValue from ItemValues where Type=@ItemValueType AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
+
+ var outerQuery = new InternalItemsQuery(query.User)
+ {
+ IsFavorite = query.IsFavorite,
+ IsFavoriteOrLiked = query.IsFavoriteOrLiked,
+ IsLiked = query.IsLiked,
+ IsLocked = query.IsLocked,
+ NameLessThan = query.NameLessThan,
+ NameStartsWith = query.NameStartsWith,
+ NameStartsWithOrGreater = query.NameStartsWithOrGreater,
+ AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
+ Tags = query.Tags,
+ OfficialRatings = query.OfficialRatings,
+ Genres = query.GenreIds,
+ Years = query.Years
+ };
+
+ var outerWhereClauses = GetWhereClauses(outerQuery, cmd);
+
+ whereText += outerWhereClauses.Count == 0 ?
+ string.Empty :
+ " AND " + string.Join(" AND ", outerWhereClauses.ToArray());
+ //cmd.CommandText += GetGroupBy(query);
+
+ cmd.CommandText += whereText;
+ cmd.CommandText += " group by PresentationUniqueKey";
+
+ cmd.Parameters.Add(cmd, "@SelectType", DbType.String).Value = returnType;
+ cmd.Parameters.Add(cmd, "@ItemValueType", DbType.Int32).Value = itemValueType;
+
+ if (EnableJoinUserData(query))
+ {
+ cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
+ }
+
+ cmd.CommandText += " order by SortName";
+
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
+
+ if (query.Limit.HasValue || offset > 0)
+ {
+ cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
+
+ if (offset > 0)
+ {
+ cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ cmd.CommandText += ";";
+
+ var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+
+ if (isReturningZeroItems)
+ {
+ cmd.CommandText = "";
+ }
+
+ if (query.EnableTotalRecordCount)
+ {
+ cmd.CommandText += "select count (distinct PresentationUniqueKey)" + GetFromText();
+
+ cmd.CommandText += GetJoinUserDataText(query);
+ cmd.CommandText += whereText;
+ }
+ else
+ {
+ cmd.CommandText = cmd.CommandText.TrimEnd(';');
+ }
+
+ var list = new List<Tuple<BaseItem, ItemCounts>>();
+ var count = 0;
+
+ var commandBehavior = isReturningZeroItems || !query.EnableTotalRecordCount
+ ? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)
+ : CommandBehavior.SequentialAccess;
+
+ using (var reader = cmd.ExecuteReader(commandBehavior))
+ {
+ LogQueryTime("GetItemValues", cmd, now);
+
+ if (isReturningZeroItems)
+ {
+ if (reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
+ }
+ else
+ {
+ while (reader.Read())
+ {
+ var item = GetItem(reader);
+ if (item != null)
+ {
+ var countStartColumn = columns.Count - typesToCount.Count;
+
+ list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(reader, countStartColumn, typesToCount)));
+ }
+ }
+
+ if (reader.NextResult() && reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
+ }
+ }
+
+ if (count == 0)
+ {
+ count = list.Count;
+ }
+
+ return new QueryResult<Tuple<BaseItem, ItemCounts>>
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+
+ }
+ }
+
+ private ItemCounts GetItemCounts(IDataReader reader, int countStartColumn, List<string> typesToCount)
+ {
+ var counts = new ItemCounts();
+
+ for (var i = 0; i < typesToCount.Count; i++)
+ {
+ var value = reader.GetInt32(countStartColumn + i);
+
+ var type = typesToCount[i];
+ if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.SeriesCount = value;
+ }
+ else if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.EpisodeCount = value;
+ }
+ else if (string.Equals(type, "Movie", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.MovieCount = value;
+ }
+ else if (string.Equals(type, "MusicAlbum", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.AlbumCount = value;
+ }
+ else if (string.Equals(type, "Audio", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.SongCount = value;
+ }
+ else if (string.Equals(type, "Game", StringComparison.OrdinalIgnoreCase))
+ {
+ counts.GameCount = value;
+ }
+ counts.ItemCount += value;
+ }
+
+ return counts;
+ }
+
+ private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item)
{
var list = new List<Tuple<int, string>>();
@@ -3540,6 +3999,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemValuesCommand.GetParameter(0).Value = itemId;
_saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
_saveItemValuesCommand.GetParameter(2).Value = pair.Item2;
+ if (pair.Item2 == null)
+ {
+ _saveItemValuesCommand.GetParameter(3).Value = null;
+ }
+ else
+ {
+ _saveItemValuesCommand.GetParameter(3).Value = pair.Item2.RemoveDiacritics();
+ }
_saveItemValuesCommand.Transaction = transaction;
_saveItemValuesCommand.ExecuteNonQuery();
@@ -3732,7 +4199,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
return list;
}
- public async Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken)
+ public async Task SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -3805,6 +4272,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveStreamCommand.GetParameter(index++).Value = stream.IsAVC;
_saveStreamCommand.GetParameter(index++).Value = stream.Title;
+ _saveStreamCommand.GetParameter(index++).Value = stream.TimeBase;
+ _saveStreamCommand.GetParameter(index++).Value = stream.CodecTimeBase;
+
_saveStreamCommand.Transaction = transaction;
_saveStreamCommand.ExecuteNonQuery();
}
@@ -3977,6 +4447,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.Title = reader.GetString(29);
}
+ if (!reader.IsDBNull(30))
+ {
+ item.TimeBase = reader.GetString(30);
+ }
+
+ if (!reader.IsDBNull(31))
+ {
+ item.CodecTimeBase = reader.GetString(31);
+ }
+
return item;
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
index bfdb9e0c7..90dbd7770 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
@@ -16,11 +16,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
private IDbConnection _connection;
- private readonly IApplicationPaths _appPaths;
- public SqliteUserDataRepository(ILogManager logManager, IApplicationPaths appPaths) : base(logManager)
+ public SqliteUserDataRepository(ILogManager logManager, IApplicationPaths appPaths, IDbConnector connector) : base(logManager, connector)
{
- _appPaths = appPaths;
+ DbFilePath = Path.Combine(appPaths.DataPath, "userdata_v2.db");
+ }
+
+ protected override bool EnableConnectionPooling
+ {
+ get { return false; }
}
/// <summary>
@@ -35,22 +39,38 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
+ protected override async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
+ {
+ var connection = await DbConnector.Connect(DbFilePath, false, false, 10000).ConfigureAwait(false);
+
+ connection.RunQueries(new[]
+ {
+ "pragma temp_store = memory"
+
+ }, Logger);
+
+ return connection;
+ }
+
/// <summary>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public async Task Initialize(IDbConnector dbConnector)
+ public async Task Initialize(IDbConnection connection)
{
- var dbFile = Path.Combine(_appPaths.DataPath, "userdata_v2.db");
-
- _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
+ _connection = connection;
string[] queries = {
- "create table if not exists userdata (key nvarchar, userId GUID, rating float null, played bit, playCount int, isFavorite bit, playbackPositionTicks bigint, lastPlayedDate datetime null)",
+ "create table if not exists UserDataDb.userdata (key nvarchar, userId GUID, rating float null, played bit, playCount int, isFavorite bit, playbackPositionTicks bigint, lastPlayedDate datetime null)",
+
+ "drop index if exists UserDataDb.idx_userdata",
+ "drop index if exists UserDataDb.idx_userdata1",
+ "drop index if exists UserDataDb.idx_userdata2",
+ "drop index if exists UserDataDb.userdataindex1",
- "create index if not exists idx_userdata on userdata(key)",
- "create unique index if not exists userdataindex on userdata (key, userId)",
+ "create unique index if not exists UserDataDb.userdataindex on userdata (key, userId)",
+ "create index if not exists UserDataDb.userdataindex2 on userdata (key, userId, played)",
//pragmas
"pragma temp_store = memory",
@@ -316,18 +336,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
var index = 0;
- var excludeIds = new List<string>();
+ var userdataKeys = new List<string>();
var builder = new StringBuilder();
foreach (var key in keys)
{
var paramName = "@Key" + index;
- excludeIds.Add("Key =" + paramName);
+ userdataKeys.Add("Key =" + paramName);
cmd.Parameters.Add(cmd, paramName, DbType.String).Value = key;
builder.Append(" WHEN Key=" + paramName + " THEN " + index);
index++;
+ break;
}
- var keyText = string.Join(" OR ", excludeIds.ToArray());
+ var keyText = string.Join(" OR ", userdataKeys.ToArray());
cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId AND (" + keyText + ") ";
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
index f7ca39a54..25ab60ca5 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
@@ -17,14 +17,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
{
- private IDbConnection _connection;
- private readonly IServerApplicationPaths _appPaths;
private readonly IJsonSerializer _jsonSerializer;
- public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer) : base(logManager)
+ public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IDbConnector dbConnector) : base(logManager, dbConnector)
{
- _appPaths = appPaths;
_jsonSerializer = jsonSerializer;
+
+ DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
}
/// <summary>
@@ -43,25 +42,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
- public async Task Initialize(IDbConnector dbConnector)
+ public async Task Initialize()
{
- var dbFile = Path.Combine(_appPaths.DataPath, "users.db");
-
- _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
-
- string[] queries = {
+ using (var connection = await CreateConnection().ConfigureAwait(false))
+ {
+ string[] queries = {
"create table if not exists users (guid GUID primary key, data BLOB)",
"create index if not exists idx_users on users(guid)",
"create table if not exists schema_version (table_name primary key, version)",
- //pragmas
- "pragma temp_store = memory",
-
"pragma shrink_memory"
};
- _connection.RunQueries(queries, Logger);
+ connection.RunQueries(queries, Logger);
+ }
}
/// <summary>
@@ -84,55 +79,54 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (var connection = await CreateConnection().ConfigureAwait(false))
{
- transaction = _connection.BeginTransaction();
+ IDbTransaction transaction = null;
- using (var cmd = _connection.CreateCommand())
+ try
{
- cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = user.Id;
- cmd.Parameters.Add(cmd, "@2", DbType.Binary).Value = serialized;
+ transaction = connection.BeginTransaction();
+
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
+ cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = user.Id;
+ cmd.Parameters.Add(cmd, "@2", DbType.Binary).Value = serialized;
- cmd.Transaction = transaction;
+ cmd.Transaction = transaction;
- cmd.ExecuteNonQuery();
- }
+ cmd.ExecuteNonQuery();
+ }
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
{
- transaction.Rollback();
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
}
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save user:", e);
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save user:", e);
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
+ throw;
}
-
- throw;
- }
- finally
- {
- if (transaction != null)
+ finally
{
- transaction.Dispose();
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
}
-
- WriteLock.Release();
}
}
@@ -142,25 +136,32 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>IEnumerable{User}.</returns>
public IEnumerable<User> RetrieveAllUsers()
{
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select guid,data from users";
+ var list = new List<User>();
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (var connection = CreateConnection(true).Result)
+ {
+ using (var cmd = connection.CreateCommand())
{
- while (reader.Read())
- {
- var id = reader.GetGuid(0);
+ cmd.CommandText = "select guid,data from users";
- using (var stream = reader.GetMemoryStream(1))
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ {
+ while (reader.Read())
{
- var user = _jsonSerializer.DeserializeFromStream<User>(stream);
- user.Id = id;
- yield return user;
+ var id = reader.GetGuid(0);
+
+ using (var stream = reader.GetMemoryStream(1))
+ {
+ var user = _jsonSerializer.DeserializeFromStream<User>(stream);
+ user.Id = id;
+ list.Add(user);
+ }
}
}
}
}
+
+ return list;
}
/// <summary>
@@ -179,69 +180,54 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (var connection = await CreateConnection().ConfigureAwait(false))
{
- transaction = _connection.BeginTransaction();
+ IDbTransaction transaction = null;
- using (var cmd = _connection.CreateCommand())
+ try
{
- cmd.CommandText = "delete from users where guid=@guid";
+ transaction = connection.BeginTransaction();
- cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = user.Id;
-
- cmd.Transaction = transaction;
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "delete from users where guid=@guid";
- cmd.ExecuteNonQuery();
- }
+ cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = user.Id;
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ cmd.Transaction = transaction;
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete user:", e);
+ cmd.ExecuteNonQuery();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
+ transaction.Commit();
}
-
- throw;
- }
- finally
- {
- if (transaction != null)
+ catch (OperationCanceledException)
{
- transaction.Dispose();
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
}
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to delete user:", e);
- WriteLock.Release();
- }
- }
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
- protected override void CloseConnection()
- {
- if (_connection != null)
- {
- if (_connection.IsOpen())
+ throw;
+ }
+ finally
{
- _connection.Close();
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
}
-
- _connection.Dispose();
- _connection = null;
}
}
}