From a83380d3a4a1bc4e627ba0f708421e46eb46422b Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Wed, 26 Oct 2022 23:55:57 +0000 Subject: [PATCH] case 4221 --- docs/8.0/ayanova/docs/changelog.md | 4 +- server/AyaNova/util/DbUtil.cs | 242 ++++++++++++++--------------- 2 files changed, 117 insertions(+), 129 deletions(-) diff --git a/docs/8.0/ayanova/docs/changelog.md b/docs/8.0/ayanova/docs/changelog.md index 852b1ebe..af890759 100644 --- a/docs/8.0/ayanova/docs/changelog.md +++ b/docs/8.0/ayanova/docs/changelog.md @@ -15,7 +15,9 @@ The most recent changes are written in the AyaNova manual [hosted on our website #### Changed -- UI: Administration -> License page now has time in addition to date for license and maintenance expiration values +- Server: Erase database huge speed improvement +- UI: Administration -> License page added _time_ in addition to existing _date_ for license and maintenance expiration +- UI: Administration -> License page only SuperUser account is offered the Erase Database menu option (no other user has rights to erase db) ### AyaNova 8.0.17 (2022-10-12) diff --git a/server/AyaNova/util/DbUtil.cs b/server/AyaNova/util/DbUtil.cs index d253fa5a..aabca152 100644 --- a/server/AyaNova/util/DbUtil.cs +++ b/server/AyaNova/util/DbUtil.cs @@ -395,7 +395,7 @@ namespace AyaNova.Util // Erase all user entered data from the db // This is called by seeder for trial seeding purposes // and by v8 migrate and by license controller when erasing db - internal static async Task EmptyBizDataFromDatabaseForSeedingOrImportingAsync(ILogger _log, bool keepTaxCodes=false) + internal static async Task EmptyBizDataFromDatabaseForSeedingOrImportingAsync(ILogger _log, bool keepTaxCodes = false) { //case 4221 //erase plan to use truncate table instead of slower delete method @@ -407,8 +407,8 @@ namespace AyaNova.Util //- $profit - - + + _log.LogInformation("Erasing Database \"{0}\"", _dbName); AyaNova.Api.ControllerHelpers.ApiServerState apiServerState = (AyaNova.Api.ControllerHelpers.ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(AyaNova.Api.ControllerHelpers.ApiServerState)); @@ -519,135 +519,119 @@ namespace AyaNova.Util //REMOVE ALL REMAINING DATA - await EraseTableAsync("aunitmeterreading", conn); - await EraseTableAsync("acustomerservicerequest", conn); + await TruncateTableAsync("aunitmeterreading", conn); + await TruncateTableAsync("acustomerservicerequest", conn); //--- WorkOrder - await EraseTableAsync("aworkorderitemexpense", conn); - await EraseTableAsync("aworkorderitemlabor", conn); - await EraseTableAsync("aworkorderitemloan", conn); - await EraseTableAsync("aworkorderitempart", conn); - await EraseTableAsync("aworkorderitempartrequest", conn); - await EraseTableAsync("aworkorderitemscheduleduser", conn); - await EraseTableAsync("aworkorderitemtask", conn); - await EraseTableAsync("aworkorderitemtravel", conn); - await EraseTableAsync("aworkorderitemunit", conn); - await EraseTableAsync("aworkorderitemoutsideservice", conn); - await EraseTableAsync("aworkorderitem", conn); - await EraseTableAsync("aworkorderstate", conn); - await EraseTableAsync("aworkorder", conn); + await TruncateTableAsync("aworkorderitemexpense", conn); + await TruncateTableAsync("aworkorderitemlabor", conn); + await TruncateTableAsync("aworkorderitemloan", conn); + await TruncateTableAsync("aworkorderitempart", conn); + await TruncateTableAsync("aworkorderitempartrequest", conn); + await TruncateTableAsync("aworkorderitemscheduleduser", conn); + await TruncateTableAsync("aworkorderitemtask", conn); + await TruncateTableAsync("aworkorderitemtravel", conn); + await TruncateTableAsync("aworkorderitemunit", conn); + await TruncateTableAsync("aworkorderitemoutsideservice", conn); + await TruncateTableAsync("aworkorderitem", conn); + await TruncateTableAsync("aworkorderstate", conn); + await TruncateTableAsync("aworkorder", conn); //--- //--- QUOTE - await EraseTableAsync("aquoteitemexpense", conn); - await EraseTableAsync("aquoteitemlabor", conn); - await EraseTableAsync("aquoteitemloan", conn); - await EraseTableAsync("aquoteitempart", conn); - await EraseTableAsync("aquoteitemscheduleduser", conn); - await EraseTableAsync("aquoteitemtask", conn); - await EraseTableAsync("aquoteitemtravel", conn); - await EraseTableAsync("aquoteitemunit", conn); - await EraseTableAsync("aquoteitemoutsideservice", conn); - await EraseTableAsync("aquoteitem", conn); - await EraseTableAsync("aquotestate", conn); - await EraseTableAsync("aquote", conn); + await TruncateTableAsync("aquoteitemexpense", conn); + await TruncateTableAsync("aquoteitemlabor", conn); + await TruncateTableAsync("aquoteitemloan", conn); + await TruncateTableAsync("aquoteitempart", conn); + await TruncateTableAsync("aquoteitemscheduleduser", conn); + await TruncateTableAsync("aquoteitemtask", conn); + await TruncateTableAsync("aquoteitemtravel", conn); + await TruncateTableAsync("aquoteitemunit", conn); + await TruncateTableAsync("aquoteitemoutsideservice", conn); + await TruncateTableAsync("aquoteitem", conn); + await TruncateTableAsync("aquotestate", conn); + await TruncateTableAsync("aquote", conn); //--- //--- PM - await EraseTableAsync("apmitemexpense", conn); - await EraseTableAsync("apmitemlabor", conn); - await EraseTableAsync("apmitemloan", conn); - await EraseTableAsync("apmitempart", conn); - await EraseTableAsync("apmitemscheduleduser", conn); - await EraseTableAsync("apmitemtask", conn); - await EraseTableAsync("apmitemtravel", conn); - await EraseTableAsync("apmitemunit", conn); - await EraseTableAsync("apmitemoutsideservice", conn); - await EraseTableAsync("apmitem", conn); - await EraseTableAsync("apm", conn);//bugbug - //--- + await TruncateTableAsync("apmitemexpense", conn); + await TruncateTableAsync("apmitemlabor", conn); + await TruncateTableAsync("apmitemloan", conn); + await TruncateTableAsync("apmitempart", conn); + await TruncateTableAsync("apmitemscheduleduser", conn); + await TruncateTableAsync("apmitemtask", conn); + await TruncateTableAsync("apmitemtravel", conn); + await TruncateTableAsync("apmitemunit", conn); + await TruncateTableAsync("apmitemoutsideservice", conn); + await TruncateTableAsync("apmitem", conn); + await TruncateTableAsync("apm", conn);//bugbug + //--- - await EraseTableAsync("afileattachment", conn); - await EraseTableAsync("aevent", conn); - await EraseTableAsync("adatalistsavedfilter", conn); - await EraseTableAsync("adatalistcolumnview", conn); - await EraseTableAsync("apicklisttemplate", conn, true); - await EraseTableAsync("aformcustom", conn); - await EraseTableAsync("asearchkey", conn); - await EraseTableAsync("asearchdictionary", conn); - await EraseTableAsync("atag", conn); - await EraseTableAsync("apurchaseorderitem", conn); - await EraseTableAsync("apurchaseorder", conn); + await TruncateTableAsync("afileattachment", conn); + await TruncateTableAsync("aevent", conn); + await TruncateTableAsync("adatalistsavedfilter", conn); + await TruncateTableAsync("adatalistcolumnview", conn); + await TruncateTableAsync("apicklisttemplate", conn, true); + await TruncateTableAsync("aformcustom", conn); + await TruncateTableAsync("asearchkey", conn); + await TruncateTableAsync("asearchdictionary", conn); + await TruncateTableAsync("atag", conn); + await TruncateTableAsync("apurchaseorderitem", conn); + await TruncateTableAsync("apurchaseorder", conn); - await EraseTableAsync("apartassemblyitem", conn); - await EraseTableAsync("apartassembly", conn); - await EraseTableAsync("apartinventory", conn); - await EraseTableAsync("apart", conn); + await TruncateTableAsync("apartassemblyitem", conn); + await TruncateTableAsync("apartassembly", conn); + await TruncateTableAsync("apartinventory", conn); + await TruncateTableAsync("apart", conn); - await EraseTableAsync("aloanunit", conn); - await EraseTableAsync("aunitmodel", conn); - await EraseTableAsync("avendor", conn); + await TruncateTableAsync("aloanunit", conn); + await TruncateTableAsync("aunitmodel", conn); + await TruncateTableAsync("avendor", conn); - await EraseTableAsync("aunit", conn); - await EraseTableAsync("aproject", conn);//depends on User, dependants are wo,quote,pm + await TruncateTableAsync("aunit", conn); + await TruncateTableAsync("aproject", conn);//depends on User, dependants are wo,quote,pm - await EraseTableAsync("acustomernote", conn); - await EraseTableAsync("acustomer", conn); - await EraseTableAsync("aheadoffice", conn); - await EraseTableAsync("acontract", conn); + await TruncateTableAsync("acustomernote", conn); + await TruncateTableAsync("acustomer", conn); + await TruncateTableAsync("aheadoffice", conn); + await TruncateTableAsync("acontract", conn); //----- NOTIFICATION - await EraseTableAsync("ainappnotification", conn); - await EraseTableAsync("anotifyevent", conn); - await EraseTableAsync("anotifydeliverylog", conn); - await EraseTableAsync("anotifysubscription", conn); - await EraseTableAsync("acustomernotifyevent", conn); - await EraseTableAsync("acustomernotifydeliverylog", conn); - await EraseTableAsync("acustomernotifysubscription", conn); + await TruncateTableAsync("ainappnotification", conn); + await TruncateTableAsync("anotifyevent", conn); + await TruncateTableAsync("anotifydeliverylog", conn); + await TruncateTableAsync("anotifysubscription", conn); + await TruncateTableAsync("acustomernotifyevent", conn); + await TruncateTableAsync("acustomernotifydeliverylog", conn); + await TruncateTableAsync("acustomernotifysubscription", conn); - await EraseTableAsync("amemo", conn); - await EraseTableAsync("areminder", conn);//depends on User - await EraseTableAsync("areview", conn);//depends on User + await TruncateTableAsync("amemo", conn); + await TruncateTableAsync("areminder", conn);//depends on User + await TruncateTableAsync("areview", conn);//depends on User - await EraseTableAsync("aservicerate", conn); - await EraseTableAsync("atravelrate", conn); + await TruncateTableAsync("aservicerate", conn); + await TruncateTableAsync("atravelrate", conn); if (!keepTaxCodes) await EraseTableAsync("ataxcode", conn); - await EraseTableAsync("aquotestatus", conn); - await EraseTableAsync("aworkorderstatus", conn); - await EraseTableAsync("aworkorderitemstatus", conn); - await EraseTableAsync("aworkorderitempriority", conn); - await EraseTableAsync("ataskgroup", conn);//items cascade + await TruncateTableAsync("aquotestatus", conn); + await TruncateTableAsync("aworkorderstatus", conn); + await TruncateTableAsync("aworkorderitemstatus", conn); + await TruncateTableAsync("aworkorderitempriority", conn); + await TruncateTableAsync("ataskgroup", conn);//items cascade - await EraseTableAsync("ametricmm", conn, true); - await EraseTableAsync("ametricdd", conn, true); - await EraseTableAsync("adashboardview", conn); + await TruncateTableAsync("ametricmm", conn, true); + await TruncateTableAsync("ametricdd", conn, true); + await TruncateTableAsync("adashboardview", conn); - await EraseTableAsync("aintegration", conn); + await TruncateTableAsync("aintegration", conn); - - - - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); - // await EraseTableAsync("XXXXX", conn); + //############# WARNING: there can be unintended consequences easily if new tables or fields are added that REFERENCE other tables triggering a cascade delete unexpectedly + //be sure about that before making changes and test thoroughly anything that calls this method: + // the seeding and manual erase and v8-migrate code when making such changes //case 4221 truncate support @@ -673,8 +657,8 @@ namespace AyaNova.Util + "FROM auseroptions_backup where userid = 1;"; await cmd.ExecuteNonQueryAsync(); - cmd.CommandText="DROP TABLE IF EXISTS AUSEROPTIONS_BACKUP, AUSER_BACKUP;"; - await cmd.ExecuteNonQueryAsync(); + cmd.CommandText = "DROP TABLE IF EXISTS AUSEROPTIONS_BACKUP, AUSER_BACKUP;"; + await cmd.ExecuteNonQueryAsync(); } @@ -682,15 +666,6 @@ namespace AyaNova.Util using (var cmd = new Npgsql.NpgsqlCommand()) { cmd.Connection = conn; - //Removed for case 4221 handled above - // // cmd.CommandText = "delete from \"auseroptions\" where UserId <> 1;"; - // // await cmd.ExecuteNonQueryAsync(); - // // cmd.CommandText = "ALTER SEQUENCE auseroptions_id_seq RESTART WITH 2;"; - // // await cmd.ExecuteNonQueryAsync(); - // // cmd.CommandText = "delete from \"auser\" where id <> 1;"; - // // await cmd.ExecuteNonQueryAsync(); - // // cmd.CommandText = "ALTER SEQUENCE auser_id_seq RESTART WITH 2;"; - // // await cmd.ExecuteNonQueryAsync(); cmd.CommandText = "delete from \"apartwarehouse\" where id <> 1;"; await cmd.ExecuteNonQueryAsync(); @@ -724,25 +699,36 @@ namespace AyaNova.Util /////////////////////////////////////////// - // Erase all data from the table specified + // Truncate all data from the table specified // - private static async Task EraseTableAsync(string sTable, Npgsql.NpgsqlConnection conn, bool tableHasNoSequence = false) + private static async Task TruncateTableAsync(string sTable, Npgsql.NpgsqlConnection conn, bool tableHasNoSequence = false) { using (var cmd = new Npgsql.NpgsqlCommand()) { cmd.Connection = conn; - //Boo! Can't do this becuase it will fail if there is a foreign key which nearly all tables have unless cascade option is used - //but then cascade causes things to delete in any referenced table cmd.CommandText = "TRUNCATE \"" + sTable + "\" RESTART IDENTITY CASCADE;"; - - //// cmd.CommandText = $"delete from {sTable};"; await cmd.ExecuteNonQueryAsync(); - // // if (!tableHasNoSequence) - // // { - // // cmd.CommandText = $"ALTER SEQUENCE {sTable}_id_seq RESTART WITH 1;"; - // // await cmd.ExecuteNonQueryAsync(); - // // } + } + } + + /////////////////////////////////////////// + // Erase all data from the table specified + // + private static async Task EraseTableAsync(string sTable, Npgsql.NpgsqlConnection conn, bool tableHasNoSequence = false) + { + //this variant is used for tables that don't need to be truncated due to smaller size + //and would trigger need to backup referenced tables first + using (var cmd = new Npgsql.NpgsqlCommand()) + { + cmd.Connection = conn; + cmd.CommandText = $"delete from {sTable};"; + await cmd.ExecuteNonQueryAsync(); + if (!tableHasNoSequence) + { + cmd.CommandText = $"ALTER SEQUENCE {sTable}_id_seq RESTART WITH 1;"; + await cmd.ExecuteNonQueryAsync(); + } } }