/// <summary> /// Returns all migration versions applied in the target database /// </summary> public List <DbVersion> GetAllVersions(string schemaName = null, string tableName = null) { return(_configurationDataService.GetAllAppliedVersions(schemaName, tableName)); }
/// <inheritdoc /> //TODO: Move this to MigrationServiceBase public virtual List <DbVersion> GetAllVersions(string metaSchemaName = null, string metaTableName = null) { return(_configurationDataService.GetAllAppliedVersions(metaSchemaName, metaTableName)); }
/// <inheritdoc /> public override void Run( string workingPath, string targetVersion = null, bool?autoCreateDatabase = false, List <KeyValuePair <string, string> > tokenKeyPairs = null, bool?verifyOnly = false, string bulkSeparator = null, string metaSchemaName = null, string metaTableName = null, int?commandTimeout = null, int?bulkBatchSize = null, string appliedByTool = null, string appliedByToolVersion = null, string environmentCode = null, NonTransactionalResolvingOption?nonTransactionalResolvingOption = null, bool noTransaction = false ) { if (_dataService.IsAtomicDDLSupported && nonTransactionalResolvingOption != null) { throw new NotSupportedException(@$ "The non-transactional failure resolving option " "{nonTransactionalResolvingOption}" " is not available for this platform."); } //check the workspace structure if required directories are present _localVersionService.Validate(workingPath); //when uncomitted run is not supported, fail migration, throw exceptions and return error exit code if (verifyOnly.HasValue && verifyOnly == true && !_dataService.IsAtomicDDLSupported) { throw new NotSupportedException("Yuniql.Verify is not supported in the target platform. " + "The feature requires support for atomic DDL operations. " + "An atomic DDL operations ensures creation of tables, views and other objects and data are rolledback in case of error. " + "For more information see https://yuniql.io/docs/."); } //when no target version specified, we use the latest local version if (string.IsNullOrEmpty(targetVersion)) { targetVersion = _localVersionService.GetLatestVersion(workingPath); _traceService.Info($"No explicit target version requested. We'll use latest available locally {targetVersion} on {workingPath}."); } var connectionInfo = _dataService.GetConnectionInfo(); var targetDatabaseName = connectionInfo.Database; var targetDatabaseServer = connectionInfo.DataSource; //we try to auto-create the database, we need this to be outside of the transaction scope //in an event of failure, users have to manually drop the auto-created database! //we only check if the db exists when --auto-create-db is true if (autoCreateDatabase.HasValue && autoCreateDatabase == true) { var targetDatabaseExists = _configurationDataService.IsDatabaseExists(); if (!targetDatabaseExists) { _traceService.Info($"Target database does not exist. Creating database {targetDatabaseName} on {targetDatabaseServer}."); _configurationDataService.CreateDatabase(); _traceService.Info($"Created database {targetDatabaseName} on {targetDatabaseServer}."); } } //check if database has been pre-configured to support migration and setup when its not var targetDatabaseConfigured = _configurationDataService.IsDatabaseConfigured(metaSchemaName, metaTableName); if (!targetDatabaseConfigured) { //create custom schema when user supplied and only if platform supports it if (_dataService.IsSchemaSupported && null != metaSchemaName && !_dataService.SchemaName.Equals(metaSchemaName)) { _traceService.Info($"Target schema does not exist. Creating schema {metaSchemaName} on {targetDatabaseName} on {targetDatabaseServer}."); _configurationDataService.CreateSchema(metaSchemaName); _traceService.Info($"Created schema {metaSchemaName} on {targetDatabaseName} on {targetDatabaseServer}."); } //create empty versions tracking table _traceService.Info($"Target database {targetDatabaseName} on {targetDatabaseServer} not yet configured for migration."); _configurationDataService.ConfigureDatabase(metaSchemaName, metaTableName); _traceService.Info($"Configured database migration support for {targetDatabaseName} on {targetDatabaseServer}."); } var targetDatabaseUpdated = _configurationDataService.UpdateDatabaseConfiguration(metaSchemaName, metaTableName); if (targetDatabaseUpdated) { _traceService.Info($"The configuration of migration has been updated for {targetDatabaseName} on {targetDatabaseServer}."); } else { _traceService.Debug($"The configuration of migration is up to date for {targetDatabaseName} on {targetDatabaseServer}."); } NonTransactionalContext nonTransactionalContext = null; //check for presence of failed no-transactional versions from previous runs var allVersions = _configurationDataService.GetAllVersions(metaSchemaName, metaTableName); var failedVersion = allVersions.Where(x => x.Status == Status.Failed).FirstOrDefault(); if (failedVersion != null) { //check if user had issue resolving option such as continue on failure if (nonTransactionalResolvingOption == null) { //program should exit with non zero exit code var message = @$ "Previous migration of " "{failedVersion.Version}" " version was not running in transaction and has failed when executing of script " "{failedVersion.FailedScriptPath}" " with following error: {failedVersion.FailedScriptError} {MESSAGES.ManualResolvingAfterFailureMessage}"; _traceService.Error(message); throw new InvalidOperationException(message); } _traceService.Info($@"The non-transactional failure resolving option ""{nonTransactionalResolvingOption}"" was used. Version scripts already applied by previous migration run will be skipped."); nonTransactionalContext = new NonTransactionalContext(failedVersion, nonTransactionalResolvingOption.Value); } else { if (nonTransactionalResolvingOption != null) { //program should exit with non zero exit code _traceService.Error(@$ "The non-transactional failure resolving option " "{nonTransactionalResolvingOption}" " is available only if previous migration has failed."); throw new InvalidOperationException(); } } var appliedVersions = _configurationDataService.GetAllAppliedVersions(metaSchemaName, metaTableName) .Select(dv => dv.Version) .OrderBy(v => v) .ToList(); //checks if target database already runs the latest version and skips work if it already is var targeDatabaseLatest = IsTargetDatabaseLatest(targetVersion, metaSchemaName, metaTableName); if (!targeDatabaseLatest) { //create a shared open connection to entire migration run using (var connection = _dataService.CreateConnection()) { connection.Open(); RunAllInternal(connection, null); } } else { //runs all scripts files inside _draft on every yuniql run regardless of state of target database using (var connection = _dataService.CreateConnection()) { connection.Open(); RunDraftInternal(connection, null); } } //local method void RunAllInternal(IDbConnection connection, IDbTransaction transaction) { //check if database has been pre-configured and execute init scripts if (!targetDatabaseConfigured) { //runs all scripts in the _init folder RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_init"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_init")}"); } //checks if target database already runs the latest version and skips work if it already is //runs all scripts in the _pre folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_pre"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_pre")}"); //runs all scripts int the vxx.xx folders and subfolders RunVersionScripts(connection, transaction, appliedVersions, workingPath, targetVersion, nonTransactionalContext, tokenKeyPairs, bulkSeparator: bulkSeparator, metaSchemaName: metaSchemaName, metaTableName: metaTableName, commandTimeout: commandTimeout, bulkBatchSize: bulkBatchSize, appliedByTool: appliedByTool, appliedByToolVersion: appliedByToolVersion, environmentCode: environmentCode); //runs all scripts in the _draft folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_draft"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_draft")}"); //runs all scripts in the _post folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_post"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_post")}"); } //local method void RunDraftInternal(IDbConnection connection, IDbTransaction transaction) { //runs all scripts in the _pre folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_pre"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_pre")}"); //runs all scripts in the _draft folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_draft"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_draft")}"); //runs all scripts in the _post folder and subfolders RunNonVersionScripts(connection, transaction, Path.Combine(workingPath, "_post"), tokenKeyPairs, bulkSeparator: bulkSeparator, commandTimeout: commandTimeout, environmentCode: environmentCode); _traceService.Info($"Executed script files on {Path.Combine(workingPath, "_post")}"); } }