#!/usr/bin/env ruby -w # frozen_string_literal: true # Driver script executes this script like this: # ruby /usr/src/m2/mysql/partitions/generate_partitions_list.rb "$DAYS" "$TABLE" "$START_DATE" "$SHELL_PID" > "$partitions_sql" # Takes existing partitions from existing_partitions_dates file and # generates SQLs to create partitions for next DAYS in the future, skipping existing ones # SQLs go to STDOUT # Use only stdlib, no gems, no bs require 'date' class Date # d20200101 def partition_format format('d%04d%02d%02d', year, month, day) end # 20200101 def simple_format format('%04d%02d%02d', year, month, day) end end def warn_and_exit(message) warn "#{$PROGRAM_NAME}:", message exit 1 end # All errors, warnings and exceptions go to SDTDER # SQL list goes to STDOUT, which driver script will redirect to file # In case of detected error, script will return 1 and driver script will exit accordingly # In casae of uncaught exception, script will return non-zero exit value and driver script will exit accordingly ############################## Argument Handling ############################## DAYS = ARGV[0] TABLE = ARGV[1] START_DATE = ARGV[2] SHELL_PID = ARGV[3] unless DAYS =~ /^\d+$/ warn_and_exit 'Script first argument should be a number (days to generate partitions in the future)' end unless TABLE == 'calls' || TABLE == 'calls_old' warn_and_exit 'Script 2nd argument should be either calls or calls_old' end today = Date.jd(DateTime.now.jd) start_date = today # start_day is either today or START_DATE unless START_DATE.to_s == 'today' begin start_date = Date.parse(START_DATE) if (today - start_date).to_i > 200 warn_and_exit 'START_DATE is more than 150 days in the past. Please check your START_DATE parameter' end rescue StandardError warn_and_exit 'Error parsing START_DATE. Exiting.' end end ############################## Main Script ############################## existing_partitions_dates = "/tmp/partition_managment/partitions_dates#{SHELL_PID}.txt" warn_and_exit "Partition file #{existing_partitions_dates} does not exist. Exiting" unless File.file?(existing_partitions_dates) # Read dates from file into hash # Validate lines in process, exit if bad value detected dates_hash = {} File.open(existing_partitions_dates) do |fp| fp.each do |line| partition_date = line.chomp unless partition_date =~ /\d{4}\d{2}\d{2}$/ warn_and_exit "Bad partition date detected #{partition_date}" end dates_hash[partition_date] = true end end warn_and_exit "Cannot find at least one partition with correct date in #{existing_partitions_dates}. Exiting" if dates_hash.empty? begin max_date = Date.parse(dates_hash.keys.max) rescue StandardError warn_and_exit 'Error parsing max date from dates_hash. Exiting.' end if !max_date.nil? && start_date < max_date && !dates_hash.key?(start_date.simple_format) warn_and_exit "Cannot create partition for #{start_date}, because latest partition is already at #{max_date}" end # Iterate over dates from start_date to start_date + DAYS in the future # and create partitions SQLs for missing partitions # Abort if holes detected in date space end_date = start_date + DAYS.to_i - 1 start_date.upto(end_date) do |day| next if dates_hash.key?(day.simple_format) if day < max_date warn_and_exit "Cannot create partition for #{day}, because latest partition is already at #{max_date}" end day_after = day + 1 puts "ALTER TABLE #{TABLE} REORGANIZE PARTITION future INTO (PARTITION #{day.partition_format} VALUES LESS THAN ('#{day_after}'), PARTITION future VALUES LESS THAN MAXVALUE);" end