Skip to content

Commit

Permalink
fixed moderation menu not working.
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielWillett committed Jan 11, 2025
1 parent 4a382c6 commit 2805365
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 118 deletions.
8 changes: 4 additions & 4 deletions UncreatedWarfare/Commands/Migrate/MigrateUserDataCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if DEBUG
#if DEBUG
using System;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -117,7 +117,7 @@ await _mySqlProvider.QueryAsync(
username.NickName = reader.IsDBNull(1) ? null! : reader.GetString(1);
username.PlayerName = reader.IsDBNull(2) ? null! : reader.GetString(2);
username.WasFound = true;
return true;
return false;
});

username.CharacterName ??= steam64.ToString("D17", CultureInfo.InvariantCulture);
Expand All @@ -131,7 +131,7 @@ await _mySqlProvider.QueryAsync(
{
firstJoined = reader.IsDBNull(0) ? null : new DateTimeOffset(DateTime.SpecifyKind(reader.GetDateTime(0), DateTimeKind.Utc));
lastJoined = reader.IsDBNull(1) ? null : new DateTimeOffset(DateTime.SpecifyKind(reader.GetDateTime(1), DateTimeKind.Utc));
return true;
return false;
});

ulong discordId = 0;
Expand All @@ -142,7 +142,7 @@ await _mySqlProvider.QueryAsync(
reader =>
{
discordId = reader.GetUInt64(0);
return true;
return false;
});

WarfareUserData data = new WarfareUserData
Expand Down
207 changes: 176 additions & 31 deletions UncreatedWarfare/Database/Manual/ManualMySqlProvider.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using DanielWillett.ReflectionTools;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using MySqlConnector;
using System.Text;

namespace Uncreated.Warfare.Database.Manual;

Expand All @@ -13,51 +16,71 @@ public interface IManualMySqlProvider
/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, ReadLoopAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token, ReadLoopAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, ReadLoopAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row until it returns <see langword="true"/>.
/// Query a MySql database and run <paramref name="callback"/> for each returned row while it returns <see langword="true"/>.
/// </summary>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, ReadLoopUntilAction callback);
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, ReadLoopWhileAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row until it returns <see langword="true"/>.
/// Query a MySql database and run <paramref name="callback"/> for each returned row while it returns <see langword="true"/>.
/// </summary>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token, ReadLoopUntilAction callback);
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token, ReadLoopWhileAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row until it returns <see langword="true"/>.
/// Query a MySql database and run <paramref name="callback"/> for each returned row while it returns <see langword="true"/>.
/// </summary>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, ReadLoopUntilAction callback);
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, ReadLoopWhileAction callback);

/// <summary>
/// Query a MySql database and run <paramref name="callback"/> for each returned row until it returns <see langword="true"/>.
/// Query a MySql database and run <paramref name="callback"/> for each returned row while it returns <see langword="true"/>.
/// </summary>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopUntilAction callback);
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopWhileAction callback);

/// <summary>
/// Execute a command against a MySql database.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> NonQueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token = default);

/// <summary>
/// Execute a command against a MySql database.
/// </summary>
/// <exception cref="DbException"/>
/// <exception cref="ArgumentNullException"/>
Task<int> NonQueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token = default);
}

Expand All @@ -69,7 +92,7 @@ public interface IManualMySqlProvider
/// <summary>
/// Called per row while reading a query response. Can return <see langword="true"/> to break from the loop.
/// </summary>
public delegate bool ReadLoopUntilAction(MySqlDataReader reader);
public delegate bool ReadLoopWhileAction(MySqlDataReader reader);


/// <summary>
Expand All @@ -78,9 +101,12 @@ public interface IManualMySqlProvider
public class ManualMySqlProvider : IManualMySqlProvider
{
private readonly string _connectionString;
public ManualMySqlProvider(string connectionString)
private readonly ILogger<ManualMySqlProvider> _logger;

public ManualMySqlProvider(string connectionString, ILogger<ManualMySqlProvider> logger)
{
_connectionString = connectionString;
_logger = logger;
}

/// <inheritdoc />
Expand All @@ -98,67 +124,124 @@ public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, in
/// <inheritdoc />
public async Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopAction callback)
{
if (query == null)
throw new ArgumentNullException(nameof(query));

token.ThrowIfCancellationRequested();

CheckLengthIndex(parameters, ref index, ref length);
await using MySqlConnection connection = new MySqlConnection(_connectionString);

await connection.OpenAsync(token);
try
{
await connection.OpenAsync(token);
}
catch (Exception ex)
{
if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error opening database connection.", ex);
}

await using MySqlCommand command = new MySqlCommand(query, connection);

AppendParameters(command, parameters, index, length);

await using MySqlDataReader dataReader = await command.ExecuteReaderAsync(token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Executing query: {0}.", QueryToString(query, parameters, index, length));
}

int row = 0;
while (await dataReader.ReadAsync(token).ConfigureAwait(false))
try
{
++row;
await using MySqlDataReader dataReader = await command.ExecuteReaderAsync(token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
callback(dataReader);

while (await dataReader.ReadAsync(token).ConfigureAwait(false))
{
++row;
token.ThrowIfCancellationRequested();
callback?.Invoke(dataReader);
}
}
catch (Exception ex)
{
_logger.LogWarning("Query failed to execute: {0}.", QueryToString(query, parameters, index, length));

if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error executing query.", ex);
}

return row;
}

/// <inheritdoc />
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, ReadLoopUntilAction callback)
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, ReadLoopWhileAction callback)
=> QueryAsync(query, parameters, 0, -1, CancellationToken.None, callback);

