Module: TemporalTables::TemporalAdapterSixOh

Defined in:
lib/temporal_tables/temporal_adapter_six_oh.rb

Overview

The main difference here is the add_index method, which still uses the old options={} syntax

Instance Method Summary collapse

Instance Method Details

#add_column(table_name, column_name, type, **options) ⇒ Object



88
89
90
91
92
93
94
95
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 88

def add_column(table_name, column_name, type, **options)
  super(table_name, column_name, type, **options)

  return unless table_exists?(temporal_name(table_name))

  super temporal_name(table_name), column_name, type, **options
  create_temporal_triggers table_name, original_primary_key(table_name)
end

#add_index(table_name, column_name, options = {}) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 133

def add_index(table_name, column_name, options = {})
  super(table_name, column_name, options)

  return unless table_exists?(temporal_name(table_name))

  column_names = Array.wrap(column_name)
  idx_name = temporal_index_name(options[:name] || index_name(table_name, column: column_names))
  super temporal_name(table_name), column_name, options.except(:unique).merge(name: idx_name)
end

#add_temporal_table(table_name, **options) ⇒ Object

rubocop:disable Metrics/MethodLength, Metrics/AbcSize



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
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 34

def add_temporal_table(table_name, **options) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  create_table(
    temporal_name(table_name),
    **options.merge(id: false, primary_key: 'history_id', temporal_bypass: true)
  ) do |t|
    t.datetime :eff_from, null: false, limit: 6
    t.datetime :eff_to,   null: false, limit: 6, default: TemporalTables::END_OF_TIME

    columns(table_name).each do |c|
      column_type = c.type == :enum ? c..sql_type : c.type
      t.column c.name, column_type, limit: c.limit
    end
  end

  if TemporalTables.add_updated_by_field && !column_exists?(table_name, :updated_by)
    change_table table_name do |t|
      t.column :updated_by, TemporalTables.updated_by_type
    end
  end

  original_primary_key = original_primary_key(table_name)
  temporal_table_index_name = index_name(temporal_name(table_name), [original_primary_key, :eff_to])
  if temporal_table_index_name.length > index_name_length
    temporal_table_index_name = truncated_index_name(temporal_table_index_name)
  end
  add_index temporal_name(table_name), [original_primary_key, :eff_to], { name: temporal_table_index_name }
  create_temporal_triggers table_name, original_primary_key
  create_temporal_indexes table_name
end

#change_column(table_name, column_name, type, **options) ⇒ Object



115
116
117
118
119
120
121
122
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 115

def change_column(table_name, column_name, type, **options)
  super(table_name, column_name, type, **options)

  return unless table_exists?(temporal_name(table_name))

  super temporal_name(table_name), column_name, type, **options
  # Don't need to update triggers here...
end

#create_table(table_name, **options, &block) ⇒ Object

rubocop:disable Metrics/ModuleLength



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 9

