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.HttpOverrides" Version="2.2.0" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
|
<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.Design" Version="7.0.10" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.10" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.10" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SQLitePCL.pretty;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
@ -45,24 +45,6 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <value>The logger.</value>
|
/// <value>The logger.</value>
|
||||||
protected ILogger<BaseSqliteRepository> Logger { get; }
|
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>
|
/// <summary>
|
||||||
/// Gets the cache size.
|
/// Gets the cache size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -107,23 +89,8 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <see cref="SynchronousMode"/>
|
/// <see cref="SynchronousMode"/>
|
||||||
protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal;
|
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()
|
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.
|
// Configuration and pragmas can affect VACUUM so it needs to be last.
|
||||||
using (var connection = GetConnection())
|
using (var connection = GetConnection())
|
||||||
{
|
{
|
||||||
@ -131,15 +98,9 @@ namespace Emby.Server.Implementations.Data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ManagedConnection GetConnection(bool readOnly = false)
|
protected SqliteConnection GetConnection(bool readOnly = false)
|
||||||
=> readOnly ? ReadConnections.GetConnection() : WriteConnections.GetConnection();
|
|
||||||
|
|
||||||
protected SQLiteDatabaseConnection CreateWriteConnection()
|
|
||||||
{
|
{
|
||||||
var writeConnection = SQLite3.Open(
|
var writeConnection = new SqliteConnection($"Filename={DbFilePath}");
|
||||||
DbFilePath,
|
|
||||||
DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite,
|
|
||||||
null);
|
|
||||||
|
|
||||||
if (CacheSize.HasValue)
|
if (CacheSize.HasValue)
|
||||||
{
|
{
|
||||||
@ -176,50 +137,14 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return writeConnection;
|
return writeConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SQLiteDatabaseConnection CreateReadConnection()
|
public SqliteCommand PrepareStatement(SqliteConnection connection, string sql)
|
||||||
{
|
{
|
||||||
var connection = SQLite3.Open(
|
var command = connection.CreateCommand();
|
||||||
DbFilePath,
|
command.CommandText = sql;
|
||||||
DefaultConnectionFlags | ConnectionFlags.ReadOnly,
|
return command;
|
||||||
null);
|
|
||||||
|
|
||||||
if (CacheSize.HasValue)
|
|
||||||
{
|
|
||||||
connection.Execute("PRAGMA cache_size=" + CacheSize.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(LockingMode))
|
protected bool TableExists(SqliteConnection connection, string name)
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return connection.RunInTransaction(
|
return connection.RunInTransaction(
|
||||||
db =>
|
db =>
|
||||||
@ -236,11 +161,10 @@ namespace Emby.Server.Implementations.Data
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
});
|
||||||
ReadTransactionMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<string> GetColumnNames(IDatabaseConnection connection, string table)
|
protected List<string> GetColumnNames(SqliteConnection connection, string table)
|
||||||
{
|
{
|
||||||
var columnNames = new List<string>();
|
var columnNames = new List<string>();
|
||||||
|
|
||||||
@ -255,7 +179,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return columnNames;
|
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))
|
if (existingColumnNames.Contains(columnName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@ -291,12 +215,6 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispose)
|
|
||||||
{
|
|
||||||
WriteConnections.Dispose();
|
|
||||||
ReadConnections.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
_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
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using SQLitePCL.pretty;
|
using Microsoft.Data.Sqlite;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
@ -52,7 +53,68 @@ namespace Emby.Server.Implementations.Data
|
|||||||
"yy-MM-dd"
|
"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);
|
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)
|
public static string ToDateTimeParamValue(this DateTime dateValue)
|
||||||
{
|
{
|
||||||
var kind = DateTimeKind.Utc;
|
var kind = DateTimeKind.Utc;
|
||||||
@ -83,27 +140,26 @@ namespace Emby.Server.Implementations.Data
|
|||||||
private static string GetDateTimeKindFormat(DateTimeKind kind)
|
private static string GetDateTimeKindFormat(DateTimeKind kind)
|
||||||
=> (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
|
=> (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
|
||||||
|
|
||||||
public static DateTime ReadDateTime(this ResultSetValue result)
|
public static DateTime ReadDateTime(this SqliteDataReader result)
|
||||||
{
|
{
|
||||||
var dateText = result.ToString();
|
var dateText = result.ToString();
|
||||||
|
|
||||||
return DateTime.ParseExact(
|
return DateTime.ParseExact(
|
||||||
dateText,
|
dateText!,
|
||||||
_datetimeFormats,
|
_datetimeFormats,
|
||||||
DateTimeFormatInfo.InvariantInfo,
|
DateTimeFormatInfo.InvariantInfo,
|
||||||
DateTimeStyles.AdjustToUniversal);
|
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 (reader.IsDBNull(index))
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dateText = item.ToString();
|
var dateText = reader.GetString(index);
|
||||||
|
|
||||||
if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult))
|
if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult))
|
||||||
{
|
{
|
||||||
@ -115,335 +171,175 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return false;
|
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 (reader.IsDBNull(index))
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ReadGuidFromBlob();
|
result = reader.GetGuid(index);
|
||||||
return true;
|
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)
|
if (reader.IsDBNull(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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToString();
|
result = reader.GetString(index);
|
||||||
return true;
|
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();
|
if (reader.IsDBNull(index))
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetBoolean(this IReadOnlyList<ResultSetValue> reader, int index, out bool result)
|
|
||||||
{
|
|
||||||
var item = reader[index];
|
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToBool();
|
result = reader.GetBoolean(index);
|
||||||
return true;
|
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 (reader.IsDBNull(index))
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToInt();
|
result = reader.GetInt32(index);
|
||||||
return true;
|
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();
|
if (reader.IsDBNull(index))
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetInt64(this IReadOnlyList<ResultSetValue> reader, int index, out long result)
|
|
||||||
{
|
|
||||||
var item = reader[index];
|
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToInt64();
|
result = reader.GetInt64(index);
|
||||||
return true;
|
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 (reader.IsDBNull(index))
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToFloat();
|
result = reader.GetFloat(index);
|
||||||
return true;
|
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 (reader.IsDBNull(index))
|
||||||
if (item.IsDbNull())
|
|
||||||
{
|
{
|
||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = item.ToDouble();
|
result = reader.GetDouble(index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Guid GetGuid(this IReadOnlyList<ResultSetValue> result, int index)
|
|
||||||
{
|
|
||||||
return result[index].ReadGuidFromBlob();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
private static void CheckName(string name)
|
private static void CheckName(string name)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Invalid param name: " + name, nameof(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
|
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;
|
||||||
{
|
|
||||||
bindParam.BindNull();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bindParam.Bind(value);
|
statement.Parameters.AddWithValue(name, preparedValue);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CheckName(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
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))
|
statement.TryBind(name, DBNull.Value);
|
||||||
{
|
|
||||||
bindParam.Bind(value);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
public static IEnumerable<SqliteDataReader> ExecuteQuery(this SqliteCommand command)
|
||||||
{
|
{
|
||||||
CheckName(name);
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
yield return reader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TryBind(this IStatement statement, string name, int value)
|
public static int SelectScalarInt(this SqliteCommand command)
|
||||||
{
|
{
|
||||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
var result = command.ExecuteScalar();
|
||||||
{
|
return Convert.ToInt32(result!, CultureInfo.InvariantCulture);
|
||||||
bindParam.Bind(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CheckName(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TryBind(this IStatement statement, string name, Guid value)
|
public static SqliteCommand PrepareStatement(this SqliteConnection sqliteConnection, string sql)
|
||||||
{
|
{
|
||||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
sqliteConnection.EnsureOpen();
|
||||||
{
|
var command = sqliteConnection.CreateCommand();
|
||||||
Span<byte> byteValue = stackalloc byte[16];
|
command.CommandText = sql;
|
||||||
value.TryWriteBytes(byteValue);
|
return command;
|
||||||
bindParam.Bind(byteValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CheckName(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TryBind(this IStatement statement, string name, DateTime value)
|
// Hacky
|
||||||
|
public static void MoveNext(this SqliteCommand sqliteCommand)
|
||||||
{
|
{
|
||||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
sqliteCommand.Prepare();
|
||||||
{
|
var result = sqliteCommand.ExecuteNonQuery();
|
||||||
bindParam.Bind(value.ToDateTimeParamValue());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CheckName(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TryBind(this IStatement statement, string name, long value)
|
public static byte[] GetBlob(this SqliteDataReader reader, int index)
|
||||||
{
|
{
|
||||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
// Have to reset to casting as there isn't a publicly available GetBlob method
|
||||||
{
|
return (byte[])reader.GetValue(index);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ using MediaBrowser.Model.Entities;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SQLitePCL.pretty;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
@ -555,8 +555,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames);
|
AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames);
|
||||||
|
|
||||||
AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames);
|
AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames);
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
|
|
||||||
connection.RunQueries(postQueries);
|
connection.RunQueries(postQueries);
|
||||||
}
|
}
|
||||||
@ -580,8 +579,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
saveImagesStatement.MoveNext();
|
saveImagesStatement.MoveNext();
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,12 +622,11 @@ namespace Emby.Server.Implementations.Data
|
|||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
SaveItemsInTransaction(db, tuples);
|
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 saveItemStatement = PrepareStatement(db, SaveItemCommandText))
|
||||||
using (var deleteAncestorsStatement = PrepareStatement(db, "delete from AncestorIds where ItemId=@ItemId"))
|
using (var deleteAncestorsStatement = PrepareStatement(db, "delete from AncestorIds where ItemId=@ItemId"))
|
||||||
@ -639,7 +636,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
if (requiresReset)
|
if (requiresReset)
|
||||||
{
|
{
|
||||||
saveItemStatement.Reset();
|
// TODO saveItemStatement.Parameters.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = tuple.Item;
|
var item = tuple.Item;
|
||||||
@ -677,7 +674,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return _appHost.ExpandVirtualPath(path);
|
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();
|
Type type = item.GetType();
|
||||||
|
|
||||||
@ -1389,12 +1386,12 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return true;
|
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));
|
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);
|
var typeString = reader.GetString(0);
|
||||||
|
|
||||||
@ -1411,7 +1408,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
item = JsonSerializer.Deserialize(reader[1].ToBlob(), type, _jsonOptions) as BaseItem;
|
item = JsonSerializer.Deserialize(reader.GetStream(1), type, _jsonOptions) as BaseItem;
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
@ -1452,17 +1449,9 @@ namespace Emby.Server.Implementations.Data
|
|||||||
item.EndDate = endDate;
|
item.EndDate = endDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
var channelId = reader[index];
|
if (reader.TryGetGuid(index, out var guid))
|
||||||
if (!channelId.IsDbNull())
|
|
||||||
{
|
{
|
||||||
if (!Utf8Parser.TryParse(channelId.ToBlob(), out Guid value, out _, standardFormat: 'N'))
|
item.ChannelId = guid;
|
||||||
{
|
|
||||||
var str = reader.GetString(index);
|
|
||||||
Logger.LogWarning("{ChannelId} isn't in the expected format", str);
|
|
||||||
value = new Guid(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.ChannelId = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
@ -2018,7 +2007,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <param name="reader">The reader.</param>
|
/// <param name="reader">The reader.</param>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <returns>ChapterInfo.</returns>
|
/// <returns>ChapterInfo.</returns>
|
||||||
private ChapterInfo GetChapter(IReadOnlyList<ResultSetValue> reader, BaseItem item)
|
private ChapterInfo GetChapter(SqliteDataReader reader, BaseItem item)
|
||||||
{
|
{
|
||||||
var chapter = new ChapterInfo
|
var chapter = new ChapterInfo
|
||||||
{
|
{
|
||||||
@ -2071,23 +2060,22 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(chapters);
|
ArgumentNullException.ThrowIfNull(chapters);
|
||||||
|
|
||||||
var idBlob = id.ToByteArray();
|
|
||||||
|
|
||||||
using (var connection = GetConnection())
|
using (var connection = GetConnection())
|
||||||
{
|
{
|
||||||
connection.RunInTransaction(
|
connection.RunInTransaction(
|
||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
// First delete chapters
|
// 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);
|
InsertChapters(id, chapters, db);
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertChapters(byte[] idBlob, IReadOnlyList<ChapterInfo> chapters, IDatabaseConnection db)
|
private void InsertChapters(Guid idBlob, IReadOnlyList<ChapterInfo> chapters, SqliteConnection db)
|
||||||
{
|
{
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
var limit = 100;
|
var limit = 100;
|
||||||
@ -2126,7 +2114,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
chapterIndex++;
|
chapterIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
// TODO statement.Parameters.Clear();
|
||||||
statement.MoveNext();
|
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;
|
var searchTerm = query.SearchTerm;
|
||||||
|
|
||||||
@ -2475,7 +2463,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
searchTerm = FixUnicodeChars(searchTerm);
|
searchTerm = FixUnicodeChars(searchTerm);
|
||||||
searchTerm = GetCleanValue(searchTerm);
|
searchTerm = GetCleanValue(searchTerm);
|
||||||
|
|
||||||
var commandText = statement.SQL;
|
var commandText = statement.CommandText;
|
||||||
if (commandText.Contains("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase))
|
if (commandText.Contains("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
|
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;
|
var item = query.SimilarTo;
|
||||||
|
|
||||||
@ -2501,7 +2489,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandText = statement.SQL;
|
var commandText = statement.CommandText;
|
||||||
|
|
||||||
if (commandText.Contains("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase))
|
if (commandText.Contains("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@ -2598,7 +2586,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
// Running this again will bind the params
|
// Running this again will bind the params
|
||||||
GetWhereClauses(query, statement);
|
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
|
// Running this again will bind the params
|
||||||
GetWhereClauses(query, statement);
|
GetWhereClauses(query, statement);
|
||||||
|
|
||||||
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
|
result.TotalRecordCount = statement.SelectScalarInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
ReadTransactionMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.StartIndex = query.StartIndex ?? 0;
|
result.StartIndex = query.StartIndex ?? 0;
|
||||||
@ -3188,7 +3175,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
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
|
#nullable enable
|
||||||
private List<string> GetWhereClauses(InternalItemsQuery query, IStatement? statement)
|
private List<string> GetWhereClauses(InternalItemsQuery query, SqliteCommand? statement)
|
||||||
{
|
{
|
||||||
if (query.IsResumable ?? false)
|
if (query.IsResumable ?? false)
|
||||||
{
|
{
|
||||||
@ -3647,8 +3634,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
if (statement is not null)
|
if (statement is not null)
|
||||||
{
|
{
|
||||||
query.PersonIds[i].TryWriteBytes(idBytes);
|
statement.TryBind(paramName, query.PersonIds[i]);
|
||||||
statement.TryBind(paramName, idBytes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4696,8 +4682,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
connection.ExecuteAll(sql);
|
connection.ExecuteAll(sql);
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4735,16 +4720,15 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||||||
|
|
||||||
// Delete the item
|
// Delete the item
|
||||||
ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob);
|
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))
|
using (var statement = PrepareStatement(db, query))
|
||||||
{
|
{
|
||||||
statement.TryBind("@Id", value);
|
statement.TryBind("@Id", value.ToArray());
|
||||||
|
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
@ -4826,7 +4810,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IStatement statement)
|
private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, SqliteCommand statement)
|
||||||
{
|
{
|
||||||
var whereClauses = new List<string>();
|
var whereClauses = new List<string>();
|
||||||
|
|
||||||
@ -4896,7 +4880,7 @@ AND Type = @InternalPersonType)");
|
|||||||
return whereClauses;
|
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))
|
if (itemId.Equals(default))
|
||||||
{
|
{
|
||||||
@ -4907,12 +4891,14 @@ AND Type = @InternalPersonType)");
|
|||||||
|
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
Span<byte> itemIdBlob = stackalloc byte[16];
|
// TODO how to handle span?
|
||||||
itemId.TryWriteBytes(itemIdBlob);
|
Span<byte> itemIdBlob2 = stackalloc byte[16];
|
||||||
|
itemId.TryWriteBytes(itemIdBlob2);
|
||||||
|
var itemIdBlob = Encoding.ASCII.GetBytes(itemId.ToString());
|
||||||
|
|
||||||
// First delete
|
// First delete
|
||||||
deleteAncestorsStatement.Reset();
|
// TODO deleteAncestorsStatement.Parameters.Clear();
|
||||||
deleteAncestorsStatement.TryBind("@ItemId", itemIdBlob);
|
deleteAncestorsStatement.TryBind("@ItemId", itemId);
|
||||||
deleteAncestorsStatement.MoveNext();
|
deleteAncestorsStatement.MoveNext();
|
||||||
|
|
||||||
if (ancestorIds.Count == 0)
|
if (ancestorIds.Count == 0)
|
||||||
@ -4942,13 +4928,13 @@ AND Type = @InternalPersonType)");
|
|||||||
var index = i.ToString(CultureInfo.InvariantCulture);
|
var index = i.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
var ancestorId = ancestorIds[i];
|
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.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
// TODO statement.Parameters.Clear();
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5323,11 +5309,10 @@ AND Type = @InternalPersonType)");
|
|||||||
GetWhereClauses(innerQuery, statement);
|
GetWhereClauses(innerQuery, statement);
|
||||||
GetWhereClauses(outerQuery, statement);
|
GetWhereClauses(outerQuery, statement);
|
||||||
|
|
||||||
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
|
result.TotalRecordCount = statement.SelectScalarInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
ReadTransactionMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.TotalRecordCount == 0)
|
if (result.TotalRecordCount == 0)
|
||||||
@ -5341,7 +5326,7 @@ AND Type = @InternalPersonType)");
|
|||||||
return result;
|
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();
|
var counts = new ItemCounts();
|
||||||
|
|
||||||
@ -5420,7 +5405,7 @@ AND Type = @InternalPersonType)");
|
|||||||
return list;
|
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))
|
if (itemId.Equals(default))
|
||||||
{
|
{
|
||||||
@ -5434,12 +5419,14 @@ AND Type = @InternalPersonType)");
|
|||||||
var guidBlob = itemId.ToByteArray();
|
var guidBlob = itemId.ToByteArray();
|
||||||
|
|
||||||
// First delete
|
// 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);
|
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;
|
const int Limit = 100;
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
@ -5484,7 +5471,7 @@ AND Type = @InternalPersonType)");
|
|||||||
statement.TryBind("@CleanValue" + index, GetCleanValue(itemValue));
|
statement.TryBind("@CleanValue" + index, GetCleanValue(itemValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
// TODO statement.Parameters.Clear();
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5512,15 +5499,17 @@ AND Type = @InternalPersonType)");
|
|||||||
var itemIdBlob = itemId.ToByteArray();
|
var itemIdBlob = itemId.ToByteArray();
|
||||||
|
|
||||||
// First delete chapters
|
// 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);
|
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;
|
const int Limit = 100;
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
@ -5561,7 +5550,6 @@ AND Type = @InternalPersonType)");
|
|||||||
listIndex++;
|
listIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5570,7 +5558,7 @@ AND Type = @InternalPersonType)");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PersonInfo GetPerson(IReadOnlyList<ResultSetValue> reader)
|
private PersonInfo GetPerson(SqliteDataReader reader)
|
||||||
{
|
{
|
||||||
var item = new PersonInfo
|
var item = new PersonInfo
|
||||||
{
|
{
|
||||||
@ -5666,15 +5654,16 @@ AND Type = @InternalPersonType)");
|
|||||||
var itemIdBlob = id.ToByteArray();
|
var itemIdBlob = id.ToByteArray();
|
||||||
|
|
||||||
// Delete existing mediastreams
|
// 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);
|
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;
|
const int Limit = 10;
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
@ -5770,7 +5759,7 @@ AND Type = @InternalPersonType)");
|
|||||||
statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired);
|
statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
// TODO statement.Parameters.Clear();
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5784,15 +5773,14 @@ AND Type = @InternalPersonType)");
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The reader.</param>
|
/// <param name="reader">The reader.</param>
|
||||||
/// <returns>MediaStream.</returns>
|
/// <returns>MediaStream.</returns>
|
||||||
private MediaStream GetMediaStream(IReadOnlyList<ResultSetValue> reader)
|
private MediaStream GetMediaStream(SqliteDataReader reader)
|
||||||
{
|
{
|
||||||
var item = new MediaStream
|
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))
|
if (reader.TryGetString(3, out var codec))
|
||||||
{
|
{
|
||||||
item.Codec = codec;
|
item.Codec = codec;
|
||||||
@ -6050,18 +6038,19 @@ AND Type = @InternalPersonType)");
|
|||||||
{
|
{
|
||||||
var itemIdBlob = id.ToByteArray();
|
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);
|
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertMediaAttachments(
|
private void InsertMediaAttachments(
|
||||||
byte[] idBlob,
|
byte[] idBlob,
|
||||||
IReadOnlyList<MediaAttachment> attachments,
|
IReadOnlyList<MediaAttachment> attachments,
|
||||||
IDatabaseConnection db,
|
SqliteConnection db,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
const int InsertAtOnce = 10;
|
const int InsertAtOnce = 10;
|
||||||
@ -6111,7 +6100,7 @@ AND Type = @InternalPersonType)");
|
|||||||
statement.TryBind("@MIMEType" + index, attachment.MimeType);
|
statement.TryBind("@MIMEType" + index, attachment.MimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.Reset();
|
// TODO statement.Parameters.Clear();
|
||||||
statement.MoveNext();
|
statement.MoveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6124,11 +6113,11 @@ AND Type = @InternalPersonType)");
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The reader.</param>
|
/// <param name="reader">The reader.</param>
|
||||||
/// <returns>MediaAttachment.</returns>
|
/// <returns>MediaAttachment.</returns>
|
||||||
private MediaAttachment GetMediaAttachment(IReadOnlyList<ResultSetValue> reader)
|
private MediaAttachment GetMediaAttachment(SqliteDataReader reader)
|
||||||
{
|
{
|
||||||
var item = new MediaAttachment
|
var item = new MediaAttachment
|
||||||
{
|
{
|
||||||
Index = reader[1].ToInt()
|
Index = reader.GetInt32(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (reader.TryGetString(2, out var codec))
|
if (reader.TryGetString(2, out var codec))
|
||||||
|
@ -11,8 +11,8 @@ using MediaBrowser.Controller.Configuration;
|
|||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SQLitePCL.pretty;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
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");
|
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);
|
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
|
||||||
|
|
||||||
@ -100,14 +99,14 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
statement.TryBind("@UserId", user.Id);
|
statement.TryBind("@UserId", user.Id);
|
||||||
statement.TryBind("@InternalUserId", user.InternalId);
|
statement.TryBind("@InternalUserId", user.InternalId);
|
||||||
|
statement.Prepare();
|
||||||
|
|
||||||
statement.MoveNext();
|
statement.ExecuteNonQuery();
|
||||||
statement.Reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Guid> GetAllUserIdsWithUserData(IDatabaseConnection db)
|
private List<Guid> GetAllUserIdsWithUserData(SqliteConnection db)
|
||||||
{
|
{
|
||||||
var list = new List<Guid>();
|
var list = new List<Guid>();
|
||||||
|
|
||||||
@ -117,7 +116,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
list.Add(row[0].ReadGuidFromBlob());
|
list.Add(row.GetGuid(0));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -174,12 +173,11 @@ namespace Emby.Server.Implementations.Data
|
|||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
SaveUserData(db, internalUserId, key, userData);
|
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)"))
|
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);
|
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
TransactionMode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +333,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The list of result set values.</param>
|
/// <param name="reader">The list of result set values.</param>
|
||||||
/// <returns>The user item data.</returns>
|
/// <returns>The user item data.</returns>
|
||||||
private UserItemData ReadRow(IReadOnlyList<ResultSetValue> reader)
|
private UserItemData ReadRow(SqliteDataReader reader)
|
||||||
{
|
{
|
||||||
var userData = new UserItemData();
|
var userData = new UserItemData();
|
||||||
|
|
||||||
@ -348,10 +345,10 @@ namespace Emby.Server.Implementations.Data
|
|||||||
userData.Rating = rating;
|
userData.Rating = rating;
|
||||||
}
|
}
|
||||||
|
|
||||||
userData.Played = reader[3].ToBool();
|
userData.Played = reader.GetBoolean(3);
|
||||||
userData.PlayCount = reader[4].ToInt();
|
userData.PlayCount = reader.GetInt32(4);
|
||||||
userData.IsFavorite = reader[5].ToBool();
|
userData.IsFavorite = reader.GetBoolean(5);
|
||||||
userData.PlaybackPositionTicks = reader[6].ToInt64();
|
userData.PlaybackPositionTicks = reader.GetInt64(6);
|
||||||
|
|
||||||
if (reader.TryReadDateTime(7, out var lastPlayedDate))
|
if (reader.TryReadDateTime(7, out var lastPlayedDate))
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DiscUtils.Udf" />
|
<PackageReference Include="DiscUtils.Udf" />
|
||||||
<PackageReference Include="Jellyfin.XmlTv" />
|
<PackageReference Include="Jellyfin.XmlTv" />
|
||||||
|
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
|
||||||
@ -31,7 +32,6 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
|
||||||
<PackageReference Include="Mono.Nat" />
|
<PackageReference Include="Mono.Nat" />
|
||||||
<PackageReference Include="prometheus-net.DotNetRuntime" />
|
<PackageReference Include="prometheus-net.DotNetRuntime" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.netstandard" />
|
|
||||||
<PackageReference Include="DotNet.Glob" />
|
<PackageReference Include="DotNet.Glob" />
|
||||||
</ItemGroup>
|
</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.Async" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" />
|
<PackageReference Include="Serilog.Sinks.Console" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" />
|
<PackageReference Include="Serilog.Sinks.File" />
|
||||||
<PackageReference Include="Serilog.Sinks.Graylog" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Emby.Server.Implementations.Data;
|
using Emby.Server.Implementations.Data;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
using Jellyfin.Server.Extensions;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Emby.Server.Implementations.Data;
|
using Emby.Server.Implementations.Data;
|
||||||
using Jellyfin.Data.Entities.Security;
|
using Jellyfin.Data.Entities.Security;
|
||||||
|
using Jellyfin.Server.Extensions;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -3,6 +3,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using Emby.Server.Implementations.Data;
|
using Emby.Server.Implementations.Data;
|
||||||
|
using Jellyfin.Server.Extensions;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
@ -91,7 +92,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
ratingValue = "NULL";
|
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("@Value", ratingValue);
|
||||||
statement.TryBind("@Rating", ratingString);
|
statement.TryBind("@Rating", ratingString);
|
||||||
statement.ExecuteQuery();
|
statement.ExecuteQuery();
|
||||||
|
@ -4,6 +4,7 @@ using Emby.Server.Implementations.Data;
|
|||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
|
using Jellyfin.Server.Extensions;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
using Jellyfin.Server.Implementations.Users;
|
using Jellyfin.Server.Implementations.Users;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user