loading works

pull/10138/head
cvium 10 months ago
parent d3f8874a3e
commit 613f4296e3

@ -27,6 +27,7 @@
<PackageVersion Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />

@ -5,8 +5,8 @@
using System;
using System.Collections.Generic;
using Jellyfin.Extensions;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
@ -45,24 +45,6 @@ namespace Emby.Server.Implementations.Data
/// <value>The logger.</value>
protected ILogger<BaseSqliteRepository> 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;
/// <summary>
/// Gets the cache size.
/// </summary>
@ -107,23 +89,8 @@ namespace Emby.Server.Implementations.Data
/// <see cref="SynchronousMode"/>
protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal;
/// <summary>
/// Gets or sets the write lock.
/// </summary>
/// <value>The write lock.</value>
protected ConnectionPool WriteConnections { get; set; }
/// <summary>
/// Gets or sets the write connection.
/// </summary>
/// <value>The write connection.</value>
protected ConnectionPool ReadConnections { get; set; }
public virtual void Initialize()
{
WriteConnections = new ConnectionPool(WriteConnectionsCount, CreateWriteConnection);
ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection);
// Configuration and pragmas can affect VACUUM so it needs to be last.
using (var connection = GetConnection())
{
@ -131,15 +98,9 @@ namespace Emby.Server.Implementations.Data
}
}
protected ManagedConnection GetConnection(bool readOnly = false)
=> readOnly ? ReadConnections.GetConnection() : WriteConnections.GetConnection();
protected SQLiteDatabaseConnection CreateWriteConnection()
protected SqliteConnection GetConnection(bool readOnly = false)
{
var writeConnection = SQLite3.Open(
DbFilePath,
DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
null);
var writeConnection = new SqliteConnection($"Filename={DbFilePath}");
if (CacheSize.HasValue)
{
@ -176,50 +137,14 @@ namespace Emby.Server.Implementations.Data
return writeConnection;
}
protected SQLiteDatabaseConnection CreateReadConnection()
public SqliteCommand PrepareStatement(SqliteConnection connection, string sql)
{
var connection = SQLite3.Open(
DbFilePath,
DefaultConnectionFlags | ConnectionFlags.ReadOnly,
null);
if (CacheSize.HasValue)
{
connection.Execute("PRAGMA cache_size=" + CacheSize.Value);
}
if (!string.IsNullOrWhiteSpace(LockingMode))
{
connection.Execute("PRAGMA locking_mode=" + LockingMode);
}
if (!string.IsNullOrWhiteSpace(JournalMode))
{
connection.Execute("PRAGMA journal_mode=" + JournalMode);
}
if (JournalSizeLimit.HasValue)
{
connection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
}
if (Synchronous.HasValue)
{
connection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
}
connection.Execute("PRAGMA temp_store=" + (int)TempStore);
return connection;
var command = connection.CreateCommand();
command.CommandText = sql;
return command;
}
public IStatement PrepareStatement(ManagedConnection connection, string sql)
=> connection.PrepareStatement(sql);
public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
=> connection.PrepareStatement(sql);
protected bool TableExists(ManagedConnection connection, string name)
protected bool TableExists(SqliteConnection connection, string name)
{
return connection.RunInTransaction(
db =>
@ -236,11 +161,10 @@ namespace Emby.Server.Implementations.Data
}
return false;
},
ReadTransactionMode);
});
}
protected List<string> GetColumnNames(IDatabaseConnection connection, string table)
protected List<string> GetColumnNames(SqliteConnection connection, string table)
{
var columnNames = new List<string>();
@ -255,7 +179,7 @@ namespace Emby.Server.Implementations.Data
return columnNames;
}
protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
protected void AddColumn(SqliteConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
{
if (existingColumnNames.Contains(columnName, StringComparison.OrdinalIgnoreCase))
{
@ -291,12 +215,6 @@ namespace Emby.Server.Implementations.Data
return;
}
if (dispose)
{
WriteConnections.Dispose();
ReadConnections.Dispose();
}
_disposed = true;
}
}

@ -1,79 +0,0 @@
using System;
using System.Collections.Concurrent;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data;
/// <summary>
/// A pool of SQLite Database connections.
/// </summary>
public sealed class ConnectionPool : IDisposable
{
private readonly BlockingCollection<SQLiteDatabaseConnection> _connections = new();
private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionPool" /> class.
/// </summary>
/// <param name="count">The number of database connection to create.</param>
/// <param name="factory">Factory function to create the database connections.</param>
public ConnectionPool(int count, Func<SQLiteDatabaseConnection> factory)
{
for (int i = 0; i < count; i++)
{
_connections.Add(factory.Invoke());
}
}
/// <summary>
/// Gets a database connection from the pool if one is available, otherwise blocks.
/// </summary>
/// <returns>A database connection.</returns>
public ManagedConnection GetConnection()
{
if (_disposed)
{
ThrowObjectDisposedException();
}
return new ManagedConnection(_connections.Take(), this);
static void ThrowObjectDisposedException()
{
throw new ObjectDisposedException(nameof(ConnectionPool));
}
}
/// <summary>
/// Return a database connection to the pool.
/// </summary>
/// <param name="connection">The database connection to return.</param>
public void Return(SQLiteDatabaseConnection connection)
{
if (_disposed)
{
connection.Dispose();
return;
}
_connections.Add(connection);
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
foreach (var connection in _connections)
{
connection.Dispose();
}
_connections.Dispose();
_disposed = true;
}
}

@ -1,81 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public sealed class ManagedConnection : IDisposable
{
private readonly ConnectionPool _pool;
private SQLiteDatabaseConnection _db;
private bool _disposed = false;
public ManagedConnection(SQLiteDatabaseConnection db, ConnectionPool pool)
{
_db = db;
_pool = pool;
}
public IStatement PrepareStatement(string sql)
{
return _db.PrepareStatement(sql);
}
public IEnumerable<IStatement> PrepareAll(string sql)
{
return _db.PrepareAll(sql);
}
public void ExecuteAll(string sql)
{
_db.ExecuteAll(sql);
}
public void Execute(string sql, params object[] values)
{
_db.Execute(sql, values);
}
public void RunQueries(string[] sql)
{
_db.RunQueries(sql);
}
public void RunInTransaction(Action<IDatabaseConnection> action, TransactionMode mode)
{
_db.RunInTransaction(action, mode);
}
public T RunInTransaction<T>(Func<IDatabaseConnection, T> action, TransactionMode mode)
{
return _db.RunInTransaction(action, mode);
}
public IEnumerable<IReadOnlyList<ResultSetValue>> Query(string sql)
{
return _db.Query(sql);
}
public IEnumerable<IReadOnlyList<ResultSetValue>> Query(string sql, params object[] values)
{
return _db.Query(sql, values);
}
public void Dispose()
{
if (_disposed)
{
return;
}
_pool.Return(_db);
_db = null!; // Don't dispose it
_disposed = true;
}
}
}

