chrono_model 1.2.2 → 2.0.0

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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -20
  3. data/README.md +62 -40
  4. data/lib/active_record/connection_adapters/chronomodel_adapter.rb +17 -11
  5. data/lib/active_record/tasks/chronomodel_database_tasks.rb +64 -23
  6. data/lib/chrono_model/adapter/ddl.rb +168 -153
  7. data/lib/chrono_model/adapter/indexes.rb +99 -94
  8. data/lib/chrono_model/adapter/migrations.rb +81 -104
  9. data/lib/chrono_model/adapter/migrations_modules/legacy.rb +41 -0
  10. data/lib/chrono_model/adapter/migrations_modules/stable.rb +41 -0
  11. data/lib/chrono_model/adapter/tsrange.rb +20 -5
  12. data/lib/chrono_model/adapter/upgrade.rb +89 -91
  13. data/lib/chrono_model/adapter.rb +64 -31
  14. data/lib/chrono_model/chrono.rb +17 -0
  15. data/lib/chrono_model/conversions.rb +15 -9
  16. data/lib/chrono_model/db_console.rb +9 -0
  17. data/lib/chrono_model/json.rb +9 -6
  18. data/lib/chrono_model/patches/as_of_time_holder.rb +2 -2
  19. data/lib/chrono_model/patches/as_of_time_relation.rb +2 -2
  20. data/lib/chrono_model/patches/association.rb +15 -12
  21. data/lib/chrono_model/patches/batches.rb +17 -0
  22. data/lib/chrono_model/patches/db_console.rb +20 -4
  23. data/lib/chrono_model/patches/join_node.rb +4 -4
  24. data/lib/chrono_model/patches/preloader.rb +41 -11
  25. data/lib/chrono_model/patches/relation.rb +53 -8
  26. data/lib/chrono_model/patches.rb +3 -1
  27. data/lib/chrono_model/railtie.rb +29 -24
  28. data/lib/chrono_model/time_gate.rb +3 -3
  29. data/lib/chrono_model/time_machine/history_model.rb +65 -31
  30. data/lib/chrono_model/time_machine/time_query.rb +65 -49
  31. data/lib/chrono_model/time_machine/timeline.rb +52 -28
  32. data/lib/chrono_model/time_machine.rb +66 -25
  33. data/lib/chrono_model/utilities.rb +3 -3
  34. data/lib/chrono_model/version.rb +3 -1
  35. data/lib/chrono_model.rb +31 -36
  36. metadata +39 -136
  37. data/.gitignore +0 -21
  38. data/.rspec +0 -2
  39. data/.travis.yml +0 -41
  40. data/Gemfile +0 -4
  41. data/README.sql +0 -161
  42. data/Rakefile +0 -25
  43. data/chrono_model.gemspec +0 -33
  44. data/gemfiles/rails_5.0.gemfile +0 -6
  45. data/gemfiles/rails_5.1.gemfile +0 -6
  46. data/gemfiles/rails_5.2.gemfile +0 -6
  47. data/spec/aruba/dbconsole_spec.rb +0 -25
  48. data/spec/aruba/fixtures/database_with_default_username_and_password.yml +0 -14
  49. data/spec/aruba/fixtures/database_without_username_and_password.yml +0 -11
  50. data/spec/aruba/fixtures/empty_structure.sql +0 -27
  51. data/spec/aruba/fixtures/migrations/56/20160812190335_create_impressions.rb +0 -10
  52. data/spec/aruba/fixtures/migrations/56/20171115195229_add_temporal_extension_to_impressions.rb +0 -10
  53. data/spec/aruba/fixtures/railsapp/config/application.rb +0 -17
  54. data/spec/aruba/fixtures/railsapp/config/boot.rb +0 -5
  55. data/spec/aruba/fixtures/railsapp/config/environments/development.rb +0 -38
  56. data/spec/aruba/migrations_spec.rb +0 -48
  57. data/spec/aruba/rake_task_spec.rb +0 -71
  58. data/spec/chrono_model/adapter/base_spec.rb +0 -157
  59. data/spec/chrono_model/adapter/ddl_spec.rb +0 -243
  60. data/spec/chrono_model/adapter/indexes_spec.rb +0 -72
  61. data/spec/chrono_model/adapter/migrations_spec.rb +0 -312
  62. data/spec/chrono_model/conversions_spec.rb +0 -43
  63. data/spec/chrono_model/history_models_spec.rb +0 -32
  64. data/spec/chrono_model/json_ops_spec.rb +0 -59
  65. data/spec/chrono_model/time_machine/as_of_spec.rb +0 -188
  66. data/spec/chrono_model/time_machine/changes_spec.rb +0 -50
  67. data/spec/chrono_model/time_machine/counter_cache_race_spec.rb +0 -46
  68. data/spec/chrono_model/time_machine/default_scope_spec.rb +0 -37
  69. data/spec/chrono_model/time_machine/history_spec.rb +0 -104
  70. data/spec/chrono_model/time_machine/keep_cool_spec.rb +0 -27
  71. data/spec/chrono_model/time_machine/manipulations_spec.rb +0 -84
  72. data/spec/chrono_model/time_machine/model_identification_spec.rb +0 -46
  73. data/spec/chrono_model/time_machine/sequence_spec.rb +0 -74
  74. data/spec/chrono_model/time_machine/sti_spec.rb +0 -100
  75. data/spec/chrono_model/time_machine/time_query_spec.rb +0 -261
  76. data/spec/chrono_model/time_machine/timeline_spec.rb +0 -63
  77. data/spec/chrono_model/time_machine/timestamps_spec.rb +0 -43
  78. data/spec/chrono_model/time_machine/transactions_spec.rb +0 -69
  79. data/spec/config.travis.yml +0 -5
  80. data/spec/config.yml.example +0 -9
  81. data/spec/spec_helper.rb +0 -33
  82. data/spec/support/adapter/helpers.rb +0 -53
  83. data/spec/support/adapter/structure.rb +0 -44
  84. data/spec/support/aruba.rb +0 -44
  85. data/spec/support/connection.rb +0 -70
  86. data/spec/support/matchers/base.rb +0 -56
  87. data/spec/support/matchers/column.rb +0 -99
  88. data/spec/support/matchers/function.rb +0 -79
  89. data/spec/support/matchers/index.rb +0 -69
  90. data/spec/support/matchers/schema.rb +0 -39
  91. data/spec/support/matchers/table.rb +0 -275
  92. data/spec/support/time_machine/helpers.rb +0 -47
  93. data/spec/support/time_machine/structure.rb +0 -111
  94. data/sql/json_ops.sql +0 -56
  95. data/sql/uninstall-json_ops.sql +0 -24
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ChronoModel
2
4
  class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
3
-
4
5
  module Migrations
5
6
  # Creates the given table, possibly creating the temporal schema
6
7
  # objects if the `:temporal` option is given and set to true.
7
8
  #
8
- def create_table(table_name, options = {})
9
+ def create_table(table_name, **options)
9
10
  # No temporal features requested, skip
10
11
  return super unless options[:temporal]
11
12
 
12
13
  if options[:id] == false
13
- logger.warn "ChronoModel: Temporal Temporal tables require a primary key."
14
+ logger.warn 'ChronoModel: Temporal Temporal tables require a primary key.'
14
15
  logger.warn "ChronoModel: Adding a `__chrono_id' primary key to #{table_name} definition."
15
16
 
16
17
  options[:id] = '__chrono_id'
@@ -26,8 +27,12 @@ module ChronoModel
26
27
 
27
28
  # If renaming a temporal table, rename the history and view as well.
28
29
  #
29
- def rename_table(name, new_name)
30
- return super unless is_chrono?(name)
30
+ def rename_table(name, new_name, **options)
31
+ unless is_chrono?(name)
32
+ return super(name, new_name) if method(:rename_table).super_method.arity == 2
33
+
34
+ return super
35
+ end
31
36
 
32
37
  clear_cache!
33
38
 
@@ -63,38 +68,35 @@ module ChronoModel
63
68
  # features on the given table. Please note that you'll lose your history
64
69
  # when demoting a temporal table to a plain one.
65
70
  #
66
- def change_table(table_name, options = {}, &block)
71
+ def change_table(table_name, **options, &block)
67
72
  transaction do
68
-
69
73
  # Add an empty proc to support calling change_table without a block.
70
74
  #
71
- block ||= proc { }
75
+ block ||= proc {}
72
76
 
73
- case options[:temporal]
74
- when true
75
- if !is_chrono?(table_name)
77
+ if options[:temporal]
78
+ unless is_chrono?(table_name)
76
79
  chrono_make_temporal_table(table_name, options)
77
80
  end
78
81
 
79
82
  drop_and_recreate_public_view(table_name, options) do
80
- super table_name, options, &block
83
+ super(table_name, **options, &block)
81
84
  end
82
85
 
83
- when false
86
+ else
84
87
  if is_chrono?(table_name)
85
88
  chrono_undo_temporal_table(table_name)
86
89
  end
87
90
 
88
- super table_name, options, &block
91
+ super(table_name, **options, &block)
89
92
  end
90
-
91
93
  end
92
94
  end
93
95
 
94
96
  # If dropping a temporal table, drops it from the temporal schema
95
97
  # adding the CASCADE option so to delete the history, view and triggers.
96
98
  #
97
- def drop_table(table_name, *)
99
+ def drop_table(table_name, **options)
98
100
  return super unless is_chrono?(table_name)
99
101
 
100
102
  on_temporal_schema { execute "DROP TABLE #{table_name} CASCADE" }
@@ -102,39 +104,10 @@ module ChronoModel
102
104
  chrono_drop_trigger_functions_for(table_name)
103
105
  end
104
106
 
105
- # If adding an index to a temporal table, add it to the one in the
106
- # temporal schema and to the history one. If the `:unique` option is
107
- # present, it is removed from the index created in the history table.
108
- #
109
- def add_index(table_name, column_name, options = {})
110
- return super unless is_chrono?(table_name)
111
-
112
- transaction do
113
- on_temporal_schema { super }
114
-
115
- # Uniqueness constraints do not make sense in the history table
116
- options = options.dup.tap {|o| o.delete(:unique)} if options[:unique].present?
117
-
118
- on_history_schema { super table_name, column_name, options }
119
- end
120
- end
121
-
122
- # If removing an index from a temporal table, remove it both from the
123
- # temporal and the history schemas.
124
- #
125
- def remove_index(table_name, *)
126
- return super unless is_chrono?(table_name)
127
-
128
- transaction do
129
- on_temporal_schema { super }
130
- on_history_schema { super }
131
- end
132
- end
133
-
134
107
  # If adding a column to a temporal table, creates it in the table in
135
108
  # the temporal schema and updates the triggers.
136
109
  #
137
- def add_column(table_name, *)
110
+ def add_column(table_name, column_name, type, **options)
138
111
  return super unless is_chrono?(table_name)
139
112
 
140
113
  transaction do
@@ -166,8 +139,9 @@ module ChronoModel
166
139
  # view, then change the column from the table in the temporal schema and
167
140
  # eventually recreate the triggers.
168
141
  #
169
- def change_column(table_name, *)
142
+ def change_column(table_name, column_name, type, **options)
170
143
  return super unless is_chrono?(table_name)
144
+
171
145
  drop_and_recreate_public_view(table_name) { super }
172
146
  end
173
147
 
@@ -175,6 +149,7 @@ module ChronoModel
175
149
  #
176
150
  def change_column_default(table_name, *)
177
151
  return super unless is_chrono?(table_name)
152
+
178
153
  on_temporal_schema { super }
179
154
  end
180
155
 
@@ -182,6 +157,7 @@ module ChronoModel
182
157
  #
183
158
  def change_column_null(table_name, *)
184
159
  return super unless is_chrono?(table_name)
160
+
185
161
  on_temporal_schema { super }
186
162
  end
187
163
 
@@ -189,54 +165,56 @@ module ChronoModel
189
165
  # view, then drop the column from the table in the temporal schema and
190
166
  # eventually recreate the triggers.
191
167
  #
192
- def remove_column(table_name, *)
168
+ def remove_column(table_name, column_name, type = nil, **options)
193
169
  return super unless is_chrono?(table_name)
