diff --git a/TestScenarios/Read_AllColumnTypes/Database.MySql.sql b/TestScenarios/Read_AllColumnTypes/Database.MySql.sql new file mode 100644 index 0000000..604bfb4 --- /dev/null +++ b/TestScenarios/Read_AllColumnTypes/Database.MySql.sql @@ -0,0 +1,10 @@ +CREATE TABLE `Person` ( + Id int not null auto_increment primary key, + Name nvarchar(255) not null, + Title varchar(10), + Age int, + Price decimal(18, 2), + DoB date, + Deleted bit(1) not null default(0), + PriceIncVat decimal(18, 2) AS (`Price` * 1.2) +); \ No newline at end of file diff --git a/TestScenarios/Read_AllColumnTypes/ExpectedOutput.MySql.json b/TestScenarios/Read_AllColumnTypes/ExpectedOutput.MySql.json new file mode 100644 index 0000000..792d0f0 --- /dev/null +++ b/TestScenarios/Read_AllColumnTypes/ExpectedOutput.MySql.json @@ -0,0 +1,45 @@ +{ + "tables": { + "`Person`": { + "columns": { + "Id": { + "type": "int", + "nullable": false, + "primaryKey": true + }, + "Name": { + "type": "nvarchar(255)", + "nullable": false, + "primaryKey": false + }, + "Title": { + "type": "varchar(10)", + "primaryKey": false + }, + "Age": { + "type": "int", + "primaryKey": false + }, + "Price": { + "type": "decimal(18,2)", + "primaryKey": false + }, + "DoB": { + "type": "date", + "primaryKey": false + }, + "Deleted": { + "type": "bit(1)", + "nullable": false, + "default": 0, + "primaryKey": false + }, + "PriceIncVat": { + "expression": "`Price` * 1.2", + "primaryKey": false, + "type": "decimal(18,2)" + } + } + } + } +} \ No newline at end of file diff --git a/TestScenarios/Read_AllColumnTypes/ExpectedOutput.json b/TestScenarios/Read_AllColumnTypes/ExpectedOutput.SqlServer.json similarity index 100% rename from TestScenarios/Read_AllColumnTypes/ExpectedOutput.json rename to TestScenarios/Read_AllColumnTypes/ExpectedOutput.SqlServer.json diff --git a/vcdb.MySql/MySqlInstaller.cs b/vcdb.MySql/MySqlInstaller.cs index 7935af9..9abef59 100644 --- a/vcdb.MySql/MySqlInstaller.cs +++ b/vcdb.MySql/MySqlInstaller.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.DependencyInjection; using vcdb.CommandLine; using vcdb.DependencyInjection; +using vcdb.MySql.SchemaBuilding; +using vcdb.MySql.Scripting; namespace vcdb.MySql { @@ -8,9 +10,9 @@ public class MySqlInstaller : IServicesInstaller { public void RegisterServices(IServiceCollection services, DatabaseVersion databaseVersion) { - services.AddSingleton(); - //services.InNamespace().AddAsSingleton(); - //services.InNamespace().AddAsSingleton(); + services.InNamespace().AddAsSingleton(); + services.InNamespace().AddAsSingleton(); + services.InNamespace().AddAsSingleton(); ObjectName.Converter = new ObjectNameConverter( delimiter: ".", diff --git a/vcdb.MySql/SchemaBuilding/IMySqlComputedColumnRepository.cs b/vcdb.MySql/SchemaBuilding/IMySqlComputedColumnRepository.cs new file mode 100644 index 0000000..ab7e3ea --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/IMySqlComputedColumnRepository.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; + +namespace vcdb.MySql.SchemaBuilding +{ + public interface IMySqlComputedColumnRepository + { + Task> GetComputedColumns(DbConnection connection, ObjectName tableName); + } +} diff --git a/vcdb.MySql/SchemaBuilding/Models/ComputedColumn.cs b/vcdb.MySql/SchemaBuilding/Models/ComputedColumn.cs new file mode 100644 index 0000000..06dc50b --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/Models/ComputedColumn.cs @@ -0,0 +1,10 @@ +namespace vcdb.MySql.SchemaBuilding.Models +{ + public class ComputedColumn + { + public string TABLE_SCHEMA { get; set; } + public string TABLE_NAME { get; set; } + public string COLUMN_NAME { get; set; } + public string GENERATION_EXPRESSION { get; set; } + } +} diff --git a/vcdb.MySql/SchemaBuilding/Models/DescribeOutput.cs b/vcdb.MySql/SchemaBuilding/Models/DescribeOutput.cs new file mode 100644 index 0000000..5ae9f8e --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/Models/DescribeOutput.cs @@ -0,0 +1,12 @@ +namespace vcdb.MySql.SchemaBuilding.Models +{ + public class DescribeOutput + { + public string Field { get; set; } + public string Type { get; set; } + public string Null { get; set; } + public string Key { get; set; } + public string Default { get; set; } + public string Extra { get; set; } + } +} diff --git a/vcdb.MySql/SchemaBuilding/Models/TableCollation.cs b/vcdb.MySql/SchemaBuilding/Models/TableCollation.cs new file mode 100644 index 0000000..cfa7f44 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/Models/TableCollation.cs @@ -0,0 +1,9 @@ +namespace vcdb.MySql.SchemaBuilding.Models +{ + public class TableCollation + { + public string column_name { get; set; } + public string charset_name { get; set; } + public string collation_name { get; set; } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlCheckConstraintRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlCheckConstraintRepository.cs new file mode 100644 index 0000000..c7e7263 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlCheckConstraintRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlCheckConstraintRepository : ICheckConstraintRepository + { + public Task> GetCheckConstraints(DbConnection connection, ObjectName tableName) + { + return Task.FromResult>(new CheckConstraintDetails[0]); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlCollationRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlCollationRepository.cs new file mode 100644 index 0000000..276bbe0 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlCollationRepository.cs @@ -0,0 +1,44 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using vcdb.MySql.SchemaBuilding.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlCollationRepository : ICollationRepository + { + public async Task> GetColumnCollations(DbConnection connection, ObjectName tableName) + { + var columns = await connection.QueryAsync(@"SELECT + column_name, + character_set_name, + collation_name +FROM information_schema.columns +WHERE table_name = @table_name;", +new { table_name = tableName.Name, schema = tableName.Schema }); + + return columns.ToDictionary( + column => column.column_name, + column => column.collation_name); + } + + public async Task GetDatabaseCollation(DbConnection connection) + { + var databaseCollation = await connection.QuerySingleAsync("SHOW VARIABLES LIKE 'collation_database';"); + if (databaseCollation == "collation_database") + { + return await GetServerCollation(connection); + } + + return databaseCollation; + } + + public async Task GetServerCollation(DbConnection connection) + { + return await connection.QuerySingleAsync("SELECT @@collation_server;"); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlColumnRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlColumnRepository.cs new file mode 100644 index 0000000..4d9f679 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlColumnRepository.cs @@ -0,0 +1,112 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.MySql.SchemaBuilding.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlColumnRepository : IColumnRepository + { + /// + /// From this page: https://bugs.mysql.com/bug.php?id=69620 + /// + /// "nvarchar in MySQL is just an alias for VARCHAR and CHARACTER SET utf8. If you set the default charset in your DB and/or tables to utf8 you should be fine with VARCHAR in your columns" + /// + /// This is the name of the utf8 collation, if it is set, then consider any varchar() or char() columns to be nvarchar() or nchar() instead + /// + private const string Utf8Collation = "utf8_general_ci"; + + private readonly IDescriptionRepository descriptionRepository; + private readonly ICollationRepository collationRepository; + private readonly IPrimaryKeyRepository primaryKeyRepository; + + private readonly IMySqlComputedColumnRepository computedColumnRepository; + + public MySqlColumnRepository( + IDescriptionRepository descriptionRepository, + ICollationRepository collationRepository, + IPrimaryKeyRepository primaryKeyRepository, + IMySqlComputedColumnRepository computedColumnRepository) + { + this.descriptionRepository = descriptionRepository; + this.collationRepository = collationRepository; + this.primaryKeyRepository = primaryKeyRepository; + this.computedColumnRepository = computedColumnRepository; + } + + public async Task> GetColumns(DbConnection connection, ObjectName tableName, Permissions tablePermissions) + { + var databaseCollation = await collationRepository.GetDatabaseCollation(connection); + var columnDescriptions = await descriptionRepository.GetColumnDescriptions(connection, tableName); + var columnCollations = await collationRepository.GetColumnCollations(connection, tableName); + var columnsInPrimaryKey = await primaryKeyRepository.GetColumnsInPrimaryKey(connection, tableName); + var computedColumns = await computedColumnRepository.GetComputedColumns(connection, tableName); + + return await connection.QueryAsync($@" +describe {tableName.Name}").ToDictionaryAsync( + column => column.Field, + column => + { + return new ColumnDetails + { + Type = NormaliseDataType(column, columnCollations), + Nullable = OptOut.From(column.Null == "YES"), + Default = UnwrapDefinition(column.Default), + /*DefaultName = columnDefault == null || columnDefault.IsSystemNamed + ? null + : columnDefault.Name, + SqlDefaultName = columnDefault?.Name, + DefaultObjectId = columnDefault?.ObjectId,*/ + Description = columnDescriptions.ItemOrDefault(column.Field), + Collation = columnCollations.ItemOrDefault(column.Field) == databaseCollation || IsNationalCharacterColumn(column, columnCollations) + ? null + : columnCollations.ItemOrDefault(column.Field), + PrimaryKey = columnsInPrimaryKey.Contains(column.Field), + Permissions = tablePermissions?.SubEntityPermissions?.ItemOrDefault(column.Field), + Expression = computedColumns.ItemOrDefault(column.Field) + }; + }); + } + + private bool IsNationalCharacterColumn(DescribeOutput column, Dictionary columnCollations) + { + var columnCollation = columnCollations.ItemOrDefault(column.Field); + var isUtf8Collation = columnCollation == Utf8Collation; + return (column.Type.StartsWith("varchar(") || column.Type.StartsWith("char(")) && isUtf8Collation; + } + + private string NormaliseDataType(DescribeOutput column, Dictionary columnCollations) + { + if (IsNationalCharacterColumn(column, columnCollations)) + { + return "n" + column.Type; + } + + return column.Type; + } + + private object UnwrapDefinition(object definition) + { + if (definition is string stringDefinition) + { + if (string.IsNullOrEmpty(stringDefinition)) + return definition; + + if (stringDefinition.StartsWith("\"") && stringDefinition.EndsWith("\"")) + return stringDefinition.Trim('\"'); + + if (int.TryParse(stringDefinition, out var intValue)) + return intValue; + + if (decimal.TryParse(stringDefinition, out var decimalValue)) + return decimalValue; + } + + return definition; + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlComputedColumnRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlComputedColumnRepository.cs new file mode 100644 index 0000000..11d5c9a --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlComputedColumnRepository.cs @@ -0,0 +1,25 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using vcdb.MySql.SchemaBuilding.Models; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlComputedColumnRepository : IMySqlComputedColumnRepository + { + public async Task> GetComputedColumns(DbConnection connection, ObjectName tableName) + { + var computedColums = await connection.QueryAsync(@"select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, GENERATION_EXPRESSION +from INFORMATION_SCHEMA.COLUMNS +where TABLE_SCHEMA = DATABASE() +and TABLE_NAME = @tableName +and GENERATION_EXPRESSION <> ''", new { tableName = tableName.Name }); + + return computedColums.ToDictionary( + col => col.COLUMN_NAME, + col => col.GENERATION_EXPRESSION.UnwrapDefinition()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlDatabaseRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlDatabaseRepository.cs new file mode 100644 index 0000000..86db9b2 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlDatabaseRepository.cs @@ -0,0 +1,56 @@ +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlDatabaseRepository : IDatabaseRepository + { + private readonly ITableRepository tableRepository; + private readonly ISchemaRepository schemaRepository; + private readonly IDescriptionRepository descriptionRepository; + private readonly ICollationRepository collationRepository; + private readonly IUserRepository userRepository; + private readonly IPermissionRepository permissionRepository; + private readonly IProcedureRepository procedureRepository; + + public MySqlDatabaseRepository( + ITableRepository tableRepository, + ISchemaRepository schemaRepository, + IDescriptionRepository descriptionRepository, + ICollationRepository collationRepository, + IUserRepository userRepository, + IPermissionRepository permissionRepository, + IProcedureRepository procedureRepository) + { + this.tableRepository = tableRepository; + this.schemaRepository = schemaRepository; + this.descriptionRepository = descriptionRepository; + this.collationRepository = collationRepository; + this.userRepository = userRepository; + this.permissionRepository = permissionRepository; + this.procedureRepository = procedureRepository; + } + + public async Task GetDatabaseDetails(DbConnection connection) + { + var serverCollation = await collationRepository.GetServerCollation(connection); + var databaseCollation = await collationRepository.GetDatabaseCollation(connection); + + return new DatabaseDetails + { + Tables = await tableRepository.GetTables(connection), + Schemas = await schemaRepository.GetSchemas(connection), + Description = await descriptionRepository.GetDatabaseDescription(connection), + Collation = databaseCollation == serverCollation + ? null + : databaseCollation, + ServerCollation = serverCollation, + Users = await userRepository.GetUsers(connection), + Permissions = await permissionRepository.GetDatabasePermissions(connection), + Procedures = await procedureRepository.GetProcedures(connection) + }; + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlDefinitionExtensions.cs b/vcdb.MySql/SchemaBuilding/MySqlDefinitionExtensions.cs new file mode 100644 index 0000000..a6b92f3 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlDefinitionExtensions.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; + +namespace vcdb.MySql.SchemaBuilding +{ + public static class MySqlDefinitionExtensions + { + public static string UnwrapDefinition(this string definition) + { + while (definition.StartsWith("(") && definition.EndsWith(")")) + definition = definition.Substring(1, definition.Length - 2); + + return Regex.Replace( + definition, + @"(\(\d+(?:\.\d+)?\))|(\(\'.+?\'\))", + match => + { + return match.Value.UnwrapDefinition(); + }); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlDescriptionRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlDescriptionRepository.cs new file mode 100644 index 0000000..89a0bae --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlDescriptionRepository.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlDescriptionRepository : IDescriptionRepository + { + public Task> GetColumnDescriptions(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(new Dictionary()); + } + + public Task GetDatabaseDescription(DbConnection connection) + { + return Task.FromResult(null); + } + + public Task> GetForeignKeyDescription(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(new Dictionary()); + } + + public Task> GetIndexDescriptions(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(new Dictionary()); + } + + public Task GetPrimaryKeyDescription(DbConnection connection, ObjectName tableName, string primaryKeyName) + { + return Task.FromResult(null); + } + + public Task GetProcedureDescription(DbConnection connection, ObjectName procedureName) + { + return Task.FromResult(null); + } + + public Task GetSchemaDescription(DbConnection connection, string schemaName) + { + return Task.FromResult(null); + } + + public Task GetTableDescription(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(null); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlForeignKeyRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlForeignKeyRepository.cs new file mode 100644 index 0000000..1041ed6 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlForeignKeyRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlForeignKeyRepository : IForeignKeyRepository + { + public Task> GetForeignKeys(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlIndexRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlIndexRepository.cs new file mode 100644 index 0000000..02da192 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlIndexRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlIndexRepository : IIndexRepository + { + public Task> GetIndexes(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlPermissionRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlPermissionRepository.cs new file mode 100644 index 0000000..5fa379b --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlPermissionRepository.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlPermissionRepository : IPermissionRepository + { + public Task GetDatabasePermissions(DbConnection connection) + { + return Task.FromResult(new Permissions()); + } + + public Task> GetProcedurePermissions(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + + public Task> GetSchemaPermissions(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + + public Task> GetTablePermissions(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlPrimaryKeyRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlPrimaryKeyRepository.cs new file mode 100644 index 0000000..7cb51b6 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlPrimaryKeyRepository.cs @@ -0,0 +1,27 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.MySql.SchemaBuilding.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlPrimaryKeyRepository : IPrimaryKeyRepository + { + public async Task> GetColumnsInPrimaryKey(DbConnection connection, ObjectName tableName) + { + var columns = await connection.QueryAsync($@" +describe {tableName.Name}"); + + return columns.Where(col => col.Key == "PRI").Select(col => col.Field).ToArray(); + } + + public Task GetPrimaryKeyDetails(DbConnection connection, ObjectName tableName) + { + return Task.FromResult(null); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlProcedureRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlProcedureRepository.cs new file mode 100644 index 0000000..a5db983 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlProcedureRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlProcedureRepository : IProcedureRepository + { + public Task> GetProcedures(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlSchemaRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlSchemaRepository.cs new file mode 100644 index 0000000..abca4b8 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlSchemaRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlSchemaRepository : ISchemaRepository + { + public Task> GetSchemas(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlTableRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlTableRepository.cs new file mode 100644 index 0000000..5897b66 --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlTableRepository.cs @@ -0,0 +1,72 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.CommandLine; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlTableRepository : ITableRepository + { + private readonly IColumnRepository columnsRepository; + private readonly IIndexRepository indexesRepository; + private readonly IDescriptionRepository descriptionRepository; + private readonly ICheckConstraintRepository checkConstraintRepository; + private readonly IPrimaryKeyRepository primaryKeyRepository; + private readonly IPermissionRepository permissionRepository; + private readonly IForeignKeyRepository foreignKeyRepository; + private readonly Options options; + + public MySqlTableRepository( + IColumnRepository columnsRepository, + IIndexRepository indexesRepository, + IDescriptionRepository descriptionRepository, + ICheckConstraintRepository checkConstraintRepository, + IPrimaryKeyRepository primaryKeyRepository, + IPermissionRepository permissionRepository, + IForeignKeyRepository foreignKeyRepository, + Options options) + { + this.columnsRepository = columnsRepository; + this.indexesRepository = indexesRepository; + this.descriptionRepository = descriptionRepository; + this.checkConstraintRepository = checkConstraintRepository; + this.primaryKeyRepository = primaryKeyRepository; + this.permissionRepository = permissionRepository; + this.foreignKeyRepository = foreignKeyRepository; + this.options = options; + } + + public async Task> GetTables(DbConnection connection) + { + var tables = await connection.QueryAsync($@" +select TABLE_NAME as `{nameof(ObjectName.Name)}` +from INFORMATION_SCHEMA.TABLES +where TABLE_TYPE = 'BASE TABLE' +and TABLE_SCHEMA = @databaseName", new { databaseName = options.Database }); + + var permissions = await permissionRepository.GetTablePermissions(connection); + + return await tables.ToDictionaryAsync( + tableName => tableName, + async tableName => + { + var tablePermissions = permissions.ItemOrDefault(tableName); + + return new TableDetails + { + Columns = await columnsRepository.GetColumns(connection, tableName, tablePermissions), + Indexes = await indexesRepository.GetIndexes(connection, tableName), + Description = await descriptionRepository.GetTableDescription(connection, tableName), + Checks = await checkConstraintRepository.GetCheckConstraints(connection, tableName).ToArrayAsync(), + PrimaryKey = await primaryKeyRepository.GetPrimaryKeyDetails(connection, tableName), + Permissions = tablePermissions, + ForeignKeys = await foreignKeyRepository.GetForeignKeys(connection, tableName) + }; + }); + + } + } +} diff --git a/vcdb.MySql/SchemaBuilding/MySqlUserRepository.cs b/vcdb.MySql/SchemaBuilding/MySqlUserRepository.cs new file mode 100644 index 0000000..fa27c2e --- /dev/null +++ b/vcdb.MySql/SchemaBuilding/MySqlUserRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using vcdb.Models; +using vcdb.SchemaBuilding; + +namespace vcdb.MySql.SchemaBuilding +{ + public class MySqlUserRepository : IUserRepository + { + public Task> GetUsers(DbConnection connection) + { + return Task.FromResult(new Dictionary()); + } + } +} diff --git a/vcdb.MySql/Scripting/MySqlDatabaseScriptBuilder.cs b/vcdb.MySql/Scripting/MySqlDatabaseScriptBuilder.cs new file mode 100644 index 0000000..67e93e4 --- /dev/null +++ b/vcdb.MySql/Scripting/MySqlDatabaseScriptBuilder.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using vcdb.Models; +using vcdb.Scripting.Database; +using vcdb.Scripting.ExecutionPlan; + +namespace vcdb.MySql.Scripting +{ + public class MySqlDatabaseScriptBuilder : IDatabaseScriptBuilder + { + public IEnumerable CreateUpgradeScripts(DatabaseDetails current, DatabaseDetails required) + { + yield break; + } + } +} diff --git a/vcdb.MySql/vcdb.MySql.csproj b/vcdb.MySql/vcdb.MySql.csproj index 34a20af..4d74475 100644 --- a/vcdb.MySql/vcdb.MySql.csproj +++ b/vcdb.MySql/vcdb.MySql.csproj @@ -13,9 +13,4 @@ - - - - -