@ -1,11 +1,12 @@
#nullable disable
#nullable enable
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using SQLitePCL.pretty;
using Microsoft.Data.Sqlite;
namespace Emby.Server.Implementations.Data
{
@ -52,7 +53,68 @@ namespace Emby.Server.Implementations.Data
"yy-MM-dd"
};
public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
private static void EnsureOpen(this SqliteConnection sqliteConnection)
{
if (sqliteConnection.State == ConnectionState.Closed)
{
sqliteConnection.Open();
}
}
public static IEnumerable<SqliteDataReader> Query(this SqliteConnection sqliteConnection, string commandText)
{
if (sqliteConnection.State != ConnectionState.Open)
{
sqliteConnection.Open();
}
var command = sqliteConnection.CreateCommand();
command.CommandText = commandText;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
public static void Execute(this SqliteConnection sqliteConnection, string commandText)
{
sqliteConnection.EnsureOpen();
var command = sqliteConnection.CreateCommand();
command.CommandText = commandText;
command.ExecuteNonQuery();
}
public static void RunInTransaction(this SqliteConnection sqliteConnection, Action<SqliteConnection> action)
{
sqliteConnection.EnsureOpen();
using var transaction = sqliteConnection.BeginTransaction();
action(sqliteConnection);
transaction.Commit();
}
public static bool RunInTransaction(this SqliteConnection sqliteConnection, Func<SqliteConnection, bool> action)
{
sqliteConnection.EnsureOpen();
using var transaction = sqliteConnection.BeginTransaction();
var result = action(sqliteConnection);
transaction.Commit();
return result;
}
public static void ExecuteAll(this SqliteConnection sqliteConnection, string commandText)
{
sqliteConnection.EnsureOpen();
var command = sqliteConnection.CreateCommand();
command.CommandText = commandText;
command.ExecuteNonQuery();
}
public static void RunQueries(this SqliteConnection connection, string[] queries)
{
ArgumentNullException.ThrowIfNull(queries);
@ -62,11 +124,6 @@ namespace Emby.Server.Implementations.Data
});
}
public static Guid ReadGuidFromBlob(this ResultSetValue result)
{
return new Guid(result.ToBlob());
}
public static string ToDateTimeParamValue(this DateTime dateValue)
{
var kind = DateTimeKind.Utc;
@ -83,27 +140,26 @@ namespace Emby.Server.Implementations.Data
private static string GetDateTimeKindFormat(DateTimeKind kind)
=> (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
public static DateTime ReadDateTime(this ResultSetValue result)
public static DateTime ReadDateTime(this SqliteDataReader result)
{
var dateText = result.ToString();
return DateTime.ParseExact(
dateText,
dateText!,
_datetimeFormats,
DateTimeFormatInfo.InvariantInfo,
DateTimeStyles.AdjustToUniversal);
}
public static bool TryReadDateTime(this IReadOnlyList<ResultSetValue> reader, int index, out DateTime result)
public static bool TryReadDateTime(this SqliteDataReader reader, int index, out DateTime result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
var dateText = item.ToString();
var dateText = reader.GetString(index);
if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult))
{
@ -115,335 +171,175 @@ namespace Emby.Server.Implementations.Data
return false;
}
public static bool TryGetGuid(this IReadOnlyList<ResultSetValue> reader, int index, out Guid result)
public static bool TryGetGuid(this SqliteDataReader reader, int index, out Guid result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ReadGuidFromBlob();
result = reader.GetGuid(index);
return true;
}
public static bool IsDbNull(this ResultSetValue result)
public static bool TryGetString(this SqliteDataReader reader, int index, out string result)
{
return result.SQLiteType == SQLiteType.Null;
}
public static string GetString(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ToString();
}
result = string.Empty;
public static bool TryGetString(this IReadOnlyList<ResultSetValue> reader, int index, out string result)
{
result = null;
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
return false;
}
result = item.ToString();
result = reader.GetString(index);
return true;
}
public static bool GetBoolean(this IReadOnlyList<ResultSetValue> result, int index)
public static bool TryGetBoolean(this SqliteDataReader reader, int index, out bool result)
{
return result[index].ToBool();
}
public static bool TryGetBoolean(this IReadOnlyList<ResultSetValue> reader, int index, out bool result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ToBool();
result = reader.GetBoolean(index);
return true;
}
public static bool TryGetInt32(this IReadOnlyList<ResultSetValue> reader, int index, out int result)
public static bool TryGetInt32(this SqliteDataReader reader, int index, out int result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ToInt();
result = reader.GetInt32(index);
return true;
}
public static long GetInt64(this IReadOnlyList<ResultSetValue> result, int index)
public static bool TryGetInt64(this SqliteDataReader reader, int index, out long result)
{
return result[index].ToInt64();
}
public static bool TryGetInt64(this IReadOnlyList<ResultSetValue> reader, int index, out long result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ToInt64();
result = reader.GetInt64(index);
return true;
}
public static bool TryGetSingle(this IReadOnlyList<ResultSetValue> reader, int index, out float result)
public static bool TryGetSingle(this SqliteDataReader reader, int index, out float result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ToFloat();
result = reader.GetFloat(index);
return true;
}
public static bool TryGetDouble(this IReadOnlyList<ResultSetValue> reader, int index, out double result)
public static bool TryGetDouble(this SqliteDataReader reader, int index, out double result)
{
var item = reader[index];
if (item.IsDbNull())
if (reader.IsDBNull(index))
{
result = default;
return false;
}
result = item.ToDouble();
result = reader.GetDouble(index);
return true;
}
public static Guid GetGuid(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ReadGuidFromBlob();
}
[Conditional("DEBUG")]
private static void CheckName(string name)
{
throw new ArgumentException("Invalid param name: " + name, nameof(name));
}
public static void TryBind(this IStatement statement, string name, double value)
public static void TryBind(this SqliteCommand statement, string name, Guid value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
if (statement.Parameters.Contains(name))
{
bindParam.Bind(value);
statement.Parameters[name].Value = value;
}
else
{
CheckName(name);
statement.Parameters.Add(new SqliteParameter(name, SqliteType.Blob) { Value = value });
}
}
public static void TryBind(this IStatement statement, string name, string value)
public static void TryBind(this SqliteCommand statement, string name, object? value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
var preparedValue = value ?? DBNull.Value;
if (statement.Parameters.Contains(name))
{
if (value is null)
{
bindParam.BindNull();
}
else
{
bindParam.Bind(value);
}
statement.Parameters[name].Value = preparedValue;
}
else
{
CheckName(name);
statement.Parameters.AddWithValue(name, preparedValue);
}
}
public static void TryBind(this IStatement statement, string name, bool value)
public static void TryBind(this SqliteCommand statement, string name, byte[] value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
if (statement.Parameters.Contains(name))
{
bindParam.Bind(value);
statement.Parameters[name].Value = value;
}
else
{
CheckName(name);
statement.Parameters.Add(new SqliteParameter(name, SqliteType.Blob, value.Length) { Value = value });
}
}
public static void TryBind(this IStatement statement, string name, float value)
public static void TryBindNull(this SqliteCommand statement, string name)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, int value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, Guid value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
Span<byte> byteValue = stackalloc byte[16];
value.TryWriteBytes(byteValue);
bindParam.Bind(byteValue);
}
else
{
CheckName(name);
}
statement.TryBind(name, DBNull.Value);
}
public static void TryBind(this IStatement statement, string name, DateTime value)
public static IEnumerable<SqliteDataReader> ExecuteQuery(this SqliteCommand command)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
using (var reader = command.ExecuteReader())
{
bindParam.Bind(value.ToDateTimeParamValue());
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, long value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, ReadOnlySpan<byte> value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBindNull(this IStatement statement, string name)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.BindNull();
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, DateTime? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
while (reader.Read())
{
yield return reader;
}
}
}
public static void TryBind(this IStatement statement, string name, Guid? value)
public static int SelectScalarInt(this SqliteCommand command)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
var result = command.ExecuteScalar();
return Convert.ToInt32(result!, CultureInfo.InvariantCulture);
}
public static void TryBind(this IStatement statement, string name, double? value)
public static SqliteCommand PrepareStatement(this SqliteConnection sqliteConnection, string sql)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
sqliteConnection.EnsureOpen();
var command = sqliteConnection.CreateCommand();
command.CommandText = sql;
return command;
}
public static void TryBind(this IStatement statement, string name, int? value)
// Hacky
public static void MoveNext(this SqliteCommand sqliteCommand)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
sqliteCommand.Prepare();
var result = sqliteCommand.ExecuteNonQuery();
}
public static void TryBind(this IStatement statement, string name, float? value)
public static byte[] GetBlob(this SqliteDataReader reader, int index)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, bool? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static IEnumerable<IReadOnlyList<ResultSetValue>> ExecuteQuery(this IStatement statement)
{
while (statement.MoveNext())
{
yield return statement.Current;
}
// Have to reset to casting as there isn't a publicly available GetBlob method
return (byte[])reader.GetValue(index);
}
}
}

