declare_schema 0.10.0 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/Gemfile.lock +1 -1
- data/lib/declare_schema/model/foreign_key_definition.rb +4 -8
- data/lib/declare_schema/model/index_definition.rb +0 -19
- data/lib/declare_schema/schema_change/all.rb +22 -0
- data/lib/declare_schema/schema_change/base.rb +45 -0
- data/lib/declare_schema/schema_change/column_add.rb +27 -0
- data/lib/declare_schema/schema_change/column_change.rb +32 -0
- data/lib/declare_schema/schema_change/column_remove.rb +20 -0
- data/lib/declare_schema/schema_change/column_rename.rb +23 -0
- data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
- data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
- data/lib/declare_schema/schema_change/index_add.rb +33 -0
- data/lib/declare_schema/schema_change/index_remove.rb +20 -0
- data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
- data/lib/declare_schema/schema_change/table_add.rb +37 -0
- data/lib/declare_schema/schema_change/table_change.rb +36 -0
- data/lib/declare_schema/schema_change/table_remove.rb +22 -0
- data/lib/declare_schema/schema_change/table_rename.rb +22 -0
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/migrator.rb +172 -174
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +52 -14
- data/spec/lib/declare_schema/migration_generator_spec.rb +175 -303
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +0 -12
- data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
- data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
- data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
- data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
- data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
- data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
- data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
- data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
- data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
- data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +59 -11
- data/spec/support/acceptance_spec_helpers.rb +2 -2
- metadata +34 -5
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module DeclareSchema
|
6
|
+
module SchemaChange
|
7
|
+
class TableChange < Base
|
8
|
+
def initialize(table_name, old_options, new_options)
|
9
|
+
@table_name = table_name
|
10
|
+
@old_options = old_options
|
11
|
+
@new_options = new_options
|
12
|
+
end
|
13
|
+
|
14
|
+
def up_command
|
15
|
+
alter_table(@table_name, @new_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def down_command
|
19
|
+
alter_table(@table_name, @old_options)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
TABLE_OPTIONS_TO_SQL_MAPPINGS = {
|
25
|
+
charset: 'CHARACTER SET',
|
26
|
+
collation: 'COLLATE'
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def alter_table(table_name, options)
|
30
|
+
sql_options = options.map { |key, value| [TABLE_OPTIONS_TO_SQL_MAPPINGS[key], value] }
|
31
|
+
statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} #{sql_options.join(' ')}"
|
32
|
+
"execute #{statement.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module DeclareSchema
|
6
|
+
module SchemaChange
|
7
|
+
class TableRemove < Base
|
8
|
+
def initialize(table_name, add_table_back)
|
9
|
+
@table_name = table_name
|
10
|
+
@add_table_back = add_table_back
|
11
|
+
end
|
12
|
+
|
13
|
+
def up_command
|
14
|
+
"drop_table #{@table_name.to_sym.inspect}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def down_command
|
18
|
+
@add_table_back
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module DeclareSchema
|
6
|
+
module SchemaChange
|
7
|
+
class TableRename < Base
|
8
|
+
def initialize(old_name, new_name)
|
9
|
+
@old_name = old_name
|
10
|
+
@new_name = new_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def up_command
|
14
|
+
"rename_table #{@old_name.to_sym.inspect}, #{@new_name.to_sym.inspect}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def down_command
|
18
|
+
"rename_table #{@new_name.to_sym.inspect}, #{@old_name.to_sym.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'active_record'
|
4
4
|
require 'active_record/connection_adapters/abstract_adapter'
|
5
|
+
require 'declare_schema/schema_change/all'
|
5
6
|
|
6
7
|
module Generators
|
7
8
|
module DeclareSchema
|
@@ -24,9 +25,7 @@ module Generators
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def run(renames = {})
|
27
|
-
|
28
|
-
g.renames = renames
|
29
|
-
g.generate
|
28
|
+
Migrator.new(renames: renames).generate
|
30
29
|
end
|
31
30
|
|
32
31
|
def default_migration_name
|
@@ -48,14 +47,12 @@ module Generators
|
|
48
47
|
deprecate :default_charset=, :default_collation=, :default_charset, :default_collation, deprecator: ActiveSupport::Deprecation.new('1.0', 'declare_schema')
|
49
48
|
end
|
50
49
|
|
51
|
-
def initialize(ambiguity_resolver = {})
|
50
|
+
def initialize(ambiguity_resolver = {}, renames: nil)
|
52
51
|
@ambiguity_resolver = ambiguity_resolver
|
53
52
|
@drops = []
|
54
|
-
@renames =
|
53
|
+
@renames = renames
|
55
54
|
end
|
56
55
|
|
57
|
-
attr_accessor :renames
|
58
|
-
|
59
56
|
def load_rails_models
|
60
57
|
ActiveRecord::Migration.verbose = false
|
61
58
|
|
@@ -109,19 +106,24 @@ module Generators
|
|
109
106
|
# return a hash of table renames and modifies the passed arrays so
|
110
107
|
# that renamed tables are no longer listed as to_create or to_drop
|
111
108
|
def extract_table_renames!(to_create, to_drop)
|
112
|
-
if renames
|
109
|
+
if @renames
|
113
110
|
# A hash of table renames has been provided
|
114
111
|
|
115
112
|
to_rename = {}
|
116
|
-
renames.
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if to_create.delete(new_name.to_s) && to_drop.delete(old_name.to_s)
|
121
|
-
to_rename[old_name.to_s] = new_name.to_s
|
122
|
-
else
|
123
|
-
raise Error, "Invalid table rename specified: #{old_name} => #{new_name}"
|
113
|
+
@renames.each do |old_name, new_name|
|
114
|
+
if new_name.is_a?(Hash)
|
115
|
+
new_name = new_name[:table_name]
|
124
116
|
end
|
117
|
+
new_name or next
|
118
|
+
|
119
|
+
old_name = old_name.to_s
|
120
|
+
new_name = new_name.to_s
|
121
|
+
|
122
|
+
to_create.delete(new_name) or raise Error,
|
123
|
+
"Rename specified new name: #{new_name.inspect} but it was not in the `to_create` list"
|
124
|
+
to_drop.delete(old_name) or raise Error,
|
125
|
+
"Rename specified old name: #{old_name.inspect} but it was not in the `to_drop` list"
|
126
|
+
to_rename[old_name] = new_name
|
125
127
|
end
|
126
128
|
to_rename
|
127
129
|
|
@@ -133,18 +135,22 @@ module Generators
|
|
133
135
|
end
|
134
136
|
end
|
135
137
|
|
138
|
+
# return a hash of column renames and modifies the passed arrays so
|
139
|
+
# that renamed columns are no longer listed as to_create or to_drop
|
136
140
|
def extract_column_renames!(to_add, to_remove, table_name)
|
137
|
-
if renames
|
141
|
+
if @renames
|
138
142
|
to_rename = {}
|
139
|
-
if (column_renames = renames
|
140
|
-
# A hash of
|
141
|
-
|
142
|
-
column_renames.
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
143
|
+
if (column_renames = @renames[table_name.to_sym])
|
144
|
+
# A hash of column renames has been provided
|
145
|
+
|
146
|
+
column_renames.each do |old_name, new_name|
|
147
|
+
old_name = old_name.to_s
|
148
|
+
new_name = new_name.to_s
|
149
|
+
to_add.delete(new_name) or raise Error,
|
150
|
+
"Rename specified new name: #{new_name.inspect} but it was not in the `to_add` list for table #{table_name}"
|
151
|
+
to_remove.delete(old_name) or raise Error,
|
152
|
+
"Rename specified old name: #{old_name.inspect} but it was not in the `to_remove` list for table #{table_name}"
|
153
|
+
to_rename[old_name] = new_name
|
148
154
|
end
|
149
155
|
end
|
150
156
|
to_rename
|
@@ -201,85 +207,103 @@ module Generators
|
|
201
207
|
to_rename = extract_table_renames!(to_create, to_drop)
|
202
208
|
|
203
209
|
renames = to_rename.map do |old_name, new_name|
|
204
|
-
|
205
|
-
end
|
206
|
-
undo_renames = to_rename.map do |old_name, new_name|
|
207
|
-
"rename_table :#{new_name}, :#{old_name}"
|
208
|
-
end * "\n"
|
210
|
+
::DeclareSchema::SchemaChange::TableRename.new(old_name, new_name)
|
211
|
+
end
|
209
212
|
|
210
213
|
drops = to_drop.map do |t|
|
211
|
-
|
212
|
-
end
|
213
|
-
undo_drops = to_drop.map do |t|
|
214
|
-
add_table_back(t)
|
215
|
-
end * "\n\n"
|
214
|
+
::DeclareSchema::SchemaChange::TableRemove.new(t, add_table_back(t))
|
215
|
+
end
|
216
216
|
|
217
217
|
creates = to_create.map do |t|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
218
|
+
model = models_by_table_name[t]
|
219
|
+
disable_auto_increment = model.try(:disable_auto_increment)
|
220
|
+
|
221
|
+
primary_key_definition =
|
222
|
+
if disable_auto_increment
|
223
|
+
[:integer, :id, limit: 8, auto_increment: false, primary_key: true]
|
224
|
+
end
|
225
|
+
|
226
|
+
field_definitions = model.field_specs.values.sort_by(&:position).map do |f|
|
227
|
+
[f.type, f.name, f.sql_options]
|
228
|
+
end
|
229
|
+
|
230
|
+
table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
231
|
+
table_options = create_table_options(model, disable_auto_increment)
|
232
|
+
|
233
|
+
table_add = ::DeclareSchema::SchemaChange::TableAdd.new(t,
|
234
|
+
Array(primary_key_definition) + field_definitions,
|
235
|
+
table_options,
|
236
|
+
sql_options: table_options_definition.settings)
|
237
|
+
[
|
238
|
+
table_add,
|
239
|
+
*Array((create_indexes(model) if ::DeclareSchema.default_generate_indexing)),
|
240
|
+
*Array((create_constraints(model) if ::DeclareSchema.default_generate_foreign_keys))
|
241
|
+
]
|
242
|
+
end
|
223
243
|
|
224
244
|
changes = []
|
225
|
-
undo_changes = []
|
226
245
|
index_changes = []
|
227
|
-
undo_index_changes = []
|
228
246
|
fk_changes = []
|
229
|
-
undo_fk_changes = []
|
230
247
|
table_options_changes = []
|
231
|
-
undo_table_options_changes = []
|
232
248
|
|
233
249
|
to_change.each do |t|
|
234
250
|
model = models_by_table_name[t]
|
235
251
|
table = to_rename.key(t) || model.table_name
|
236
252
|
if table.in?(db_tables)
|
237
|
-
change,
|
253
|
+
change, index_change, fk_change, table_options_change = change_table(model, table)
|
238
254
|
changes << change
|
239
|
-
undo_changes << undo
|
240
255
|
index_changes << index_change
|
241
|
-
undo_index_changes << undo_index
|
242
256
|
fk_changes << fk_change
|
243
|
-
undo_fk_changes << undo_fk
|
244
257
|
table_options_changes << table_options_change
|
245
|
-
undo_table_options_changes << undo_table_options_change
|
246
258
|
end
|
247
259
|
end
|
248
260
|
|
249
|
-
|
250
|
-
|
261
|
+
migration_commands = [renames, drops, creates, changes, index_changes, fk_changes, table_options_changes].flatten
|
262
|
+
|
263
|
+
ordered_migration_commands = order_migrations(migration_commands)
|
251
264
|
|
252
|
-
|
265
|
+
up_and_down_migrations(ordered_migration_commands)
|
253
266
|
end
|
254
267
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
268
|
+
MIGRATION_ORDER = %w[ TableRename
|
269
|
+
TableAdd
|
270
|
+
TableChange
|
271
|
+
ColumnAdd
|
272
|
+
ColumnRename
|
273
|
+
ColumnChange
|
274
|
+
PrimaryKeyChange
|
275
|
+
IndexAdd
|
276
|
+
ForeignKeyAdd
|
277
|
+
ForeignKeyRemove
|
278
|
+
IndexRemove
|
279
|
+
ColumnRemove
|
280
|
+
TableRemove ]
|
263
281
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
282
|
+
def order_migrations(migration_commands)
|
283
|
+
migration_commands.each_with_index.sort_by do |command, index|
|
284
|
+
command_type = command.class.name.gsub(/.*::/, '')
|
285
|
+
priority = MIGRATION_ORDER.index(command_type) or raise "#{command_type.inspect} not found in #{MIGRATION_ORDER.inspect}"
|
286
|
+
[priority, index] # index keeps the sort stable in case of a tie
|
287
|
+
end.map(&:first) # remove the index
|
288
|
+
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
def up_and_down_migrations(migration_commands)
|
293
|
+
up = migration_commands.map(&:up ).select(&:present?)
|
294
|
+
down = migration_commands.map(&:down).select(&:present?).reverse
|
268
295
|
|
269
|
-
|
270
|
-
#{create_indexes(model).join("\n") if ::DeclareSchema.default_generate_indexing}
|
271
|
-
#{create_constraints(model).join("\n") if ::DeclareSchema.default_generate_foreign_keys}
|
272
|
-
EOS
|
296
|
+
[up * "\n\n", down * "\n\n"]
|
273
297
|
end
|
274
298
|
|
275
299
|
def create_table_options(model, disable_auto_increment)
|
276
300
|
primary_key = model._defined_primary_key
|
277
301
|
if primary_key.blank? || disable_auto_increment
|
278
|
-
|
302
|
+
{ id: false }
|
279
303
|
elsif primary_key == "id"
|
280
|
-
|
304
|
+
{ id: :bigint }
|
281
305
|
else
|
282
|
-
|
306
|
+
{ primary_key: primary_key.to_sym }
|
283
307
|
end
|
284
308
|
end
|
285
309
|
|
@@ -294,18 +318,18 @@ module Generators
|
|
294
318
|
end
|
295
319
|
end
|
296
320
|
|
321
|
+
# TODO: TECH-5338: optimize that index doesn't need to be dropped on undo since entire table will be dropped
|
297
322
|
def create_indexes(model)
|
298
|
-
model.index_definitions.map
|
323
|
+
model.index_definitions.map do |i|
|
324
|
+
::DeclareSchema::SchemaChange::IndexAdd.new(model.table_name, i.columns, unique: i.unique, where: i.where, name: i.name)
|
325
|
+
end
|
299
326
|
end
|
300
327
|
|
301
328
|
def create_constraints(model)
|
302
|
-
model.constraint_specs.map
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
options = field_spec.sql_options.merge(fk_field_options(field_spec.model, field_spec.name))
|
307
|
-
args = [field_spec.name.inspect] + format_options(options.compact)
|
308
|
-
format("t.%-*s %s", field_name_width, field_spec.type, args.join(', '))
|
329
|
+
model.constraint_specs.map do |fk|
|
330
|
+
::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
|
331
|
+
column_name: fk.foreign_key_name, name: fk.constraint_name)
|
332
|
+
end
|
309
333
|
end
|
310
334
|
|
311
335
|
def change_table(model, current_table_name)
|
@@ -332,37 +356,28 @@ module Generators
|
|
332
356
|
to_change = db_column_names & model_column_names
|
333
357
|
|
334
358
|
renames = to_rename.map do |old_name, new_name|
|
335
|
-
|
336
|
-
end
|
337
|
-
undo_renames = to_rename.map do |old_name, new_name|
|
338
|
-
"rename_column :#{new_table_name}, :#{new_name}, :#{old_name}"
|
359
|
+
::DeclareSchema::SchemaChange::ColumnRename.new(new_table_name, old_name, new_name)
|
339
360
|
end
|
340
361
|
|
341
|
-
to_add
|
362
|
+
to_add.sort_by! { |c| model.field_specs[c]&.position || 0 }
|
363
|
+
|
342
364
|
adds = to_add.map do |c|
|
343
|
-
|
365
|
+
type, options =
|
344
366
|
if (spec = model.field_specs[c])
|
345
|
-
|
346
|
-
[":#{spec.type}", *format_options(options.compact)]
|
367
|
+
[spec.type, spec.sql_options.merge(fk_field_options(model, c)).compact]
|
347
368
|
else
|
348
|
-
[
|
369
|
+
[:integer, {}]
|
349
370
|
end
|
350
|
-
|
351
|
-
end
|
352
|
-
undo_adds = to_add.map do |c|
|
353
|
-
"remove_column :#{new_table_name}, :#{c}"
|
371
|
+
::DeclareSchema::SchemaChange::ColumnAdd.new(new_table_name, c, type, options)
|
354
372
|
end
|
355
373
|
|
356
374
|
removes = to_remove.map do |c|
|
357
|
-
|
358
|
-
|
359
|
-
undo_removes = to_remove.map do |c|
|
360
|
-
add_column_back(model, current_table_name, c)
|
375
|
+
old_type, old_options = add_column_back(model, current_table_name, c)
|
376
|
+
::DeclareSchema::SchemaChange::ColumnRemove.new(new_table_name, c, old_type, old_options)
|
361
377
|
end
|
362
378
|
|
363
379
|
old_names = to_rename.invert
|
364
380
|
changes = []
|
365
|
-
undo_changes = []
|
366
381
|
to_change.each do |col_name_to_change|
|
367
382
|
orig_col_name = old_names[col_name_to_change] || col_name_to_change
|
368
383
|
column = db_columns[orig_col_name] or raise "failed to find column info for #{orig_col_name.inspect}"
|
@@ -374,36 +389,33 @@ module Generators
|
|
374
389
|
|
375
390
|
if !::DeclareSchema::Model::Column.equivalent_schema_attributes?(normalized_schema_attrs, col_attrs)
|
376
391
|
type = normalized_schema_attrs.delete(:type) or raise "no :type found in #{normalized_schema_attrs.inspect}"
|
377
|
-
|
378
|
-
|
379
|
-
|
392
|
+
old_type, old_options = change_column_back(model, current_table_name, orig_col_name)
|
393
|
+
changes << ::DeclareSchema::SchemaChange::ColumnChange.new(new_table_name, col_name_to_change,
|
394
|
+
new_type: type, new_options: normalized_schema_attrs,
|
395
|
+
old_type: old_type, old_options: old_options)
|
380
396
|
end
|
381
397
|
end
|
382
398
|
|
383
|
-
index_changes
|
384
|
-
fk_changes
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
table_options_changes
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
[(renames + adds + removes + changes)
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
fk_changes * "\n",
|
400
|
-
undo_fk_changes * "\n",
|
401
|
-
table_options_changes * "\n",
|
402
|
-
undo_table_options_changes * "\n"]
|
399
|
+
index_changes = change_indexes(model, current_table_name, to_rename)
|
400
|
+
fk_changes = if ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
|
401
|
+
[]
|
402
|
+
else
|
403
|
+
change_foreign_key_constraints(model, current_table_name)
|
404
|
+
end
|
405
|
+
table_options_changes = if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
406
|
+
change_table_options(model, current_table_name)
|
407
|
+
else
|
408
|
+
[]
|
409
|
+
end
|
410
|
+
|
411
|
+
[(renames + adds + removes + changes),
|
412
|
+
index_changes,
|
413
|
+
fk_changes,
|
414
|
+
table_options_changes]
|
403
415
|
end
|
404
416
|
|
405
|
-
def change_indexes(model, old_table_name,
|
406
|
-
::DeclareSchema.default_generate_indexing or return [
|
417
|
+
def change_indexes(model, old_table_name, to_rename)
|
418
|
+
::DeclareSchema.default_generate_indexing or return []
|
407
419
|
|
408
420
|
new_table_name = model.table_name
|
409
421
|
existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_model(model, old_table_name)
|
@@ -415,69 +427,54 @@ module Generators
|
|
415
427
|
end
|
416
428
|
end || i
|
417
429
|
end
|
418
|
-
|
419
|
-
|
420
|
-
|
430
|
+
existing_primary_keys, existing_indexes_without_primary_key = existing_indexes.partition { |i| i.primary_key? }
|
431
|
+
defined_primary_keys, model_indexes_without_primary_key = model_indexes.partition { |i| i.primary_key? }
|
432
|
+
existing_primary_keys.size <= 1 or raise "too many existing primary keys! #{existing_primary_keys.inspect}"
|
433
|
+
defined_primary_keys.size <= 1 or raise "too many defined primary keys! #{defined_primary_keys.inspect}"
|
434
|
+
existing_primary_key = existing_primary_keys.first
|
435
|
+
defined_primary_key = defined_primary_keys.first
|
436
|
+
|
437
|
+
existing_primary_key_columns = (existing_primary_key&.columns || []).map { |col_name| to_rename[col_name] || col_name }
|
438
|
+
|
439
|
+
if !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
|
440
|
+
change_primary_key =
|
441
|
+
if (existing_primary_key || defined_primary_key) &&
|
442
|
+
existing_primary_key_columns != defined_primary_key&.columns
|
443
|
+
::DeclareSchema::SchemaChange::PrimaryKeyChange.new(new_table_name, existing_primary_key_columns, defined_primary_key&.columns)
|
444
|
+
end
|
421
445
|
end
|
422
|
-
model_has_primary_key = model_indexes.any? { |i| i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME }
|
423
446
|
|
424
|
-
|
425
|
-
|
426
|
-
undo_add_indexes << drop_index(old_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
|
427
|
-
i.to_add_statement(new_table_name, existing_has_primary_key)
|
447
|
+
drop_indexes = (existing_indexes_without_primary_key - model_indexes_without_primary_key).map do |i|
|
448
|
+
::DeclareSchema::SchemaChange::IndexRemove.new(new_table_name, i.columns, unique: i.unique, where: i.where, name: i.name)
|
428
449
|
end
|
429
|
-
undo_drop_indexes = []
|
430
|
-
drop_indexes = (existing_indexes - model_indexes).map do |i|
|
431
|
-
undo_drop_indexes << i.to_add_statement(old_table_name, model_has_primary_key)
|
432
|
-
drop_index(new_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
|
433
|
-
end.compact
|
434
450
|
|
435
|
-
|
436
|
-
|
437
|
-
|
451
|
+
add_indexes = (model_indexes_without_primary_key - existing_indexes_without_primary_key).map do |i|
|
452
|
+
::DeclareSchema::SchemaChange::IndexAdd.new(new_table_name, i.columns, unique: i.unique, where: i.where, name: i.name)
|
453
|
+
end
|
438
454
|
|
439
|
-
|
440
|
-
|
441
|
-
# for why the rescue exists
|
442
|
-
"remove_index :#{table}, name: :#{name} rescue ActiveRecord::StatementInvalid"
|
455
|
+
# the order is important here - adding a :unique, for instance needs to remove then add
|
456
|
+
[Array(change_primary_key) + drop_indexes + add_indexes]
|
443
457
|
end
|
444
458
|
|
445
459
|
def change_foreign_key_constraints(model, old_table_name)
|
446
460
|
ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise ArgumentError, 'SQLite does not support foreign keys'
|
447
|
-
::DeclareSchema.default_generate_foreign_keys or return [
|
461
|
+
::DeclareSchema.default_generate_foreign_keys or return []
|
448
462
|
|
449
|
-
new_table_name = model.table_name
|
450
463
|
existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_model(model, old_table_name)
|
451
464
|
model_fks = model.constraint_specs
|
452
465
|
|
453
|
-
undo_add_fks = []
|
454
|
-
add_fks = (model_fks - existing_fks).map do |fk|
|
455
|
-
# next if fk.parent.constantize.abstract_class || fk.parent == fk.model.class_name
|
456
|
-
undo_add_fks << remove_foreign_key(old_table_name, fk.constraint_name)
|
457
|
-
fk.to_add_statement
|
458
|
-
end
|
459
|
-
|
460
|
-
undo_drop_fks = []
|
461
466
|
drop_fks = (existing_fks - model_fks).map do |fk|
|
462
|
-
|
463
|
-
|
467
|
+
::DeclareSchema::SchemaChange::ForeignKeyRemove.new(fk.child_table_name, fk.parent_table_name,
|
468
|
+
column_name: fk.foreign_key_name, name: fk.constraint_name)
|
464
469
|
end
|
465
470
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
"remove_foreign_key(#{old_table_name.inspect}, name: #{fk_name.to_s.inspect})"
|
471
|
-
end
|
472
|
-
|
473
|
-
def format_options(options)
|
474
|
-
options.map do |k, v|
|
475
|
-
if k.is_a?(Symbol)
|
476
|
-
"#{k}: #{v.inspect}"
|
477
|
-
else
|
478
|
-
"#{k.inspect} => #{v.inspect}"
|
479
|
-
end
|
471
|
+
add_fks = (model_fks - existing_fks).map do |fk|
|
472
|
+
# next if fk.parent.constantize.abstract_class || fk.parent == fk.model.class_name
|
473
|
+
::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
|
474
|
+
column_name: fk.foreign_key_name, name: fk.constraint_name)
|
480
475
|
end
|
476
|
+
|
477
|
+
[drop_fks + add_fks]
|
481
478
|
end
|
482
479
|
|
483
480
|
def fk_field_options(model, field_name)
|
@@ -506,11 +503,12 @@ module Generators
|
|
506
503
|
new_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
507
504
|
|
508
505
|
if old_options_definition.equivalent?(new_options_definition)
|
509
|
-
[
|
506
|
+
[]
|
510
507
|
else
|
511
508
|
[
|
512
|
-
|
513
|
-
|
509
|
+
::DeclareSchema::SchemaChange::TableChange.new(current_table_name,
|
510
|
+
old_options_definition.settings,
|
511
|
+
new_options_definition.settings)
|
514
512
|
]
|
515
513
|
end
|
516
514
|
end
|
@@ -528,7 +526,7 @@ module Generators
|
|
528
526
|
col_spec = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
|
529
527
|
schema_attributes = col_spec.schema_attributes
|
530
528
|
type = schema_attributes.delete(:type) or raise "no :type found in #{schema_attributes.inspect}"
|
531
|
-
[
|
529
|
+
[type, schema_attributes]
|
532
530
|
end
|
533
531
|
end
|
534
532
|
|
@@ -538,7 +536,7 @@ module Generators
|
|
538
536
|
col_spec = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
|
539
537
|
schema_attributes = col_spec.schema_attributes
|
540
538
|
type = schema_attributes.delete(:type) or raise "no :type found in #{schema_attributes.inspect}"
|
541
|
-
[
|
539
|
+
[type, schema_attributes]
|
542
540
|
end
|
543
541
|
end
|
544
542
|
|