From 284bcd3bb526618794a633df713fc6f69118dd24 Mon Sep 17 00:00:00 2001 From: rhumborl Date: Fri, 25 Jun 2021 19:48:08 +0100 Subject: [PATCH 1/5] Add options for schema restriction, command timeout and dbo-prefixing --- console/BaseCommand.cs | 10 ++ console/Script.cs | 27 +++- model/Command/BaseCommand.cs | 9 +- model/Command/CompareCommand.cs | 4 +- model/Command/ScriptCommand.cs | 10 +- model/Models/Database.cs | 270 ++++++++++++++++++-------------- 6 files changed, 195 insertions(+), 135 deletions(-) diff --git a/console/BaseCommand.cs b/console/BaseCommand.cs index 74945194..96124a96 100644 --- a/console/BaseCommand.cs +++ b/console/BaseCommand.cs @@ -29,6 +29,14 @@ protected BaseCommand(string command, string oneLineDescription) { "f|databaseFilesPath=", "Path to database data and log files.", o => DatabaseFilesPath = o); + HasOption( + "timeout=", + "Number of seconds for sql command timeout.", + o => Timeout = o); + HasOption( + "noprefixdbo", + "Do not prefix objects in the dbo schema with dbo in their filename (for backwards compatibility).", + o => NoPrefixDbo = o != null); } protected string Server { get; set; } @@ -40,5 +48,7 @@ protected BaseCommand(string command, string oneLineDescription) { protected bool Overwrite { get; set; } protected bool Verbose { get; set; } protected string DatabaseFilesPath { get; set; } + protected string Timeout { get; set; } + protected bool NoPrefixDbo { get; set; } } } diff --git a/console/Script.cs b/console/Script.cs index 50fbc416..d9f722ee 100644 --- a/console/Script.cs +++ b/console/Script.cs @@ -39,6 +39,10 @@ public Script() "A comma separated list of the types that will only be scripted. Valid types: " + Database.ValidTypes, o => OnlyTypes = o); + HasOption( + "schemas=", + "A comma separated list of schemas that only the objects within these will be scripted. ", + o => Schemas = o); } private Logger _logger; @@ -48,6 +52,7 @@ public Script() protected string DataTablesPattern { get; set; } protected string DataTablesExcludePattern { get; set; } protected string TableHint { get; set; } + protected string Schemas { get; set; } public override int Run(string[] args) { _logger = new Logger(Verbose); @@ -59,6 +64,10 @@ public override int Run(string[] args) { Overwrite = true; } + int commandTimeout; + if (!int.TryParse(Timeout, out commandTimeout)) + commandTimeout = 30; // Default for SQLCommand + var scriptCommand = new ScriptCommand { ConnectionString = ConnectionString, DbName = DbName, @@ -67,16 +76,20 @@ public override int Run(string[] args) { Server = Server, User = User, Logger = _logger, - Overwrite = Overwrite + Overwrite = Overwrite, + Timeout = commandTimeout, + PrefixDbo = !NoPrefixDbo }; var filteredTypes = HandleFilteredTypes(); - var namesAndSchemas = HandleDataTables(DataTables); + var dataTableNames = HandleDataTables(DataTables); + var schemas = HandleSchemas(Schemas); try { - scriptCommand.Execute(namesAndSchemas, DataTablesPattern, DataTablesExcludePattern, - TableHint, filteredTypes); - } catch (Exception ex) { + scriptCommand.Execute(dataTableNames, DataTablesPattern, DataTablesExcludePattern, + TableHint, filteredTypes, schemas); + } + catch (Exception ex) { throw new ConsoleHelpAsException(ex.Message); } @@ -98,6 +111,10 @@ private List HandleFilteredTypes() { return Database.Dirs.Except(keepTypes.Except(removeTypes)).ToList(); } + private List HandleSchemas(string schemasString) { + return schemasString?.Split(',').ToList() ?? new List(); + } + private Dictionary HandleDataTables(string tableNames) { var dataTables = new Dictionary(); diff --git a/model/Command/BaseCommand.cs b/model/Command/BaseCommand.cs index 44212206..ec354e12 100644 --- a/model/Command/BaseCommand.cs +++ b/model/Command/BaseCommand.cs @@ -13,9 +13,12 @@ public abstract class BaseCommand { public string ScriptDir { get; set; } public ILogger Logger { get; set; } public bool Overwrite { get; set; } + public int Timeout { get; set; } + public bool PrefixDbo { get; set; } - public Database CreateDatabase(IList filteredTypes = null) { + public Database CreateDatabase(IList filteredTypes = null, IList schemas = null) { filteredTypes = filteredTypes ?? new List(); + schemas = schemas ?? new List(); if (!string.IsNullOrEmpty(ConnectionString)) { if (!string.IsNullOrEmpty(Server) || @@ -26,7 +29,7 @@ public Database CreateDatabase(IList filteredTypes = null) { "You must not provide both a connection string and a server/db/user/password"); } - return new Database(filteredTypes) { + return new Database(filteredTypes, schemas, PrefixDbo) { Connection = ConnectionString, Dir = ScriptDir }; @@ -50,7 +53,7 @@ public Database CreateDatabase(IList filteredTypes = null) { builder.Password = Pass; } - return new Database(filteredTypes) { + return new Database(filteredTypes, schemas, PrefixDbo) { Connection = builder.ToString(), Dir = ScriptDir }; diff --git a/model/Command/CompareCommand.cs b/model/Command/CompareCommand.cs index f1768045..8f5d69f5 100644 --- a/model/Command/CompareCommand.cs +++ b/model/Command/CompareCommand.cs @@ -10,8 +10,8 @@ public class CompareCommand : BaseCommand { public string OutDiff { get; set; } public bool Execute() { - var sourceDb = new Database(); - var targetDb = new Database(); + var sourceDb = new Database(prefix_dbo: PrefixDbo); + var targetDb = new Database(prefix_dbo: PrefixDbo); sourceDb.Connection = Source; targetDb.Connection = Target; sourceDb.Load(); diff --git a/model/Command/ScriptCommand.cs b/model/Command/ScriptCommand.cs index 90610fb7..ebc20c50 100644 --- a/model/Command/ScriptCommand.cs +++ b/model/Command/ScriptCommand.cs @@ -6,21 +6,21 @@ namespace SchemaZen.Library.Command { public class ScriptCommand : BaseCommand { - public void Execute(Dictionary namesAndSchemas, string dataTablesPattern, + public void Execute(Dictionary dataTableNames, string dataTablesPattern, string dataTablesExcludePattern, - string tableHint, List filteredTypes) { + string tableHint, List filteredTypes, List schemas) { if (!Overwrite && Directory.Exists(ScriptDir)) { var message = $"{ScriptDir} already exists - you must set overwrite to true"; throw new InvalidOperationException(message); } - var db = CreateDatabase(filteredTypes); + var db = CreateDatabase(filteredTypes, schemas); Logger.Log(TraceLevel.Verbose, "Loading database schema..."); - db.Load(); + db.Load(Timeout); Logger.Log(TraceLevel.Verbose, "Database schema loaded."); - foreach (var nameAndSchema in namesAndSchemas) { + foreach (var nameAndSchema in dataTableNames) { AddDataTable(db, nameAndSchema.Key, nameAndSchema.Value); } diff --git a/model/Models/Database.cs b/model/Models/Database.cs index 7d71eab9..3b4b6351 100644 --- a/model/Models/Database.cs +++ b/model/Models/Database.cs @@ -11,9 +11,14 @@ namespace SchemaZen.Library.Models { public class Database { + + private IList schemas = new List(); + private string schemaIn = ""; + private bool prefix_dbo = true; + #region " Constructors " - public Database(IList filteredTypes = null) { + public Database(IList filteredTypes = null, IList schemas = null, bool prefix_dbo = true) { Props.Add(new DbProp("COMPATIBILITY_LEVEL", "")); Props.Add(new DbProp("COLLATE", "")); Props.Add(new DbProp("AUTO_CLOSE", "")); @@ -45,10 +50,13 @@ public Database(IList filteredTypes = null) { foreach (var filteredType in filteredTypes) { Dirs.Remove(filteredType); } + + this.schemas = schemas; + this.prefix_dbo = prefix_dbo; } - public Database(string name, IList filteredTypes = null) - : this(filteredTypes) { + public Database(string name, IList filteredTypes = null, IList schemas = null, bool prefix_dbo = true) + : this(filteredTypes, schemas, prefix_dbo) { Name = name; } @@ -166,8 +174,11 @@ private void SetPropString(string propName, object dbVal) { #endregion #region Load - public void Load() { + Load(30); + } + + public void Load(int commandTimeout) { Tables.Clear(); TableTypes.Clear(); Routines.Clear(); @@ -183,8 +194,13 @@ public void Load() { using (var cn = new SqlConnection(Connection)) { cn.Open(); using (var cm = cn.CreateCommand()) { + cm.CommandTimeout = commandTimeout; LoadProps(cm); LoadSchemas(cm); + + schemas = schemas.Union(new[] { "dbo" }).ToList(); + schemaIn = "(" + string.Join(",", schemas.Select(s => $"'{s}'").ToArray()) + ")"; + LoadTables(cm); LoadUserDefinedTypes(cm); LoadColumns(cm); @@ -207,9 +223,10 @@ public void Load() { private void LoadSynonyms(SqlCommand cm) { try { - // get synonyms - cm.CommandText = @" + Console.WriteLine("-- Loading synonyms"); + cm.CommandText = $@" select object_schema_name(object_id) as schema_name, name as synonym_name, base_object_name + where object_schema_name(object_id) in {schemaIn} from sys.synonyms"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { @@ -226,16 +243,16 @@ select object_schema_name(object_id) as schema_name, name as synonym_name, base_ private void LoadPermissions(SqlCommand cm) { try { - // get permissions + Console.WriteLine("-- Loading Permissions"); // based on http://sql-articles.com/scripts/script-to-retrieve-security-information-sql-server-2005-and-above/ - cm.CommandText = @" + cm.CommandText = $@" select U.name as user_name, O.name as object_name, permission_name as permission from sys.database_permissions join sys.sysusers U on grantee_principal_id = uid - join sys.sysobjects O on major_id = id "; + join sys.sysobjects O on major_id = id and object_schema_name(id) in {schemaIn}"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { var permission = new Permission((string)dr["user_name"], @@ -249,6 +266,7 @@ from sys.database_permissions } private void LoadRoles(SqlCommand cm) { + Console.WriteLine("-- Loading Roles"); //Roles are complicated. This was adapted from https://dbaeyes.wordpress.com/2013/04/19/fully-script-out-a-mssql-database-role/ cm.CommandText = @" create table #ScriptedRoles ( @@ -365,7 +383,7 @@ WHERE USER_NAME(dp.grantee_principal_id) IN (@roleName) } private void LoadUsersAndLogins(SqlCommand cm) { - // get users that have access to the database + Console.WriteLine("-- Loading Users"); cm.CommandText = @" select dp.name as UserName, USER_NAME(drm.role_principal_id) as AssociatedDBRole, default_schema_name from sys.database_principals dp @@ -409,7 +427,7 @@ where sp.name not like '##%##' private void LoadCLRAssemblies(SqlCommand cm) { try { - // get CLR assemblies + Console.WriteLine("-- Loading CLR Assemblies"); cm.CommandText = @"select a.name as AssemblyName, a.permission_set_desc, af.name as FileName, af.content from sys.assemblies a @@ -437,11 +455,11 @@ from sys.assemblies a private void LoadXmlSchemas(SqlCommand cm) { try { - // get xml schemas - cm.CommandText = @" + Console.WriteLine("-- Loading Xml Schemas"); + cm.CommandText = $@" select s.name as DBSchemaName, x.name as XMLSchemaCollectionName, xml_schema_namespace(s.name, x.name) as definition from sys.xml_schema_collections x - inner join sys.schemas s on s.schema_id = x.schema_id + inner join sys.schemas s on s.schema_id = x.schema_id and s.name in {schemaIn} where s.name != 'sys'"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { @@ -462,8 +480,8 @@ from sys.xml_schema_collections x } private void LoadRoutines(SqlCommand cm) { - //get routines - cm.CommandText = @" + Console.WriteLine("-- Loading Routines"); + cm.CommandText = $@" select s.name as schemaName, o.name as routineName, @@ -476,7 +494,7 @@ private void LoadRoutines(SqlCommand cm) { tr.is_disabled as trigger_disabled from sys.sql_modules m inner join sys.objects o on m.object_id = o.object_id - inner join sys.schemas s on s.schema_id = o.schema_id + inner join sys.schemas s on s.schema_id = o.schema_id and s.name in {schemaIn} left join sys.triggers tr on m.object_id = tr.object_id left join sys.tables t on tr.parent_id = t.object_id left join sys.views v on tr.parent_id = v.object_id @@ -516,7 +534,8 @@ where objectproperty(o.object_id, 'IsMSShipped') = 0 } private void LoadCheckConstraints(SqlCommand cm) { - cm.CommandText = @" + Console.WriteLine("-- Loading Check Constraints"); + cm.CommandText = $@" SELECT OBJECT_NAME(o.OBJECT_ID) AS CONSTRAINT_NAME, SCHEMA_NAME(t.schema_id) AS TABLE_SCHEMA, @@ -529,7 +548,7 @@ private void LoadCheckConstraints(SqlCommand cm) { FROM sys.objects o inner join sys.check_constraints cc on cc.object_id = o.object_id inner join sys.tables t on t.object_id = o.parent_object_id - WHERE o.type_desc = 'CHECK_CONSTRAINT' + WHERE o.type_desc = 'CHECK_CONSTRAINT' and SCHEMA_NAME(t.schema_id) in {schemaIn} UNION ALL SELECT OBJECT_NAME(o.OBJECT_ID) AS CONSTRAINT_NAME, @@ -543,7 +562,7 @@ UNION ALL FROM sys.objects o inner join sys.check_constraints cc on cc.object_id = o.object_id inner join sys.table_types tt on tt.type_table_object_id = o.parent_object_id - WHERE o.type_desc = 'CHECK_CONSTRAINT' + WHERE o.type_desc = 'CHECK_CONSTRAINT' and SCHEMA_NAME(tt.schema_id) in {schemaIn} ORDER BY TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME "; @@ -565,14 +584,14 @@ FROM sys.objects o } private void LoadForeignKeys(SqlCommand cm) { - //get foreign keys - cm.CommandText = @" + Console.WriteLine("-- Loading Foreign Keys"); + cm.CommandText = $@" select TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME from INFORMATION_SCHEMA.TABLE_CONSTRAINTS - where CONSTRAINT_TYPE = 'FOREIGN KEY'"; + where CONSTRAINT_TYPE = 'FOREIGN KEY' and TABLE_SCHEMA in {schemaIn}"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { var t = FindTable((string)dr["TABLE_NAME"], (string)dr["TABLE_SCHEMA"]); @@ -583,7 +602,7 @@ from INFORMATION_SCHEMA.TABLE_CONSTRAINTS } //get foreign key props - cm.CommandText = @" + cm.CommandText = $@" select CONSTRAINT_NAME, OBJECT_SCHEMA_NAME(fk.parent_object_id) as TABLE_SCHEMA, @@ -592,7 +611,8 @@ from INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk.is_disabled, fk.is_system_named from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc - inner join sys.foreign_keys fk on rc.CONSTRAINT_NAME = fk.name and rc.CONSTRAINT_SCHEMA = OBJECT_SCHEMA_NAME(fk.parent_object_id)"; + inner join sys.foreign_keys fk on rc.CONSTRAINT_NAME = fk.name and rc.CONSTRAINT_SCHEMA = OBJECT_SCHEMA_NAME(fk.parent_object_id) + where object_schema_name(fk.parent_object_id) in {schemaIn}"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { var fk = FindForeignKey((string)dr["CONSTRAINT_NAME"], @@ -605,25 +625,26 @@ from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc } //get foreign key columns and ref table - cm.CommandText = @" -select - fk.name as CONSTRAINT_NAME, - OBJECT_SCHEMA_NAME(fk.parent_object_id) as TABLE_SCHEMA, - c1.name as COLUMN_NAME, - OBJECT_SCHEMA_NAME(fk.referenced_object_id) as REF_TABLE_SCHEMA, - OBJECT_NAME(fk.referenced_object_id) as REF_TABLE_NAME, - c2.name as REF_COLUMN_NAME -from sys.foreign_keys fk -inner join sys.foreign_key_columns fkc - on fkc.constraint_object_id = fk.object_id -inner join sys.columns c1 - on fkc.parent_column_id = c1.column_id - and fkc.parent_object_id = c1.object_id -inner join sys.columns c2 - on fkc.referenced_column_id = c2.column_id - and fkc.referenced_object_id = c2.object_id -order by fk.name, fkc.constraint_column_id -"; + cm.CommandText = $@" + select + fk.name as CONSTRAINT_NAME, + OBJECT_SCHEMA_NAME(fk.parent_object_id) as TABLE_SCHEMA, + c1.name as COLUMN_NAME, + OBJECT_SCHEMA_NAME(fk.referenced_object_id) as REF_TABLE_SCHEMA, + OBJECT_NAME(fk.referenced_object_id) as REF_TABLE_NAME, + c2.name as REF_COLUMN_NAME + from sys.foreign_keys fk + inner join sys.foreign_key_columns fkc + on fkc.constraint_object_id = fk.object_id + inner join sys.columns c1 + on fkc.parent_column_id = c1.column_id + and fkc.parent_object_id = c1.object_id + inner join sys.columns c2 + on fkc.referenced_column_id = c2.column_id + and fkc.referenced_object_id = c2.object_id + where object_schema_name(fk.parent_object_id) in {schemaIn} + order by fk.name, fkc.constraint_column_id + "; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { var fk = FindForeignKey((string)dr["CONSTRAINT_NAME"], @@ -643,8 +664,8 @@ inner join sys.columns c2 } private void LoadConstraintsAndIndexes(SqlCommand cm) { - //get constraints & indexes - cm.CommandText = @" + Console.WriteLine("-- Loading Constraints/Indexes"); + cm.CommandText = $@" select s.name as schemaName, t.name as tableName, @@ -674,7 +695,7 @@ from sys.table_types and ic.index_id = i.index_id inner join sys.columns c on c.object_id = t.object_id and c.column_id = ic.column_id - inner join sys.schemas s on s.schema_id = t.schema_id + inner join sys.schemas s on s.schema_id = t.schema_id and s.name in {schemaIn} where i.type_desc != 'HEAP' order by s.name, t.name, i.name, ic.key_ordinal, ic.index_column_id"; using (var dr = cm.ExecuteReader()) { @@ -722,8 +743,8 @@ from sys.table_types } private void LoadColumnComputes(SqlCommand cm) { - //get computed column definitions - cm.CommandText = @" + Console.WriteLine("-- Loading Computed Columns"); + cm.CommandText = $@" select object_schema_name(t.object_id) as TABLE_SCHEMA, object_name(t.object_id) as TABLE_NAME, @@ -733,7 +754,7 @@ private void LoadColumnComputes(SqlCommand cm) { cc.is_nullable as NULLABLE, cast(0 as bit) as IS_TYPE from sys.computed_columns cc - inner join sys.tables t on cc.object_id = t.object_id + inner join sys.tables t on cc.object_id = t.object_id and object_schema_name(t.object_id) in {schemaIn} UNION ALL select SCHEMA_NAME(tt.schema_id) as TABLE_SCHEMA, @@ -744,7 +765,7 @@ UNION ALL cc.is_nullable as NULLABLE, cast(1 as bit) AS IS_TYPE from sys.computed_columns cc - inner join sys.table_types tt on cc.object_id = tt.type_table_object_id + inner join sys.table_types tt on cc.object_id = tt.type_table_object_id and schema_name(tt.schema_id) in {schemaIn} "; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { @@ -761,8 +782,8 @@ from sys.computed_columns cc } private void LoadColumnDefaults(SqlCommand cm) { - //get column defaults - cm.CommandText = @" + Console.WriteLine("-- Loading Column Defaults"); + cm.CommandText = $@" select s.name as TABLE_SCHEMA, t.name as TABLE_NAME, @@ -775,7 +796,7 @@ from sys.tables t inner join sys.columns c on c.object_id = t.object_id inner join sys.default_constraints d on c.column_id = d.parent_column_id and d.parent_object_id = c.object_id - inner join sys.schemas s on s.schema_id = t.schema_id + inner join sys.schemas s on s.schema_id = t.schema_id and s.name in {schemaIn} UNION ALL select s.name as TABLE_SCHEMA, @@ -789,7 +810,7 @@ from sys.table_types tt inner join sys.columns c on c.object_id = tt.type_table_object_id inner join sys.default_constraints d on c.column_id = d.parent_column_id and d.parent_object_id = c.object_id - inner join sys.schemas s on s.schema_id = tt.schema_id + inner join sys.schemas s on s.schema_id = tt.schema_id and s.name in {schemaIn} "; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { @@ -805,8 +826,8 @@ from sys.table_types tt } private void LoadColumnIdentities(SqlCommand cm) { - //get column identities - cm.CommandText = @" + Console.WriteLine("-- Loading Column Identities"); + cm.CommandText = $@" select s.name as TABLE_SCHEMA, t.name as TABLE_NAME, @@ -816,7 +837,7 @@ from sys.tables t inner join sys.columns c on c.object_id = t.object_id inner join sys.identity_columns i on i.object_id = c.object_id and i.column_id = c.column_id - inner join sys.schemas s on s.schema_id = t.schema_id "; + inner join sys.schemas s on s.schema_id = t.schema_id and s.name in {schemaIn}"; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { try { @@ -835,8 +856,8 @@ from sys.tables t } private void LoadColumns(SqlCommand cm) { - //get columns - cm.CommandText = @" + Console.WriteLine("-- Loading Columns"); + cm.CommandText = $@" select t.TABLE_SCHEMA, c.TABLE_NAME, @@ -853,6 +874,7 @@ inner join INFORMATION_SCHEMA.TABLES t on t.TABLE_NAME = c.TABLE_NAME and t.TABLE_SCHEMA = c.TABLE_SCHEMA and t.TABLE_CATALOG = c.TABLE_CATALOG + and t.TABLE_SCHEMA in {schemaIn} where t.TABLE_TYPE = 'BASE TABLE' order by t.TABLE_SCHEMA, c.TABLE_NAME, c.ORDINAL_POSITION @@ -862,7 +884,7 @@ inner join INFORMATION_SCHEMA.TABLES t } try { - cm.CommandText = @" + cm.CommandText = $@" select s.name as TABLE_SCHEMA, tt.name as TABLE_NAME, @@ -878,7 +900,7 @@ from sys.columns c inner join sys.table_types tt on tt.type_table_object_id = c.object_id inner join sys.schemas s - on tt.schema_id = s.schema_id + on tt.schema_id = s.schema_id and s.name in {schemaIn} inner join sys.types t on t.system_type_id = c.system_type_id and t.user_type_id = c.user_type_id @@ -924,7 +946,7 @@ private static void LoadColumnsBase(IDataReader dr, List tables) { if (table == null || table.Name != (string)dr["TABLE_NAME"] || table.Owner != (string)dr["TABLE_SCHEMA"]) - // only do a lookup if the table we have isn't already the relevant one + // only do a lookup if the table we have isn't already the relevant one { table = FindTableBase(tables, (string)dr["TABLE_NAME"], (string)dr["TABLE_SCHEMA"]); @@ -935,25 +957,25 @@ private static void LoadColumnsBase(IDataReader dr, List
tables) { } private void LoadTables(SqlCommand cm) { - //get tables - cm.CommandText = @" + Console.WriteLine("-- Loading Tables"); + cm.CommandText = $@" select TABLE_SCHEMA, TABLE_NAME from INFORMATION_SCHEMA.TABLES - where TABLE_TYPE = 'BASE TABLE'"; + where TABLE_TYPE = 'BASE TABLE' and TABLE_SCHEMA in {schemaIn}"; using (var dr = cm.ExecuteReader()) { LoadTablesBase(dr, false, Tables); } //get table types try { - cm.CommandText = @" + cm.CommandText = $@" select s.name as TABLE_SCHEMA, tt.name as TABLE_NAME from sys.table_types tt - inner join sys.schemas s on tt.schema_id = s.schema_id + inner join sys.schemas s on tt.schema_id = s.schema_id and s.name in {schemaIn} where tt.is_user_defined = 1 order by s.name, tt.name"; using (var dr = cm.ExecuteReader()) { @@ -965,8 +987,8 @@ from sys.table_types tt } private void LoadUserDefinedTypes(SqlCommand cm) { - //get types - cm.CommandText = @" + Console.WriteLine("-- Loading UDTs"); + cm.CommandText = $@" select s.name as 'Type_Schema', t.name as 'Type_Name', @@ -974,7 +996,7 @@ private void LoadUserDefinedTypes(SqlCommand cm) { t.max_length as 'Max_Length', t.is_nullable as 'Nullable' from sys.types t - inner join sys.schemas s on s.schema_id = t.schema_id + inner join sys.schemas s on s.schema_id = t.schema_id and s.name in {schemaIn} inner join sys.types tt on t.system_type_id = tt.user_type_id where t.is_user_defined = 1 @@ -1005,56 +1027,64 @@ private static void LoadTablesBase(SqlDataReader dr, bool areTableTypes, } private void LoadSchemas(SqlCommand cm) { - //get schemas - cm.CommandText = @" + Console.WriteLine("-- Loading Schemas"); + var schemaRestrict = ""; + if (schemas != null && schemas.Count > 0) { + schemaRestrict = "and s.name in (" + string.Join(",", schemas.Select(s => $"'{s}'").ToArray()) + ")"; + } + cm.CommandText = $@" select s.name as schemaName, p.name as principalName from sys.schemas s inner join sys.database_principals p on s.principal_id = p.principal_id where s.schema_id < 16384 - and s.name not in ('dbo','guest','sys','INFORMATION_SCHEMA') - order by schema_id -"; + and s.name not in ('dbo','guest','sys','INFORMATION_SCHEMA') {schemaRestrict} + order by schema_id"; + using (var dr = cm.ExecuteReader()) { while (dr.Read()) { Schemas.Add(new Schema((string)dr["schemaName"], (string)dr["principalName"])); } } + + if (schemas == null || schemas.Count == 0) { + schemas = Schemas.Select(s => s.Name).ToList(); + } } private void LoadProps(SqlCommand cm) { var cnStrBuilder = new SqlConnectionStringBuilder(Connection); // query schema for database properties cm.CommandText = @" -select - [compatibility_level], - [collation_name], - [is_auto_close_on], - [is_auto_shrink_on], - [snapshot_isolation_state], - [is_read_committed_snapshot_on], - [recovery_model_desc], - [page_verify_option_desc], - [is_auto_create_stats_on], - [is_auto_update_stats_on], - [is_auto_update_stats_async_on], - [is_ansi_null_default_on], - [is_ansi_nulls_on], - [is_ansi_padding_on], - [is_ansi_warnings_on], - [is_arithabort_on], - [is_concat_null_yields_null_on], - [is_numeric_roundabort_on], - [is_quoted_identifier_on], - [is_recursive_triggers_on], - [is_cursor_close_on_commit_on], - [is_local_cursor_default], - [is_trustworthy_on], - [is_db_chaining_on], - [is_parameterization_forced], - [is_date_correlation_on] -from sys.databases -where name = @dbname -"; + select + [compatibility_level], + [collation_name], + [is_auto_close_on], + [is_auto_shrink_on], + [snapshot_isolation_state], + [is_read_committed_snapshot_on], + [recovery_model_desc], + [page_verify_option_desc], + [is_auto_create_stats_on], + [is_auto_update_stats_on], + [is_auto_update_stats_async_on], + [is_ansi_null_default_on], + [is_ansi_nulls_on], + [is_ansi_padding_on], + [is_ansi_warnings_on], + [is_arithabort_on], + [is_concat_null_yields_null_on], + [is_numeric_roundabort_on], + [is_quoted_identifier_on], + [is_recursive_triggers_on], + [is_cursor_close_on_commit_on], + [is_local_cursor_default], + [is_trustworthy_on], + [is_db_chaining_on], + [is_parameterization_forced], + [is_date_correlation_on] + from sys.databases + where name = @dbname + "; cm.Parameters.AddWithValue("@dbname", cnStrBuilder.InitialCatalog); using (IDataReader dr = cm.ExecuteReader()) { if (dr.Read()) { @@ -1115,9 +1145,9 @@ public DatabaseDiff Compare(Database db) { //compare database properties foreach (var p in from p in Props - let p2 = db.FindProp(p.Name) - where p.Script() != p2.Script() - select p) { + let p2 = db.FindProp(p.Name) + where p.Script() != p2.Script() + select p) { diff.PropsChanged.Add(p); } @@ -1407,7 +1437,7 @@ public void ScriptToDir(string tableHint = null, Action log WriteScriptDir("synonyms", Synonyms.ToArray(), log); WriteScriptDir("permissions", Permissions.ToArray(), log); - ExportData(tableHint, log); + ExportData(tableHint, prefix_dbo, log); } private void WritePropsScript(Action log) { @@ -1443,31 +1473,31 @@ private void WriteScriptDir(string name, ICollection objects, foreach (var o in objects) { log(TraceLevel.Verbose, $"Scripting {name} {++index} of {objects.Count}...{(index < objects.Count ? "\r" : string.Empty)}"); - var filePath = Path.Combine(dir, MakeFileName(o) + ".sql"); + var filePath = Path.Combine(dir, MakeFileName(o, prefix_dbo) + ".sql"); var script = o.ScriptCreate() + "\r\nGO\r\n"; File.AppendAllText(filePath, script); } } - private static string MakeFileName(object o) { + private static string MakeFileName(object o, bool prefix_dbo = true) { // combine foreign keys into one script per table var fk = o as ForeignKey; - if (fk != null) return MakeFileName(fk.Table); + if (fk != null) return MakeFileName(fk.Table, prefix_dbo); // combine defaults into one script per table if (o is Default) { - return MakeFileName((o as Default).Table); + return MakeFileName((o as Default).Table, prefix_dbo); } // combine check constraints into one script per table if (o is Constraint && (o as Constraint).Type == "CHECK") { - return MakeFileName((o as Constraint).Table); + return MakeFileName((o as Constraint).Table, prefix_dbo); } var schema = o as IHasOwner == null ? "" : (o as IHasOwner).Owner; var name = o as INameable == null ? "" : (o as INameable).Name; - var fileName = MakeFileName(schema, name); + var fileName = MakeFileName(schema, name, prefix_dbo); // prefix user defined types with TYPE_ var prefix = o as Table == null ? "" : (o as Table).IsType ? "TYPE_" : ""; @@ -1475,12 +1505,12 @@ private static string MakeFileName(object o) { return string.Concat(prefix, fileName); } - private static string MakeFileName(string schema, string name) { + private static string MakeFileName(string schema, string name, bool prefix_dbo = true) { // Dont' include schema name for objects in the dbo schema. // This maintains backward compatability for those who use // SchemaZen to keep their schemas under version control. var fileName = name; - if (!string.IsNullOrEmpty(schema) && schema.ToLower() != "dbo") { + if (!string.IsNullOrEmpty(schema) && (prefix_dbo || schema.ToLower() != "dbo")) { fileName = $"{schema}.{name}"; } @@ -1488,7 +1518,7 @@ private static string MakeFileName(string schema, string name) { (current, invalidChar) => current.Replace(invalidChar, '-')); } - public void ExportData(string tableHint = null, Action log = null) { + public void ExportData(string tableHint = null, bool prefix_dbo = true, Action log = null) { if (!DataTables.Any()) return; var dataDir = Dir + "/data"; @@ -1501,7 +1531,7 @@ public void ExportData(string tableHint = null, Action log = foreach (var t in DataTables) { log?.Invoke(TraceLevel.Verbose, $"Exporting data from {t.Owner + "." + t.Name} (table {++index} of {DataTables.Count})..."); - var filePathAndName = dataDir + "/" + MakeFileName(t) + ".tsv"; + var filePathAndName = dataDir + "/" + MakeFileName(t, prefix_dbo) + ".tsv"; var sw = File.CreateText(filePathAndName); t.ExportData(Connection, sw, tableHint); From 95e1eb429758359d0b7b19d802e3c9efb9bb8a8f Mon Sep 17 00:00:00 2001 From: rhumborl Date: Fri, 25 Jun 2021 19:54:55 +0100 Subject: [PATCH 2/5] Debug message for load steps, indent roles script --- model/Models/Database.cs | 242 ++++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 118 deletions(-) diff --git a/model/Models/Database.cs b/model/Models/Database.cs index 3b4b6351..a6c54d9b 100644 --- a/model/Models/Database.cs +++ b/model/Models/Database.cs @@ -223,7 +223,7 @@ public void Load(int commandTimeout) { private void LoadSynonyms(SqlCommand cm) { try { - Console.WriteLine("-- Loading synonyms"); + WriteDebug("-- Loading synonyms"); cm.CommandText = $@" select object_schema_name(object_id) as schema_name, name as synonym_name, base_object_name where object_schema_name(object_id) in {schemaIn} @@ -243,7 +243,7 @@ where object_schema_name(object_id) in {schemaIn} private void LoadPermissions(SqlCommand cm) { try { - Console.WriteLine("-- Loading Permissions"); + // get permissions // based on http://sql-articles.com/scripts/script-to-retrieve-security-information-sql-server-2005-and-above/ cm.CommandText = $@" select @@ -266,110 +266,110 @@ from sys.database_permissions } private void LoadRoles(SqlCommand cm) { - Console.WriteLine("-- Loading Roles"); + WriteDebug("-- Loading Roles"); //Roles are complicated. This was adapted from https://dbaeyes.wordpress.com/2013/04/19/fully-script-out-a-mssql-database-role/ cm.CommandText = @" -create table #ScriptedRoles ( - name nvarchar(255) not null -, script nvarchar(max) -) - -insert into #ScriptedRoles -select - name -, null as script -from sys.database_principals -where type = 'R' - and name not in ( - -- Ignore default roles, just look for custom ones - 'db_accessadmin' - , 'db_backupoperator' - , 'db_datareader' - , 'db_datawriter' - , 'db_ddladmin' - , 'db_denydatareader' - , 'db_denydatawriter' - , 'db_owner' - , 'db_securityadmin' - , 'public' - ) - -while(exists(select 1 from #ScriptedRoles where script is null)) -begin - - DECLARE @RoleName VARCHAR(255) - SET @RoleName = (select top 1 name from #ScriptedRoles where script is null) - - -- Script out the Role - DECLARE @roleDesc VARCHAR(MAX), @crlf VARCHAR(2) - SET @crlf = CHAR(13) + CHAR(10) - SET @roleDesc = 'CREATE ROLE [' + @roleName + ']' + @crlf + 'GO' + @crlf + @crlf - - SELECT @roleDesc = @roleDesc + - CASE dp.state - WHEN 'D' THEN 'DENY ' - WHEN 'G' THEN 'GRANT ' - WHEN 'R' THEN 'REVOKE ' - WHEN 'W' THEN 'GRANT ' - END + - dp.permission_name + ' ' + - CASE dp.class - WHEN 0 THEN '' - WHEN 1 THEN --table or column subset on the table - CASE WHEN dp.major_id < 0 THEN - + 'ON [sys].[' + OBJECT_NAME(dp.major_id) + '] ' - ELSE - + 'ON [' + - (SELECT SCHEMA_NAME(schema_id) + '].[' + name FROM sys.objects WHERE object_id = dp.major_id) - + -- optionally concatenate column names - CASE WHEN MAX(dp.minor_id) > 0 - THEN '] ([' + REPLACE( - (SELECT name + '], [' - FROM sys.columns - WHERE object_id = dp.major_id - AND column_id IN (SELECT minor_id - FROM sys.database_permissions - WHERE major_id = dp.major_id - AND USER_NAME(grantee_principal_id) IN (@roleName) - ) - FOR XML PATH('') - ) --replace final square bracket pair - + '])', ', []', '') - ELSE ']' - END + ' ' - END - WHEN 3 THEN 'ON SCHEMA::[' + SCHEMA_NAME(dp.major_id) + '] ' - WHEN 4 THEN 'ON ' + (SELECT RIGHT(type_desc, 4) + '::[' + name FROM sys.database_principals WHERE principal_id = dp.major_id) + '] ' - WHEN 5 THEN 'ON ASSEMBLY::[' + (SELECT name FROM sys.assemblies WHERE assembly_id = dp.major_id) + '] ' - WHEN 6 THEN 'ON TYPE::[' + (SELECT name FROM sys.types WHERE user_type_id = dp.major_id) + '] ' - WHEN 10 THEN 'ON XML SCHEMA COLLECTION::[' + (SELECT SCHEMA_NAME(schema_id) + '.' + name FROM sys.xml_schema_collections WHERE xml_collection_id = dp.major_id) + '] ' - WHEN 15 THEN 'ON MESSAGE TYPE::[' + (SELECT name FROM sys.service_message_types WHERE message_type_id = dp.major_id) + '] ' - WHEN 16 THEN 'ON CONTRACT::[' + (SELECT name FROM sys.service_contracts WHERE service_contract_id = dp.major_id) + '] ' - WHEN 17 THEN 'ON SERVICE::[' + (SELECT name FROM sys.services WHERE service_id = dp.major_id) + '] ' - WHEN 18 THEN 'ON REMOTE SERVICE BINDING::[' + (SELECT name FROM sys.remote_service_bindings WHERE remote_service_binding_id = dp.major_id) + '] ' - WHEN 19 THEN 'ON ROUTE::[' + (SELECT name FROM sys.routes WHERE route_id = dp.major_id) + '] ' - WHEN 23 THEN 'ON FULLTEXT CATALOG::[' + (SELECT name FROM sys.fulltext_catalogs WHERE fulltext_catalog_id = dp.major_id) + '] ' - WHEN 24 THEN 'ON SYMMETRIC KEY::[' + (SELECT name FROM sys.symmetric_keys WHERE symmetric_key_id = dp.major_id) + '] ' - WHEN 25 THEN 'ON CERTIFICATE::[' + (SELECT name FROM sys.certificates WHERE certificate_id = dp.major_id) + '] ' - WHEN 26 THEN 'ON ASYMMETRIC KEY::[' + (SELECT name FROM sys.asymmetric_keys WHERE asymmetric_key_id = dp.major_id) + '] ' - END COLLATE SQL_Latin1_General_CP1_CI_AS - + 'TO [' + @roleName + ']' + - CASE dp.state WHEN 'W' THEN ' WITH GRANT OPTION' ELSE '' END + @crlf - FROM sys.database_permissions dp - WHERE USER_NAME(dp.grantee_principal_id) IN (@roleName) - GROUP BY dp.state, dp.major_id, dp.permission_name, dp.class - - update #ScriptedRoles - set script = @roleDesc - where name = @RoleName - -end - -select - name -, script -from #ScriptedRoles -"; + create table #ScriptedRoles ( + name nvarchar(255) not null + , script nvarchar(max) + ) + + insert into #ScriptedRoles + select + name + , null as script + from sys.database_principals + where type = 'R' + and name not in ( + -- Ignore default roles, just look for custom ones + 'db_accessadmin' + , 'db_backupoperator' + , 'db_datareader' + , 'db_datawriter' + , 'db_ddladmin' + , 'db_denydatareader' + , 'db_denydatawriter' + , 'db_owner' + , 'db_securityadmin' + , 'public' + ) + + while(exists(select 1 from #ScriptedRoles where script is null)) + begin + + DECLARE @RoleName VARCHAR(255) + SET @RoleName = (select top 1 name from #ScriptedRoles where script is null) + + -- Script out the Role + DECLARE @roleDesc VARCHAR(MAX), @crlf VARCHAR(2) + SET @crlf = CHAR(13) + CHAR(10) + SET @roleDesc = 'CREATE ROLE [' + @roleName + ']' + @crlf + 'GO' + @crlf + @crlf + + SELECT @roleDesc = @roleDesc + + CASE dp.state + WHEN 'D' THEN 'DENY ' + WHEN 'G' THEN 'GRANT ' + WHEN 'R' THEN 'REVOKE ' + WHEN 'W' THEN 'GRANT ' + END + + dp.permission_name + ' ' + + CASE dp.class + WHEN 0 THEN '' + WHEN 1 THEN --table or column subset on the table + CASE WHEN dp.major_id < 0 THEN + + 'ON [sys].[' + OBJECT_NAME(dp.major_id) + '] ' + ELSE + + 'ON [' + + (SELECT SCHEMA_NAME(schema_id) + '].[' + name FROM sys.objects WHERE object_id = dp.major_id) + + -- optionally concatenate column names + CASE WHEN MAX(dp.minor_id) > 0 + THEN '] ([' + REPLACE( + (SELECT name + '], [' + FROM sys.columns + WHERE object_id = dp.major_id + AND column_id IN (SELECT minor_id + FROM sys.database_permissions + WHERE major_id = dp.major_id + AND USER_NAME(grantee_principal_id) IN (@roleName) + ) + FOR XML PATH('') + ) --replace final square bracket pair + + '])', ', []', '') + ELSE ']' + END + ' ' + END + WHEN 3 THEN 'ON SCHEMA::[' + SCHEMA_NAME(dp.major_id) + '] ' + WHEN 4 THEN 'ON ' + (SELECT RIGHT(type_desc, 4) + '::[' + name FROM sys.database_principals WHERE principal_id = dp.major_id) + '] ' + WHEN 5 THEN 'ON ASSEMBLY::[' + (SELECT name FROM sys.assemblies WHERE assembly_id = dp.major_id) + '] ' + WHEN 6 THEN 'ON TYPE::[' + (SELECT name FROM sys.types WHERE user_type_id = dp.major_id) + '] ' + WHEN 10 THEN 'ON XML SCHEMA COLLECTION::[' + (SELECT SCHEMA_NAME(schema_id) + '.' + name FROM sys.xml_schema_collections WHERE xml_collection_id = dp.major_id) + '] ' + WHEN 15 THEN 'ON MESSAGE TYPE::[' + (SELECT name FROM sys.service_message_types WHERE message_type_id = dp.major_id) + '] ' + WHEN 16 THEN 'ON CONTRACT::[' + (SELECT name FROM sys.service_contracts WHERE service_contract_id = dp.major_id) + '] ' + WHEN 17 THEN 'ON SERVICE::[' + (SELECT name FROM sys.services WHERE service_id = dp.major_id) + '] ' + WHEN 18 THEN 'ON REMOTE SERVICE BINDING::[' + (SELECT name FROM sys.remote_service_bindings WHERE remote_service_binding_id = dp.major_id) + '] ' + WHEN 19 THEN 'ON ROUTE::[' + (SELECT name FROM sys.routes WHERE route_id = dp.major_id) + '] ' + WHEN 23 THEN 'ON FULLTEXT CATALOG::[' + (SELECT name FROM sys.fulltext_catalogs WHERE fulltext_catalog_id = dp.major_id) + '] ' + WHEN 24 THEN 'ON SYMMETRIC KEY::[' + (SELECT name FROM sys.symmetric_keys WHERE symmetric_key_id = dp.major_id) + '] ' + WHEN 25 THEN 'ON CERTIFICATE::[' + (SELECT name FROM sys.certificates WHERE certificate_id = dp.major_id) + '] ' + WHEN 26 THEN 'ON ASYMMETRIC KEY::[' + (SELECT name FROM sys.asymmetric_keys WHERE asymmetric_key_id = dp.major_id) + '] ' + END COLLATE SQL_Latin1_General_CP1_CI_AS + + 'TO [' + @roleName + ']' + + CASE dp.state WHEN 'W' THEN ' WITH GRANT OPTION' ELSE '' END + @crlf + FROM sys.database_permissions dp + WHERE USER_NAME(dp.grantee_principal_id) IN (@roleName) + GROUP BY dp.state, dp.major_id, dp.permission_name, dp.class + + update #ScriptedRoles + set script = @roleDesc + where name = @RoleName + + end + + select + name + , script + from #ScriptedRoles + "; Role r = null; using (var dr = cm.ExecuteReader()) { while (dr.Read()) { @@ -383,7 +383,7 @@ WHERE USER_NAME(dp.grantee_principal_id) IN (@roleName) } private void LoadUsersAndLogins(SqlCommand cm) { - Console.WriteLine("-- Loading Users"); + WriteDebug("-- Loading Users"); cm.CommandText = @" select dp.name as UserName, USER_NAME(drm.role_principal_id) as AssociatedDBRole, default_schema_name from sys.database_principals dp @@ -427,7 +427,7 @@ where sp.name not like '##%##' private void LoadCLRAssemblies(SqlCommand cm) { try { - Console.WriteLine("-- Loading CLR Assemblies"); + WriteDebug("-- Loading CLR Assemblies"); cm.CommandText = @"select a.name as AssemblyName, a.permission_set_desc, af.name as FileName, af.content from sys.assemblies a @@ -455,7 +455,7 @@ from sys.assemblies a private void LoadXmlSchemas(SqlCommand cm) { try { - Console.WriteLine("-- Loading Xml Schemas"); + WriteDebug("-- Loading Xml Schemas"); cm.CommandText = $@" select s.name as DBSchemaName, x.name as XMLSchemaCollectionName, xml_schema_namespace(s.name, x.name) as definition from sys.xml_schema_collections x @@ -480,7 +480,7 @@ from sys.xml_schema_collections x } private void LoadRoutines(SqlCommand cm) { - Console.WriteLine("-- Loading Routines"); + WriteDebug("-- Loading Routines"); cm.CommandText = $@" select s.name as schemaName, @@ -534,7 +534,7 @@ where objectproperty(o.object_id, 'IsMSShipped') = 0 } private void LoadCheckConstraints(SqlCommand cm) { - Console.WriteLine("-- Loading Check Constraints"); + WriteDebug("-- Loading Check Constraints"); cm.CommandText = $@" SELECT OBJECT_NAME(o.OBJECT_ID) AS CONSTRAINT_NAME, @@ -584,7 +584,7 @@ FROM sys.objects o } private void LoadForeignKeys(SqlCommand cm) { - Console.WriteLine("-- Loading Foreign Keys"); + WriteDebug("-- Loading Foreign Keys"); cm.CommandText = $@" select TABLE_SCHEMA, @@ -664,7 +664,7 @@ where object_schema_name(fk.parent_object_id) in {schemaIn} } private void LoadConstraintsAndIndexes(SqlCommand cm) { - Console.WriteLine("-- Loading Constraints/Indexes"); + WriteDebug("-- Loading Constraints/Indexes"); cm.CommandText = $@" select s.name as schemaName, @@ -743,7 +743,7 @@ from sys.table_types } private void LoadColumnComputes(SqlCommand cm) { - Console.WriteLine("-- Loading Computed Columns"); + WriteDebug("-- Loading Computed Columns"); cm.CommandText = $@" select object_schema_name(t.object_id) as TABLE_SCHEMA, @@ -782,7 +782,7 @@ from sys.computed_columns cc } private void LoadColumnDefaults(SqlCommand cm) { - Console.WriteLine("-- Loading Column Defaults"); + WriteDebug("-- Loading Column Defaults"); cm.CommandText = $@" select s.name as TABLE_SCHEMA, @@ -826,7 +826,7 @@ from sys.table_types tt } private void LoadColumnIdentities(SqlCommand cm) { - Console.WriteLine("-- Loading Column Identities"); + WriteDebug("-- Loading Column Identities"); cm.CommandText = $@" select s.name as TABLE_SCHEMA, @@ -856,7 +856,7 @@ from sys.tables t } private void LoadColumns(SqlCommand cm) { - Console.WriteLine("-- Loading Columns"); + WriteDebug("-- Loading Columns"); cm.CommandText = $@" select t.TABLE_SCHEMA, @@ -957,7 +957,7 @@ private static void LoadColumnsBase(IDataReader dr, List
tables) { } private void LoadTables(SqlCommand cm) { - Console.WriteLine("-- Loading Tables"); + WriteDebug("-- Loading Tables"); cm.CommandText = $@" select TABLE_SCHEMA, @@ -987,7 +987,7 @@ from sys.table_types tt } private void LoadUserDefinedTypes(SqlCommand cm) { - Console.WriteLine("-- Loading UDTs"); + WriteDebug("-- Loading UDTs"); cm.CommandText = $@" select s.name as 'Type_Schema', @@ -1027,7 +1027,7 @@ private static void LoadTablesBase(SqlDataReader dr, bool areTableTypes, } private void LoadSchemas(SqlCommand cm) { - Console.WriteLine("-- Loading Schemas"); + WriteDebug("-- Loading Schemas"); var schemaRestrict = ""; if (schemas != null && schemas.Count > 0) { schemaRestrict = "and s.name in (" + string.Join(",", schemas.Select(s => $"'{s}'").ToArray()) + ")"; @@ -1755,6 +1755,12 @@ public void ExecCreate(bool dropIfExists) { } #endregion + + private static void WriteDebug(string format, params object[] args) { +#if DEBUG + Console.WriteLine(format, args); +#endif + } } public class DatabaseDiff { From 9f1fced98e653b359f190114f484f2f5a9bd1843 Mon Sep 17 00:00:00 2001 From: rhumborl Date: Fri, 25 Jun 2021 20:01:46 +0100 Subject: [PATCH 3/5] Replace resharper test tools with nunit adapter --- test/Tests.csproj | 4 ---- test/packages.config | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/test/Tests.csproj b/test/Tests.csproj index 754cada0..75f37457 100644 --- a/test/Tests.csproj +++ b/test/Tests.csproj @@ -130,15 +130,11 @@ - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - -