ServerDatabase/Database/Table.cs

557 lines
23 KiB
C#
Raw Normal View History

2023-06-28 12:44:59 -04:00
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Npgsql;
using ServerDatabase.Utils;
namespace ServerDatabase;
public class Table<TRow> : ITable where TRow : class, new()
{
public Table(string Name, Database DB)
{
if (Name == "table_versions") throw new ArgumentException("The table name 'table_versions' is not allowed", nameof(Name));
this.Name = Name;
DatabaseHandler = DB;
}
internal Table(Database DB)
{
this.Name = "table_versions";
DatabaseHandler = DB;
}
public Table<TRow> WithColumn(ITableColumn Column, string name)
{
Colums_.Add(Column);
colnamesraw.Add(Column.Name, name);
return this;
}
public void AddColumn(ITableColumn Column, string name)
{
colnamesraw.Add(Column.Name, name);
Colums_.Add(Column);
}
public IReadOnlyList<ITableColumn> GetMissingColumns(out IReadOnlyList<ITableColumn> UpdatedColumns, out string[] ExtraColumns)
2023-06-28 12:44:59 -04:00
{
List<ITableColumn> coll = new(Colums_);
List<ITableColumn> col = new(Colums_);
List<string> co = new();
2023-06-28 12:44:59 -04:00
foreach (DataRow row in DatabaseHandler.CreateConnection().GetSchema("Columns", new[] { DatabaseHandler.DB, null, Name }).Rows)
{
string name = row.Field<string>("COLUMN_NAME")!,
ty = row.Field<string>("DATA_TYPE")!;
IEnumerable<ITableColumn> t = Colums_.Where(s => s.Name == name);
if (t.Any())
{
col.Remove(t.First());
if (t.First()!.GetDatabaseTypeStr() == ty) coll.Remove(t.First());
}
else
{
co.Add(name);
}
2023-06-28 12:44:59 -04:00
}
UpdatedColumns = coll;
ExtraColumns = co.ToArray();
2023-06-28 12:44:59 -04:00
return col.AsReadOnly();
}
public void UpdateTable()
{
Console.WriteLine("Verifying Table: {0}", Name);
bool i = true;
IReadOnlyList<ITableColumn> m = GetMissingColumns(out IReadOnlyList<ITableColumn> n, out string[] o);
2023-06-28 12:44:59 -04:00
if (m.Any())
{
if (i)
{
i = false;
Console.WriteLine("Fixing Table: {0}", Name);
}
2023-06-28 12:44:59 -04:00
if (DatabaseHandler.Read<long>(
$"SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema LIKE 'public' AND table_type LIKE 'BASE TABLE' AND table_name = '{Name}';") ==
0)
{
Create();
}
else
{
foreach (ITableColumn col in m)
{
try
{
if (col.IsPrimaryKey) throw new Exception($"DELETE THE TABLE '{Name}' TO FIX TABLE GENERATION");
string sql = $"ALTER TABLE IF EXISTS public.{Name} ADD COLUMN {col.Name} {col.GetDatabaseTypeStr()}";
sql += " NOT NULL";
if (col.GetDefaultStr() is not null) sql += $" DEFAULT {col.GetDefaultStr()}";
sql += ";";
DatabaseHandler.ExecuteNonQuery(sql);
}
catch (Exception e)
{
Console.WriteLine("Posible fail to set default for public.{0}.{1}", Name, col.Name);
}
}
}
}
if (n.Any())
{
if (i)
{
i = false;
Console.WriteLine("Fixing Table: {0}", Name);
}
2023-06-28 12:44:59 -04:00
foreach (ITableColumn col in n)
{
string sql = $"ALTER TABLE public.{Name} ALTER COLUMN {col.Name} TYPE {col.GetDatabaseTypeStr()};";
DatabaseHandler.ExecuteNonQuery(sql);
if (col.GetDefaultStr() is not null)
{
try
{
sql = $"ALTER TABLE IF EXISTS public.{Name} ALTER COLUMN {col.Name} SET DEFAULT {col.GetDefaultStr()};";
DatabaseHandler.ExecuteNonQuery(sql);
}
catch (Exception e)
{
Console.WriteLine("Failed to set default for public.{0}.{1}\nSQL: {2}", Name, col.Name, sql);
}
}
}
}
if (o.Any())
{
if (i)
{
i = false;
Console.WriteLine("Fixing Table: {0}", Name);
}
foreach (string c in o)
{
DatabaseHandler.ExecuteNonQuery($"ALTER TABLE IF EXISTS {Name} DROP COLUMN IF EXISTS {c};");
}
}
2023-06-28 12:44:59 -04:00
}
public Table<TRow> DropColumn<TType>(TableColumn<TType> Column) where TType : notnull
{
Colums_.Remove(Column);
return this;
}
private Dictionary<string, string> colnamesraw = new();
private List<ITableColumn> Colums_ { get; set; } = new();
public IReadOnlyList<ITableColumn> Colums => Colums_.AsReadOnly();
public string Name { get; set; } = default!;
public Database DatabaseHandler { get; set; } = default!;
public void Insert(params Parameter[] Parameters)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
List<ITableColumn> l = new List<ITableColumn>(Colums_);
foreach (Parameter param in Parameters)
{
l.Remove(l.Where(s => s.Name == param.PGParameter.ParameterName).First());
vals += "@" + param.PGParameter.ParameterName + ", ";
cmd.Parameters.Add(param.PGParameter);
}
foreach (ITableColumn cop in l)
{
var param = cop.CreateDefaultParameter();
vals += "@" + param.PGParameter.ParameterName + ", ";
cmd.Parameters.Add(param.PGParameter);
}
vals = vals.Remove(vals.Length - 2, 2);
cmd.CommandText = $"INSERT INTO {Name} ({vals.Replace("@", "")}) VALUES({vals})";
cmd.Prepare();
cmd.ExecuteNonQuery();
con.Close();
}
public void DeleteRow(Parameter Parameter)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
cmd.Parameters.Add(Parameter.PGParameter);
cmd.CommandText = $"DELETE FROM {Name} WHERE {Parameter.PGParameter.ParameterName} IN (@{Parameter.PGParameter.ParameterName});";
cmd.Prepare();
cmd.ExecuteNonQuery();
con.Close();
}
public void DeleteRows()
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
cmd.CommandText = $"DELETE FROM {Name};";
cmd.Prepare();
cmd.ExecuteNonQuery();
con.Close();
}
2023-06-28 12:44:59 -04:00
public void Create()
{
Console.WriteLine("Making Table: {0}", Name);
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string command = $"CREATE TABLE public.{Name}(";
string key = "";
foreach (ITableColumn column in Colums_)
{
command += $"{column.Name} {column.GetDatabaseTypeStr()}";
if (column.IsPrimaryKey) key = $"PRIMARY KEY ({column.Name})";
command += " NOT NULL, ";
}
if (key == "") command = command.Remove(command.Length - 2, 2) + ");";
command += $"{key});";
cmd.CommandText = command;
cmd.Prepare();
cmd.ExecuteNonQuery();
con.Close();
DatabaseHandler.ExecuteNonQuery($"ALTER TABLE IF EXISTS " +
$"public.{Name} OWNER to \"{DatabaseHandler.Uname}\";");
2023-06-28 12:44:59 -04:00
}
public bool TryRead<T>(TableColumn<T> column, Order order, [NotNullWhen(true)][MaybeNullWhen(false)]out T result, params Parameter[] Parameters) where T : notnull
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
string command = $"SELECT {column.Name} FROM {Name} WHERE";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName} AND ";
cmd.Parameters.Add(param.PGParameter);
}
if (command.EndsWith(";")) command = command.Remove(command.Length - 1, 1);
if (!string.IsNullOrWhiteSpace(vals)) vals = vals.Remove(vals.Length - 5, 5);
if (!string.IsNullOrWhiteSpace(vals) && command.EndsWith("WHERE")) command += $" {vals}";
if (order is not null) command += $" ORDER BY {order.Type} ";
if (order is not null && order.Assending) command += "ASC";
else { if (order is not null) command += "DESC"; }
command += ";";
cmd.CommandText = command;
//object? temp = cmd.ExecuteScalar();
object? temp = null;
try
{
temp = cmd.ExecuteScalar();
}
catch
{
result = default(T);
return false;
}
con.Close();
if (temp is DBNull || temp is null)
{
result = default(T);
return false;
}
if (typeof(T).IsEnum)
{
result = (T)Enum.Parse(typeof(T), temp.ToString()!)!;
return true;
}
/*
if (typeof(T).IsNullableEnum())
{
result = (T?)Enum.Parse(Nullable.GetUnderlyingType(typeof(T))!, temp.ToString()!)!;
return true;
}
*/
result = (T)temp!;
return true;
}
public bool TryReadRow(out TRow row, params Parameter[] Parameters) => TryReadRow(null, out row, Parameters);
public bool TryReadRow(Order order, out TRow row, params Parameter[] Parameters)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
string command = $"SELECT * FROM {Name} WHERE";
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName} AND ";
cmd.Parameters.Add(param.PGParameter);
}
if (command.EndsWith(";")) command = command.Remove(command.Length - 1, 1);
if (!string.IsNullOrWhiteSpace(vals)) vals = vals.Remove(vals.Length - 5, 5);
if (!string.IsNullOrWhiteSpace(vals) && command.EndsWith("WHERE")) command += $" {vals}";
if (order is not null) command += $" ORDER BY {order.Type} ";
if (order is not null && order.Assending) command += "ASC";
else { if (order is not null) command += "DESC"; }
command += ";";
cmd.CommandText = command;
NpgsqlDataReader reader = cmd.ExecuteReader();
TRow Row = new();
reader.Read();
bool r = false;
for (int i = 0; i < reader.FieldCount; i++)
{
r = true;
string colname = reader.GetName(i);
object? val = null;
try
{
val = reader.GetValue(i);
}
catch
{
row = default(TRow);
return false;
}
if (colnamesraw.ContainsKey(colname))
{
if (val is DBNull || val is null)
{
row = default(TRow);
return false;
}
PropertyInfo? pi = Row.GetType()
.GetProperty(colnamesraw[colname]);
if (pi is null)
{
Console.WriteLine("Could not find row {0}", colnamesraw[colname]);
continue;
}
pi.SetValue(Row, val, null);
}
}
row = Row;
return r;
}
public bool TryReadRows(out TRow[] rows, params Parameter[] Parameters) => TryReadRows(null!, out rows, Parameters);
public bool TryReadRows(Order order, out TRow[] rows, params Parameter[] Parameters)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
string command = $"SELECT * FROM {Name} WHERE";
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName} AND ";
cmd.Parameters.Add(param.PGParameter);
}
if (command.EndsWith(";")) command = command.Remove(command.Length - 1, 1);
if (!string.IsNullOrWhiteSpace(vals)) vals = vals.Remove(vals.Length - 5, 5);
if (!string.IsNullOrWhiteSpace(vals) && command.EndsWith("WHERE")) command += $" {vals}";
if (order is not null) command += $" ORDER BY {order.Type} ";
if (order is not null && order.Assending) command += "ASC";
else { if (order is not null) command += "DESC"; }
command += ";";
cmd.CommandText = command;
NpgsqlDataReader reader = cmd.ExecuteReader();
List<TRow> Rows = new();
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
TRow Row = new();
string colname = reader.GetName(i);
object? val = reader.GetValue(i);
if (colnamesraw.ContainsKey(colname))
{
PropertyInfo? pi = Row.GetType()
.GetProperty(colnamesraw[colname]);
if (pi is null)
{
Console.WriteLine("Could not find row {0}", colnamesraw[colname]);
continue;
}
pi.SetValue(Row, val, null);
}
Rows.Add(Row);
}
}
rows = Rows.ToArray();
return Rows.Any();
}
2023-06-28 12:44:59 -04:00
public bool TryRead<T>(TableColumn<T> column, [NotNullWhen(true)][MaybeNullWhen(false)]out T result, params Parameter[] Parameters) where T : notnull
{
return TryRead(column, null!, out result, Parameters);
}
public void Update<Ttype>(TableColumn<Ttype> condiction_column, Ttype condiction_value, params Parameter[] Parameters) where Ttype : notnull
{
Update(condiction_column, condiction_value, "=", Parameters);
}
public void Update<Ttype>(TableColumn<Ttype> condiction_column, Ttype condiction_value, string Sign, params Parameter[] Parameters) where Ttype : notnull
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName}, ";
cmd.Parameters.Add(param.PGParameter);
}
vals = vals.Remove(vals.Length - 2, 2);
cmd.Parameters.Add(condiction_column.CreateParameter(condiction_value).PGParameter);
cmd.CommandText = $"UPDATE {Name} SET {vals} WHERE {condiction_column.Name} {Sign} @{condiction_column.Name};";
cmd.Prepare();
cmd.ExecuteNonQuery();
con.Close();
}
public TRow ReadRow(params Parameter[] Parameters) => ReadRow(null, Parameters);
public TRow ReadRow(Order order, params Parameter[] Parameters)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
string command = $"SELECT * FROM {Name} WHERE";
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName} AND ";
cmd.Parameters.Add(param.PGParameter);
}
if (command.EndsWith(";")) command = command.Remove(command.Length - 1, 1);
if (!string.IsNullOrWhiteSpace(vals)) vals = vals.Remove(vals.Length - 5, 5);
if (!string.IsNullOrWhiteSpace(vals) && command.EndsWith("WHERE")) command += $" {vals}";
if (order is not null) command += $" ORDER BY {order.Type} ";
if (order is not null && order.Assending) command += "ASC";
else { if (order is not null) command += "DESC"; }
command += ";";
cmd.CommandText = command;
NpgsqlDataReader reader = cmd.ExecuteReader();
TRow Row = new();
reader.Read();
for (int i = 0; i < reader.FieldCount; i++)
{
string colname = reader.GetName(i);
object? val = reader.GetValue(i);
if (colnamesraw.ContainsKey(colname))
{
PropertyInfo? pi = Row.GetType()
.GetProperty(colnamesraw[colname]);
if (pi is null)
{
Console.WriteLine("Could not find row {0}", colnamesraw[colname]);
continue;
}
pi.SetValue(Row, val, null);
}
}
return Row;
}
public TRow[] ReadRows(uint NumRows, params Parameter[] Parameters) => ReadRows(NumRows, null, Parameters);
public TRow[] ReadRows(uint NumRows, Order order, params Parameter[] Parameters)
{
if (string.IsNullOrEmpty(DatabaseHandler.DB) || string.IsNullOrEmpty(DatabaseHandler.IP) || string.IsNullOrEmpty(DatabaseHandler.Uname) || string.IsNullOrEmpty(DatabaseHandler.PW)) throw new Exception("Database conection not fully defined");
using NpgsqlConnection con = new(DatabaseHandler.ConectionString);
string command = $"SELECT * FROM {Name} WHERE";
con.Open();
using NpgsqlCommand cmd = new();
cmd.Connection = con;
string vals = "";
foreach (Parameter param in Parameters)
{
vals += $"{param.PGParameter.ParameterName} {param.Sign} @{param.PGParameter.ParameterName} AND ";
cmd.Parameters.Add(param.PGParameter);
}
if (command.EndsWith(";")) command = command.Remove(command.Length - 1, 1);
if (!string.IsNullOrWhiteSpace(vals)) vals = vals.Remove(vals.Length - 5, 5);
if (!string.IsNullOrWhiteSpace(vals) && command.EndsWith("WHERE")) command += $" {vals}";
if (order is not null) command += $" ORDER BY {order.Type} ";
if (order is not null && order.Assending) command += "ASC";
else { if (order is not null) command += "DESC"; }
command += $" LIMIT {NumRows};";
cmd.CommandText = command;
NpgsqlDataReader reader = cmd.ExecuteReader();
List<TRow> Rows = new();
for (int j = 0; j < NumRows; j++)
{
if (!reader.Read()) break;
TRow Row = new();
2023-06-28 12:44:59 -04:00
for (int i = 0; i < reader.FieldCount; i++)
{
string colname = reader.GetName(i);
object? val = reader.GetValue(i);
if (colnamesraw.ContainsKey(colname))
{
PropertyInfo? pi = Row.GetType()
.GetProperty(colnamesraw[colname]);
if (pi is null)
{
Console.WriteLine("Could not find row {0}", colnamesraw[colname]);
continue;
}
pi.SetValue(Row, val, null);
}
}
Rows.Add(Row);
2023-06-28 12:44:59 -04:00
}
return Rows.ToArray();
}
public T Read<T>(TableColumn<T> column, params Parameter[] Parameters) where T : notnull
{
if (Parameters is null) throw new ArgumentNullException(nameof(Parameters));
return DatabaseHandler.Read<T>($"SELECT {column.Name} FROM {Name} WHERE", (Order?)null, Parameters);
}
public T Read<T>(TableColumn<T> column, Order order, params Parameter[] Parameters) where T : notnull
{
if (Parameters is null) throw new ArgumentNullException(nameof(Parameters));
return DatabaseHandler.Read<T>($"SELECT {column.Name} FROM {Name} WHERE", order, Parameters);
}
}