using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Database.Providers.Sqlite;
///
/// Injects a series of PRAGMA on each connection starts.
///
public class PragmaConnectionInterceptor : DbConnectionInterceptor
{
private readonly ILogger _logger;
private readonly int? _cacheSize;
private readonly string _lockingMode;
private readonly int? _journalSizeLimit;
private readonly int _tempStoreMode;
private readonly int _syncMode;
private readonly IDictionary _customPragma;
///
/// Initializes a new instance of the class.
///
/// The logger.
/// Cache size.
/// Locking mode.
/// Journal Size.
/// The https://sqlite.org/pragma.html#pragma_temp_store pragma.
/// The https://sqlite.org/pragma.html#pragma_synchronous pragma.
/// A list of custom provided Pragma in the list of CustomOptions starting with "#PRAGMA:".
public PragmaConnectionInterceptor(ILogger logger, int? cacheSize, string lockingMode, int? journalSizeLimit, int tempStoreMode, int syncMode, IDictionary customPragma)
{
_logger = logger;
_cacheSize = cacheSize;
_lockingMode = lockingMode;
_journalSizeLimit = journalSizeLimit;
_tempStoreMode = tempStoreMode;
_syncMode = syncMode;
_customPragma = customPragma;
InitialCommand = BuildCommandText();
_logger.LogInformation("SQLITE connection pragma command set to: \r\n{PragmaCommand}", InitialCommand);
}
private string? InitialCommand { get; set; }
///
public override void ConnectionOpened(DbConnection connection, ConnectionEndEventData eventData)
{
base.ConnectionOpened(connection, eventData);
using (var command = connection.CreateCommand())
{
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
command.CommandText = InitialCommand;
#pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
command.ExecuteNonQuery();
}
}
///
public override async Task ConnectionOpenedAsync(DbConnection connection, ConnectionEndEventData eventData, CancellationToken cancellationToken = default)
{
await base.ConnectionOpenedAsync(connection, eventData, cancellationToken).ConfigureAwait(false);
var command = connection.CreateCommand();
await using (command.ConfigureAwait(false))
{
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
command.CommandText = InitialCommand;
#pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
}
private string BuildCommandText()
{
var sb = new StringBuilder();
if (_cacheSize.HasValue)
{
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA cache_size={_cacheSize.Value};");
}
if (!string.IsNullOrWhiteSpace(_lockingMode))
{
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA locking_mode={_lockingMode};");
}
if (_journalSizeLimit.HasValue)
{
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA journal_size_limit={_journalSizeLimit};");
}
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA synchronous={_syncMode};");
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA temp_store={_tempStoreMode};");
foreach (var item in _customPragma)
{
sb.AppendLine(CultureInfo.InvariantCulture, $"PRAGMA {item.Key}={item.Value};");
}
return sb.ToString();
}
}