dynamic_migrations 2.2.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -4
  3. data/lib/dynamic_migrations/active_record/migrators/column.rb +21 -0
  4. data/lib/dynamic_migrations/active_record/migrators/foreign_key_constraint.rb +112 -0
  5. data/lib/dynamic_migrations/active_record/migrators/function.rb +108 -0
  6. data/lib/dynamic_migrations/active_record/migrators/index.rb +27 -0
  7. data/lib/dynamic_migrations/active_record/migrators/schema.rb +21 -0
  8. data/lib/dynamic_migrations/active_record/migrators/table.rb +21 -0
  9. data/lib/dynamic_migrations/active_record/migrators/trigger.rb +109 -0
  10. data/lib/dynamic_migrations/active_record/migrators/unique_constraint.rb +63 -0
  11. data/lib/dynamic_migrations/active_record/migrators/validation.rb +67 -0
  12. data/lib/dynamic_migrations/active_record/migrators.rb +64 -0
  13. data/lib/dynamic_migrations/name_helper.rb +13 -0
  14. data/lib/dynamic_migrations/postgres/generator/column.rb +92 -0
  15. data/lib/dynamic_migrations/postgres/generator/foreign_key_constraint.rb +84 -0
  16. data/lib/dynamic_migrations/postgres/generator/fragment.rb +30 -0
  17. data/lib/dynamic_migrations/postgres/generator/function.rb +77 -0
  18. data/lib/dynamic_migrations/postgres/generator/index.rb +101 -0
  19. data/lib/dynamic_migrations/postgres/generator/primary_key.rb +55 -0
  20. data/lib/dynamic_migrations/postgres/generator/schema.rb +19 -0
  21. data/lib/dynamic_migrations/postgres/generator/schema_migrations/section.rb +37 -0
  22. data/lib/dynamic_migrations/postgres/generator/schema_migrations.rb +92 -0
  23. data/lib/dynamic_migrations/postgres/generator/table.rb +122 -0
  24. data/lib/dynamic_migrations/postgres/generator/trigger.rb +101 -0
  25. data/lib/dynamic_migrations/postgres/generator/unique_constraint.rb +79 -0
  26. data/lib/dynamic_migrations/postgres/generator/validation.rb +87 -0
  27. data/lib/dynamic_migrations/postgres/generator.rb +359 -0
  28. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/functions.rb +68 -0
  29. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/columns.rb +72 -0
  30. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/foreign_key_constraints.rb +73 -0
  31. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/indexes.rb +73 -0
  32. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/primary_key.rb +49 -0
  33. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/triggers.rb +73 -0
  34. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/unique_constraints.rb +73 -0
  35. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/validations.rb +73 -0
  36. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables.rb +80 -0
  37. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas.rb +48 -0
  38. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations.rb +59 -0
  39. data/lib/dynamic_migrations/postgres/server/database/differences.rb +81 -6
  40. data/lib/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rb +35 -9
  41. data/lib/dynamic_migrations/postgres/server/database/loaded_schemas_builder.rb +49 -8
  42. data/lib/dynamic_migrations/postgres/server/database/schema/function.rb +69 -0
  43. data/lib/dynamic_migrations/postgres/server/database/schema/functions.rb +63 -0
  44. data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +4 -0
  45. data/lib/dynamic_migrations/postgres/server/database/schema/table/columns.rb +1 -1
  46. data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraint.rb +40 -5
  47. data/lib/dynamic_migrations/postgres/server/database/schema/table/index.rb +23 -9
  48. data/lib/dynamic_migrations/postgres/server/database/schema/table/primary_key.rb +21 -6
  49. data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +151 -0
  50. data/lib/dynamic_migrations/postgres/server/database/schema/table/triggers.rb +66 -0
  51. data/lib/dynamic_migrations/postgres/server/database/schema/table/unique_constraint.rb +19 -9
  52. data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +20 -1
  53. data/lib/dynamic_migrations/postgres/server/database/schema/table.rb +15 -5
  54. data/lib/dynamic_migrations/postgres/server/database/schema/tables.rb +63 -0
  55. data/lib/dynamic_migrations/postgres/server/database/schema.rb +3 -49
  56. data/lib/dynamic_migrations/postgres/server/database/source.rb +21 -0
  57. data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +6 -6
  58. data/lib/dynamic_migrations/postgres/server/database/triggers_and_functions_loader.rb +131 -0
  59. data/lib/dynamic_migrations/postgres/server/database/validations_loader.rb +10 -4
  60. data/lib/dynamic_migrations/postgres/server/database.rb +2 -1
  61. data/lib/dynamic_migrations/postgres/server.rb +6 -0
  62. data/lib/dynamic_migrations/postgres.rb +1 -1
  63. data/lib/dynamic_migrations/version.rb +1 -1
  64. data/lib/dynamic_migrations.rb +47 -3
  65. metadata +44 -2