/// <inheritdoc />
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token, ReadLoopUntilAction callback)
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, CancellationToken token, ReadLoopWhileAction callback)
=> QueryAsync(query, parameters, 0, -1, token, callback);

/// <inheritdoc />
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, ReadLoopUntilAction callback)
public Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, ReadLoopWhileAction callback)
=> QueryAsync(query, parameters, index, length, CancellationToken.None, callback);

/// <inheritdoc />
public async Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopUntilAction callback)
public async Task<int> QueryAsync(string query, IReadOnlyList<object?>? parameters, int index, int length, CancellationToken token, ReadLoopWhileAction callback)
{
token.ThrowIfCancellationRequested();

CheckLengthIndex(parameters, ref index, ref length);
await using MySqlConnection connection = new MySqlConnection(_connectionString);

await connection.OpenAsync(token);
try
{
await connection.OpenAsync(token);
}
catch (Exception ex)
{
if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error opening database connection.", ex);
}

await using MySqlCommand command = new MySqlCommand(query, connection);

AppendParameters(command, parameters, index, length);

await using MySqlDataReader dataReader = await command.ExecuteReaderAsync(token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Executing query: {0}.", QueryToString(query, parameters, index, length));
}

int row = 0;
while (await dataReader.ReadAsync(token).ConfigureAwait(false))
try
{
++row;
await using MySqlDataReader dataReader = await command.ExecuteReaderAsync(token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
if (callback(dataReader))
break;

while (await dataReader.ReadAsync(token).ConfigureAwait(false))
{
++row;
token.ThrowIfCancellationRequested();
if (callback != null && callback(dataReader))
break;
}
}
catch (Exception ex)
{
_logger.LogWarning("Query failed to execute: {0}.", QueryToString(query, parameters, index, length));

if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error executing query.", ex);
}

return row;
Expand All @@ -176,13 +259,40 @@ public async Task<int> NonQueryAsync(string query, IReadOnlyList<object?>? param
CheckLengthIndex(parameters, ref index, ref length);
await using MySqlConnection connection = new MySqlConnection(_connectionString);

await connection.OpenAsync(token);
try
{
await connection.OpenAsync(token);
}
catch (Exception ex)
{
if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error opening database connection.", ex);
}

await using MySqlCommand command = new MySqlCommand(query, connection);

AppendParameters(command, parameters, index, length);

return await command.ExecuteNonQueryAsync(token).ConfigureAwait(false);
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Executing command: {0}.", QueryToString(query, parameters, index, length));
}

try
{
return await command.ExecuteNonQueryAsync(token).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogWarning("Command failed to execute: {0}.", QueryToString(query, parameters, index, length));

if (ex is DbException)
throw;

throw new ManualMySqlProviderDbException("Error executing command.", ex);
}
}

private static void AppendParameters(MySqlCommand cmd, IReadOnlyList<object?>? parameters, int index, int length)
Expand All @@ -196,6 +306,33 @@ private static void AppendParameters(MySqlCommand cmd, IReadOnlyList<object?>? p
cmd.Parameters.AddWithValue("@" + i.ToString(CultureInfo.InvariantCulture), obj ?? DBNull.Value);
}
}

private static string QueryToString(string query, IReadOnlyList<object?>? parameters, int index, int length)
{
if (parameters is not { Count: > 0 } || length <= 0)
return query;

StringBuilder sb = new StringBuilder(query);

for (int i = 0; i < length; ++i)
{
object? obj = parameters[i + index];
sb.AppendLine();
sb.Append('@').Append(i).Append(" - ");

if (obj is null || ReferenceEquals(obj, DBNull.Value))
{
sb.Append("null");
}
else
{
sb.Append(Accessor.Formatter.Format(obj.GetType())).Append(" - ").Append('"').Append(obj).Append('"');
}
}

return sb.ToString();
}

private static void CheckLengthIndex(IReadOnlyList<object?>? list, ref int index, ref int length)
{
if (list == null)
Expand All @@ -209,4 +346,12 @@ private static void CheckLengthIndex(IReadOnlyList<object?>? list, ref int index
else if (index + length > list.Count)
throw new ArgumentOutOfRangeException(nameof(length));
}
}

/// <summary>
/// Wraps non-<see cref="DbException"/> errors thrown by <see cref="IManualMySqlProvider"/> implementations.
/// </summary>
public class ManualMySqlProviderDbException : DbException
{
public ManualMySqlProviderDbException(string message, Exception inner) : base(message, inner) { }
}
Loading

0 comments on commit 2805365

Please sign in to comment.