mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
loading works
This commit is contained in:
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;
|
||||
}
|
||||
result = string.Empty;
|
||||
|
||||
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())
|
||||
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)
|
||||
statement.Parameters[name].Value = preparedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
statement.Parameters.AddWithValue(name, preparedValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryBind(this SqliteCommand statement, string name, byte[] value)
|
||||
{
|
||||
if (statement.Parameters.Contains(name))
|
||||
{
|
||||
statement.Parameters[name].Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
statement.Parameters.Add(new SqliteParameter(name, SqliteType.Blob, value.Length) { Value = value });
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryBindNull(this SqliteCommand statement, string name)
|
||||
{
|
||||
statement.TryBind(name, DBNull.Value);
|
||||
}
|
||||
|
||||
public static IEnumerable<SqliteDataReader> ExecuteQuery(this SqliteCommand command)
|
||||
{
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
bindParam.BindNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
bindParam.Bind(value);
|
||||
yield return reader;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckName(name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryBind(this IStatement statement, string name, bool value)
|
||||
public static int SelectScalarInt(this SqliteCommand command)
|
||||
{
|
||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
||||
{
|
||||
bindParam.Bind(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckName(name);
|
||||
}
|
||||
var result = command.ExecuteScalar();
|
||||
return Convert.ToInt32(result!, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static void TryBind(this IStatement statement, string name, float value)
|
||||
public static SqliteCommand PrepareStatement(this SqliteConnection sqliteConnection, string sql)
|
||||
{
|
||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
||||
{
|
||||
bindParam.Bind(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckName(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 (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
||||
{
|
||||
bindParam.Bind(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckName(name);
|
||||
}
|
||||
sqliteCommand.Prepare();
|
||||
var result = sqliteCommand.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public static void TryBind(this IStatement statement, string name, Guid value)
|
||||
public static byte[] GetBlob(this SqliteDataReader reader, int index)
|
||||
{
|
||||
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;
|
||||
}
|
||||
// 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>
|
||||
|
||||
|
449
Jellyfin.Server/Extensions/SqliteExtensions.cs
Normal file
449
Jellyfin.Server/Extensions/SqliteExtensions.cs
Normal file
@ -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…
x
Reference in New Issue
Block a user