@@ -0,0 +1,359 @@
1
+ module DynamicMigrations
2
+ module Postgres
3
+ class Generator
4
+ class ExpectedSymbolError < StandardError
5
+ end
6
+
7
+ class DeferrableOptionsError < StandardError
8
+ end
9
+
10
+ class UnexpectedMigrationMethodNameError < StandardError
11
+ end
12
+
13
+ class MissingDescriptionError < StandardError
14
+ end
15
+
16
+ class NoDifferenceError < StandardError
17
+ end
18
+
19
+ # these sections are in order for which they will appear in a migration,
20
+ # note that removals come before additions, and that the order here optomizes
21
+ # for dependencies (i.e. columns have to be created before indexes are added and
22
+ # triggers are removed before functions are dropped)
23
+ STRUCTURE = [
24
+ {
25
+ header_comment: <<~COMMENT,
26
+ #
27
+ # Remove Functions
28
+ #
29
+ COMMENT
30
+ methods: [
31
+ :remove_function_comment,
32
+ :drop_function
33
+ ]
34
+ },
35
+ {
36
+ header_comment: <<~COMMENT,
37
+ #
38
+ # Remove Triggers
39
+ #
40
+ COMMENT
41
+ methods: [
42
+ :remove_trigger_comment,
43
+ :remove_trigger
44
+ ]
45
+ },
46
+ {
47
+ header_comment: <<~COMMENT,
48
+ #
49
+ # Remove Validations
50
+ #
51
+ COMMENT
52
+ methods: [
53
+ :remove_validation,
54
+ :remove_unique_constraint
55
+ ]
56
+ },
57
+ {
58
+ header_comment: <<~COMMENT,
59
+ #
60
+ # Remove Foreign Keys
61
+ #
62
+ COMMENT
63
+ methods: [
64
+ :remove_foreign_key
65
+ ]
66
+ },
67
+ {
68
+ header_comment: <<~COMMENT,
69
+ #
70
+ # Remove Primary Keys
71
+ #
72
+ COMMENT
73
+ methods: [
74
+ :remove_primary_key
75
+ ]
76
+ },
77
+ {
78
+ header_comment: <<~COMMENT,
79
+ #
80
+ # Remove Indexes
81
+ #
82
+ COMMENT
83
+ methods: [
84
+ :remove_index,
85
+ :remove_index_comment
86
+ ]
87
+ },
88
+ {
89
+ header_comment: <<~COMMENT,
90
+ #
91
+ # Remove Columns
92
+ #
93
+ COMMENT
94
+ methods: [
95
+ :remove_column
96
+ ]
97
+ },
98
+ {
99
+ header_comment: <<~COMMENT,
100
+ #
101
+ # Remove Tables
102
+ #
103
+ COMMENT
104
+ break_after: true,
105
+ methods: [
106
+ :drop_table
107
+ ]
108
+ },
109
+ {
110
+ # this is important enough to get it's own migration
111
+ break_before: true,
112
+ break_after: true,
113
+ header_comment: <<~COMMENT,
114
+ #
115
+ # Drop this schema
116
+ #
117
+ COMMENT
118
+ methods: [
119
+ :drop_schema
120
+ ]
121
+ },
122
+ {
123
+ # this is important enough to get it's own migration
124
+ break_before: true,
125
+ break_after: true,
126
+ header_comment: <<~COMMENT,
127
+ #
128
+ # Create this schema
129
+ #
130
+ COMMENT
131
+ methods: [
132
+ :create_schema
133
+ ]
134
+ },
135
+ {
136
+ header_comment: <<~COMMENT,
137
+ #
138
+ # Create Table
139
+ #
140
+ COMMENT
141
+ methods: [
142
+ :create_table
143
+ ]
144
+ },
145
+ {
146
+ header_comment: <<~COMMENT,
147
+ #
148
+ # Tables
149
+ #
150
+ COMMENT
151
+ methods: [
152
+ :remove_table_comment,
153
+ :set_table_comment
154
+ ]
155
+ },
156
+ {
157
+ header_comment: <<~COMMENT,
158
+ #
159
+ # Additional Columns
160
+ #
161
+ COMMENT
162
+ methods: [
163
+ :add_column
164
+ ]
165
+ },
166
+ {
167
+ header_comment: <<~COMMENT,
168
+ #
169
+ # Update Columns
170
+ #
171
+ COMMENT
172
+ methods: [
173
+ :change_column,
174
+ :remove_column_comment,
175
+ :set_column_comment
176
+ ]
177
+ },
178
+ {
179
+ header_comment: <<~COMMENT,
180
+ #
181
+ # Primary Key
182
+ #
183
+ COMMENT
184
+ methods: [
185
+ :add_primary_key
186
+ ]
187
+ },
188
+ {
189
+ header_comment: <<~COMMENT,
190
+ #
191
+ # Indexes
192
+ #
193
+ COMMENT
194
+ methods: [
195
+ :add_index,
196
+ :set_index_comment
197
+ ]
198
+ },
199
+ {
200
+ header_comment: <<~COMMENT,
201
+ #
202
+ # Foreign Keys
203
+ #
204
+ COMMENT
205
+ methods: [
206
+ :add_foreign_key,
207
+ :set_foreign_key_constraint_comment,
208
+ :remove_foreign_key_constraint_comment
209
+ ]
210
+ },
211
+ {
212
+ header_comment: <<~COMMENT,
213
+ #
214
+ # Validations
215
+ #
216
+ COMMENT
217
+ methods: [
218
+ :add_validation,
219
+ :add_unique_constraint,
220
+ :set_validation_comment,
221
+ :remove_validation_comment,
222
+ :set_unique_constraint_comment,
223
+ :remove_unique_constraint_comment
224
+ ]
225
+ },
226
+ {
227
+ header_comment: <<~COMMENT,
228
+ #
229
+ # Functions
230
+ #
231
+ COMMENT
232
+ methods: [
233
+ :create_function
234
+ ]
235
+ },
236
+ {
237
+ header_comment: <<~COMMENT,
238
+ #
239
+ # Triggers
240
+ #
241
+ COMMENT
242
+ methods: [
243
+ :add_trigger,
244
+ :set_trigger_comment
245
+ ]
246
+ },
247
+ {
248
+ header_comment: <<~COMMENT,
249
+ #
250
+ # Update Functions
251
+ #
252
+ COMMENT
253
+ methods: [
254
+ :update_function,
255
+ :set_function_comment
256
+ ]
257
+ }
258
+ ]
259
+
260
+ include Schema
261
+ include Table
262
+ include Column
263
+ include ForeignKeyConstraint
264
+ include Index
265
+ include PrimaryKey
266
+ include UniqueConstraint
267
+ include Validation
268
+ include Function
269
+ include Trigger
270
+
271
+ def initialize
272
+ @migrations = {}
273
+ end
274
+
275
+ # builds an array of migrations that can be used to create the provided schema
276
+ def migrations
277
+ final_migrations = {}
278
+ # an array of table names which have migrations, we group migrations for the same table together
279
+ @migrations.map do |schema_name, table_migrations|
280
+ schema_migrations = SchemaMigrations.new
281
+ # iterate through the tables which have migrations
282
+ table_migrations.map do |table_name, fragments|
283
+ # iterate through the structure object in order, and create the final migrations
284
+ STRUCTURE.each do |section|
285
+ # if this section requires a new migration, then end any current one
286
+ if section[:break_before]
287
+ schema_migrations.finalize
288
+ end
289
+
290
+ # add the header comment if we have a migration which matches one of the
291
+ # methods in this section
292
+ if (section[:methods] & fragments.keys).any?
293
+ header_fragment = Fragment.new nil, nil, section[:header_comment]
294
+ schema_migrations.add_fragment schema_name, table_name, :comment, header_fragment
295
+ end
296
+
297
+ # iterate through this sections methods in order and look
298
+ # for any that match the migrations we have
299
+ section[:methods].each do |method_name|
300
+ # if we have any migration fragments for this method then add them
301
+ fragments[method_name]&.each do |fragment|
302
+ schema_migrations.add_fragment schema_name, table_name, method_name, fragment
303
+ end
304
+ end
305
+
306
+ # if this section causes a new migration then end any current one
307
+ if section[:break_after]
308
+ schema_migrations.finalize
309
+ end
310
+ end
311
+ schema_migrations.finalize
312
+ end
313
+ final_migrations[schema_name] = schema_migrations.to_a
314
+ end
315
+ final_migrations
316
+ end
317
+
318
+ private
319
+
320
+ def supported_migration_method_names
321
+ @supported_migration_method_names ||= STRUCTURE.map { |s| s[:methods] }.flatten
322
+ end
323
+
324
+ def supported_migration_method? method_name
325
+ supported_migration_method_names.include? method_name
326
+ end
327
+
328
+ def add_migration schema_name, table_name, migration_method, object_name, code_comment, migration
329
+ raise ExpectedSymbolError, "Expected schema_name to be a symbol, got #{schema_name}" unless schema_name.is_a?(Symbol)
330
+ raise ExpectedSymbolError, "Expected table_name to be a symbol, got #{table_name}" unless schema_name.is_a?(Symbol)
331
+
332
+ unless supported_migration_method? migration_method
333
+ raise UnexpectedMigrationMethodNameError, "Expected migration_method to be a valid migrator method, got `#{migration_method}`"
334
+ end
335
+
336
+ final_migration = strip_empty_lines(migration).strip
337
+ fragment = Fragment.new(object_name, code_comment, final_migration)
338
+
339
+ # note, table_name can be nil, which is OK because nil is a valid
340
+ # key and we do want to group them all together
341
+ @migrations[schema_name] ||= {}
342
+ @migrations[schema_name][table_name] ||= {}
343
+ @migrations[schema_name][table_name][migration_method] ||= []
344
+ @migrations[schema_name][table_name][migration_method] << fragment
345
+
346
+ # return the newly created migration fragment
347
+ fragment
348
+ end
349
+
350
+ def indent multi_line_string
351
+ multi_line_string.gsub("\n", "\n ")
352
+ end
353
+
354
+ def strip_empty_lines multi_line_string
355
+ multi_line_string.gsub(/^\s*\n/, "")
356
+ end
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicMigrations
4
+ module Postgres
5
+ class Server
6
+ class Database
7
+ class Differences
8
+ class ToMigrations
9
+ module Schemas
10
+ module Functions
11
+ def process_functions schema_name, configuration_functions, database_functions
12
+ # process all the functions
13
+ function_names = (configuration_functions.keys + database_functions.keys).uniq
14
+ function_names.each do |function_name|
15
+ process_function schema_name, function_name, configuration_functions[function_name] || {}, database_functions[function_name] || {}
16
+ end
17
+ end
18
+
19
+ def process_function schema_name, function_name, configuration_function, database_function
20
+ # If the function exists in the configuration but not in the database
21
+ # then we have to create it.
22
+ if configuration_function[:exists] == true && database_function[:exists] == false
23
+ # a migration to create the function
24
+ function = @database.configured_schema(schema_name).function(function_name)
25
+ @generator.create_function function
26
+
27
+ # If the schema exists in the database but not in the configuration
28
+ # then we need to delete it.
29
+ elsif configuration_function[:exists] == false && database_function[:exists] == true
30
+ # a migration to create the function
31
+ function = @database.loaded_schema(schema_name).function(function_name)
32
+ @generator.drop_function function
33
+
34
+ # If the function exists in both the configuration and database representations
35
+ # but the definition is different then we need to update the definition.
36
+ elsif configuration_function[:definition][:matches] == false
37
+ function = @database.configured_schema(schema_name).function(function_name)
38
+ @generator.update_function function
39
+ # does the description also need to be updated
40
+ if configuration_function[:description][:matches] == false
41
+ # if the description was removed
42
+ if configuration_function[:description].nil?
43
+ @generator.remove_function_comment function
44
+ else
45
+ @generator.set_function_comment function
46
+ end
47
+ end
48
+
49
+ # If the function exists in both the configuration and database representations
50
+ # but the description is different then we need to update the description.
51
+ elsif configuration_function[:description][:matches] == false
52
+ function = @database.configured_schema(schema_name).function(function_name)
53
+ # if the description was removed
54
+ if configuration_function[:description].nil?
55
+ @generator.remove_function_comment function
56
+ else
57
+ @generator.set_function_comment function
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicMigrations
4
+ module Postgres
5
+ class Server
6
+ class Database
7
+ class Differences
8
+ class ToMigrations
9
+ module Schemas
10
+ module Tables
11
+ module Columns
12
+ def process_columns schema_name, table_name, configuration_columns, database_columns
13
+ # process all the columns
14
+ column_names = (configuration_columns.keys + database_columns.keys).uniq
15
+ column_names.each do |column_name|
16
+ process_column schema_name, table_name, column_name, configuration_columns[column_name] || {}, database_columns[column_name] || {}
17
+ end
18
+ end
19
+
20
+ def process_column schema_name, table_name, column_name, configuration_column, database_column
21
+ # If the column exists in the configuration but not in the database
22
+ # then we have to create it.
23
+ if configuration_column[:exists] == true && database_column[:exists] == false
24
+ # a migration to create the column
25
+ column = @database.configured_schema(schema_name).table(table_name).column(column_name)
26
+ @generator.add_column column
27
+
28
+ # If the schema exists in the database but not in the configuration
29
+ # then we need to delete it.
30
+ elsif configuration_column[:exists] == false && database_column[:exists] == true
31
+ # a migration to create the column
32
+ column = @database.loaded_schema(schema_name).table(table_name).column(column_name)
33
+ @generator.remove_column column
34
+
35
+ # If the column exists in both the configuration and database representations
36
+ # but the definition (except description, which is handled seeprately below) is different
37
+ # then we need to update the definition.
38
+ elsif configuration_column.except(:exists, :description).filter { |name, attributes| attributes[:matches] == false }.any?
39
+ # update the column
40
+ column = @database.configured_schema(schema_name).table(table_name).column(column_name)
41
+ @generator.change_column column
42
+ # does the description also need to be updated
43
+ if configuration_column[:description][:matches] == false
44
+ # if the description was removed
45
+ if configuration_column[:description].nil?
46
+ @generator.remove_column_comment column
47
+ else
48
+ @generator.set_column_comment column
49
+ end
50
+ end
51
+
52
+ # If the column exists in both the configuration and database representations
53
+ # but the description is different then we need to update the description.
54
+ elsif configuration_column[:description][:matches] == false
55
+ column = @database.configured_schema(schema_name).table(table_name).column(column_name)
56
+ # if the description was removed
57
+ if configuration_column[:description].nil?
58
+ @generator.remove_column_comment column
59
+ else
60
+ @generator.set_column_comment column
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicMigrations
4
+ module Postgres
5
+ class Server
6
+ class Database
7
+ class Differences
8
+ class ToMigrations
9
+ module Schemas
10
+ module Tables
11
+ module ForeignKeyConstraints
12
+ def process_foreign_key_constraints schema_name, table_name, configuration_foreign_key_constraints, database_foreign_key_constraints
13
+ # process all the foreign_key_constraints
14
+ foreign_key_constraint_names = (configuration_foreign_key_constraints.keys + database_foreign_key_constraints.keys).uniq
15
+ foreign_key_constraint_names.each do |foreign_key_constraint_name|
16
+ process_foreign_key_constraint schema_name, table_name, foreign_key_constraint_name, configuration_foreign_key_constraints[foreign_key_constraint_name] || {}, database_foreign_key_constraints[foreign_key_constraint_name] || {}
17
+ end
18
+ end
19
+
20
+ def process_foreign_key_constraint schema_name, table_name, foreign_key_constraint_name, configuration_foreign_key_constraint, database_foreign_key_constraint
21
+ # If the foreign_key_constraint exists in the configuration but not in the database
22
+ # then we have to create it.
23
+ if configuration_foreign_key_constraint[:exists] == true && database_foreign_key_constraint[:exists] == false
24
+ # a migration to create the foreign_key_constraint
25
+ foreign_key_constraint = @database.configured_schema(schema_name).table(table_name).foreign_key_constraint(foreign_key_constraint_name)
26
+ @generator.add_foreign_key_constraint foreign_key_constraint
27
+
28
+ # If the schema exists in the database but not in the configuration
29
+ # then we need to delete it.
30
+ elsif configuration_foreign_key_constraint[:exists] == false && database_foreign_key_constraint[:exists] == true
31
+ # a migration to create the foreign_key_constraint
32
+ foreign_key_constraint = @database.loaded_schema(schema_name).table(table_name).foreign_key_constraint(foreign_key_constraint_name)
33
+ @generator.remove_foreign_key_constraint foreign_key_constraint
34
+
35
+ # If the foreign_key_constraint exists in both the configuration and database representations
36
+ # but the definition (except description, which is handled seeprately below) is different
37
+ # then we need to update the definition.
38
+ elsif configuration_foreign_key_constraint.except(:exists, :description).filter { |name, attributes| attributes[:matches] == false }.any?
39
+ # recreate the foreign_key_constraint
40
+ original_foreign_key_constraint = @database.loaded_schema(schema_name).table(table_name).foreign_key_constraint(foreign_key_constraint_name)
41
+ updated_foreign_key_constraint = @database.configured_schema(schema_name).table(table_name).foreign_key_constraint(foreign_key_constraint_name)
42
+ @generator.recreate_foreign_key_constraint original_foreign_key_constraint, updated_foreign_key_constraint
43
+ # does the description also need to be updated
44
+ if configuration_foreign_key_constraint[:description][:matches] == false
45
+ # if the description was removed
46
+ if configuration_foreign_key_constraint[:description].nil?
47
+ @generator.remove_foreign_key_constraint_comment foreign_key_constraint
48
+ else
49
+ @generator.set_foreign_key_constraint_comment foreign_key_constraint
50
+ end
51
+ end
52
+
53
+ # If the foreign_key_constraint exists in both the configuration and database representations
54
+ # but the description is different then we need to update the description.
55
+ elsif configuration_foreign_key_constraint[:description][:matches] == false
56
+ foreign_key_constraint = @database.configured_schema(schema_name).table(table_name).foreign_key_constraint(foreign_key_constraint_name)
57
+ # if the description was removed
58
+ if configuration_foreign_key_constraint[:description].nil?
59
+ @generator.remove_foreign_key_constraint_comment foreign_key_constraint
60
+ else
61
+ @generator.set_foreign_key_constraint_comment foreign_key_constraint
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicMigrations
4
+ module Postgres
5
+ class Server
6
+ class Database
7
+ class Differences
8
+ class ToMigrations
9
+ module Schemas
10
+ module Tables
11
+ module Indexes
12
+ def process_indexes schema_name, table_name, configuration_indexes, database_indexes
13
+ # process all the indexes
14
+ index_names = (configuration_indexes.keys + database_indexes.keys).uniq
15
+ index_names.each do |index_name|
16
+ process_index schema_name, table_name, index_name, configuration_indexes[index_name] || {}, database_indexes[index_name] || {}
17
+ end
18
+ end
19
+
20
+ def process_index schema_name, table_name, index_name, configuration_index, database_index
21
+ # If the index exists in the configuration but not in the database
22
+ # then we have to create it.
23
+ if configuration_index[:exists] == true && database_index[:exists] == false
24
+ # a migration to create the index
25
+ index = @database.configured_schema(schema_name).table(table_name).index(index_name)
26
+ @generator.add_index index
27
+
28
+ # If the schema exists in the database but not in the configuration
29
+ # then we need to delete it.
30
+ elsif configuration_index[:exists] == false && database_index[:exists] == true
31
+ # a migration to create the index
32
+ index = @database.loaded_schema(schema_name).table(table_name).index(index_name)
33
+ @generator.remove_index index
34
+
35
+ # If the index exists in both the configuration and database representations
36
+ # but the definition (except description, which is handled seeprately below) is different
37
+ # then we need to update the definition.
38
+ elsif configuration_index.except(:exists, :description).filter { |name, attributes| attributes[:matches] == false }.any?
39
+ # rebild the index
40
+ original_index = @database.loaded_schema(schema_name).table(table_name).index(index_name)
41
+ updated_index = @database.configured_schema(schema_name).table(table_name).index(index_name)
42
+ @generator.recreate_index original_index, updated_index
43
+ # does the description also need to be updated
44
+ if configuration_index[:description][:matches] == false
45
+ # if the description was removed
46
+ if configuration_index[:description].nil?
47
+ @generator.remove_index_comment index
48
+ else
49
+ @generator.set_index_comment index
50
+ end
51
+ end
52
+
53
+ # If the index exists in both the configuration and database representations
54
+ # but the description is different then we need to update the description.
55
+ elsif configuration_index[:description][:matches] == false
56
+ index = @database.configured_schema(schema_name).table(table_name).index(index_name)
57
+ # if the description was removed
58
+ if configuration_index[:description].nil?
59
+ @generator.remove_index_comment index
60
+ else
61
+ @generator.set_index_comment index
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end