diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/mroonga/vendor/groonga/plugins/sharding/logical_enumerator.rb | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.tar.xz mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | storage/mroonga/vendor/groonga/plugins/sharding/logical_enumerator.rb | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/storage/mroonga/vendor/groonga/plugins/sharding/logical_enumerator.rb b/storage/mroonga/vendor/groonga/plugins/sharding/logical_enumerator.rb new file mode 100644 index 00000000..d05a220f --- /dev/null +++ b/storage/mroonga/vendor/groonga/plugins/sharding/logical_enumerator.rb @@ -0,0 +1,317 @@ +module Groonga + module Sharding + class LogicalEnumerator + include Enumerable + + attr_reader :target_range + attr_reader :logical_table + attr_reader :shard_key_name + def initialize(command_name, input, options={}) + @command_name = command_name + @input = input + @options = options + initialize_parameters + end + + def each(&block) + each_internal(:ascending, &block) + end + + def reverse_each(&block) + each_internal(:descending, &block) + end + + private + def each_internal(order) + context = Context.instance + each_shard_with_around(order) do |prev_shard, current_shard, next_shard| + shard_range_data = current_shard.range_data + shard_range = nil + + if shard_range_data.day.nil? + if order == :ascending + if next_shard + next_shard_range_data = next_shard.range_data + else + next_shard_range_data = nil + end + else + if prev_shard + next_shard_range_data = prev_shard.range_data + else + next_shard_range_data = nil + end + end + max_day = compute_month_shard_max_day(shard_range_data.year, + shard_range_data.month, + next_shard_range_data) + shard_range = MonthShardRange.new(shard_range_data.year, + shard_range_data.month, + max_day) + else + shard_range = DayShardRange.new(shard_range_data.year, + shard_range_data.month, + shard_range_data.day) + end + + yield(current_shard, shard_range) + end + end + + def each_shard_with_around(order) + context = Context.instance + prefix = "#{@logical_table}_" + + shards = [nil] + context.database.each_name(:prefix => prefix, + :order_by => :key, + :order => order) do |name| + shard_range_raw = name[prefix.size..-1] + + case shard_range_raw + when /\A(\d{4})(\d{2})\z/ + shard_range_data = ShardRangeData.new($1.to_i, $2.to_i, nil) + when /\A(\d{4})(\d{2})(\d{2})\z/ + shard_range_data = ShardRangeData.new($1.to_i, $2.to_i, $3.to_i) + else + next + end + + shards << Shard.new(name, @shard_key_name, shard_range_data) + next if shards.size < 3 + yield(*shards) + shards.shift + end + + if shards.size == 2 + yield(shards[0], shards[1], nil) + end + end + + private + def initialize_parameters + @logical_table = @input[:logical_table] + if @logical_table.nil? + raise InvalidArgument, "[#{@command_name}] logical_table is missing" + end + + @shard_key_name = @input[:shard_key] + if @shard_key_name.nil? + require_shard_key = @options[:require_shard_key] + require_shard_key = true if require_shard_key.nil? + if require_shard_key + raise InvalidArgument, "[#{@command_name}] shard_key is missing" + end + end + + @target_range = TargetRange.new(@command_name, @input) + end + + def compute_month_shard_max_day(year, month, next_shard_range) + return nil if next_shard_range.nil? + + return nil if month != next_shard_range.month + + next_shard_range.day + end + + class Shard + attr_reader :table_name, :key_name, :range_data + def initialize(table_name, key_name, range_data) + @table_name = table_name + @key_name = key_name + @range_data = range_data + end + + def table + @table ||= Context.instance[@table_name] + end + + def full_key_name + "#{@table_name}.#{@key_name}" + end + + def key + @key ||= Context.instance[full_key_name] + end + end + + class ShardRangeData + attr_reader :year, :month, :day + def initialize(year, month, day) + @year = year + @month = month + @day = day + end + + def to_suffix + if @day.nil? + "_%04d%02d" % [@year, @month] + else + "_%04d%02d%02d" % [@year, @month, @day] + end + end + end + + class DayShardRange + attr_reader :year, :month, :day + def initialize(year, month, day) + @year = year + @month = month + @day = day + end + + def least_over_time + next_day = Time.local(@year, @month, @day) + (60 * 60 * 24) + while next_day.day == @day # For leap second + next_day += 1 + end + next_day + end + + def min_time + Time.local(@year, @month, @day) + end + + def include?(time) + @year == time.year and + @month == time.month and + @day == time.day + end + end + + class MonthShardRange + attr_reader :year, :month, :max_day + def initialize(year, month, max_day) + @year = year + @month = month + @max_day = max_day + end + + def least_over_time + if @max_day.nil? + if @month == 12 + Time.local(@year + 1, 1, 1) + else + Time.local(@year, @month + 1, 1) + end + else + Time.local(@year, @month, @max_day) + end + end + + def min_time + Time.local(@year, @month, 1) + end + + def include?(time) + return false unless @year == time.year + return false unless @month == time.month + + if @max_day.nil? + true + else + time.day <= @max_day + end + end + end + + class TargetRange + attr_reader :min, :min_border + attr_reader :max, :max_border + def initialize(command_name, input) + @command_name = command_name + @input = input + @min = parse_value(:min) + @min_border = parse_border(:min_border) + @max = parse_value(:max) + @max_border = parse_border(:max_border) + end + + def cover_type(shard_range) + return :all if @min.nil? and @max.nil? + + if @min and @max + return :none unless in_min?(shard_range) + return :none unless in_max?(shard_range) + min_partial_p = in_min_partial?(shard_range) + max_partial_p = in_max_partial?(shard_range) + if min_partial_p and max_partial_p + :partial_min_and_max + elsif min_partial_p + :partial_min + elsif max_partial_p + :partial_max + else + :all + end + elsif @min + return :none unless in_min?(shard_range) + if in_min_partial?(shard_range) + :partial_min + else + :all + end + else + return :none unless in_max?(shard_range) + if in_max_partial?(shard_range) + :partial_max + else + :all + end + end + end + + private + def parse_value(name) + value = @input[name] + return nil if value.nil? + + Converter.convert(value, Time) + end + + def parse_border(name) + border = @input[name] + return :include if border.nil? + + case border + when "include" + :include + when "exclude" + :exclude + else + message = + "[#{@command_name}] #{name} must be \"include\" or \"exclude\": " + + "<#{border}>" + raise InvalidArgument, message + end + end + + def in_min?(shard_range) + @min < shard_range.least_over_time + end + + def in_min_partial?(shard_range) + return false unless shard_range.include?(@min) + + return true if @min_border == :exclude + + shard_range.min_time != @min + end + + def in_max?(shard_range) + max_base_time = shard_range.min_time + if @max_border == :include + @max >= max_base_time + else + @max > max_base_time + end + end + + def in_max_partial?(shard_range) + shard_range.include?(@max) + end + end + end + end +end |