From c8f174d3e81bbc67e7fa636d17b70e2636f62c1e Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Thu, 4 Jun 2020 15:19:09 +0200 Subject: [PATCH] Proper rework attributes, creation functional --- ChaosBot/Attribute/AssemblyController.cs | 104 +++++++++++++++-------- ChaosBot/Attribute/DBAutoIncrement.cs | 13 +++ ChaosBot/Attribute/DBEntity.cs | 12 ++- ChaosBot/Attribute/DBFieldAttribute.cs | 7 ++ ChaosBot/Attribute/DBNotNull.cs | 13 +++ ChaosBot/Attribute/DBPrimaryKey.cs | 13 +++ ChaosBot/Attribute/DBUnique.cs | 13 +++ ChaosBot/Database/Entity/Raffle.cs | 4 + 8 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 ChaosBot/Attribute/DBAutoIncrement.cs create mode 100644 ChaosBot/Attribute/DBFieldAttribute.cs create mode 100644 ChaosBot/Attribute/DBNotNull.cs create mode 100644 ChaosBot/Attribute/DBPrimaryKey.cs create mode 100644 ChaosBot/Attribute/DBUnique.cs diff --git a/ChaosBot/Attribute/AssemblyController.cs b/ChaosBot/Attribute/AssemblyController.cs index b7fb2de..27a68e5 100644 --- a/ChaosBot/Attribute/AssemblyController.cs +++ b/ChaosBot/Attribute/AssemblyController.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; +using Antlr4.Runtime; using ChaosBot.Database; using Microsoft.Data.Sqlite; using NLog; @@ -20,50 +22,82 @@ namespace ChaosBot.Attribute { if (type.GetCustomAttributes(typeof(DBEntity), true).Length > 0) { - // type == all classes that have [DBEntity(...)] - // Get a reference to the attribute DBEntity dbEntity = type.GetCustomAttributes(typeof(DBEntity)).Cast().First(); - - string table = dbEntity.Table; - // TODO: Generate this in one go instead of many separate commands - Controller.RawQuery($"CREATE TABLE IF NOT EXISTS {table} (id INTEGER NOT NULL CONSTRAINT {table}_pk PRIMARY KEY AUTOINCREMENT)"); - foreach (MethodInfo field in type.GetMethods()) - { - if (field.Name.StartsWith("get_")) - { - try - { - // TODO: Use an attribute to detect primary autoincrement key - if (field.Name == "id") - { - Controller.RawQuery($"CREATE UNIQUE INDEX {table}_id_uindex ON {table} (id)", false, true); - continue; - } - - string fieldname = field.Name.Substring(4); - string fieldType = DBEntity.DataTypes.GetValueOrDefault(field.ReturnType).ToString(); - string fieldModifiers = null; + // Generate columns + // name, type, constraintName, constraints, + List> columnList = new List>(); + + // Get the table as an easy variable + string table = dbEntity.Table; - string query = $"ALTER TABLE {table} ADD COLUMN {fieldname} {fieldType} {fieldModifiers}"; - Controller.RawQuery(query, false, true); - } - catch (Exception ex) + // Loop through all fields + foreach (PropertyInfo prop in type.GetProperties()) + { + string columnName = prop.Name; + string columnType = DBEntity.DataTypes.GetValueOrDefault(prop.PropertyType).ToString(); + StringBuilder constraintNameBuilder = new StringBuilder($"{table}_{columnName}"); + List constraintsList = new List(); + + // Go through every attribute + foreach (object fieldAttribute in prop.GetCustomAttributes(true)) + { + if (fieldAttribute is DBFieldAttribute dBFieldAttribute) { - if (ex is SqliteException sqliteException) + string constraintSuffix = + DBEntity.ConstrainNames.GetValueOrDefault(dBFieldAttribute.GetType(), null); + if (constraintSuffix != null) { - if (!sqliteException.Message.Contains("duplicate column name")) - { - _logger.Fatal($"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); - } - } - else - { - _logger.Error($"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); + constraintNameBuilder.Append($"_{constraintSuffix}"); + constraintsList.Add($"{dBFieldAttribute.GetSQLiteQuery()}"); } } } + + string constraintName = constraintNameBuilder.ToString(); + if (constraintsList.Count > 0) + { + constraintsList.Insert(0, "CONSTRAINT"); + constraintsList.Insert(1, constraintName); + } + string constraints = String.Join(" ", constraintsList); + columnList.Add(new Tuple(columnName, columnType, constraintName, constraints)); + } + + // Check table exists + bool tableExists = + Controller.RawQuery($"SELECT name FROM sqlite_master WHERE type='table' AND name='{table}'") + .Rows.Count > 0; + + try + { + if (!tableExists) + { + string columnDefs = String.Join(", ", columnList.Select(c => $"{c.Item1} {c.Item2} {c.Item4}")); + string query = $"CREATE TABLE {table} ({columnDefs})"; + Controller.RawQuery(query); + } + // // TODO: Generate this in one go instead of many separate commands + // Controller.RawQuery($"CREATE TABLE IF NOT EXISTS {table} (id INTEGER NOT NULL CONSTRAINT {table}_pk PRIMARY KEY AUTOINCREMENT)"); + // Controller.RawQuery($"CREATE UNIQUE INDEX {table}_id_uindex ON {table} (id)", false, true); + // string query = $"ALTER TABLE {table} ADD COLUMN {fieldname} {fieldType} {fieldModifiers}"; + } + catch (Exception ex) + { + if (ex is SqliteException sqliteException) + { + if (!sqliteException.Message.Contains("duplicate column name")) + { + _logger.Fatal( + $"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + else + { + _logger.Error( + $"AssemblyController.Register: Exception [{ex}] thrown, <[{ex.Message}]>."); + } } } } diff --git a/ChaosBot/Attribute/DBAutoIncrement.cs b/ChaosBot/Attribute/DBAutoIncrement.cs new file mode 100644 index 0000000..0ede23d --- /dev/null +++ b/ChaosBot/Attribute/DBAutoIncrement.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChaosBot.Attribute +{ + [AttributeUsage(AttributeTargets.Property)] + public class DBAutoIncrement : DBFieldAttribute + { + public override string GetSQLiteQuery() + { + return "AUTOINCREMENT"; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Attribute/DBEntity.cs b/ChaosBot/Attribute/DBEntity.cs index 4cf1804..6ee2b60 100644 --- a/ChaosBot/Attribute/DBEntity.cs +++ b/ChaosBot/Attribute/DBEntity.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Discord; using Microsoft.Data.Sqlite; namespace ChaosBot.Attribute @@ -9,6 +10,7 @@ namespace ChaosBot.Attribute { public readonly string Table; + // Yeah... this table does not exist elsewhere except human-readable documentation public static readonly Dictionary DataTypes = new Dictionary { {typeof(bool), SqliteType.Integer}, @@ -30,7 +32,15 @@ namespace ChaosBot.Attribute {typeof(UInt16), SqliteType.Integer}, {typeof(UInt32), SqliteType.Integer}, {typeof(UInt64), SqliteType.Integer} - }; + }; + + public static readonly Dictionary ConstrainNames = new Dictionary + { + {typeof(DBNotNull), "nn"}, + {typeof(DBUnique), "uq"}, + {typeof(DBPrimaryKey), "pk"}, + {typeof(DBAutoIncrement), "ai"}, + }; public DBEntity(string table) { diff --git a/ChaosBot/Attribute/DBFieldAttribute.cs b/ChaosBot/Attribute/DBFieldAttribute.cs new file mode 100644 index 0000000..b0a0c1f --- /dev/null +++ b/ChaosBot/Attribute/DBFieldAttribute.cs @@ -0,0 +1,7 @@ +namespace ChaosBot.Attribute +{ + public abstract class DBFieldAttribute : System.Attribute + { + public abstract string GetSQLiteQuery(); + } +} \ No newline at end of file diff --git a/ChaosBot/Attribute/DBNotNull.cs b/ChaosBot/Attribute/DBNotNull.cs new file mode 100644 index 0000000..201bd7b --- /dev/null +++ b/ChaosBot/Attribute/DBNotNull.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChaosBot.Attribute +{ + [AttributeUsage(AttributeTargets.Property)] + public class DBNotNull : DBFieldAttribute + { + public override string GetSQLiteQuery() + { + return "NOT NULL"; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Attribute/DBPrimaryKey.cs b/ChaosBot/Attribute/DBPrimaryKey.cs new file mode 100644 index 0000000..3a3bd31 --- /dev/null +++ b/ChaosBot/Attribute/DBPrimaryKey.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChaosBot.Attribute +{ + [AttributeUsage(AttributeTargets.Property)] + public class DBPrimaryKey : DBFieldAttribute + { + public override string GetSQLiteQuery() + { + return "PRIMARY KEY"; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Attribute/DBUnique.cs b/ChaosBot/Attribute/DBUnique.cs new file mode 100644 index 0000000..30529c0 --- /dev/null +++ b/ChaosBot/Attribute/DBUnique.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChaosBot.Attribute +{ + [AttributeUsage(AttributeTargets.Property)] + public class DBUnique : DBFieldAttribute + { + public override string GetSQLiteQuery() + { + return "UNIQUE"; + } + } +} \ No newline at end of file diff --git a/ChaosBot/Database/Entity/Raffle.cs b/ChaosBot/Database/Entity/Raffle.cs index de8f954..d230177 100644 --- a/ChaosBot/Database/Entity/Raffle.cs +++ b/ChaosBot/Database/Entity/Raffle.cs @@ -5,6 +5,10 @@ namespace ChaosBot.Database.Entity [DBEntity("RaffleTable")] public class Raffle { + [DBPrimaryKey] + [DBAutoIncrement] + [DBNotNull] + [DBUnique] public int id { get; } public string userId { get; private set; } public string guildId { get; private set; }