170
+
194
171
  drop_and_recreate_public_view(table_name) { super }
195
172
  end
196
173
 
197
174
  private
198
- # In destructive changes, such as removing columns or changing column
199
- # types, the view must be dropped and recreated, while the change has
200
- # to be applied to the table in the temporal schema.
201
- #
202
- def drop_and_recreate_public_view(table_name, opts = {})
203
- transaction do
204
- options = chrono_metadata_for(table_name).merge(opts)
205
-
206
- execute "DROP VIEW #{table_name}"
207
175
 
208
- on_temporal_schema { yield }
176
+ # In destructive changes, such as removing columns or changing column
177
+ # types, the view must be dropped and recreated, while the change has
178
+ # to be applied to the table in the temporal schema.
179
+ #
180
+ def drop_and_recreate_public_view(table_name, opts = {}, &block)
181
+ transaction do
182
+ options = chrono_metadata_for(table_name).merge(opts)
209
183
 
210
- # Recreate the triggers
211
- chrono_public_view_ddl(table_name, options)
212
- end
213
- end
184
+ execute "DROP VIEW #{table_name}"
214
185
 
215
- def chrono_make_temporal_table(table_name, options)
216
- # Add temporal features to this table
217
- #
218
- if !primary_key(table_name)
219
- execute "ALTER TABLE #{table_name} ADD __chrono_id SERIAL PRIMARY KEY"
220
- end
186
+ on_temporal_schema(&block)
221
187
 
222
- execute "ALTER TABLE #{table_name} SET SCHEMA #{TEMPORAL_SCHEMA}"
223
- on_history_schema { chrono_history_table_ddl(table_name) }
188
+ # Recreate the triggers
224
189
  chrono_public_view_ddl(table_name, options)
225
- chrono_copy_indexes_to_history(table_name)
190
+ end
191
+ end
226
192
 
227
- # Optionally copy the plain table data, setting up history
228
- # retroactively.
229
- #
230
- if options[:copy_data]
231
- chrono_copy_temporal_to_history(table_name, options)
232
- end
193
+ def chrono_make_temporal_table(table_name, options)
194
+ # Add temporal features to this table
195
+ #
196
+ unless primary_key(table_name)
197
+ execute "ALTER TABLE #{table_name} ADD __chrono_id SERIAL PRIMARY KEY"
233
198
  end
234
199
 
235
- def chrono_copy_temporal_to_history(table_name, options)
236
- seq = on_history_schema { serial_sequence(table_name, primary_key(table_name)) }
237
- from = options[:validity] || '0001-01-01 00:00:00'
200
+ execute "ALTER TABLE #{table_name} SET SCHEMA #{TEMPORAL_SCHEMA}"
201
+ on_history_schema { chrono_history_table_ddl(table_name) }
202
+ chrono_public_view_ddl(table_name, options)
203
+ chrono_copy_indexes_to_history(table_name)
204
+
205
+ # Optionally copy the plain table data, setting up history
206
+ # retroactively.
207
+ #
208
+ return unless options[:copy_data]
209
+
210
+ chrono_copy_temporal_to_history(table_name, options)
211
+ end
212
+
213
+ def chrono_copy_temporal_to_history(table_name, options)
214
+ seq = on_history_schema { pk_and_sequence_for(table_name).last.to_s }
215
+ from = options[:validity] || '0001-01-01 00:00:00'
238
216
 