@ -35,9 +35,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
@ -555,8 +555,7 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames);
AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames);
},
TransactionMode);
});
connection.RunQueries(postQueries);
}
@ -580,8 +579,7 @@ namespace Emby.Server.Implementations.Data
saveImagesStatement.MoveNext();
}
},
TransactionMode);
});
}
}
@ -624,12 +622,11 @@ namespace Emby.Server.Implementations.Data
db =>
{
SaveItemsInTransaction(db, tuples);
},
TransactionMode);
});
}
}
private void SaveItemsInTransaction(IDatabaseConnection db, IEnumerable<(BaseItem Item, List<Guid> AncestorIds, BaseItem TopParent, string UserDataKey, List<string> InheritedTags)> tuples)
private void SaveItemsInTransaction(SqliteConnection db, IEnumerable<(BaseItem Item, List<Guid> AncestorIds, BaseItem TopParent, string UserDataKey, List<string> InheritedTags)> tuples)
{
using (var saveItemStatement = PrepareStatement(db, SaveItemCommandText))
using (var deleteAncestorsStatement = PrepareStatement(db, "delete from AncestorIds where ItemId=@ItemId"))
@ -639,7 +636,7 @@ namespace Emby.Server.Implementations.Data
{
if (requiresReset)
{
saveItemStatement.Reset();
// TODO saveItemStatement.Parameters.Clear();
}
var item = tuple.Item;
@ -677,7 +674,7 @@ namespace Emby.Server.Implementations.Data
return _appHost.ExpandVirtualPath(path);
}
private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, IStatement saveItemStatement)
private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, SqliteCommand saveItemStatement)
{
Type type = item.GetType();
@ -1389,12 +1386,12 @@ namespace Emby.Server.Implementations.Data
return true;
}
private BaseItem GetItem(IReadOnlyList<ResultSetValue> reader, InternalItemsQuery query)
private BaseItem GetItem(SqliteDataReader reader, InternalItemsQuery query)
{
return GetItem(reader, query, HasProgramAttributes(query), HasEpisodeAttributes(query), HasServiceName(query), HasStartDate(query), HasTrailerTypes(query), HasArtistFields(query), HasSeriesFields(query));
}
private BaseItem GetItem(IReadOnlyList<ResultSetValue> reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool hasServiceName, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields)
private BaseItem GetItem(SqliteDataReader reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool hasServiceName, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields)
{
var typeString = reader.GetString(0);
@ -1411,7 +1408,7 @@ namespace Emby.Server.Implementations.Data
{
try
{
item = JsonSerializer.Deserialize(reader[1].ToBlob(), type, _jsonOptions) as BaseItem;
item = JsonSerializer.Deserialize(reader.GetStream(1), type, _jsonOptions) as BaseItem;
}
catch (JsonException ex)
{
@ -1452,17 +1449,9 @@ namespace Emby.Server.Implementations.Data
item.EndDate = endDate;
}
var channelId = reader[index];
if (!channelId.IsDbNull())
if (reader.TryGetGuid(index, out var guid))
{
if (!Utf8Parser.TryParse(channelId.ToBlob(), out Guid value, out _, standardFormat: 'N'))
{
var str = reader.GetString(index);
Logger.LogWarning("{ChannelId} isn't in the expected format", str);
value = new Guid(str);
}
item.ChannelId = value;
item.ChannelId = guid;
}
index++;
@ -2018,7 +2007,7 @@ namespace Emby.Server.Implementations.Data
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
/// <returns>ChapterInfo.</returns>
private ChapterInfo GetChapter(IReadOnlyList<ResultSetValue> reader, BaseItem item)
private ChapterInfo GetChapter(SqliteDataReader reader, BaseItem item)
{
var chapter = new ChapterInfo
{
@ -2071,23 +2060,22 @@ namespace Emby.Server.Implementations.Data
ArgumentNullException.ThrowIfNull(chapters);
var idBlob = id.ToByteArray();
using (var connection = GetConnection())
{
connection.RunInTransaction(
db =>
{
// First delete chapters
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
var command = db.PrepareStatement($"delete from {ChaptersTableName} where ItemId=@ItemId");
command.TryBind("@ItemId", id);
command.ExecuteNonQuery();
InsertChapters(idBlob, chapters, db);
},
TransactionMode);
InsertChapters(id, chapters, db);
});
}
}
private void InsertChapters(byte[] idBlob, IReadOnlyList<ChapterInfo> chapters, IDatabaseConnection db)
private void InsertChapters(Guid idBlob, IReadOnlyList<ChapterInfo> chapters, SqliteConnection db)
{
var startIndex = 0;
var limit = 100;
@ -2126,7 +2114,7 @@ namespace Emby.Server.Implementations.Data
chapterIndex++;
}
statement.Reset();
// TODO statement.Parameters.Clear();
statement.MoveNext();
}
@ -2463,7 +2451,7 @@ namespace Emby.Server.Implementations.Data
}
}
private void BindSearchParams(InternalItemsQuery query, IStatement statement)
private void BindSearchParams(InternalItemsQuery query, SqliteCommand statement)
{
var searchTerm = query.SearchTerm;
@ -2475,7 +2463,7 @@ namespace Emby.Server.Implementations.Data
searchTerm = FixUnicodeChars(searchTerm);
searchTerm = GetCleanValue(searchTerm);
var commandText = statement.SQL;
var commandText = statement.CommandText;
if (commandText.Contains("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
@ -2492,7 +2480,7 @@ namespace Emby.Server.Implementations.Data
}
}
private void BindSimilarParams(InternalItemsQuery query, IStatement statement)
private void BindSimilarParams(InternalItemsQuery query, SqliteCommand statement)
{
var item = query.SimilarTo;
@ -2501,7 +2489,7 @@ namespace Emby.Server.Implementations.Data
return;
}
var commandText = statement.SQL;
var commandText = statement.CommandText;
if (commandText.Contains("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase))
{
@ -2598,7 +2586,7 @@ namespace Emby.Server.Implementations.Data
// Running this again will bind the params
GetWhereClauses(query, statement);
return statement.ExecuteQuery().SelectScalarInt().First();
return statement.SelectScalarInt();
}
}
@ -2916,11 +2904,10 @@ namespace Emby.Server.Implementations.Data
// Running this again will bind the params
GetWhereClauses(query, statement);
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
result.TotalRecordCount = statement.SelectScalarInt();
}
}
},
ReadTransactionMode);
});
}
result.StartIndex = query.StartIndex ?? 0;
@ -3188,7 +3175,7 @@ namespace Emby.Server.Implementations.Data
foreach (var row in statement.ExecuteQuery())
{
list.Add(row[0].ReadGuidFromBlob());
list.Add(row.GetGuid(0));
}
}
@ -3224,7 +3211,7 @@ namespace Emby.Server.Implementations.Data
}
#nullable enable
private List<string> GetWhereClauses(InternalItemsQuery query, IStatement? statement)
private List<string> GetWhereClauses(InternalItemsQuery query, SqliteCommand? statement)
{
if (query.IsResumable ?? false)
{
@ -3647,8 +3634,7 @@ namespace Emby.Server.Implementations.Data
if (statement is not null)
{
query.PersonIds[i].TryWriteBytes(idBytes);
statement.TryBind(paramName, idBytes);
statement.TryBind(paramName, query.PersonIds[i]);
}
}
@ -4696,8 +4682,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db =>
{
connection.ExecuteAll(sql);
},
TransactionMode);
});
}
}
@ -4735,16 +4720,15 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
// Delete the item
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob);
},
TransactionMode);
});
}
}
private void ExecuteWithSingleParam(IDatabaseConnection db, string query, ReadOnlySpan<byte> value)
private void ExecuteWithSingleParam(SqliteConnection db, string query, ReadOnlySpan<byte> value)
{
using (var statement = PrepareStatement(db, query))
{
statement.TryBind("@Id", value);
statement.TryBind("@Id", value.ToArray());
statement.MoveNext();
}
@ -4826,7 +4810,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return list;
}
private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IStatement statement)
private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, SqliteCommand statement)
{
var whereClauses = new List<string>();
@ -4896,7 +4880,7 @@ AND Type = @InternalPersonType)");
return whereClauses;
}
private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDatabaseConnection db, IStatement deleteAncestorsStatement)
private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, SqliteConnection db, SqliteCommand deleteAncestorsStatement)
{
if (itemId.Equals(default))
{
@ -4907,12 +4891,14 @@ AND Type = @InternalPersonType)");
CheckDisposed();
Span<byte> itemIdBlob = stackalloc byte[16];
itemId.TryWriteBytes(itemIdBlob);
// TODO how to handle span?
Span<byte> itemIdBlob2 = stackalloc byte[16];
itemId.TryWriteBytes(itemIdBlob2);
var itemIdBlob = Encoding.ASCII.GetBytes(itemId.ToString());
// First delete
deleteAncestorsStatement.Reset();
deleteAncestorsStatement.TryBind("@ItemId", itemIdBlob);
// TODO deleteAncestorsStatement.Parameters.Clear();
deleteAncestorsStatement.TryBind("@ItemId", itemId);
deleteAncestorsStatement.MoveNext();
if (ancestorIds.Count == 0)
@ -4942,13 +4928,13 @@ AND Type = @InternalPersonType)");
var index = i.ToString(CultureInfo.InvariantCulture);
var ancestorId = ancestorIds[i];
ancestorId.TryWriteBytes(itemIdBlob);
itemIdBlob = Encoding.ASCII.GetBytes(itemId.ToString());
statement.TryBind("@AncestorId" + index, itemIdBlob);
statement.TryBind("@AncestorId" + index, ancestorId);
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
}
statement.Reset();
// TODO statement.Parameters.Clear();
statement.MoveNext();
}
}
@ -5323,11 +5309,10 @@ AND Type = @InternalPersonType)");
GetWhereClauses(innerQuery, statement);
GetWhereClauses(outerQuery, statement);
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
result.TotalRecordCount = statement.SelectScalarInt();
}
}
},
ReadTransactionMode);
});
}
if (result.TotalRecordCount == 0)
@ -5341,7 +5326,7 @@ AND Type = @InternalPersonType)");
return result;
}
private static ItemCounts GetItemCounts(IReadOnlyList<ResultSetValue> reader, int countStartColumn, BaseItemKind[] typesToCount)
private static ItemCounts GetItemCounts(SqliteDataReader reader, int countStartColumn, BaseItemKind[] typesToCount)
{
var counts = new ItemCounts();
@ -5420,7 +5405,7 @@ AND Type = @InternalPersonType)");
return list;
}
private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, IDatabaseConnection db)
private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, SqliteConnection db)
{
if (itemId.Equals(default))
{
@ -5434,12 +5419,14 @@ AND Type = @InternalPersonType)");
var guidBlob = itemId.ToByteArray();
// First delete
db.Execute("delete from ItemValues where ItemId=@Id", guidBlob);
using var command = db.PrepareStatement("delete from ItemValues where ItemId=@Id");
command.TryBind("@Id", guidBlob);
command.ExecuteNonQuery();
InsertItemValues(guidBlob, values, db);
}
private void InsertItemValues(byte[] idBlob, List<(int MagicNumber, string Value)> values, IDatabaseConnection db)
private void InsertItemValues(byte[] idBlob, List<(int MagicNumber, string Value)> values, SqliteConnection db)
{
const int Limit = 100;
var startIndex = 0;
@ -5484,7 +5471,7 @@ AND Type = @InternalPersonType)");
statement.TryBind("@CleanValue" + index, GetCleanValue(itemValue));
}
statement.Reset();
// TODO statement.Parameters.Clear();
statement.MoveNext();
}
@ -5512,15 +5499,17 @@ AND Type = @InternalPersonType)");
var itemIdBlob = itemId.ToByteArray();
// First delete chapters
db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
using var command = db.CreateCommand();
command.CommandText = "delete from People where ItemId=@ItemId";
command.TryBind("@ItemId", itemIdBlob);
command.ExecuteNonQuery();
InsertPeople(itemIdBlob, people, db);
},
TransactionMode);
});
}
}
private void InsertPeople(byte[] idBlob, List<PersonInfo> people, IDatabaseConnection db)
private void InsertPeople(byte[] idBlob, List<PersonInfo> people, SqliteConnection db)
{
const int Limit = 100;
var startIndex = 0;
@ -5561,7 +5550,6 @@ AND Type = @InternalPersonType)");
listIndex++;
}
statement.Reset();
statement.MoveNext();
}
@ -5570,7 +5558,7 @@ AND Type = @InternalPersonType)");
}
}
private PersonInfo GetPerson(IReadOnlyList<ResultSetValue> reader)
private PersonInfo GetPerson(SqliteDataReader reader)
{
var item = new PersonInfo
{
@ -5666,15 +5654,16 @@ AND Type = @InternalPersonType)");
var itemIdBlob = id.ToByteArray();
// Delete existing mediastreams
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
using var command = db.PrepareStatement("delete from mediastreams where ItemId=@ItemId");
command.TryBind("@ItemId", itemIdBlob);
command.ExecuteNonQuery();
InsertMediaStreams(itemIdBlob, streams, db);
},
TransactionMode);
});
}
}
private void InsertMediaStreams(byte[] idBlob, IReadOnlyList<MediaStream> streams, IDatabaseConnection db)
private void InsertMediaStreams(byte[] idBlob, IReadOnlyList<MediaStream> streams, SqliteConnection db)
{
const int Limit = 10;
var startIndex = 0;
@ -5770,7 +5759,7 @@ AND Type = @InternalPersonType)");
statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired);
}
statement.Reset();
// TODO statement.Parameters.Clear();
statement.MoveNext();
}
@ -5784,15 +5773,14 @@ AND Type = @InternalPersonType)");
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>MediaStream.</returns>
private MediaStream GetMediaStream(IReadOnlyList<ResultSetValue> reader)
private MediaStream GetMediaStream(SqliteDataReader reader)
{
var item = new MediaStream
{
Index = reader[1].ToInt()
Index = reader.GetInt32(1),
Type = Enum.Parse<MediaStreamType>(reader.GetString(2), true)
};
item.Type = Enum.Parse<MediaStreamType>(reader[2].ToString(), true);
if (reader.TryGetString(3, out var codec))
{
item.Codec = codec;
@ -6050,18 +6038,19 @@ AND Type = @InternalPersonType)");
{
var itemIdBlob = id.ToByteArray();
db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
using var command = db.PrepareStatement("delete from mediaattachments where ItemId=@ItemId");
command.TryBind("@ItemId", itemIdBlob);
command.ExecuteNonQuery();
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
},
TransactionMode);
});
}
}
private void InsertMediaAttachments(
byte[] idBlob,
IReadOnlyList<MediaAttachment> attachments,
IDatabaseConnection db,
SqliteConnection db,
CancellationToken cancellationToken)
{
const int InsertAtOnce = 10;
@ -6111,7 +6100,7 @@ AND Type = @InternalPersonType)");
statement.TryBind("@MIMEType" + index, attachment.MimeType);
}
statement.Reset();
// TODO statement.Parameters.Clear();
statement.MoveNext();
}
@ -6124,11 +6113,11 @@ AND Type = @InternalPersonType)");
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>MediaAttachment.</returns>
private MediaAttachment GetMediaAttachment(IReadOnlyList<ResultSetValue> reader)
private MediaAttachment GetMediaAttachment(SqliteDataReader reader)
{
var item = new MediaAttachment
{
Index = reader[1].ToInt()
Index = reader.GetInt32(1)
};
if (reader.TryGetString(2, out var codec))

@ -11,8 +11,8 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
@ -80,12 +80,11 @@ namespace Emby.Server.Implementations.Data
db.ExecuteAll("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
}
}
},
TransactionMode);
});
}
}
private void ImportUserIds(IDatabaseConnection db, IEnumerable<User> users)
private void ImportUserIds(SqliteConnection db, IEnumerable<User> users)
{
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
@ -100,14 +99,14 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@UserId", user.Id);
statement.TryBind("@InternalUserId", user.InternalId);
statement.Prepare();
statement.MoveNext();
statement.Reset();
statement.ExecuteNonQuery();
}
}
}
private List<Guid> GetAllUserIdsWithUserData(IDatabaseConnection db)
private List<Guid> GetAllUserIdsWithUserData(SqliteConnection db)
{
var list = new List<Guid>();
@ -117,7 +116,7 @@ namespace Emby.Server.Implementations.Data
{
try
{
list.Add(row[0].ReadGuidFromBlob());
list.Add(row.GetGuid(0));
}
catch (Exception ex)
{
@ -174,12 +173,11 @@ namespace Emby.Server.Implementations.Data
db =>
{
SaveUserData(db, internalUserId, key, userData);
},
TransactionMode);
});
}
}
private static void SaveUserData(IDatabaseConnection db, long internalUserId, string key, UserItemData userData)
private static void SaveUserData(SqliteConnection db, long internalUserId, string key, UserItemData userData)
{
using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
{
@ -247,8 +245,7 @@ namespace Emby.Server.Implementations.Data
{
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
},
TransactionMode);
});
}
}
@ -336,7 +333,7 @@ namespace Emby.Server.Implementations.Data
/// </summary>
/// <param name="reader">The list of result set values.</param>
/// <returns>The user item data.</returns>
private UserItemData ReadRow(IReadOnlyList<ResultSetValue> reader)
private UserItemData ReadRow(SqliteDataReader reader)
{
var userData = new UserItemData();
@ -348,10 +345,10 @@ namespace Emby.Server.Implementations.Data
userData.Rating = rating;
}
userData.Played = reader[3].ToBool();
userData.PlayCount = reader[4].ToInt();
userData.IsFavorite = reader[5].ToBool();
userData.PlaybackPositionTicks = reader[6].ToInt64();
userData.Played = reader.GetBoolean(3);
userData.PlayCount = reader.GetInt32(4);
userData.IsFavorite = reader.GetBoolean(5);
userData.PlaybackPositionTicks = reader.GetInt64(6);
if (reader.TryReadDateTime(7, out var lastPlayedDate))
{

@ -24,6 +24,7 @@
<ItemGroup>
<PackageReference Include="DiscUtils.Udf" />
<PackageReference Include="Jellyfin.XmlTv" />
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
@ -31,7 +32,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Mono.Nat" />
<PackageReference Include="prometheus-net.DotNetRuntime" />
<PackageReference Include="SQLitePCL.pretty.netstandard" />
<PackageReference Include="DotNet.Glob" />
</ItemGroup>

@ -0,0 +1,449 @@
#nullable disable
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using SQLitePCL.pretty;
namespace Jellyfin.Server.Extensions
{
public static class SqliteExtensions
{
private const string DatetimeFormatUtc = "yyyy-MM-dd HH:mm:ss.FFFFFFFK";
private const string DatetimeFormatLocal = "yyyy-MM-dd HH:mm:ss.FFFFFFF";
/// <summary>
/// An array of ISO-8601 DateTime formats that we support parsing.
/// </summary>
private static readonly string[] _datetimeFormats = new string[]
{
"THHmmssK",
"THHmmK",
"HH:mm:ss.FFFFFFFK",
"HH:mm:ssK",
"HH:mmK",
DatetimeFormatUtc,
"yyyy-MM-dd HH:mm:ssK",
"yyyy-MM-dd HH:mmK",
"yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
"yyyy-MM-ddTHH:mmK",
"yyyy-MM-ddTHH:mm:ssK",
"yyyyMMddHHmmssK",
"yyyyMMddHHmmK",
"yyyyMMddTHHmmssFFFFFFFK",
"THHmmss",
"THHmm",
"HH:mm:ss.FFFFFFF",
"HH:mm:ss",
"HH:mm",
DatetimeFormatLocal,
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy-MM-ddTHH:mm:ss.FFFFFFF",
"yyyy-MM-ddTHH:mm",
"yyyy-MM-ddTHH:mm:ss",
"yyyyMMddHHmmss",
"yyyyMMddHHmm",
"yyyyMMddTHHmmssFFFFFFF",
"yyyy-MM-dd",
"yyyyMMdd",
"yy-MM-dd"
};
public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
{
ArgumentNullException.ThrowIfNull(queries);
connection.RunInTransaction(conn =>
{
conn.ExecuteAll(string.Join(';', queries));
});
}
public static Guid ReadGuidFromBlob(this ResultSetValue result)
{
return new Guid(result.ToBlob());
}
public static string ToDateTimeParamValue(this DateTime dateValue)
{
var kind = DateTimeKind.Utc;
return (dateValue.Kind == DateTimeKind.Unspecified)
? DateTime.SpecifyKind(dateValue, kind).ToString(
GetDateTimeKindFormat(kind),
CultureInfo.InvariantCulture)
: dateValue.ToString(
GetDateTimeKindFormat(dateValue.Kind),
CultureInfo.InvariantCulture);
}
private static string GetDateTimeKindFormat(DateTimeKind kind)
=> (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
public static DateTime ReadDateTime(this ResultSetValue result)
{
var dateText = result.ToString();
return DateTime.ParseExact(
dateText,
_datetimeFormats,
DateTimeFormatInfo.InvariantInfo,
DateTimeStyles.AdjustToUniversal);
}
public static bool TryReadDateTime(this IReadOnlyList<ResultSetValue> reader, int index, out DateTime result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
var dateText = item.ToString();
if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult))
{
result = dateTimeResult;
return true;
}
result = default;
return false;
}
public static bool TryGetGuid(this IReadOnlyList<ResultSetValue> reader, int index, out Guid result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ReadGuidFromBlob();
return true;
}
public static bool IsDbNull(this ResultSetValue result)
{
return result.SQLiteType == SQLiteType.Null;
}
public static string GetString(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ToString();
}
public static bool TryGetString(this IReadOnlyList<ResultSetValue> reader, int index, out string result)
{
result = null;
var item = reader[index];
if (item.IsDbNull())
{
return false;
}
result = item.ToString();
return true;
}
public static bool GetBoolean(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ToBool();
}
public static bool TryGetBoolean(this IReadOnlyList<ResultSetValue> reader, int index, out bool result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ToBool();
return true;
}
public static bool TryGetInt32(this IReadOnlyList<ResultSetValue> reader, int index, out int result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ToInt();
return true;
}
public static long GetInt64(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ToInt64();
}
public static bool TryGetInt64(this IReadOnlyList<ResultSetValue> reader, int index, out long result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ToInt64();
return true;
}
public static bool TryGetSingle(this IReadOnlyList<ResultSetValue> reader, int index, out float result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ToFloat();
return true;
}
public static bool TryGetDouble(this IReadOnlyList<ResultSetValue> reader, int index, out double result)
{
var item = reader[index];
if (item.IsDbNull())
{
result = default;
return false;
}
result = item.ToDouble();
return true;
}
public static Guid GetGuid(this IReadOnlyList<ResultSetValue> result, int index)
{
return result[index].ReadGuidFromBlob();
}
[Conditional("DEBUG")]
private static void CheckName(string name)
{
throw new ArgumentException("Invalid param name: " + name, nameof(name));
}
public static void TryBind(this IStatement statement, string name, double value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, string value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
if (value is null)
{
bindParam.BindNull();
}
else
{
bindParam.Bind(value);
}
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, bool value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, float value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, int value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, Guid value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
Span<byte> byteValue = stackalloc byte[16];
value.TryWriteBytes(byteValue);
bindParam.Bind(byteValue);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, DateTime value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value.ToDateTimeParamValue());
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, long value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, ReadOnlySpan<byte> value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.Bind(value);
}
else
{
CheckName(name);
}
}
public static void TryBindNull(this IStatement statement, string name)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
bindParam.BindNull();
}
else
{
CheckName(name);
}
}
public static void TryBind(this IStatement statement, string name, DateTime? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, Guid? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, double? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, int? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, float? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static void TryBind(this IStatement statement, string name, bool? value)
{
if (value.HasValue)
{
TryBind(statement, name, value.Value);
}
else
{
TryBindNull(statement, name);
}
}
public static IEnumerable<IReadOnlyList<ResultSetValue>> ExecuteQuery(this IStatement statement)
{
while (statement.MoveNext())
{
yield return statement.Current;
}
}
}
}

@ -47,7 +47,7 @@
<PackageReference Include="Serilog.Sinks.Async" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Sinks.Graylog" />
<PackageReference Include="SQLitePCL.pretty.netstandard" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" />
</ItemGroup>

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Emby.Server.Implementations.Data;
using Jellyfin.Data.Entities;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
using Microsoft.EntityFrameworkCore;

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Emby.Server.Implementations.Data;
using Jellyfin.Data.Entities.Security;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;

@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using Emby.Server.Implementations.Data;
using Jellyfin.Server.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Globalization;
@ -91,7 +92,7 @@ namespace Jellyfin.Server.Migrations.Routines
ratingValue = "NULL";
}
var statement = connection.PrepareStatement("UPDATE TypedBaseItems SET InheritedParentalRatingValue = @Value WHERE OfficialRating = @Rating;");
using var statement = connection.PrepareStatement("UPDATE TypedBaseItems SET InheritedParentalRatingValue = @Value WHERE OfficialRating = @Rating;");
statement.TryBind("@Value", ratingValue);
statement.TryBind("@Rating", ratingString);
statement.ExecuteQuery();

@ -4,6 +4,7 @@ using Emby.Server.Implementations.Data;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions.Json;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Controller;

Loading…
Cancel
Save