temporal_tables 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/gemfiles/Gemfile.6.0.mysql.lock +1 -1
- data/gemfiles/Gemfile.6.0.pg.lock +1 -1
- data/gemfiles/Gemfile.6.1.mysql.lock +1 -1
- data/gemfiles/Gemfile.6.1.pg.lock +1 -1
- data/gemfiles/Gemfile.7.0.mysql.lock +1 -1
- data/gemfiles/Gemfile.7.0.pg.lock +1 -1
- data/lib/temporal_tables/connection_adapters/mysql_adapter.rb +4 -1
- data/lib/temporal_tables/temporal_adapter.rb +34 -27
- data/lib/temporal_tables/temporal_adapter_six_oh.rb +187 -0
- data/lib/temporal_tables/version.rb +1 -1
- data/lib/temporal_tables.rb +6 -1
- data/spec/internal/db/schema.rb +6 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7482588a765da3aeea2a1a5d7f32fdc000ee7f4c46b1affc2d9084822dd92313
|
4
|
+
data.tar.gz: 18eab05d32391261c5bf39bc4ddac2b91a177550d30100da2e270530f7bee8f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25a32d75519abc56c1dcd1debb1313ffefe964d1f514b82a8ad3919af84d5072418c87d0e580668f48617e41e2ed4bb33b28edd8ae2537315a0428b85a694cf2
|
7
|
+
data.tar.gz: 88b6b7f2db88810911f52c37faf86412ef9f315a6d664c2785a89c486cd4a38564a8d3b0849137ea796f5fc823788b2dc5b4c5b20629593b06d380452df5ab46
|
@@ -9,9 +9,10 @@ module TemporalTables
|
|
9
9
|
execute "drop trigger #{table_name}_ad"
|
10
10
|
end
|
11
11
|
|
12
|
-
def create_temporal_triggers(table_name) # rubocop:disable Metrics/MethodLength
|
12
|
+
def create_temporal_triggers(table_name) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
13
13
|
column_names = columns(table_name).map(&:name)
|
14
14
|
|
15
|
+
execute "drop trigger if exists #{table_name}_ai"
|
15
16
|
execute %{
|
16
17
|
create trigger #{table_name}_ai after insert on #{table_name}
|
17
18
|
for each row
|
@@ -24,6 +25,7 @@ module TemporalTables
|
|
24
25
|
end
|
25
26
|
}
|
26
27
|
|
28
|
+
execute "drop trigger if exists #{table_name}_au"
|
27
29
|
execute %{
|
28
30
|
create trigger #{table_name}_au after update on #{table_name}
|
29
31
|
for each row
|
@@ -40,6 +42,7 @@ module TemporalTables
|
|
40
42
|
end
|
41
43
|
}
|
42
44
|
|
45
|
+
execute "drop trigger if exists #{table_name}_ad"
|
43
46
|
execute %{
|
44
47
|
create trigger #{table_name}_ad after delete on #{table_name}
|
45
48
|
for each row
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module TemporalTables
|
4
4
|
module TemporalAdapter # rubocop:disable Metrics/ModuleLength
|
5
|
-
def create_table(table_name, options
|
5
|
+
def create_table(table_name, **options, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
6
6
|
if options[:temporal_bypass]
|
7
7
|
super(table_name, **options, &block)
|
8
8
|
else
|
@@ -22,15 +22,15 @@ module TemporalTables
|
|
22
22
|
end
|
23
23
|
|
24
24
|
if options[:temporal] || (TemporalTables.create_by_default && !skip_table)
|
25
|
-
add_temporal_table table_name, options
|
25
|
+
add_temporal_table table_name, **options
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def add_temporal_table(table_name, options
|
30
|
+
def add_temporal_table(table_name, **options) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
31
31
|
create_table(
|
32
32
|
temporal_name(table_name),
|
33
|
-
options.merge(id: false, primary_key: 'history_id', temporal_bypass: true)
|
33
|
+
**options.merge(id: false, primary_key: 'history_id', temporal_bypass: true)
|
34
34
|
) do |t|
|
35
35
|
t.column :id, options.fetch(:id, :integer) if options[:id] != false
|
36
36
|
t.datetime :eff_from, null: false, limit: 6
|
@@ -61,7 +61,7 @@ module TemporalTables
|
|
61
61
|
drop_table_without_temporal temporal_name(table_name)
|
62
62
|
end
|
63
63
|
|
64
|
-
def drop_table(table_name, options
|
64
|
+
def drop_table(table_name, **options)
|
65
65
|
super(table_name, **options)
|
66
66
|
|
67
67
|
super(temporal_name(table_name), **options) if table_exists?(temporal_name(table_name))
|
@@ -78,30 +78,39 @@ module TemporalTables
|
|
78
78
|
create_temporal_triggers new_name
|
79
79
|
end
|
80
80
|
|
81
|
-
def add_column(table_name, column_name, type, options
|
81
|
+
def add_column(table_name, column_name, type, **options)
|
82
82
|
super(table_name, column_name, type, **options)
|
83
83
|
|
84
84
|
return unless table_exists?(temporal_name(table_name))
|
85
85
|
|
86
|
-
super temporal_name(table_name), column_name, type, options
|
86
|
+
super temporal_name(table_name), column_name, type, **options
|
87
87
|
create_temporal_triggers table_name
|
88
88
|
end
|
89
89
|
|
90
|
-
def
|
91
|
-
super(table_name, *column_names)
|
90
|
+
def remove_columns(table_name, *column_names, **options)
|
91
|
+
super(table_name, *column_names, **options)
|
92
92
|
|
93
93
|
return unless table_exists?(temporal_name(table_name))
|
94
94
|
|
95
|
-
super temporal_name(table_name), *column_names
|
95
|
+
super temporal_name(table_name), *column_names, **options
|
96
96
|
create_temporal_triggers table_name
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
100
|
-
super(table_name, column_name, type, options)
|
99
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
100
|
+
super(table_name, column_name, type, **options)
|
101
101
|
|
102
102
|
return unless table_exists?(temporal_name(table_name))
|
103
103
|
|
104
|
-
super temporal_name(table_name), column_name, type, options
|
104
|
+
super temporal_name(table_name), column_name, type, **options
|
105
|
+
create_temporal_triggers table_name
|
106
|
+
end
|
107
|
+
|
108
|
+
def change_column(table_name, column_name, type, **options)
|
109
|
+
super(table_name, column_name, type, **options)
|
110
|
+
|
111
|
+
return unless table_exists?(temporal_name(table_name))
|
112
|
+
|
113
|
+
super temporal_name(table_name), column_name, type, **options
|
105
114
|
# Don't need to update triggers here...
|
106
115
|
end
|
107
116
|
|
@@ -114,23 +123,22 @@ module TemporalTables
|
|
114
123
|
create_temporal_triggers table_name
|
115
124
|
end
|
116
125
|
|
117
|
-
def add_index(table_name, column_name, options
|
126
|
+
def add_index(table_name, column_name, **options)
|
118
127
|
super(table_name, column_name, **options)
|
119
128
|
|
120
129
|
return unless table_exists?(temporal_name(table_name))
|
121
130
|
|
122
|
-
|
123
|
-
|
124
|
-
super temporal_name(table_name), column_name, options.except(:unique).merge(name: idx_name)
|
131
|
+
idx_name = temporal_index_name(options[:name] || index_name(table_name, column: column_name))
|
132
|
+
super temporal_name(table_name), column_name, **options.except(:unique).merge(name: idx_name)
|
125
133
|
end
|
126
134
|
|
127
|
-
def remove_index(table_name,
|
128
|
-
super(table_name, options)
|
135
|
+
def remove_index(table_name, column_name = nil, **options)
|
136
|
+
super(table_name, column_name, **options)
|
129
137
|
|
130
138
|
return unless table_exists?(temporal_name(table_name))
|
131
139
|
|
132
|
-
idx_name = temporal_index_name(
|
133
|
-
super temporal_name(table_name), name: idx_name
|
140
|
+
idx_name = temporal_index_name(options[:name] || index_name_for_remove(table_name, column_name, options))
|
141
|
+
super temporal_name(table_name), column_name, name: idx_name
|
134
142
|
end
|
135
143
|
|
136
144
|
def create_temporal_indexes(table_name) # rubocop:disable Metrics/MethodLength
|
@@ -143,12 +151,11 @@ module TemporalTables
|
|
143
151
|
|
144
152
|
add_index(
|
145
153
|
temporal_name(table_name),
|
146
|
-
index.columns,
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
}
|
154
|
+
index.columns,
|
155
|
+
# exclude unique constraints for temporal tables
|
156
|
+
name: index_name,
|
157
|
+
length: index.lengths,
|
158
|
+
order: index.orders
|
152
159
|
)
|
153
160
|
end
|
154
161
|
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TemporalTables
|
4
|
+
# The main difference here is the add_index method, which still uses
|
5
|
+
# the old options={} syntax
|
6
|
+
module TemporalAdapterSixOh # rubocop:disable Metrics/ModuleLength
|
7
|
+
def create_table(table_name, **options, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
8
|
+
if options[:temporal_bypass]
|
9
|
+
super(table_name, **options, &block)
|
10
|
+
else
|
11
|
+
skip_table = TemporalTables.skipped_temporal_tables.include?(table_name.to_sym) || table_name.to_s =~ /_h$/
|
12
|
+
|
13
|
+
super(table_name, **options) do |t|
|
14
|
+
block.call t
|
15
|
+
|
16
|
+
if TemporalTables.add_updated_by_field && !skip_table
|
17
|
+
updated_by_already_exists = t.columns.any? { |c| c.name == 'updated_by' }
|
18
|
+
if updated_by_already_exists
|
19
|
+
puts "consider adding #{table_name} to TemporalTables skip_table" # rubocop:disable Rails/Output
|
20
|
+
else
|
21
|
+
t.column(:updated_by, TemporalTables.updated_by_type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:temporal] || (TemporalTables.create_by_default && !skip_table)
|
27
|
+
add_temporal_table table_name, **options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_temporal_table(table_name, **options) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
33
|
+
create_table(
|
34
|
+
temporal_name(table_name),
|
35
|
+
**options.merge(id: false, primary_key: 'history_id', temporal_bypass: true)
|
36
|
+
) do |t|
|
37
|
+
t.column :id, options.fetch(:id, :integer) if options[:id] != false
|
38
|
+
t.datetime :eff_from, null: false, limit: 6
|
39
|
+
t.datetime :eff_to, null: false, limit: 6, default: '9999-12-31'
|
40
|
+
|
41
|
+
columns(table_name).each do |c|
|
42
|
+
next if c.name == 'id'
|
43
|
+
|
44
|
+
t.send c.type, c.name, limit: c.limit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if TemporalTables.add_updated_by_field && !column_exists?(table_name, :updated_by)
|
49
|
+
change_table table_name do |t|
|
50
|
+
t.column :updated_by, TemporalTables.updated_by_type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
add_index temporal_name(table_name), [:id, :eff_to]
|
55
|
+
create_temporal_triggers table_name
|
56
|
+
create_temporal_indexes table_name
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_temporal_table(table_name)
|
60
|
+
return unless table_exists?(temporal_name(table_name))
|
61
|
+
|
62
|
+
drop_temporal_triggers table_name
|
63
|
+
drop_table_without_temporal temporal_name(table_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def drop_table(table_name, **options)
|
67
|
+
super(table_name, **options)
|
68
|
+
|
69
|
+
super(temporal_name(table_name), **options) if table_exists?(temporal_name(table_name))
|
70
|
+
end
|
71
|
+
|
72
|
+
def rename_table(name, new_name)
|
73
|
+
drop_temporal_triggers name if table_exists?(temporal_name(name))
|
74
|
+
|
75
|
+
super name, new_name
|
76
|
+
|
77
|
+
return unless table_exists?(temporal_name(name))
|
78
|
+
|
79
|
+
super(temporal_name(name), temporal_name(new_name))
|
80
|
+
create_temporal_triggers new_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_column(table_name, column_name, type, **options)
|
84
|
+
super(table_name, column_name, type, **options)
|
85
|
+
|
86
|
+
return unless table_exists?(temporal_name(table_name))
|
87
|
+
|
88
|
+
super temporal_name(table_name), column_name, type, **options
|
89
|
+
create_temporal_triggers table_name
|
90
|
+
end
|
91
|
+
|
92
|
+
def remove_columns(table_name, *column_names, **options)
|
93
|
+
super(table_name, *column_names, **options)
|
94
|
+
|
95
|
+
return unless table_exists?(temporal_name(table_name))
|
96
|
+
|
97
|
+
super temporal_name(table_name), *column_names, **options
|
98
|
+
create_temporal_triggers table_name
|
99
|
+
end
|
100
|
+
|
101
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
102
|
+
super(table_name, column_name, type, **options)
|
103
|
+
|
104
|
+
return unless table_exists?(temporal_name(table_name))
|
105
|
+
|
106
|
+
super temporal_name(table_name), column_name, type, **options
|
107
|
+
create_temporal_triggers table_name
|
108
|
+
end
|
109
|
+
|
110
|
+
def change_column(table_name, column_name, type, **options)
|
111
|
+
super(table_name, column_name, type, **options)
|
112
|
+
|
113
|
+
return unless table_exists?(temporal_name(table_name))
|
114
|
+
|
115
|
+
super temporal_name(table_name), column_name, type, **options
|
116
|
+
# Don't need to update triggers here...
|
117
|
+
end
|
118
|
+
|
119
|
+
def rename_column(table_name, column_name, new_column_name)
|
120
|
+
super(table_name, column_name, new_column_name)
|
121
|
+
|
122
|
+
return unless table_exists?(temporal_name(table_name))
|
123
|
+
|
124
|
+
super temporal_name(table_name), column_name, new_column_name
|
125
|
+
create_temporal_triggers table_name
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_index(table_name, column_name, options = {})
|
129
|
+
super(table_name, column_name, options)
|
130
|
+
|
131
|
+
return unless table_exists?(temporal_name(table_name))
|
132
|
+
|
133
|
+
column_names = Array.wrap(column_name)
|
134
|
+
idx_name = temporal_index_name(options[:name] || index_name(table_name, column: column_names))
|
135
|
+
super temporal_name(table_name), column_name, options.except(:unique).merge(name: idx_name)
|
136
|
+
end
|
137
|
+
|
138
|
+
def remove_index(table_name, options = {})
|
139
|
+
super(table_name, options)
|
140
|
+
|
141
|
+
return unless table_exists?(temporal_name(table_name))
|
142
|
+
|
143
|
+
idx_name = temporal_index_name(index_name_for_remove(table_name, options))
|
144
|
+
super temporal_name(table_name), name: idx_name
|
145
|
+
end
|
146
|
+
|
147
|
+
def create_temporal_indexes(table_name) # rubocop:disable Metrics/MethodLength
|
148
|
+
indexes = ActiveRecord::Base.connection.indexes(table_name)
|
149
|
+
|
150
|
+
indexes.each do |index|
|
151
|
+
index_name = temporal_index_name(index.name)
|
152
|
+
|
153
|
+
next if temporal_index_exists?(table_name, index_name)
|
154
|
+
|
155
|
+
add_index(
|
156
|
+
temporal_name(table_name),
|
157
|
+
index.columns,
|
158
|
+
# exclude unique constraints for temporal tables
|
159
|
+
name: index_name,
|
160
|
+
length: index.lengths,
|
161
|
+
order: index.orders
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def temporal_name(table_name)
|
167
|
+
"#{table_name}_h"
|
168
|
+
end
|
169
|
+
|
170
|
+
def create_temporal_triggers(_table_name)
|
171
|
+
raise NotImplementedError, 'create_temporal_triggers is not implemented'
|
172
|
+
end
|
173
|
+
|
174
|
+
def drop_temporal_triggers(_table_name)
|
175
|
+
raise NotImplementedError, 'drop_temporal_triggers is not implemented'
|
176
|
+
end
|
177
|
+
|
178
|
+
# It's important not to increase the length of the returned string.
|
179
|
+
def temporal_index_name(index_name)
|
180
|
+
index_name.to_s.sub(/^index/, 'ind_h').sub(/_ix(\d+)$/, '_hi\1')
|
181
|
+
end
|
182
|
+
|
183
|
+
def temporal_index_exists?(table_name, index_name)
|
184
|
+
index_name_exists?(temporal_name(table_name), index_name)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/lib/temporal_tables.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'temporal_tables/temporal_adapter'
|
4
|
+
require 'temporal_tables/temporal_adapter_six_oh'
|
4
5
|
require 'temporal_tables/connection_adapters/mysql_adapter'
|
5
6
|
require 'temporal_tables/connection_adapters/postgresql_adapter'
|
6
7
|
require 'temporal_tables/whodunnit'
|
@@ -21,7 +22,11 @@ module TemporalTables
|
|
21
22
|
# It's necessary to do this on the implementations in order for the
|
22
23
|
# alias method chain hooks to work.
|
23
24
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.subclasses.each do |subclass|
|
24
|
-
|
25
|
+
if ActiveRecord.version < ::Gem::Version.new('6.1')
|
26
|
+
subclass.send :prepend, TemporalTables::TemporalAdapterSixOh
|
27
|
+
else
|
28
|
+
subclass.send :prepend, TemporalTables::TemporalAdapter
|
29
|
+
end
|
25
30
|
|
26
31
|
module_name = subclass.name.split('::').last
|
27
32
|
next unless TemporalTables::ConnectionAdapters.const_defined?(module_name)
|
data/spec/internal/db/schema.rb
CHANGED
@@ -9,20 +9,21 @@ end
|
|
9
9
|
ActiveRecord::Schema.define do
|
10
10
|
enable_extension 'pgcrypto' if postgres
|
11
11
|
|
12
|
-
create_table :
|
13
|
-
t.belongs_to :coven
|
12
|
+
create_table :covens, force: true do |t|
|
14
13
|
t.string :name
|
15
14
|
end
|
15
|
+
add_temporal_table :covens
|
16
16
|
|
17
|
-
create_table :
|
17
|
+
create_table :people, temporal: true, force: true do |t|
|
18
|
+
t.belongs_to :coven
|
18
19
|
t.string :name
|
19
20
|
end
|
20
|
-
|
21
|
+
add_index :people, :name, unique: true
|
21
22
|
|
22
23
|
create_table :warts, temporal: true, force: true do |t|
|
23
24
|
t.belongs_to :person
|
24
|
-
t.integer :hairiness
|
25
25
|
end
|
26
|
+
add_column :warts, :hairiness, :integer
|
26
27
|
|
27
28
|
create_table :flying_machines, temporal: true, force: true do |t|
|
28
29
|
t.belongs_to :person
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: temporal_tables
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brent Kroeker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -184,6 +184,7 @@ files:
|
|
184
184
|
- lib/temporal_tables/reflection_extensions.rb
|
185
185
|
- lib/temporal_tables/relation_extensions.rb
|
186
186
|
- lib/temporal_tables/temporal_adapter.rb
|
187
|
+
- lib/temporal_tables/temporal_adapter_six_oh.rb
|
187
188
|
- lib/temporal_tables/temporal_class.rb
|
188
189
|
- lib/temporal_tables/version.rb
|
189
190
|
- lib/temporal_tables/whodunnit.rb
|