239
- execute %[
217
+ execute %[
240
218
  INSERT INTO #{HISTORY_SCHEMA}.#{table_name}
241
219
  SELECT *,
242
220
  nextval('#{seq}') AS hid,
@@ -244,39 +222,38 @@ module ChronoModel
244
222
  timezone('UTC', now()) AS recorded_at
245
223
  FROM #{TEMPORAL_SCHEMA}.#{table_name}
246
224
  ]
247
- end
248
-
249
- # Removes temporal features from this table
250
- #
251
- def chrono_undo_temporal_table(table_name)
252
- execute "DROP VIEW #{table_name}"
225
+ end
253
226
 
254
- chrono_drop_trigger_functions_for(table_name)
227
+ # Removes temporal features from this table
228
+ #
229
+ def chrono_undo_temporal_table(table_name)
230
+ execute "DROP VIEW #{table_name}"
255
231
 
256
- on_history_schema { execute "DROP TABLE #{table_name}" }
232
+ chrono_drop_trigger_functions_for(table_name)
257
233
 
258
- default_schema = select_value 'SELECT current_schema()'
259
- on_temporal_schema do
260
- if primary_key(table_name) == '__chrono_id'
261
- execute "ALTER TABLE #{table_name} DROP __chrono_id"
262
- end
234
+ on_history_schema { execute "DROP TABLE #{table_name}" }
263
235
 
264
- execute "ALTER TABLE #{table_name} SET SCHEMA #{default_schema}"
236
+ default_schema = select_value 'SELECT current_schema()'
237
+ on_temporal_schema do
238
+ if primary_key(table_name) == '__chrono_id'
239
+ execute "ALTER TABLE #{table_name} DROP __chrono_id"
265
240
  end
241
+
242
+ execute "ALTER TABLE #{table_name} SET SCHEMA #{default_schema}"
266
243
  end
244
+ end
267
245
 
268
- # Renames a table and its primary key sequence name
269
- #
270
- def rename_table_and_pk(name, new_name)
271
- seq = serial_sequence(name, primary_key(name))
272
- new_seq = seq.sub(name.to_s, new_name.to_s).split('.').last
246
+ # Renames a table and its primary key sequence name
247
+ #
248
+ def rename_table_and_pk(name, new_name)
249
+ seq = pk_and_sequence_for(name).last.to_s
250
+ new_seq = seq.sub(name.to_s, new_name.to_s).split('.').last
273
251
 
274
- execute "ALTER SEQUENCE #{seq} RENAME TO #{new_seq}"
275
- execute "ALTER TABLE #{name} RENAME TO #{new_name}"
276
- end
252
+ execute "ALTER SEQUENCE #{seq} RENAME TO #{new_seq}"
253
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
254
+ end
277
255
 
278
256
  # private
279
257
  end
280
-
281
258
  end
282
259
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoModel
4
+ class Adapter
5
+ module MigrationsModules
6
+ module Legacy
7
+ # If adding an index to a temporal table, add it to the one in the
8
+ # temporal schema and to the history one. If the `:unique` option is
9
+ # present, it is removed from the index created in the history table.
10
+ #
11
+ def add_index(table_name, column_name, options = {})
12
+ return super unless is_chrono?(table_name)
13
+
14
+ transaction do
15
+ on_temporal_schema { super }
16
+
17
+ # Uniqueness constraints do not make sense in the history table
18
+ options = options.dup.tap { |o| o.delete(:unique) } if options[:unique].present?
19
+
20
+ on_history_schema { super(table_name, column_name, options) }
21
+ end
22
+ end
23
+
24
+ # If removing an index from a temporal table, remove it both from the
25
+ # temporal and the history schemas.
26
+ #
27
+ def remove_index(table_name, options = {})
28
+ return super unless is_chrono?(table_name)
29
+
30
+ transaction do
31
+ on_temporal_schema { super }
32
+
33
+ on_history_schema { super }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ChronoModel::Adapter::Migrations.include ChronoModel::Adapter::MigrationsModules::Legacy
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoModel
4
+ class Adapter
5
+ module MigrationsModules
6
+ module Stable
7
+ # If adding an index to a temporal table, add it to the one in the
8
+ # temporal schema and to the history one. If the `:unique` option is
9
+ # present, it is removed from the index created in the history table.
10
+ #
11
+ def add_index(table_name, column_name, **options)
12
+ return super unless is_chrono?(table_name)
13
+
14
+ transaction do
15
+ on_temporal_schema { super }
16
+
17
+ # Uniqueness constraints do not make sense in the history table
18
+ options = options.dup.tap { |o| o.delete(:unique) } if options[:unique].present?
19
+
20
+ on_history_schema { super(table_name, column_name, **options) }
21
+ end
22
+ end
23
+
24
+ # If removing an index from a temporal table, remove it both from the
25
+ # temporal and the history schemas.
26
+ #
27
+ def remove_index(table_name, column_name = nil, **options)
28
+ return super unless is_chrono?(table_name)
29
+
30
+ transaction do
31
+ on_temporal_schema { super }
32
+
33
+ on_history_schema { super }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ChronoModel::Adapter::Migrations.include ChronoModel::Adapter::MigrationsModules::Stable
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ChronoModel
2
4
  class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
3
-
4
5
  module TSRange
5
6
  # HACK: Redefine tsrange parsing support, as it is broken currently.
6
7
  #
@@ -26,16 +27,31 @@ module ChronoModel
26
27
  extracted = extract_bounds(value)
27
28
 
28
29
  from = Conversions.string_to_utc_time extracted[:from]
29
- to = Conversions.string_to_utc_time extracted[:to ]
30
+ to = Conversions.string_to_utc_time extracted[:to]
30
31
 
31
32
  [from, to]
32
33
  end
33
34
 
34
35
  def extract_bounds(value)
35
36
  from, to = value[1..-2].split(',')
37
+
38
+ from_bound =
39
+ if value[1] == ',' || from == '-infinity'
40
+ nil
41
+ else
42
+ from[1..-2]
43
+ end
44
+
45
+ to_bound =
46
+ if value[-2] == ',' || to == 'infinity'
47
+ nil
48
+ else
49
+ to[1..-2]
50
+ end
51
+
36
52
  {
37
- from: (value[1] == ',' || from == '-infinity') ? nil : from[1..-2],
38
- to: (value[-2] == ',' || to == 'infinity') ? nil : to[1..-2],
53
+ from: from_bound,
54
+ to: to_bound
39
55
  }
40
56
  end
41
57
  end
@@ -52,6 +68,5 @@ module ChronoModel
52
68
  end
53
69
  end
54
70
  end
55
-
56
71
  end
57
72
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ChronoModel
2
4
  class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
3
-
4
5
  module Upgrade
5
6
  def chrono_upgrade!
6
7
  chrono_ensure_schemas
@@ -9,112 +10,109 @@ module ChronoModel
9
10
  end
10
11
 
11
12
  private
12
- # Locate tables needing a structure upgrade
13
- #
14
- def chrono_tables_needing_upgrade
15
- tables = { }
16
-
17
- on_temporal_schema { self.tables }.each do |table_name|
18
- next unless is_chrono?(table_name)
19
- metadata = chrono_metadata_for(table_name)
20
- version = metadata['chronomodel']
21
-
22
- if version.blank?
23
- tables[table_name] = { version: nil, priority: 'CRITICAL' }
24
- elsif version != VERSION
25
- tables[table_name] = { version: version, priority: 'LOW' }
26
- end
27
- end
28
13
 
29
- return tables
30
- end
14
+ # Locate tables needing a structure upgrade
15
+ #
16
+ def chrono_tables_needing_upgrade
17
+ tables = {}
31
18
 
32
- # Emit a warning about tables needing an upgrade
33
- #
34
- def chrono_upgrade_warning
35
- upgrade = chrono_tables_needing_upgrade.map do |table, desc|
36
- "#{table} - priority: #{desc[:priority]}"
37
- end.join('; ')
38
-
39
- return if upgrade.empty?
40
-
41
- logger.warn "ChronoModel: There are tables needing a structure upgrade, and ChronoModel structures need to be recreated."
42
- logger.warn "ChronoModel: Please run ChronoModel.upgrade! to attempt the upgrade. If you have dependant database objects"
43
- logger.warn "ChronoModel: the upgrade will fail and you have to drop the dependent objects, run .upgrade! and create them"
44
- logger.warn "ChronoModel: again. Sorry. Some features or the whole library may not work correctly until upgrade is complete."
45
- logger.warn "ChronoModel: Tables pending upgrade: #{upgrade}"
19
+ on_temporal_schema { self.tables }.each do |table_name|
20
+ next unless is_chrono?(table_name)
21
+
22
+ metadata = chrono_metadata_for(table_name)
23
+ version = metadata['chronomodel']
24
+
25
+ if version.blank?
26
+ tables[table_name] = { version: nil, priority: 'CRITICAL' }
27
+ elsif version != VERSION
28
+ tables[table_name] = { version: version, priority: 'LOW' }
29
+ end
46
30
  end
47
31
 
48
- # Upgrades existing structure for each table, if required.
49
- #
50
- def chrono_upgrade_structure!
51
- transaction do
32
+ tables
33
+ end
34
+
35
+ # Emit a warning about tables needing an upgrade
36
+ #
37
+ def chrono_upgrade_warning
38
+ upgrade = chrono_tables_needing_upgrade.map do |table, desc|
39
+ "#{table} - priority: #{desc[:priority]}"
40
+ end.join('; ')
52
41
 
53
- chrono_tables_needing_upgrade.each do |table_name, desc|
42
+ return if upgrade.empty?
54
43
 
55
- if desc[:version].blank?
56
- logger.info "ChronoModel: Upgrading legacy PG 9.0 table #{table_name} to #{VERSION}"
57
- chrono_upgrade_from_postgres_9_0(table_name)
58
- logger.info "ChronoModel: legacy #{table_name} upgrade complete"
59
- else
60
- logger.info "ChronoModel: upgrading #{table_name} from #{desc[:version]} to #{VERSION}"
61
- chrono_create_view_for(table_name)
62
- logger.info "ChronoModel: #{table_name} upgrade complete"
63
- end
44
+ logger.warn 'ChronoModel: There are tables needing a structure upgrade, and ChronoModel structures need to be recreated.'
45
+ logger.warn 'ChronoModel: Please run ChronoModel.upgrade! to attempt the upgrade. If you have dependant database objects'
46
+ logger.warn 'ChronoModel: the upgrade will fail and you have to drop the dependent objects, run .upgrade! and create them'
47
+ logger.warn 'ChronoModel: again. Sorry. Some features or the whole library may not work correctly until upgrade is complete.'
48
+ logger.warn "ChronoModel: Tables pending upgrade: #{upgrade}"
49
+ end
64
50
 
51
+ # Upgrades existing structure for each table, if required.
52
+ #
53
+ def chrono_upgrade_structure!
54
+ transaction do
55
+ chrono_tables_needing_upgrade.each do |table_name, desc|
56
+ if desc[:version].blank?
57
+ logger.info "ChronoModel: Upgrading legacy PG 9.0 table #{table_name} to #{VERSION}"
58
+ chrono_upgrade_from_postgres_v90(table_name)
59
+ logger.info "ChronoModel: legacy #{table_name} upgrade complete"
60
+ else
61
+ logger.info "ChronoModel: upgrading #{table_name} from #{desc[:version]} to #{VERSION}"
62
+ chrono_public_view_ddl(table_name)
63
+ logger.info "ChronoModel: #{table_name} upgrade complete"
65
64
  end
66
65
  end
67
- rescue => e
68
- message = "ChronoModel structure upgrade failed: #{e.message}. Please drop dependent objects first and then run ChronoModel.upgrade! again."
69
-
70
- # Quite important, output it also to stderr.
71
- #
72
- logger.error message
73
- $stderr.puts message
74
66
  end
67
+ rescue StandardError => e
68
+ message = "ChronoModel structure upgrade failed: #{e.message}. Please drop dependent objects first and then run ChronoModel.upgrade! again."
75
69
 
76
- def chrono_upgrade_from_postgres_9_0(table_name)
77
- # roses are red
78
- # violets are blue
79
- # and this is the most boring piece of code ever
80
- history_table = "#{HISTORY_SCHEMA}.#{table_name}"
81
- p_pkey = primary_key(table_name)
70
+ # Quite important, output it also to stderr.
71
+ #
72
+ logger.error message
73
+ warn message
74
+ end
82
75
 
83
- execute "ALTER TABLE #{history_table} ADD COLUMN validity tsrange;"
84
- execute """
85
- UPDATE #{history_table} SET validity = tsrange(valid_from,
76
+ def chrono_upgrade_from_postgres_v90(table_name)
77
+ # roses are red
78
+ # violets are blue
79
+ # and this is the most boring piece of code ever
80
+ history_table = "#{HISTORY_SCHEMA}.#{table_name}"
81
+ p_pkey = primary_key(table_name)
82
+
83
+ execute "ALTER TABLE #{history_table} ADD COLUMN validity tsrange;"
84
+ execute <<-SQL.squish
85
+ UPDATE #{history_table} SET validity = tsrange(valid_from,
86
86
  CASE WHEN extract(year from valid_to) = 9999 THEN NULL
87
87
  ELSE valid_to
88
88
  END
89
- );
90
- """
91
-
92
- execute "DROP INDEX #{history_table}_temporal_on_valid_from;"
93
- execute "DROP INDEX #{history_table}_temporal_on_valid_from_and_valid_to;"
94
- execute "DROP INDEX #{history_table}_temporal_on_valid_to;"
95
- execute "DROP INDEX #{history_table}_inherit_pkey"
96
- execute "DROP INDEX #{history_table}_recorded_at"
97
- execute "DROP INDEX #{history_table}_instance_history"
98
- execute "ALTER TABLE #{history_table} DROP CONSTRAINT #{table_name}_valid_from_before_valid_to;"
99
- execute "ALTER TABLE #{history_table} DROP CONSTRAINT #{table_name}_timeline_consistency;"
100
- execute "DROP RULE #{table_name}_upd_first ON #{table_name};"
101
- execute "DROP RULE #{table_name}_upd_next ON #{table_name};"
102
- execute "DROP RULE #{table_name}_del ON #{table_name};"
103
- execute "DROP RULE #{table_name}_ins ON #{table_name};"
104
- execute "DROP TRIGGER history_ins ON #{TEMPORAL_SCHEMA}.#{table_name};"
105
- execute "DROP FUNCTION #{TEMPORAL_SCHEMA}.#{table_name}_ins();"
106
- execute "ALTER TABLE #{history_table} DROP COLUMN valid_from;"
107
- execute "ALTER TABLE #{history_table} DROP COLUMN valid_to;"
108
-
109
- execute "CREATE EXTENSION IF NOT EXISTS btree_gist;"
110
-
111
- chrono_create_view_for(table_name)
112
- on_history_schema { add_history_validity_constraint(table_name, p_pkey) }
113
- on_history_schema { chrono_create_history_indexes_for(table_name, p_pkey) }
114
- end
89
+ );
90
+ SQL
91
+
92
+ execute "DROP INDEX #{history_table}_temporal_on_valid_from;"
93
+ execute "DROP INDEX #{history_table}_temporal_on_valid_from_and_valid_to;"
94
+ execute "DROP INDEX #{history_table}_temporal_on_valid_to;"
95
+ execute "DROP INDEX #{history_table}_inherit_pkey"
96
+ execute "DROP INDEX #{history_table}_recorded_at"
97
+ execute "DROP INDEX #{history_table}_instance_history"
98
+ execute "ALTER TABLE #{history_table} DROP CONSTRAINT #{table_name}_valid_from_before_valid_to;"
99
+ execute "ALTER TABLE #{history_table} DROP CONSTRAINT #{table_name}_timeline_consistency;"
100
+ execute "DROP RULE #{table_name}_upd_first ON #{table_name};"
101
+ execute "DROP RULE #{table_name}_upd_next ON #{table_name};"
102
+ execute "DROP RULE #{table_name}_del ON #{table_name};"
103
+ execute "DROP RULE #{table_name}_ins ON #{table_name};"
104
+ execute "DROP TRIGGER history_ins ON #{TEMPORAL_SCHEMA}.#{table_name};"
105
+ execute "DROP FUNCTION #{TEMPORAL_SCHEMA}.#{table_name}_ins();"
106
+ execute "ALTER TABLE #{history_table} DROP COLUMN valid_from;"
107
+ execute "ALTER TABLE #{history_table} DROP COLUMN valid_to;"
108
+
109
+ execute 'CREATE EXTENSION IF NOT EXISTS btree_gist;'
110
+
111
+ chrono_public_view_ddl(table_name)
112
+ on_history_schema { add_history_validity_constraint(table_name, p_pkey) }
113
+ on_history_schema { chrono_create_history_indexes_for(table_name, p_pkey) }
114
+ end
115
115
  # private
116
116
  end
117
-
118
117
  end
119
-
120
118
  end