390 lines
15 KiB
C#
390 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Text;
|
|
using Microsoft.Data.Sqlite;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.VisualBasic;
|
|
using NLog;
|
|
|
|
namespace ChaosBot.Database
|
|
{
|
|
public static class Controller
|
|
{
|
|
static SqliteConnection _conn = new SqliteConnection($"Data Source={System.IO.Directory.GetCurrentDirectory()}/{Program.Cfg.GetValue<string>("Bot:Database")}");
|
|
private static Logger _logger = Program._logger;
|
|
|
|
/// <summary>
|
|
/// Run a raw query on the database
|
|
/// </summary>
|
|
/// <code>
|
|
/// string query = "TRUNCATE SomeTable";
|
|
/// Controller.RawQuery(query);
|
|
/// </code>
|
|
/// <param name="query">Raw query to execute</param>
|
|
/// <param name="readOutput">Whether to read the output and return it as a <c>DataFrame</c></param>
|
|
/// <param name="throwError">Whether to throw any exceptions or to log and shrug.</param>
|
|
/// <returns></returns>
|
|
public static DataTable RawQuery(string query, bool readOutput = true, bool throwError = false)
|
|
{
|
|
DataTable dt = new DataTable();
|
|
|
|
try
|
|
{
|
|
using (_conn)
|
|
{
|
|
// Open the SQLite connection
|
|
_conn.Open();
|
|
|
|
// Start creating the command and assign the query given
|
|
SqliteCommand cmd = _conn.CreateCommand();
|
|
cmd.CommandText = query;
|
|
|
|
if (readOutput)
|
|
{
|
|
// output needs to be read, make a reader and fill the datatable with the data
|
|
SqliteDataReader executeReader = cmd.ExecuteReader(CommandBehavior.SingleResult);
|
|
dt.Load(executeReader);
|
|
}
|
|
else
|
|
{
|
|
// output does not need to be read, just run the query without reading
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
// Clean up after ourselves
|
|
_conn.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (throwError)
|
|
{
|
|
// If we throw any exception, just throw it so other code can handle it
|
|
throw;
|
|
}
|
|
|
|
// We handle the exception, log it and be done
|
|
_logger.Fatal($"Database.Controller.RawQuery: Exception [{ex}] thrown, <[{ex.Message}]>.");
|
|
}
|
|
|
|
return dt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Insert a list of parameters into a table in the database
|
|
/// </summary>
|
|
/// <code>
|
|
/// string table = "SomeTable";
|
|
/// Dictionary<string, object> parameters = new Dictionary<string, object>
|
|
/// {
|
|
/// { "someInt", 123 },
|
|
/// { "someString", "asdf" },
|
|
/// { "someChar", 'x' }
|
|
/// }
|
|
///
|
|
/// Controller.InsertQuery(table, parameters);
|
|
/// </code>
|
|
/// <param name="table">Table to insert into</param>
|
|
/// <param name="parameters">List of parameters to insert to the row. Every type is a key</param>
|
|
public static void InsertQuery(string table, Dictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
using (_conn)
|
|
{
|
|
// Open the SQLite connection
|
|
_conn.Open();
|
|
|
|
// Start the command creation
|
|
SqliteCommand cmd = _conn.CreateCommand();
|
|
|
|
// Build the command bit by bit
|
|
// INSERT INTO {table} (key1, key2, key3) VALUES (@key1,@key2,@key3)
|
|
StringBuilder commandText = new StringBuilder();
|
|
commandText.Append("INSERT INTO ");
|
|
commandText.Append(table);
|
|
commandText.Append(" (");
|
|
|
|
foreach (string key in parameters.Keys)
|
|
{
|
|
commandText.Append($"{key}, ");
|
|
}
|
|
|
|
commandText.Remove(commandText.Length - 2, 2);
|
|
commandText.Append(") VALUES (");
|
|
|
|
foreach (string key in parameters.Keys)
|
|
{
|
|
// We use @key to indicate the parameter placeholder
|
|
commandText.Append($"@{key},");
|
|
}
|
|
|
|
// Remove the trailing comma
|
|
commandText.Remove(commandText.Length - 1, 1);
|
|
commandText.Append(")");
|
|
|
|
// Assign onto QueryCommand
|
|
cmd.CommandText = commandText.ToString();
|
|
|
|
// Replace all the parameters to the query
|
|
foreach (string key in parameters.Keys)
|
|
{
|
|
cmd.Parameters.AddWithValue($"@{key}", parameters.GetValueOrDefault(key));
|
|
}
|
|
|
|
// Prepare the parameters
|
|
cmd.Prepare();
|
|
|
|
// Execute, do not return anything
|
|
cmd.ExecuteNonQuery();
|
|
|
|
_conn.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Fatal($"Database.Controller.InsertQuery: Exception [{ex}] thrown, <[{ex.Message}]>.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update rows in the database
|
|
/// </summary>
|
|
/// <code>
|
|
/// string table = "SomeTable";
|
|
/// Dictionary<string, object> values = new Dictionary<string, object>
|
|
/// {
|
|
/// {"key", "newValue"}
|
|
/// };
|
|
/// Dictionary<string, object> parameters = new Dictionary<string, object>
|
|
/// {
|
|
/// {"key", "oldValue"}
|
|
/// };
|
|
/// Controller.UpdateQuery(table, values, parameters);
|
|
/// </code>
|
|
/// <param name="table">Table to update data in</param>
|
|
/// <param name="values">List of keys to set. Equivalent of SQL: SET {key} = {value}</param>
|
|
/// <param name="parameters">List of filters to apply in the SQL WHERE condition</param>
|
|
public static void UpdateQuery(string table, Dictionary<string, object> values, Dictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
using (_conn)
|
|
{
|
|
// Open SQLite connection
|
|
_conn.Open();
|
|
|
|
// Create the command
|
|
SqliteCommand cmd = _conn.CreateCommand();
|
|
|
|
// Build the commandtext
|
|
// UPDATE {table} SET {key}=@val_{key} WHERE {key}=@fil_{key}
|
|
StringBuilder commandText = new StringBuilder();
|
|
commandText.Append("UPDATE ");
|
|
commandText.Append(table);
|
|
commandText.Append(" SET ");
|
|
|
|
// Build the list of values
|
|
List<string> updateList = new List<string>();
|
|
foreach (string key in values.Keys)
|
|
{
|
|
updateList.Add($"{key}=@val_{key} ");
|
|
cmd.Parameters.AddWithValue($@"val_{key}", values.GetValueOrDefault(key));
|
|
}
|
|
|
|
// Append the list of values, comma-separated
|
|
commandText.Append(string.Join(", ", updateList));
|
|
|
|
// Build the list of filters
|
|
List<string> filterList = new List<string>();
|
|
foreach (string key in parameters.Keys)
|
|
{
|
|
filterList.Add($"{key}=@fil_{key} ");
|
|
cmd.Parameters.AddWithValue($"@fil_{key}", parameters.GetValueOrDefault(key));
|
|
}
|
|
|
|
if (filterList.Count > 0)
|
|
{
|
|
// Prepend with WHERE if there are parameters
|
|
commandText.Append("WHERE ");
|
|
// Append the list of filters AND separated
|
|
commandText.Append(string.Join("AND ", filterList));
|
|
}
|
|
|
|
// Assign the command
|
|
cmd.CommandText = commandText.ToString();
|
|
|
|
// Prepare the parameters
|
|
cmd.Prepare();
|
|
|
|
// Execute, do not return
|
|
cmd.ExecuteNonQuery();
|
|
|
|
// Close connection
|
|
_conn.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Fatal($"Database.Controller.UpdateQuery: Exception [{ex}] thrown, <[{ex.Message}]>.");
|
|
}
|
|
}
|
|
|
|
public static int TransactionQuery(List<SqliteCommand> cmds)
|
|
{
|
|
SqliteCommand command = _conn.CreateCommand();
|
|
SqliteTransaction transaction;
|
|
|
|
_conn.Open();
|
|
|
|
// Start a local transaction.
|
|
transaction = _conn.BeginTransaction();
|
|
|
|
// Must assign both transaction object and connection
|
|
// to Command object for a pending local transaction
|
|
command.Connection = _conn;
|
|
command.Transaction = transaction;
|
|
|
|
try
|
|
{
|
|
foreach (var cmd in cmds)
|
|
{
|
|
command.CommandText = cmd.CommandText;
|
|
command.ExecuteNonQuery();
|
|
}
|
|
|
|
// Attempt to commit the transaction.
|
|
transaction.Commit();
|
|
_logger.Info($"{cmds.Count} record(s) are written to database.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Warn("Commit Exception Type: {0}", ex.GetType());
|
|
_logger.Warn(" Message: {0}", ex.Message);
|
|
|
|
// Attempt to roll back the transaction.
|
|
try
|
|
{
|
|
transaction.Rollback();
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
// This catch block will handle any errors that may have occurred
|
|
// on the server that would cause the rollback to fail, such as
|
|
// a closed connection.
|
|
_logger.Warn("Rollback Exception Type: {0}", ex2.GetType());
|
|
_logger.Warn(" Message: {0}", ex2.Message);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
_conn.Close();
|
|
return cmds.Count;
|
|
}
|
|
|
|
public static DataTable SelectQuery(string table, string selectColumns = "*", Dictionary<string, object> filterColumns = null, string orderByKey = null)
|
|
{
|
|
DataTable dt = new DataTable();
|
|
|
|
try
|
|
{
|
|
using (_conn)
|
|
{
|
|
_conn.Open();
|
|
SqliteCommand cmd = _conn.CreateCommand();
|
|
|
|
string filter = null;
|
|
if (filterColumns != null)
|
|
{
|
|
List<string> filterList = new List<string>();
|
|
foreach (string key in filterColumns.Keys)
|
|
{
|
|
filterList.Add($"{key} = @{key}");
|
|
}
|
|
|
|
filter = $"WHERE {Strings.Join(filterList.ToArray(), " AND ")}";
|
|
|
|
foreach (string key in filterColumns.Keys)
|
|
{
|
|
cmd.Parameters.AddWithValue($@"{key}", filterColumns.GetValueOrDefault(key));
|
|
}
|
|
}
|
|
|
|
string order = null;
|
|
if (orderByKey != null)
|
|
{
|
|
order = $"ORDER BY {orderByKey}";
|
|
}
|
|
|
|
string query = $"SELECT {selectColumns} FROM {table} {filter} {order}";
|
|
|
|
cmd.CommandText = query;
|
|
cmd.Prepare();
|
|
SqliteDataReader executeReader = cmd.ExecuteReader();
|
|
|
|
dt.Load(executeReader);
|
|
|
|
_conn.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Fatal($"Database.Controller.SelectQuery: Exception [{ex}] thrown, <[{ex.Message}]>.");
|
|
}
|
|
|
|
return dt;
|
|
}
|
|
|
|
public static void DeleteQuery(string table, Dictionary<string, object> filterColumns = null, string orderByKey = null, int limit = -1, int offset = -1)
|
|
{
|
|
try
|
|
{
|
|
using (_conn)
|
|
{
|
|
_conn.Open();
|
|
SqliteCommand cmd = _conn.CreateCommand();
|
|
|
|
string filter = null;
|
|
if (filterColumns != null)
|
|
{
|
|
List<string> filterList = new List<string>();
|
|
foreach (string key in filterColumns.Keys)
|
|
{
|
|
filterList.Add($"{key} = @{key}");
|
|
}
|
|
|
|
filter = $"WHERE {Strings.Join(filterList.ToArray(), " AND ")}";
|
|
|
|
foreach (string key in filterColumns.Keys)
|
|
{
|
|
cmd.Parameters.AddWithValue($@"{key}", filterColumns.GetValueOrDefault(key));
|
|
}
|
|
}
|
|
|
|
string order = null;
|
|
if (orderByKey != null)
|
|
{
|
|
order = $"ORDER BY {orderByKey}";
|
|
}
|
|
|
|
string limitString = limit > 0 ? $"LIMIT {limit}" : null;
|
|
string offsetString = offset > 0 ? $"OFFSET {offset}" : null;
|
|
|
|
string query = $"DELETE FROM {table} {filter} {order} {limitString} {offsetString}";
|
|
|
|
cmd.CommandText = query;
|
|
cmd.Prepare();
|
|
cmd.ExecuteNonQuery();
|
|
|
|
_conn.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Fatal($"Database.Controller.DeleteQuery: Exception [{ex}] thrown, <[{ex.Message}]>.");
|
|
}
|
|
}
|
|
}
|
|
} |