sql_cmd 0.3.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 266298d96407b2fb372453d3989ef5fb21047f1a364c857c3c559e1826257d5a
4
- data.tar.gz: b3053a685e3e0202e1b68bddb20cec62bce549f6d7a8f7164ead4709f355dd9a
3
+ metadata.gz: 84ae28535256cbad4ab450ea06fe4807b593b938548edc13f3edad224174f819
4
+ data.tar.gz: d6dfe44866a53bbc52d4bc7ebd1caf80d521a8b8ce70a56d7af9dc5bae3bc5cd
5
5
  SHA512:
6
- metadata.gz: 99aab9a7af85a77a7da945aaff55823364b497ef4d91d2551994362d87120c379784b8ff753d4339ac44ca91d14ba1c02f17f07942c03646f956c5c9da609190
7
- data.tar.gz: 5c6b0287eb928e2356e99b329eee771f5add55af0b13d0ec29e131fee1ab1590c7c726dcfd280d25b96e753b5febc8626841c844e4ae1f122468b8270d19e923
6
+ metadata.gz: d89c55abf7b3cf3abc9bc03d4ee15077326054b959af3a22f68e3b0278cd3af23a12dd027521cfc50d036cc888b5d0b515c62089424079bef7c535a9a55bdd82
7
+ data.tar.gz: dc56ef0c9e56263982497ef965a9108ab0710a10992dcad49a9f67e81bd4f451eeaa41f64e182e0a1ff2d628ef061844e66e0c745842c99225d699ff25193b52
@@ -62,8 +62,10 @@ module SqlCmd
62
62
  EasyIO.logger.header 'AlwaysOn Permissions Migration to Replica'
63
63
  EasyIO.logger.debug 'Migrating logins to replica...'
64
64
  import_script_filename = SqlCmd.export_logins(start_time, primary_connection_string, database_name)
65
- EasyIO.logger.info "Importing logins on [#{SqlCmd.connection_string_part(replica_connection_string, :server)}]..."
66
- SqlCmd.execute_script_file(replica_connection_string, import_script_filename)
65
+ unless import_script_filename.nil?
66
+ EasyIO.logger.info "Importing logins on [#{SqlCmd.connection_string_part(replica_connection_string, :server)}]..."
67
+ SqlCmd.execute_script_file(replica_connection_string, import_script_filename)
68
+ end
67
69
  EasyIO.logger.debug 'Running database_status script...'
68
70
  database_info = SqlCmd::Database.info(connection_string, database_name)
69
71
  replica_database_info = SqlCmd::Database.info(replica_connection_string, database_name)
@@ -263,5 +265,10 @@ module SqlCmd
263
265
  raise 'Failed to remove database from AlwaysOn Availability Group' unless database_info['AvailabilityGroup'].nil? && replica_database_info['AvailabilityGroup'].nil?
264
266
  EasyIO.logger.info "[#{database_name}] removed from AlwaysOn availability group successfully..."
265
267
  end
268
+
269
+ def enabled?(connection_string)
270
+ server_info = SqlCmd.get_sql_server_settings(connection_string)
271
+ !server_info['AvailabilityGroup'].nil? && !server_info['SecondaryReplica'].nil?
272
+ end
266
273
  end
267
274
  end
@@ -54,22 +54,27 @@ module SqlCmd
54
54
  files.map { |f, properties| ["#{base_url}/#{f}", properties[:last_modified]] }.to_h
55
55
  end
56
56
 
57
- def backup_location_and_basename(start_time, connection_string, database_name, options = {}, backup_url: nil)
57
+ def backup_location_and_basename(start_time, connection_string, database_name, options = {}, backup_folder: nil, backup_url: nil, backup_basename: nil)
58
58
  start_time = SqlCmd.unify_start_time(start_time)
59
59
  database_info = SqlCmd::Database.info(connection_string, database_name)
60
60
  server_settings = get_sql_server_settings(connection_string)
61
61
  backup_type = backup_url.nil? || backup_url.empty? ? 'DISK' : 'URL'
62
62
  if database_info['DatabaseNotFound']
63
- backup_name = "#{database_name}_#{EasyTime.yyyymmdd(start_time)}"
64
- return [backup_url, backup_name] if backup_type == 'URL'
63
+ backup_basename = "#{database_name}_#{EasyTime.yyyymmdd(start_time)}"
64
+ return [backup_url, backup_basename] if backup_type == 'URL'
65
65
  backup_unc_location = SqlCmd.config['sql_cmd']['backups']['backup_to_host_sql_server'] ? "\\\\#{server_settings['ServerName']}\\#{SqlCmd.config['sql_cmd']['backups']['default_backup_share']}" : SqlCmd.config['sql_cmd']['backups']['default_destination']
66
- return Dir.glob("#{backup_unc_location}/#{backup_name}*".tr('\\', '/')).empty? ? [nil, nil] : [backup_unc_location, backup_name]
66
+ return Dir.glob("#{backup_unc_location}/#{backup_basename}*".tr('\\', '/')).empty? ? [nil, nil] : [backup_unc_location, backup_basename]
67
67
  end
68
68
 
69
69
  backup_file_path = database_info['BackupFileLocation']
70
+ if backup_file_path =~ /^\{.*\}$/
71
+ backup_basename ||= "#{database_name}_#{EasyTime.yyyymmdd(start_time)}"
72
+ backup_files, backup_basename = SqlCmd::Database.existing_backup_files(server_settings, options, backup_folder: backup_folder, backup_url: backup_url, backup_basename: backup_basename, log_only: options['logonly'])
73
+ backup_file_path = backup_files.first
74
+ end
70
75
  backup_file = ::File.basename(backup_file_path)
71
- backup_name = backup_basename(backup_file)
72
- return [nil, backup_name] if backup_type == 'URL'
76
+ backup_basename = backup_basename_from_path(backup_file)
77
+ return [nil, backup_basename] if backup_type == 'URL'
73
78
  backup_unc_location = to_unc_path(::File.dirname(backup_file_path), server_settings['ServerName'])
74
79
  backup_folder = if ::File.exist?("#{backup_unc_location}/#{backup_file}")
75
80
  backup_unc_location
@@ -77,7 +82,7 @@ module SqlCmd
77
82
  "\\\\#{server_settings['ServerName']}\\#{SqlCmd.config['sql_cmd']['backups']['default_backup_share']}"
78
83
  end
79
84
  return [nil, nil] unless defined?(backup_folder)
80
- [backup_folder, backup_name]
85
+ [backup_folder, backup_basename]
81
86
  end
82
87
 
83
88
  # sql_server_settings can be for any server that has network access to these files
@@ -313,10 +313,10 @@ module SqlCmd
313
313
  SqlCmd.run_sql_as_job(connection_string, sql_backup_script, "Backup: #{backup_basename}", values: values, retries: 1, retry_delay: 30)
314
314
  end
315
315
 
316
- def monitor_backup(backup_start_time, connection_string, database_name, options = {}, backup_folder: nil, backup_url: nil, backup_basename: nil, log_only: false, retries: 3, retry_delay: 10)
316
+ def monitor_backup(backup_start_time, connection_string, database_name, options = {}, job_name: nil, backup_folder: nil, backup_url: nil, backup_basename: nil, log_only: false, retries: 3, retry_delay: 10)
317
317
  backup_start_time = SqlCmd.unify_start_time(backup_start_time)
318
318
  backup_status_script = ::File.read("#{SqlCmd.sql_script_dir}/Status/BackupProgress.sql")
319
- job_name = "Backup: #{database_name}"
319
+ job_name ||= "Backup: #{database_name}"
320
320
  EasyIO.logger.info 'Checking backup status...'
321
321
  EasyIO.logger.debug "Backup start time: #{backup_start_time}"
322
322
  sleep(3) # Give the job time to start
@@ -340,9 +340,9 @@ module SqlCmd
340
340
  # job_message = job_history_message(connection_string, job_name) unless result == :current
341
341
  if job_started # Check if job has timed out after stopping without completing
342
342
  job_completion_time ||= Time.now
343
- _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, backup_basename, job_status: job_status, job_started: job_started) if Time.now > job_completion_time + timeout
343
+ _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, job_name, job_status: job_status, job_started: job_started) if Time.now > job_completion_time + timeout
344
344
  elsif Time.now > monitoring_start_time + timeout # Job never started and timed out
345
- _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, backup_basename, job_status: job_status, job_started: job_started)
345
+ _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, job_name, job_status: job_status, job_started: job_started)
346
346
  end
347
347
  sleep(timer_interval)
348
348
  next
@@ -380,18 +380,18 @@ module SqlCmd
380
380
  result
381
381
  end
382
382
 
383
- def _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, backup_basename, job_status: nil, job_started: false)
383
+ def _raise_backup_failure(connection_string, database_name, last_backup_date_key, backup_start_time, job_name, job_status: nil, job_started: false)
384
384
  server_name = SqlCmd.connection_string_part(connection_string, :server)
