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);
}
}
}