dynamic_migrations 2.1.0 → 3.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  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 +76 -16
  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 +50 -26
  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 +6 -44
  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 +22 -112
  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 -4
  65. metadata +44 -3
  66. data/lib/dynamic_migrations/postgres/data_types.rb +0 -320
@@ -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