def create_table(table_name, **options, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
  if options[:temporal_bypass]
    super(table_name, **options, &block)
  else
    skip_table = TemporalTables.skipped_temporal_tables.include?(table_name.to_sym) || table_name.to_s =~ /_h$/

    super(table_name, **options) do |t|
      block.call t

      if TemporalTables.add_updated_by_field && !skip_table
        updated_by_already_exists = t.columns.any? { |c| c.name == 'updated_by' }
        if updated_by_already_exists
          puts "consider adding #{table_name} to TemporalTables skip_table" # rubocop:disable Rails/Output
        else
          t.column(:updated_by, TemporalTables.updated_by_type)
        end
      end
    end

    if options[:temporal] || (TemporalTables.create_by_default && !skip_table)
      add_temporal_table table_name, **options
    end
  end
end

#create_temporal_indexes(table_name) ⇒ Object

rubocop:disable Metrics/MethodLength



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 153

def create_temporal_indexes(table_name) # rubocop:disable Metrics/MethodLength
  indexes = ActiveRecord::Base.connection.indexes(table_name)

  indexes.each do |index|
    index_name = temporal_index_name(index.name)

    next if temporal_index_exists?(table_name, index_name)

    add_index(
      temporal_name(table_name),
      index.columns,
      # exclude unique constraints for temporal tables
      name: index_name,
      length: index.lengths,
      order: index.orders
    )
  end
end

#create_temporal_triggers(_table_name) ⇒ Object

Raises:

  • (NotImplementedError)


176
177
178
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 176

def create_temporal_triggers(_table_name)
  raise NotImplementedError, 'create_temporal_triggers is not implemented'
end

#drop_table(table_name, **options) ⇒ Object



71
72
73
74
75
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 71

def drop_table(table_name, **options)
  super(table_name, **options)

  super(temporal_name(table_name), **options) if table_exists?(temporal_name(table_name))
end

#drop_temporal_triggers(_table_name) ⇒ Object

Raises:

  • (NotImplementedError)


180
181
182
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 180

def drop_temporal_triggers(_table_name)
  raise NotImplementedError, 'drop_temporal_triggers is not implemented'
end

#original_primary_key(table_name) ⇒ Object



200
201
202
203
204
205
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 200

def original_primary_key(table_name)
  original_primary_key = primary_key(table_name)
  raise 'temporal_adapter requires that the table has a single primary key' unless original_primary_key.is_a? String

  original_primary_key
end

#remove_column(table_name, column_name, type = nil, **options) ⇒ Object



106
107
108
109
110
111
112
113
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 106

def remove_column(table_name, column_name, type = nil, **options)
  super(table_name, column_name, type, **options)

  return unless table_exists?(temporal_name(table_name))

  super temporal_name(table_name), column_name, type, **options
  create_temporal_triggers table_name, original_primary_key(table_name)
end

#remove_columns(table_name, *column_names, **options) ⇒ Object



97
98
99
100
101
102
103
104
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 97

def remove_columns(table_name, *column_names, **options)
  super(table_name, *column_names, **options)

  return unless table_exists?(temporal_name(table_name))

  super temporal_name(table_name), *column_names, **options
  create_temporal_triggers table_name, original_primary_key(table_name)
end

#remove_index(table_name, options = {}) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 143

def remove_index(table_name, options = {})
  original_index_name = index_name_for_remove(table_name, options)
  super(table_name, options)

  return unless table_exists?(temporal_name(table_name))

  idx_name = temporal_index_name(original_index_name)
  super temporal_name(table_name), name: idx_name
end

#remove_temporal_table(table_name) ⇒ Object



64
65
66
67
68
69
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 64

def remove_temporal_table(table_name)
  return unless table_exists?(temporal_name(table_name))

  drop_temporal_triggers table_name
  drop_table_without_temporal temporal_name(table_name)
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 124

def rename_column(table_name, column_name, new_column_name)
  super(table_name, column_name, new_column_name)

  return unless table_exists?(temporal_name(table_name))

  super temporal_name(table_name), column_name, new_column_name
  create_temporal_triggers table_name, original_primary_key(table_name)
end

#rename_table(name, new_name) ⇒ Object



77
78
79
80
81
82
83
84
85
86
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 77

def rename_table(name, new_name)
  drop_temporal_triggers name if table_exists?(temporal_name(name))

  super name, new_name

  return unless table_exists?(temporal_name(name))

  super(temporal_name(name), temporal_name(new_name))
  create_temporal_triggers new_name, original_primary_key(table_name)
end

#temporal_index_exists?(table_name, index_name) ⇒ Boolean

Returns:

  • (Boolean)


196
197
198
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 196

def temporal_index_exists?(table_name, index_name)
  index_name_exists?(temporal_name(table_name), index_name)
end

#temporal_index_name(index_name) ⇒ Object

Index names max out at 63 characters. If appending _h to the index name would surpass that limit, we can trim the index name and append a deterministically generated 5 character hash as well as _h.



186
187
188
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 186

def temporal_index_name(index_name)
  "#{index_name.length < 62 ? index_name : truncated_index_name(index_name, 2)}_h"
end

#temporal_name(table_name) ⇒ Object



172
173
174
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 172

def temporal_name(table_name)
  "#{table_name}_h"
end

#truncated_index_name(index_name, required_padding = 0) ⇒ Object



190
191
192
193
194
# File 'lib/temporal_tables/temporal_adapter_six_oh.rb', line 190

def truncated_index_name(index_name, required_padding = 0)
  max_length = index_name_length - required_padding
  index_name_hash = Digest::SHA1.base64digest(index_name.to_s)[0, 5]
  "#{index_name[0, max_length - 6]}_#{index_name_hash}"
end