385
- if job_started
386
- failure_message = "Backup may have failed as the backup has stopped and the last backup time shows #{SqlCmd::Database.info(connection_string, database_name)[last_backup_date_key]} " \
387
- "but the backup should be newer than #{backup_start_time}! "
388
- failure_message += job_status == 'NoJob' ? 'The job exited with success and so does not exist!' : "Check sql job 'Backup: #{backup_basename}' history on [#{server_name}] for details. \n"
389
- raise failure_message + "Last backup time retrieved from: #{server_name}\\#{database_name}"
390
- end
391
- failure_message = "Backup appears to have failed! The last backup time shows #{SqlCmd::Database.info(connection_string, database_name)[last_backup_date_key]} " \
392
- "but the backup should be newer than #{backup_start_time}! "
393
- failure_message += job_status == 'NoJob' ? 'The backup job could not be found!' : "The job did not start in time. Check sql job 'Backup: #{backup_basename}' history on [#{server_name}] for details."
394
- raise failure_message + "Last backup time retrieved from: #{server_name}\\#{database_name}"
385
+ failure_message = if job_started
386
+ "Backup may have failed as the backup has stopped and the last backup time shows #{SqlCmd::Database.info(connection_string, database_name)[last_backup_date_key]} " \
387
+ "but the backup should be newer than #{backup_start_time}! " +
388
+ (job_status == 'NoJob' ? 'The job exited with success and so does not exist!' : "Check sql job '#{job_name}' history on [#{server_name}] for details. \n")
389
+ else
390
+ "Backup appears to have failed! The last backup time shows #{SqlCmd::Database.info(connection_string, database_name)[last_backup_date_key]} " \
391
+ "but the backup should be newer than #{backup_start_time}! " +
392
+ (job_status == 'NoJob' ? 'The backup job could not be found!' : "The job did not start in time. Check sql job '#{job_name}' history on [#{server_name}] for details.")
393
+ end
394
+ raise failure_message + " Last backup time retrieved from: #{server_name}\\#{database_name}"
395
395
  end
396
396
 
397
397
  # Creates and starts a SQL job to restore a database.
@@ -477,7 +477,7 @@ module SqlCmd
477
477
  end
478
478
  failure_message = 'Restore appears to have failed! '
479
479
  failure_message += job_status == 'NoJob' ? 'The job could not be found and the restored database is not up to date!' : "The job did not start in time. Check sql job 'Restore: #{database_name}' history on [#{server_name}] for details."
480
- raise failure_message + "Restore destination: #{server_name}\\#{database_name}"
480
+ raise failure_message + "\nRestore destination: #{server_name}\\#{database_name}"
481
481
  end
482
482
 
483
483
  def check_restore_date(start_time, connection_string, database_name, messages = :none, log_only: false)
@@ -500,8 +500,8 @@ module SqlCmd
500
500
  database_info = info(connection_string, database_name) if database_info.nil?
501
501
  server_name = SqlCmd.connection_string_part(connection_string, :server)
502
502
  EasyIO.logger.info "Ensuring full backup has taken place for [#{server_name}].[#{database_name}]..."
503
- if force_backup || database_info['LastNonCopyOnlyFullBackupDate'].nil? || # Ensure last full backup occurred AFTER the DB was last restored
504
- (!database_info['LastRestoreDate'].nil? && database_info['LastNonCopyOnlyFullBackupDate'] < database_info['LastRestoreDate'])
503
+ start_time = database_info['LastRestoreDate'] || database_info['create_date']
504
+ if force_backup || database_info['LastNonCopyOnlyFullBackupDate'].nil? || (database_info['LastNonCopyOnlyFullBackupDate'] < start_time) # Ensure last full backup occurred AFTER the DB was last restored/created
505
505
  EasyIO.logger.info 'Running full backup...'
506
506
  backup_basename = "full_backup-#{database_name}_#{EasyTime.yyyymmdd}" # If a full_backup_method was not provided, use this name for the database backup for clarity
507
507
  full_backup_method.nil? ? SqlCmd::Database.backup(Time.now, connection_string, database_name, backup_basename: backup_basename, options: { 'copyonly' => false }) : full_backup_method.call
@@ -511,7 +511,7 @@ module SqlCmd
511
511
  def duplicate(start_time, source_connection_string, source_database_name, destination_connection_string, destination_database_name, backup_folder: nil, backup_url: nil, backup_basename: nil, force_restore: false, full_backup_method: nil, options: {})
512
512
  start_time = SqlCmd.unify_start_time(start_time)
