Welcome to mirror list, hosted at ThFree Co, Russian Federation.

time_partition.rb « partitioning « database « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 649687bdd1218a8a007dea159f2866a7878b8305 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 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