96 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module Gitlab
 | 
						|
  module Database
 | 
						|
    module Partitioning
 | 
						|
      class TimePartition
 | 
						|
        include Comparable
 | 
						|
 | 
						|
        def self.from_sql(table, partition_name, definition)
 | 
						|
          matches = definition.match(/FOR VALUES FROM \('?(?<from>.+)'?\) TO \('?(?<to>.+)'?\)/)
 | 
						|
 | 
						|
          raise ArgumentError, "Unknown partition definition: #{definition}" unless matches
 | 
						|
 | 
						|
          raise NotImplementedError, "Open-end time partitions with MAXVALUE are not supported yet" if matches[:to] == 'MAXVALUE'
 | 
						|
 | 
						|
          from = matches[:from] == 'MINVALUE' ? nil : matches[:from]
 | 
						|
          to = matches[:to]
 | 
						|
 | 
						|
          new(table, from, to, partition_name: partition_name)
 | 
						|
        end
 | 
						|
 | 
						|
        attr_reader :table, :from, :to
 | 
						|
 | 
						|
        def initialize(table, from, to, partition_name: nil)
 | 
						|
          @table = table.to_s
 | 
						|
          @from = date_or_nil(from)
 | 
						|
          @to = date_or_nil(to)
 | 
						|
          @partition_name = partition_name
 | 
						|
        end
 | 
						|
 | 
						|
        def partition_name
 | 
						|
          return @partition_name if @partition_name
 | 
						|
 | 
						|
          suffix = from&.strftime('%Y%m') || '000000'
 | 
						|
 | 
						|
          "#{table}_#{suffix}"
 | 
						|
        end
 | 
						|
 | 
						|
        def to_sql
 | 
						|
          from_sql = from ? conn.quote(from.strftime('%Y-%m-%d')) : 'MINVALUE'
 | 
						|
          to_sql = conn.quote(to.strftime('%Y-%m-%d'))
 | 
						|
 | 
						|
          <<~SQL
 | 
						|
            CREATE TABLE IF NOT EXISTS #{fully_qualified_partition}
 | 
						|
            PARTITION OF #{conn.quote_table_name(table)}
 | 
						|
            FOR VALUES FROM (#{from_sql}) TO (#{to_sql})
 | 
						|
          SQL
 | 
						|
        end
 | 
						|
 | 
						|
        def to_detach_sql
 | 
						|
          <<~SQL
 | 
						|
            ALTER TABLE #{conn.quote_table_name(table)}
 | 
						|
            DETACH PARTITION #{fully_qualified_partition}
 | 
						|
          SQL
 | 
						|
        end
 | 
						|
 | 
						|
        def ==(other)
 | 
						|
          table == other.table && partition_name == other.partition_name && from == other.from && to == other.to
 | 
						|
        end
 | 
						|
        alias_method :eql?, :==
 | 
						|
 | 
						|
        def hash
 | 
						|
          [table, partition_name, from, to].hash
 | 
						|
        end
 | 
						|
 | 
						|
        def <=>(other)
 | 
						|
          return if table != other.table
 | 
						|
 | 
						|
          partition_name <=> other.partition_name
 | 
						|
        end
 | 
						|
 | 
						|
        def holds_data?
 | 
						|
          conn.execute("SELECT 1 FROM #{fully_qualified_partition} LIMIT 1").ntuples > 0
 | 
						|
        end
 | 
						|
 | 
						|
        private
 | 
						|
 | 
						|
        def date_or_nil(obj)
 | 
						|
          return unless obj
 | 
						|
          return obj if obj.is_a?(Date)
 | 
						|
 | 
						|
          Date.parse(obj)
 | 
						|
        end
 | 
						|
 | 
						|
        def fully_qualified_partition
 | 
						|
          "%s.%s" % [conn.quote_table_name(Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA), conn.quote_table_name(partition_name)]
 | 
						|
        end
 | 
						|
 | 
						|
        def conn
 | 
						|
          @conn ||= Gitlab::Database::SharedModel.connection
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |