online_migrations 0.1.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.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +112 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +113 -0
- data/.yardopts +1 -0
- data/BACKGROUND_MIGRATIONS.md +288 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +108 -0
- data/LICENSE.txt +21 -0
- data/README.md +1067 -0
- data/Rakefile +23 -0
- data/gemfiles/activerecord_42.gemfile +6 -0
- data/gemfiles/activerecord_50.gemfile +5 -0
- data/gemfiles/activerecord_51.gemfile +5 -0
- data/gemfiles/activerecord_52.gemfile +5 -0
- data/gemfiles/activerecord_60.gemfile +5 -0
- data/gemfiles/activerecord_61.gemfile +5 -0
- data/gemfiles/activerecord_70.gemfile +5 -0
- data/gemfiles/activerecord_head.gemfile +5 -0
- data/lib/generators/online_migrations/background_migration_generator.rb +29 -0
- data/lib/generators/online_migrations/install_generator.rb +34 -0
- data/lib/generators/online_migrations/templates/background_migration.rb.tt +22 -0
- data/lib/generators/online_migrations/templates/initializer.rb.tt +94 -0
- data/lib/generators/online_migrations/templates/migration.rb.tt +46 -0
- data/lib/online_migrations/background_migration.rb +64 -0
- data/lib/online_migrations/background_migrations/advisory_lock.rb +62 -0
- data/lib/online_migrations/background_migrations/backfill_column.rb +52 -0
- data/lib/online_migrations/background_migrations/background_migration_class_validator.rb +36 -0
- data/lib/online_migrations/background_migrations/config.rb +98 -0
- data/lib/online_migrations/background_migrations/copy_column.rb +90 -0
- data/lib/online_migrations/background_migrations/migration.rb +210 -0
- data/lib/online_migrations/background_migrations/migration_helpers.rb +238 -0
- data/lib/online_migrations/background_migrations/migration_job.rb +92 -0
- data/lib/online_migrations/background_migrations/migration_job_runner.rb +63 -0
- data/lib/online_migrations/background_migrations/migration_job_status_validator.rb +27 -0
- data/lib/online_migrations/background_migrations/migration_runner.rb +97 -0
- data/lib/online_migrations/background_migrations/migration_status_validator.rb +45 -0
- data/lib/online_migrations/background_migrations/scheduler.rb +49 -0
- data/lib/online_migrations/batch_iterator.rb +87 -0
- data/lib/online_migrations/change_column_type_helpers.rb +587 -0
- data/lib/online_migrations/command_checker.rb +590 -0
- data/lib/online_migrations/command_recorder.rb +137 -0
- data/lib/online_migrations/config.rb +198 -0
- data/lib/online_migrations/copy_trigger.rb +91 -0
- data/lib/online_migrations/database_tasks.rb +19 -0
- data/lib/online_migrations/error_messages.rb +388 -0
- data/lib/online_migrations/foreign_key_definition.rb +17 -0
- data/lib/online_migrations/foreign_keys_collector.rb +33 -0
- data/lib/online_migrations/indexes_collector.rb +48 -0
- data/lib/online_migrations/lock_retrier.rb +250 -0
- data/lib/online_migrations/migration.rb +63 -0
- data/lib/online_migrations/migrator.rb +23 -0
- data/lib/online_migrations/schema_cache.rb +96 -0
- data/lib/online_migrations/schema_statements.rb +1042 -0
- data/lib/online_migrations/utils.rb +140 -0
- data/lib/online_migrations/version.rb +5 -0
- data/lib/online_migrations.rb +74 -0
- data/online_migrations.gemspec +28 -0
- metadata +119 -0
@@ -0,0 +1,388 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlineMigrations
|
4
|
+
# @private
|
5
|
+
module ErrorMessages
|
6
|
+
ERROR_MESSAGES = {
|
7
|
+
short_primary_key_type:
|
8
|
+
"Using short integer types for primary keys is dangerous due to the risk of running
|
9
|
+
out of IDs on inserts. Better to use one of 'bigint', 'bigserial' or 'uuid'.",
|
10
|
+
|
11
|
+
create_table:
|
12
|
+
"The `:force` option will destroy existing table. If this is intended, drop the existing table first.
|
13
|
+
Otherwise, remove the `:force` option.",
|
14
|
+
|
15
|
+
change_table:
|
16
|
+
"Online Migrations does not support inspecting what happens inside a
|
17
|
+
change_table block, so cannot help you here. Make really sure that what
|
18
|
+
you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
|
19
|
+
|
20
|
+
rename_table:
|
21
|
+
"Renaming a table that's in use will cause errors in your application.
|
22
|
+
migration_helpers provides a safer approach to do this:
|
23
|
+
|
24
|
+
1. Instruct Rails that you are going to rename a table:
|
25
|
+
|
26
|
+
OnlineMigrations.config.table_renames = {
|
27
|
+
<%= table_name.to_s.inspect %> => <%= new_name.to_s.inspect %>
|
28
|
+
}
|
29
|
+
|
30
|
+
2. Deploy
|
31
|
+
3. Tell the database that you are going to rename a table. This will not actually rename any tables,
|
32
|
+
nor any data/indexes/foreign keys copying will be made, so will be very fast.
|
33
|
+
It will use a VIEW to work with both table names simultaneously:
|
34
|
+
|
35
|
+
class Initialize<%= migration_name %> < <%= migration_parent %>
|
36
|
+
def change
|
37
|
+
initialize_table_rename <%= table_name.inspect %>, <%= new_name.inspect %>
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
4. Replace usages of the old table with a new table in the codebase
|
42
|
+
5. Remove the table rename config from step 1
|
43
|
+
6. Deploy
|
44
|
+
7. Remove the VIEW created on step 3:
|
45
|
+
|
46
|
+
class Finalize<%= migration_name %> < <%= migration_parent %>
|
47
|
+
def change
|
48
|
+
finalize_table_rename <%= table_name.inspect %>, <%= new_name.inspect %>
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
8. Deploy",
|
53
|
+
|
54
|
+
add_column_with_default:
|
55
|
+
"Adding a column with a non-null default blocks reads and writes while the entire table is rewritten.
|
56
|
+
|
57
|
+
A safer approach is to:
|
58
|
+
1. add the column without a default value
|
59
|
+
2. change the column default
|
60
|
+
3. backfill existing rows with the new value
|
61
|
+
<% if not_null %>
|
62
|
+
4. add the NOT NULL constraint
|
63
|
+
<% end %>
|
64
|
+
|
65
|
+
<% unless volatile_default %>
|
66
|
+
add_column_with_default takes care of all this steps:
|
67
|
+
|
68
|
+
class <%= migration_name %> < <%= migration_parent %>
|
69
|
+
disable_ddl_transaction!
|
70
|
+
|
71
|
+
def change
|
72
|
+
<%= code %>
|
73
|
+
end
|
74
|
+
end
|
75
|
+
<% end %>",
|
76
|
+
|
77
|
+
add_column_json:
|
78
|
+
"There's no equality operator for the json column type, which can cause errors for
|
79
|
+
existing SELECT DISTINCT queries in your application. Use jsonb instead.
|
80
|
+
|
81
|
+
class <%= migration_name %> < <%= migration_parent %>
|
82
|
+
def change
|
83
|
+
<%= code %>
|
84
|
+
end
|
85
|
+
end",
|
86
|
+
|
87
|
+
rename_column:
|
88
|
+
"Renaming a column that's in use will cause errors in your application.
|
89
|
+
migration_helpers provides a safer approach to do this:
|
90
|
+
|
91
|
+
1. Instruct Rails that you are going to rename a column:
|
92
|
+
|
93
|
+
OnlineMigrations.config.column_renames = {
|
94
|
+
<%= table_name.to_s.inspect %> => {
|
95
|
+
<%= column_name.to_s.inspect %> => <%= new_column.to_s.inspect %>
|
96
|
+
}
|
97
|
+
}
|
98
|
+
<% unless partial_writes %>
|
99
|
+
NOTE: You also need to temporarily enable partial writes until the process of column rename is fully done.
|
100
|
+
# config/application.rb
|
101
|
+
config.active_record.<%= partial_writes_setting %> = true
|
102
|
+
<% end %>
|
103
|
+
|
104
|
+
2. Deploy
|
105
|
+
3. Tell the database that you are going to rename a column. This will not actually rename any columns,
|
106
|
+
nor any data/indexes/foreign keys copying will be made, so will be instantaneous.
|
107
|
+
It will use a combination of a VIEW and column aliasing to work with both column names simultaneously:
|
108
|
+
|
109
|
+
class Initialize<%= migration_name %> < <%= migration_parent %>
|
110
|
+
def change
|
111
|
+
initialize_column_rename <%= table_name.inspect %>, <%= column_name.inspect %>, <%= new_column.inspect %>
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
4. Replace usages of the old column with a new column in the codebase
|
116
|
+
5. Deploy
|
117
|
+
6. Remove the column rename config from step 1
|
118
|
+
7. Remove the VIEW created in step 3:
|
119
|
+
|
120
|
+
class Finalize<%= migration_name %> < <%= migration_parent %>
|
121
|
+
def change
|
122
|
+
finalize_column_rename <%= table_name.inspect %>, <%= column_name.inspect %>, <%= new_column.inspect %>
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
8. Deploy",
|
127
|
+
|
128
|
+
change_column_with_not_null:
|
129
|
+
"Changing the type is safe, but setting NOT NULL is not.",
|
130
|
+
|
131
|
+
change_column:
|
132
|
+
"Changing the type of an existing column blocks reads and writes while the entire table is rewritten.
|
133
|
+
A safer approach can be accomplished in several steps:
|
134
|
+
|
135
|
+
1. Create a new column and keep column's data in sync:
|
136
|
+
|
137
|
+
class Initialize<%= migration_name %> < <%= migration_parent %>
|
138
|
+
def change
|
139
|
+
<%= initialize_change_code %>
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
2. Backfill data from the old column to the new column:
|
144
|
+
|
145
|
+
class Backfill<%= migration_name %> < <%= migration_parent %>
|
146
|
+
disable_ddl_transaction!
|
147
|
+
|
148
|
+
def up
|
149
|
+
<%= backfill_code %>
|
150
|
+
end
|
151
|
+
|
152
|
+
def down
|
153
|
+
# no op
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
3. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
|
158
|
+
|
159
|
+
class Finalize<%= migration_name %> < <%= migration_parent %>
|
160
|
+
disable_ddl_transaction!
|
161
|
+
|
162
|
+
def change
|
163
|
+
<%= finalize_code %>
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
4. Deploy
|
168
|
+
5. Finally, if everything is working as expected, remove copy trigger and old column:
|
169
|
+
|
170
|
+
class Cleanup<%= migration_name %> < <%= migration_parent %>
|
171
|
+
def up
|
172
|
+
<%= cleanup_code %>
|
173
|
+
end
|
174
|
+
|
175
|
+
def down
|
176
|
+
<%= cleanup_down_code %>
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
6. Deploy",
|
181
|
+
|
182
|
+
change_column_null:
|
183
|
+
"Setting NOT NULL on an existing column blocks reads and writes while every row is checked.
|
184
|
+
A safer approach is to add a NOT NULL check constraint and validate it in a separate transaction.
|
185
|
+
add_not_null_constraint and validate_not_null_constraint take care of that.
|
186
|
+
|
187
|
+
class <%= migration_name %> < <%= migration_parent %>
|
188
|
+
disable_ddl_transaction!
|
189
|
+
|
190
|
+
def change
|
191
|
+
<%= add_constraint_code %>
|
192
|
+
<% if backfill_code %>
|
193
|
+
<%= backfill_code %>
|
194
|
+
<% end %>
|
195
|
+
<%= validate_constraint_code %>
|
196
|
+
<% if remove_constraint_code %>
|
197
|
+
<%= remove_constraint_code %>
|
198
|
+
<%= change_column_null_code %>
|
199
|
+
<% end %>
|
200
|
+
end
|
201
|
+
end",
|
202
|
+
|
203
|
+
remove_column:
|
204
|
+
"<% if indexes.any? %>
|
205
|
+
Removing a column will automatically remove all of the indexes that involved the removed column.
|
206
|
+
But the indexes would be removed non-concurrently, so you need to safely remove the indexes first:
|
207
|
+
|
208
|
+
class <%= migration_name %>RemoveIndexes < <%= migration_parent %>
|
209
|
+
disable_ddl_transaction!
|
210
|
+
|
211
|
+
def change
|
212
|
+
<% indexes.each do |index| %>
|
213
|
+
remove_index <%= table_name %>, name: <%= index %>, algorithm: :concurrently
|
214
|
+
<% end %>
|
215
|
+
end
|
216
|
+
end
|
217
|
+
<% else %>
|
218
|
+
ActiveRecord caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
|
219
|
+
A safer approach is to:
|
220
|
+
|
221
|
+
1. Ignore the column(s):
|
222
|
+
|
223
|
+
class <%= model %> < <%= model_parent %>
|
224
|
+
self.ignored_columns = <%= columns %>
|
225
|
+
end
|
226
|
+
|
227
|
+
2. Deploy
|
228
|
+
3. Wrap column removing in a safety_assured { ... } block
|
229
|
+
|
230
|
+
class <%= migration_name %> < <%= migration_parent %>
|
231
|
+
def change
|
232
|
+
safety_assured { <%= command %> }
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
4. Remove columns ignoring
|
237
|
+
5. Deploy
|
238
|
+
<% end %>",
|
239
|
+
|
240
|
+
add_timestamps_with_default:
|
241
|
+
"Adding timestamps columns with non-null defaults blocks reads and writes while the entire table is rewritten.
|
242
|
+
|
243
|
+
A safer approach is to, for both timestamps columns:
|
244
|
+
1. add the column without a default value
|
245
|
+
2. change the column default
|
246
|
+
3. backfill existing rows with the new value
|
247
|
+
<% if not_null %>
|
248
|
+
4. add the NOT NULL constraint
|
249
|
+
<% end %>
|
250
|
+
|
251
|
+
<% unless volatile_default %>
|
252
|
+
add_column_with_default takes care of all this steps:
|
253
|
+
|
254
|
+
class <%= migration_name %> < <%= migration_parent %>
|
255
|
+
disable_ddl_transaction!
|
256
|
+
|
257
|
+
def change
|
258
|
+
<%= code %>
|
259
|
+
end
|
260
|
+
end
|
261
|
+
<% end %>",
|
262
|
+
|
263
|
+
add_reference:
|
264
|
+
"<% if bad_foreign_key %>
|
265
|
+
Adding a foreign key blocks writes on both tables.
|
266
|
+
<% end %>
|
267
|
+
<% if bad_index %>
|
268
|
+
Adding an index non-concurrently blocks writes.
|
269
|
+
<% end %>
|
270
|
+
Instead, use add_reference_concurrently helper. It will create a reference and take care of safely adding <% if bad_foreign_key %>a foreign key<% end %><% if bad_index && bad_foreign_key %> and <% end %><% if bad_index %>index<% end %>.
|
271
|
+
|
272
|
+
class <%= migration_name %> < <%= migration_parent %>
|
273
|
+
disable_ddl_transaction!
|
274
|
+
|
275
|
+
def change
|
276
|
+
<%= code %>
|
277
|
+
end
|
278
|
+
end",
|
279
|
+
|
280
|
+
add_hash_index:
|
281
|
+
"Hash index operations are not WAL-logged, so hash indexes might need to be rebuilt with REINDEX
|
282
|
+
after a database crash if there were unwritten changes. Also, changes to hash indexes are not replicated
|
283
|
+
over streaming or file-based replication after the initial base backup, so they give wrong answers
|
284
|
+
to queries that subsequently use them. For these reasons, hash index use is discouraged.
|
285
|
+
Use B-tree indexes instead.",
|
286
|
+
|
287
|
+
add_index:
|
288
|
+
"Adding an index non-concurrently blocks writes. Instead, use:
|
289
|
+
|
290
|
+
class <%= migration_name %> < <%= migration_parent %>
|
291
|
+
disable_ddl_transaction!
|
292
|
+
|
293
|
+
def change
|
294
|
+
<%= command %>
|
295
|
+
end
|
296
|
+
end",
|
297
|
+
|
298
|
+
remove_index:
|
299
|
+
"Removing an index non-concurrently blocks writes. Instead, use:
|
300
|
+
|
301
|
+
class <%= migration_name %> < <%= migration_parent %>
|
302
|
+
disable_ddl_transaction!
|
303
|
+
|
304
|
+
def change
|
305
|
+
<%= command %>
|
306
|
+
end
|
307
|
+
end",
|
308
|
+
|
309
|
+
add_foreign_key:
|
310
|
+
"Adding a foreign key blocks writes on both tables. Add the foreign key without validating existing rows,
|
311
|
+
and then validate them in a separate transaction.
|
312
|
+
|
313
|
+
class <%= migration_name %> < <%= migration_parent %>
|
314
|
+
disable_ddl_transaction!
|
315
|
+
|
316
|
+
def change
|
317
|
+
<%= add_code %>
|
318
|
+
<%= validate_code %>
|
319
|
+
end
|
320
|
+
end",
|
321
|
+
|
322
|
+
validate_foreign_key:
|
323
|
+
"Validating a foreign key while holding heavy locks on tables is dangerous.
|
324
|
+
Use disable_ddl_transaction! or a separate migration.",
|
325
|
+
|
326
|
+
add_check_constraint:
|
327
|
+
"Adding a check constraint blocks reads and writes while every row is checked.
|
328
|
+
A safer approach is to add the check constraint without validating existing rows,
|
329
|
+
and then validating them in a separate transaction.
|
330
|
+
|
331
|
+
class <%= migration_name %> < <%= migration_parent %>
|
332
|
+
disable_ddl_transaction!
|
333
|
+
|
334
|
+
def change
|
335
|
+
<%= add_code %>
|
336
|
+
<%= validate_code %>
|
337
|
+
end
|
338
|
+
end",
|
339
|
+
|
340
|
+
validate_constraint:
|
341
|
+
"Validating a constraint while holding heavy locks on tables is dangerous.
|
342
|
+
Use disable_ddl_transaction! or a separate migration.",
|
343
|
+
|
344
|
+
add_not_null_constraint:
|
345
|
+
"Adding a NOT NULL constraint blocks reads and writes while every row is checked.
|
346
|
+
A safer approach is to add the NOT NULL check constraint without validating existing rows,
|
347
|
+
and then validating them in a separate migration.
|
348
|
+
|
349
|
+
class <%= migration_name %> < <%= migration_parent %>
|
350
|
+
def change
|
351
|
+
<%= add_code %>
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
class <%= migration_name %>Validate < <%= migration_parent %>
|
356
|
+
def change
|
357
|
+
<%= validate_code %>
|
358
|
+
end
|
359
|
+
end",
|
360
|
+
|
361
|
+
add_text_limit_constraint:
|
362
|
+
"Adding a limit on the text column blocks reads and writes while every row is checked.
|
363
|
+
A safer approach is to add the limit check constraint without validating existing rows,
|
364
|
+
and then validating them in a separate migration.
|
365
|
+
|
366
|
+
class <%= migration_name %> < <%= migration_parent %>
|
367
|
+
def change
|
368
|
+
<%= add_code %>
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
class <%= migration_name %>Validate < <%= migration_parent %>
|
373
|
+
def change
|
374
|
+
<%= validate_code %>
|
375
|
+
end
|
376
|
+
end",
|
377
|
+
|
378
|
+
execute:
|
379
|
+
"Online Migrations does not support inspecting what happens inside an
|
380
|
+
execute call, so cannot help you here. Make really sure that what
|
381
|
+
you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
|
382
|
+
|
383
|
+
multiple_foreign_keys:
|
384
|
+
"Adding multiple foreign keys in a single migration blocks writes on all involved tables until migration is completed.
|
385
|
+
Avoid adding foreign key more than once per migration file, unless the source and target tables are identical.",
|
386
|
+
}
|
387
|
+
end
|
388
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlineMigrations
|
4
|
+
# @private
|
5
|
+
module ForeignKeyDefinition
|
6
|
+
if Utils.ar_version <= 4.2
|
7
|
+
def defined_for?(to_table: nil, **options)
|
8
|
+
(to_table.nil? || to_table.to_s == self.to_table) &&
|
9
|
+
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
10
|
+
end
|
11
|
+
elsif Utils.ar_version <= 5.1
|
12
|
+
def defined_for?(*args, **options)
|
13
|
+
super(*args, **options.except(:validate))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlineMigrations
|
4
|
+
# @private
|
5
|
+
class ForeignKeysCollector
|
6
|
+
attr_reader :referenced_tables
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@referenced_tables = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def collect(&table_definition)
|
13
|
+
table_definition.call(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def foreign_key(to_table, **_options)
|
17
|
+
@referenced_tables << to_table.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def references(*ref_names, **options)
|
21
|
+
if options[:foreign_key]
|
22
|
+
ref_names.each do |ref_name|
|
23
|
+
@referenced_tables << Utils.foreign_table_name(ref_name, options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias belongs_to references
|
28
|
+
|
29
|
+
def method_missing(*)
|
30
|
+
# we only care about foreign keys related methods
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlineMigrations
|
4
|
+
# @private
|
5
|
+
class IndexesCollector
|
6
|
+
IndexDefinition = Struct.new(:using)
|
7
|
+
|
8
|
+
COLUMN_TYPES = [:bigint, :binary, :boolean, :date, :datetime, :decimal,
|
9
|
+
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual]
|
10
|
+
|
11
|
+
attr_reader :indexes
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@indexes = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def collect(&table_definition)
|
18
|
+
table_definition.call(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def index(_column_name, **options)
|
22
|
+
@indexes << IndexDefinition.new(options[:using].to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def references(*_ref_names, **options)
|
26
|
+
index = options.fetch(:index) { Utils.ar_version >= 5.0 }
|
27
|
+
|
28
|
+
if index
|
29
|
+
using = index.is_a?(Hash) ? index[:using].to_s : nil
|
30
|
+
@indexes << IndexDefinition.new(using)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias belongs_to references
|
34
|
+
|
35
|
+
def method_missing(method_name, *_args, **options)
|
36
|
+
# Check for type-based methods, where we can also specify an index:
|
37
|
+
# t.string :email, index: true
|
38
|
+
if COLUMN_TYPES.include?(method_name)
|
39
|
+
index = options.fetch(:index, false)
|
40
|
+
|
41
|
+
if index
|
42
|
+
using = index.is_a?(Hash) ? index[:using].to_s : nil
|
43
|
+
@indexes << IndexDefinition.new(using)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|