using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Sqlite; namespace Jellyfin.DbMigrator; /// /// Reads rows from a SQLite database table using raw ADO.NET. /// public static class SqliteTableReader { /// /// Returns all rows from the specified SQLite table as a list of column-name-to-value dictionaries. /// /// An open . /// The name of the table to read. /// A token to cancel the operation. /// A list where each element is a dictionary mapping column name to its value (may be ). public static async Task>> ReadAllRowsAsync( SqliteConnection connection, string tableName, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(connection); ArgumentException.ThrowIfNullOrWhiteSpace(tableName); TableNameValidator.EnsureSafe(tableName); var rows = new List>(); var cmd = connection.CreateCommand(); await using (cmd.ConfigureAwait(false)) { cmd.CommandText = $"SELECT * FROM \"{tableName}\""; var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); await using (reader.ConfigureAwait(false)) { while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { var row = new Dictionary(reader.FieldCount, StringComparer.Ordinal); for (int i = 0; i < reader.FieldCount; i++) { string col = reader.GetName(i); bool isNull = await reader.IsDBNullAsync(i, cancellationToken).ConfigureAwait(false); object? val = isNull ? null : reader.GetValue(i); row[col] = val; } rows.Add(row); } } } return rows; } /// /// Returns the row count for the specified table in the SQLite database. /// /// An open . /// The name of the table to count. /// A token to cancel the operation. /// The number of rows in the table, or -1 if the table does not exist. public static async Task CountRowsAsync( SqliteConnection connection, string tableName, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(connection); ArgumentException.ThrowIfNullOrWhiteSpace(tableName); TableNameValidator.EnsureSafe(tableName); // Check if the table exists first. var checkCmd = connection.CreateCommand(); await using (checkCmd.ConfigureAwait(false)) { checkCmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=$name"; checkCmd.Parameters.AddWithValue("$name", tableName); var exists = await checkCmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); if (exists is not long existsLong || existsLong == 0) { return -1L; } } var cmd = connection.CreateCommand(); await using (cmd.ConfigureAwait(false)) { cmd.CommandText = $"SELECT COUNT(*) FROM \"{tableName}\""; var result = await cmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); return result is long count ? count : Convert.ToInt64(result, System.Globalization.CultureInfo.InvariantCulture); } } }