513
513
  backup(start_time, source_connection_string, source_database_name, backup_folder: backup_folder, backup_url: backup_url, backup_basename: backup_basename, options: options) unless info(source_connection_string, source_database_name)['DatabaseNotFound']
514
- backup_folder, backup_basename = SqlCmd.backup_location_and_basename(start_time, source_connection_string, source_database_name, options, backup_url: backup_url) # TODO: rework for URL
514
+ backup_folder, backup_basename = SqlCmd.backup_location_and_basename(start_time, source_connection_string, source_database_name, options, backup_folder: backup_folder, backup_url: backup_url, backup_basename: backup_basename) # TODO: rework for URL
515
515
  if (backup_folder.nil? && backup_url.nil?) || backup_basename.nil?
516
516
  source_server = SqlCmd.connection_string_part(source_connection_string, :server)
517
517
  destination_server = SqlCmd.connection_string_part(destination_connection_string, :server)
@@ -535,7 +535,11 @@ module SqlCmd
535
535
  end
536
536
 
537
537
  def apply_recovery_model(connection_string, database_name, options)
538
- return if recovery_model_set?(connection_string, database_name, options)
538
+ if recovery_model_set?(connection_string, database_name, options)
539
+ EasyIO.logger.info "Recovery model already set to '#{options['recovery_model']}'. No change needed."
540
+ return
541
+ end
542
+ EasyIO.logger.info "Setting recovery model to '#{options['recovery_model']}'..."
539
543
  options['recovery_model'] ||= 'FULL'
540
544
  options['rollback'] ||= 'ROLLBACK IMMEDIATE' # other options: ROLLBACK AFTER 30, NO_WAIT
541
545
  sql_script = "ALTER DATABASE [#{database_name}] SET RECOVERY #{options['recovery_model']} WITH #{options['rollback']}"
@@ -547,6 +551,7 @@ module SqlCmd
547
551
  #{'=' * 120}\n"
548
552
  EOS
549
553
  raise failure_message unless recovery_model_set?(connection_string, database_name, options)
554
+ EasyIO.logger.info "Recovery model updated to '#{options['recovery_model']}'."
550
555
  end
551
556
 
552
557
  def recovery_model_set?(connection_string, database_name, options)
@@ -85,7 +85,7 @@ module SqlCmd
85
85
  end
86
86
 
87
87
  # get the basename of the backup based on a full file_path such as the SQL value from [backupmediafamily].[physical_device_name]
88
- def backup_basename(backup_path)
88
+ def backup_basename_from_path(backup_path)
89
89
  return nil if backup_path.nil? || backup_path.empty?
90
90
  ::File.basename(backup_path).gsub(/(\.part\d+)?\.(bak|trn)/i, '')
91
91
  end
data/lib/sql_cmd/query.rb CHANGED
@@ -83,7 +83,18 @@ module SqlCmd
83
83
  raise
84
84
  end
85
85
  ensure
86
- ::File.delete "#{@scripts_cache_windows}\\sql_helper_#{start_time}.ps1" if defined?(start_time) && ::File.exist?("#{@scripts_cache_windows}\\sql_helper_#{start_time}.ps1")
86
+ retry_count = 0
87
+ retries = 3
88
+ retry_delay = 3
89
+ begin
90
+ ::File.delete "#{@scripts_cache_windows}\\sql_helper_#{start_time}.ps1" if defined?(start_time) && ::File.exist?("#{@scripts_cache_windows}\\sql_helper_#{start_time}.ps1")
91
+ rescue # Try to delete the file 3 times
92
+ if (retry_count += 1) <= retries
93
+ sleep(retry_delay)
94
+ retry
95
+ end
96
+ raise
97
+ end
87
98
  end
88
99
 
89
100
  def convert_powershell_tables_to_hash(json_string, return_type = :all_tables, at_timezone: 'UTC', string_to_time_by: '%Y-%m-%d %H:%M:%S %z') # options: :all_tables, :first_table, :first_row
@@ -234,6 +245,7 @@ module SqlCmd
234
245
  def migrate_logins(start_time, source_connection_string, destination_connection_string, database_name)
235
246
  start_time = SqlCmd.unify_start_time(start_time)
236
247
  import_script_filename = export_logins(start_time, source_connection_string, database_name)
248
+ return if import_script_filename.nil?
237
249
  if ::File.exist?(import_script_filename)
238
250
  EasyIO.logger.info "Importing logins on [#{connection_string_part(destination_connection_string, :server)}]..."
239
251
  execute_script_file(destination_connection_string, import_script_filename)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sql_cmd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Munoz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-15 00:00:00.000000000 Z
11
+ date: 2022-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: easy_json_config