Made MessageStorage.GetMessages more flexible.
This commit is contained in:
parent
eae379e933
commit
0bc025e5f8
5 changed files with 167 additions and 80 deletions
SharpChat.MariaDB
SharpChat.SQLite
SharpChatCommon
|
@ -1,6 +1,9 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using SharpChat.Data;
|
||||
using SharpChat.Messages;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using ZLogger;
|
||||
|
||||
|
@ -54,20 +57,20 @@ public class MariaDBMessageStorage(MariaDBStorage storage, ILogger logger) : Mes
|
|||
}
|
||||
}
|
||||
|
||||
private static Message ReadMessage(MySqlDataReader reader) {
|
||||
using Stream data = reader.GetStream("event_data");
|
||||
private static Message ReadMessage(DbDataReader reader) {
|
||||
using Stream data = reader.GetStream(reader.GetOrdinal("event_data"));
|
||||
return new Message(
|
||||
reader.GetInt64("event_id"),
|
||||
reader.GetString("event_type"),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : reader.GetString("event_sender"),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
|
||||
ColourInheritable.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||
reader.GetInt32("event_sender_rank"),
|
||||
MariaDBUserPermissionsConverter.From((MariaDBUserPermissions)reader.GetInt32("event_sender_perms")),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? string.Empty : reader.GetString("event_sender_nick"),
|
||||
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_deleted")),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_channel")) ? null : reader.GetString("event_channel"),
|
||||
reader.GetInt64(reader.GetOrdinal("event_id")),
|
||||
reader.GetString(reader.GetOrdinal("event_type")),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : reader.GetString(reader.GetOrdinal("event_sender")),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString(reader.GetOrdinal("event_sender_name")),
|
||||
ColourInheritable.FromMisuzu(reader.GetInt32(reader.GetOrdinal("event_sender_colour"))),
|
||||
reader.GetInt32(reader.GetOrdinal("event_sender_rank")),
|
||||
MariaDBUserPermissionsConverter.From((MariaDBUserPermissions)reader.GetInt32(reader.GetOrdinal("event_sender_perms"))),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? string.Empty : reader.GetString(reader.GetOrdinal("event_sender_nick")),
|
||||
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(reader.GetOrdinal("event_created"))),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(reader.GetOrdinal("event_deleted"))),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_channel")) ? null : reader.GetString(reader.GetOrdinal("event_channel")),
|
||||
JsonDocument.Parse(data)
|
||||
);
|
||||
}
|
||||
|
@ -100,39 +103,59 @@ public class MariaDBMessageStorage(MariaDBStorage storage, ILogger logger) : Mes
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Message>> GetMessages(string channelName, int amount = 20, int offset = 0) {
|
||||
List<Message> msgs = [];
|
||||
public async Task<IEnumerable<Message>> GetMessages(
|
||||
string? channelName = null,
|
||||
int? take = 20,
|
||||
long? beforeId = null,
|
||||
bool includeDeleted = false
|
||||
) {
|
||||
List<MySqlParameter> parameters = [];
|
||||
bool firstParam = true;
|
||||
StringBuilder qb = new();
|
||||
qb.Append("SELECT event_id, event_type, event_data, event_channel");
|
||||
qb.Append(", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms");
|
||||
qb.Append(", UNIX_TIMESTAMP(event_created) AS event_created");
|
||||
qb.Append(", UNIX_TIMESTAMP(event_deleted) AS event_deleted");
|
||||
qb.Append(" FROM sqc_events");
|
||||
|
||||
try {
|
||||
using MariaDBConnection conn = await storage.CreateConnection();
|
||||
using MySqlDataReader? reader = await conn.RunQuery(
|
||||
"SELECT event_id, event_type, event_data, event_channel"
|
||||
+ ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms"
|
||||
+ ", UNIX_TIMESTAMP(event_created) AS event_created"
|
||||
+ ", UNIX_TIMESTAMP(event_deleted) AS event_deleted"
|
||||
+ " FROM sqc_events"
|
||||
+ " WHERE event_deleted IS NULL AND (event_channel = @channel OR event_channel IS NULL)"
|
||||
+ " AND event_id > @offset"
|
||||
+ " ORDER BY event_id DESC"
|
||||
+ " LIMIT @amount",
|
||||
new MySqlParameter("channel", channelName),
|
||||
new MySqlParameter("amount", amount),
|
||||
new MySqlParameter("offset", offset)
|
||||
);
|
||||
if(reader is null)
|
||||
return msgs;
|
||||
|
||||
while(reader.Read()) {
|
||||
Message evt = ReadMessage(reader);
|
||||
if(evt != null)
|
||||
msgs.Add(evt);
|
||||
}
|
||||
} catch(MySqlException ex) {
|
||||
logger.ZLogError($"Error in GetMessages(): {ex}");
|
||||
if(!includeDeleted) {
|
||||
firstParam = false;
|
||||
qb.Append(" WHERE event_deleted IS NULL");
|
||||
}
|
||||
|
||||
msgs.Reverse();
|
||||
if(!string.IsNullOrEmpty(channelName)) {
|
||||
qb.AppendFormat(" {0} (event_channel = @channel OR event_channel IS NULL)", firstParam ? "WHERE" : "AND");
|
||||
parameters.Add(new MySqlParameter("channel", channelName));
|
||||
firstParam = false;
|
||||
}
|
||||
|
||||
return msgs;
|
||||
if(beforeId.HasValue) {
|
||||
qb.AppendFormat(" {0} event_id < @before", firstParam ? "WHERE" : "AND");
|
||||
parameters.Add(new MySqlParameter("before", beforeId.Value));
|
||||
}
|
||||
|
||||
qb.Append(" ORDER BY event_id DESC");
|
||||
|
||||
if(take.HasValue) {
|
||||
qb.Append(" LIMIT @take");
|
||||
parameters.Add(new MySqlParameter("take", take.Value));
|
||||
}
|
||||
|
||||
string query = string.Format("SELECT * FROM ({0}) AS _ ORDER BY event_id ASC", qb);
|
||||
|
||||
try {
|
||||
MariaDBConnection conn = await storage.CreateConnection();
|
||||
DbDataReader? reader = await conn.RunQuery(query, [.. parameters]);
|
||||
|
||||
if(reader is null) {
|
||||
conn.Dispose();
|
||||
return [];
|
||||
}
|
||||
|
||||
return new DbObjectEnumerable<Message>(reader, ReadMessage, () => conn.Dispose());
|
||||
} catch(MySqlException ex) {
|
||||
logger.ZLogError($"Error in GetMessages({channelName}, {take}, {beforeId}, {includeDeleted}): {ex}");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using SharpChat.Data;
|
||||
using SharpChat.Messages;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
@ -79,50 +80,57 @@ public class SQLiteMessageStorage(ILogger logger, SQLiteConnection conn) : Messa
|
|||
new SQLiteParameter("id", id)
|
||||
);
|
||||
|
||||
if(reader is null)
|
||||
return null;
|
||||
|
||||
while(reader.Read()) {
|
||||
Message evt = ReadMessage(reader);
|
||||
if(evt != null)
|
||||
return evt;
|
||||
}
|
||||
return reader?.Read() == true ? ReadMessage(reader) : null;
|
||||
} catch(SQLiteException ex) {
|
||||
logger.ZLogError($"Error in GetMessage(): {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Message>> GetMessages(string channelName, int amount = 20, int offset = 0) {
|
||||
List<Message> msgs = [];
|
||||
public async Task<IEnumerable<Message>> GetMessages(
|
||||
string? channelName = null,
|
||||
int? take = 20,
|
||||
long? beforeId = null,
|
||||
bool includeDeleted = false
|
||||
) {
|
||||
List<SQLiteParameter> parameters = [];
|
||||
bool firstParam = true;
|
||||
StringBuilder qb = new();
|
||||
qb.Append("SELECT msg_id, msg_type, msg_created, msg_deleted, msg_channel, msg_data");
|
||||
qb.Append(", msg_sender, msg_sender_name, msg_sender_colour, msg_sender_rank, msg_sender_nick, msg_sender_perms");
|
||||
qb.Append(" FROM messages");
|
||||
|
||||
if(!includeDeleted) {
|
||||
firstParam = false;
|
||||
qb.Append(" WHERE msg_deleted IS NULL");
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(channelName)) {
|
||||
qb.AppendFormat(" {0} (msg_channel = @channel OR msg_channel IS NULL)", firstParam ? "WHERE" : "AND");
|
||||
parameters.Add(new SQLiteParameter("channel", channelName));
|
||||
firstParam = false;
|
||||
}
|
||||
|
||||
if(beforeId.HasValue) {
|
||||
qb.AppendFormat(" {0} msg_id < @before", firstParam ? "WHERE" : "AND");
|
||||
parameters.Add(new SQLiteParameter("before", beforeId.Value));
|
||||
}
|
||||
|
||||
qb.Append(" ORDER BY msg_id DESC");
|
||||
|
||||
if(take.HasValue) {
|
||||
qb.Append(" LIMIT @take");
|
||||
parameters.Add(new SQLiteParameter("take", take.Value));
|
||||
}
|
||||
|
||||
string query = string.Format("SELECT * FROM ({0}) AS _ ORDER BY msg_id ASC", qb);
|
||||
|
||||
try {
|
||||
using DbDataReader? reader = await conn.RunQuery(
|
||||
"SELECT msg_id, msg_type, msg_created, msg_deleted, msg_channel, msg_sender, msg_sender_name, msg_sender_colour, msg_sender_rank, msg_sender_nick, msg_sender_perms, msg_data"
|
||||
+ " FROM messages"
|
||||
+ " WHERE msg_deleted IS NULL AND (msg_channel = @channel OR msg_channel IS NULL)"
|
||||
+ " AND msg_id > @offset"
|
||||
+ " ORDER BY msg_id DESC"
|
||||
+ " LIMIT @amount",
|
||||
new SQLiteParameter("channel", channelName),
|
||||
new SQLiteParameter("amount", amount),
|
||||
new SQLiteParameter("offset", offset)
|
||||
);
|
||||
if(reader is null)
|
||||
return msgs;
|
||||
|
||||
while(reader.Read()) {
|
||||
Message evt = ReadMessage(reader);
|
||||
if(evt != null)
|
||||
msgs.Add(evt);
|
||||
}
|
||||
DbDataReader? reader = await conn.RunQuery(query, [.. parameters]);
|
||||
return reader is null ? [] : new DbObjectEnumerable<Message>(reader, ReadMessage);
|
||||
} catch(SQLiteException ex) {
|
||||
logger.ZLogError($"Error in GetMessages(): {ex}");
|
||||
logger.ZLogError($"Error in GetMessages({channelName}, {take}, {beforeId}, {includeDeleted}): {ex}");
|
||||
return [];
|
||||
}
|
||||
|
||||
msgs.Reverse();
|
||||
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
|
|
14
SharpChatCommon/Data/DbObjectEnumerable.cs
Normal file
14
SharpChatCommon/Data/DbObjectEnumerable.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace SharpChat.Data;
|
||||
|
||||
public class DbObjectEnumerable<T>(DbDataReader reader, Func<DbDataReader, T> constructor, Action? dispose = null) : IEnumerable<T> {
|
||||
public IEnumerator<T> GetEnumerator() {
|
||||
return new DbObjectEnumerator<T>(reader, constructor, dispose);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
37
SharpChatCommon/Data/DbObjectEnumerator.cs
Normal file
37
SharpChatCommon/Data/DbObjectEnumerator.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace SharpChat.Data;
|
||||
|
||||
public class DbObjectEnumerator<T>(DbDataReader reader, Func<DbDataReader, T> constructor, Action? dispose) : IEnumerator<T> {
|
||||
public T Current => constructor(reader);
|
||||
|
||||
object? IEnumerator.Current => Current;
|
||||
|
||||
public bool MoveNext() {
|
||||
return reader.Read();
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
// no-op cus you can't reset a DbDataReader afaik
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
|
||||
~DbObjectEnumerator() {
|
||||
DoDispose();
|
||||
}
|
||||
public void Dispose() {
|
||||
DoDispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void DoDispose() {
|
||||
if(disposed)
|
||||
return;
|
||||
disposed = true;
|
||||
reader.Dispose();
|
||||
if(dispose is not null)
|
||||
dispose();
|
||||
}
|
||||
}
|
|
@ -15,5 +15,10 @@ public interface MessageStorage {
|
|||
);
|
||||
Task DeleteMessage(Message msg);
|
||||
Task<Message?> GetMessage(long id);
|
||||
Task<IEnumerable<Message>> GetMessages(string channelName, int amount = 20, int offset = 0);
|
||||
Task<IEnumerable<Message>> GetMessages(
|
||||
string? channelName = null,
|
||||
int? take = 20,
|
||||
long? beforeId = null,
|
||||
bool includeDeleted = false
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue