activerecord 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1013 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +332 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +258 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +508 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1165 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +512 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +154 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +761 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +821 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +949 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +557 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +267 -0
  129. data/lib/active_record/core.rb +599 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +542 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +859 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +552 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1359 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +19 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  340. metadata +415 -0
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Locking
5
+ # Locking::Pessimistic provides support for row-level locking using
6
+ # SELECT ... FOR UPDATE and other lock types.
7
+ #
8
+ # Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
9
+ # lock on the selected rows:
10
+ # # select * from accounts where id=1 for update
11
+ # Account.lock.find(1)
12
+ #
13
+ # Call <tt>lock('some locking clause')</tt> to use a database-specific locking clause
14
+ # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
15
+ #
16
+ # Account.transaction do
17
+ # # select * from accounts where name = 'shugo' limit 1 for update nowait
18
+ # shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
19
+ # yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
20
+ # shugo.balance -= 100
21
+ # shugo.save!
22
+ # yuko.balance += 100
23
+ # yuko.save!
24
+ # end
25
+ #
26
+ # You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
27
+ # This may be better if you don't need to lock every row. Example:
28
+ #
29
+ # Account.transaction do
30
+ # # select * from accounts where ...
31
+ # accounts = Account.where(...)
32
+ # account1 = accounts.detect { |account| ... }
33
+ # account2 = accounts.detect { |account| ... }
34
+ # # select * from accounts where id=? for update
35
+ # account1.lock!
36
+ # account2.lock!
37
+ # account1.balance -= 100
38
+ # account1.save!
39
+ # account2.balance += 100
40
+ # account2.save!
41
+ # end
42
+ #
43
+ # You can start a transaction and acquire the lock in one go by calling
44
+ # <tt>with_lock</tt> with a block. The block is called from within
45
+ # a transaction, the object is already locked. Example:
46
+ #
47
+ # account = Account.first
48
+ # account.with_lock do
49
+ # # This block is called within a transaction,
50
+ # # account is already locked.
51
+ # account.balance -= 100
52
+ # account.save!
53
+ # end
54
+ #
55
+ # Database-specific information on row locking:
56
+ # MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
57
+ # PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
58
+ module Pessimistic
59
+ # Obtain a row lock on this record. Reloads the record to obtain the requested
60
+ # lock. Pass an SQL locking clause to append the end of the SELECT statement
61
+ # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
62
+ # the locked record.
63
+ def lock!(lock = true)
64
+ if persisted?
65
+ if has_changes_to_save?
66
+ raise(<<-MSG.squish)
67
+ Locking a record with unpersisted changes is not supported. Use
68
+ `save` to persist the changes, or `reload` to discard them
69
+ explicitly.
70
+ MSG
71
+ end
72
+
73
+ reload(lock: lock)
74
+ end
75
+ self
76
+ end
77
+
78
+ # Wraps the passed block in a transaction, locking the object
79
+ # before yielding. You can pass the SQL locking clause
80
+ # as argument (see <tt>lock!</tt>).
81
+ def with_lock(lock = true)
82
+ transaction do
83
+ lock!(lock)
84
+ yield
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class LogSubscriber < ActiveSupport::LogSubscriber
5
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
6
+
7
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
+
9
+ def self.runtime=(value)
10
+ ActiveRecord::RuntimeRegistry.sql_runtime = value
11
+ end
12
+
13
+ def self.runtime
14
+ ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
15
+ end
16
+
17
+ def self.reset_runtime
18
+ rt, self.runtime = runtime, 0
19
+ rt
20
+ end
21
+
22
+ def sql(event)
23
+ self.class.runtime += event.duration
24
+ return unless logger.debug?
25
+
26
+ payload = event.payload
27
+
28
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
29
+
30
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
31
+ name = "CACHE #{name}" if payload[:cached]
32
+ sql = payload[:sql]
33
+ binds = nil
34
+
35
+ unless (payload[:binds] || []).empty?
36
+ casted_params = type_casted_binds(payload[:type_casted_binds])
37
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
+ render_bind(attr, value)
39
+ }.inspect
40
+ end
41
+
42
+ name = colorize_payload_name(name, payload[:name])
43
+ sql = color(sql, sql_color(sql), true)
44
+
45
+ debug " #{name} #{sql}#{binds}"
46
+ end
47
+
48
+ private
49
+ def type_casted_binds(casted_binds)
50
+ casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
51
+ end
52
+
53
+ def render_bind(attr, value)
54
+ if attr.is_a?(Array)
55
+ attr = attr.first
56
+ elsif attr.type.binary? && attr.value
57
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
58
+ end
59
+
60
+ [attr && attr.name, value]
61
+ end
62
+
63
+ def colorize_payload_name(name, payload_name)
64
+ if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
65
+ color(name, MAGENTA, true)
66
+ else
67
+ color(name, CYAN, true)
68
+ end
69
+ end
70
+
71
+ def sql_color(sql)
72
+ case sql
73
+ when /\A\s*rollback/mi
74
+ RED
75
+ when /select .*for update/mi, /\A\s*lock/mi
76
+ WHITE
77
+ when /\A\s*select/i
78
+ BLUE
79
+ when /\A\s*insert/i
80
+ GREEN
81
+ when /\A\s*update/i
82
+ YELLOW
83
+ when /\A\s*delete/i
84
+ RED
85
+ when /transaction\s*\Z/i
86
+ CYAN
87
+ else
88
+ MAGENTA
89
+ end
90
+ end
91
+
92
+ def logger
93
+ ActiveRecord::Base.logger
94
+ end
95
+
96
+ def debug(progname = nil, &block)
97
+ return unless super
98
+
99
+ if ActiveRecord::Base.verbose_query_logs
100
+ log_query_source
101
+ end
102
+ end
103
+
104
+ def log_query_source
105
+ source = extract_query_source_location(caller)
106
+
107
+ if source
108
+ logger.debug(" ↳ #{source}")
109
+ end
110
+ end
111
+
112
+ def extract_query_source_location(locations)
113
+ backtrace_cleaner.clean(locations.lazy).first
114
+ end
115
+ end
116
+ end
117
+
118
+ ActiveRecord::LogSubscriber.attach_to :active_record
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ # The DatabaseSelector Middleware provides a framework for automatically
8
+ # swapping from the primary to the replica database connection. Rails
9
+ # provides a basic framework to determine when to swap and allows for
10
+ # applications to write custom strategy classes to override the default
11
+ # behavior.
12
+ #
13
+ # The resolver class defines when the application should switch (i.e. read
14
+ # from the primary if a write occurred less than 2 seconds ago) and a
15
+ # resolver context class that sets a value that helps the resolver class
16
+ # decide when to switch.
17
+ #
18
+ # Rails default middleware uses the request's session to set a timestamp
19
+ # that informs the application when to read from a primary or read from a
20
+ # replica.
21
+ #
22
+ # To use the DatabaseSelector in your application with default settings add
23
+ # the following options to your environment config:
24
+ #
25
+ # config.active_record.database_selector = { delay: 2.seconds }
26
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
27
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
28
+ #
29
+ # New applications will include these lines commented out in the production.rb.
30
+ #
31
+ # The default behavior can be changed by setting the config options to a
32
+ # custom class:
33
+ #
34
+ # config.active_record.database_selector = { delay: 2.seconds }
35
+ # config.active_record.database_resolver = MyResolver
36
+ # config.active_record.database_resolver_context = MyResolver::MySession
37
+ class DatabaseSelector
38
+ def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
39
+ @app = app
40
+ @resolver_klass = resolver_klass || Resolver
41
+ @context_klass = context_klass || Resolver::Session
42
+ @options = options
43
+ end
44
+
45
+ attr_reader :resolver_klass, :context_klass, :options
46
+
47
+ # Middleware that determines which database connection to use in a multiple
48
+ # database application.
49
+ def call(env)
50
+ request = ActionDispatch::Request.new(env)
51
+
52
+ select_database(request) do
53
+ @app.call(env)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def select_database(request, &blk)
60
+ context = context_klass.call(request)
61
+ resolver = resolver_klass.call(context, options)
62
+
63
+ if reading_request?(request)
64
+ resolver.read(&blk)
65
+ else
66
+ resolver.write(&blk)
67
+ end
68
+ end
69
+
70
+ def reading_request?(request)
71
+ request.get? || request.head?
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver/session"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ class DatabaseSelector
8
+ # The Resolver class is used by the DatabaseSelector middleware to
9
+ # determine which database the request should use.
10
+ #
11
+ # To change the behavior of the Resolver class in your application,
12
+ # create a custom resolver class that inherits from
13
+ # DatabaseSelector::Resolver and implements the methods that need to
14
+ # be changed.
15
+ #
16
+ # By default the Resolver class will send read traffic to the replica
17
+ # if it's been 2 seconds since the last write.
18
+ class Resolver # :nodoc:
19
+ SEND_TO_REPLICA_DELAY = 2.seconds
20
+
21
+ def self.call(context, options = {})
22
+ new(context, options)
23
+ end
24
+
25
+ def initialize(context, options = {})
26
+ @context = context
27
+ @options = options
28
+ @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
29
+ @instrumenter = ActiveSupport::Notifications.instrumenter
30
+ end
31
+
32
+ attr_reader :context, :delay, :instrumenter
33
+
34
+ def read(&blk)
35
+ if read_from_primary?
36
+ read_from_primary(&blk)
37
+ else
38
+ read_from_replica(&blk)
39
+ end
40
+ end
41
+
42
+ def write(&blk)
43
+ write_to_primary(&blk)
44
+ end
45
+
46
+ private
47
+
48
+ def read_from_primary(&blk)
49
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
50
+ ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
51
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
52
+ yield
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def read_from_replica(&blk)
59
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
60
+ instrumenter.instrument("database_selector.active_record.read_from_replica") do
61
+ yield
62
+ end
63
+ end
64
+ end
65
+
66
+ def write_to_primary(&blk)
67
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
68
+ ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
69
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
70
+ yield
71
+ ensure
72
+ context.update_last_write_timestamp
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def read_from_primary?
79
+ !time_since_last_write_ok?
80
+ end
81
+
82
+ def send_to_replica_delay
83
+ delay
84
+ end
85
+
86
+ def time_since_last_write_ok?
87
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Middleware
5
+ class DatabaseSelector
6
+ class Resolver
7
+ # The session class is used by the DatabaseSelector::Resolver to save
8
+ # timestamps of the last write in the session.
9
+ #
10
+ # The last_write is used to determine whether it's safe to read
11
+ # from the replica or the request needs to be sent to the primary.
12
+ class Session # :nodoc:
13
+ def self.call(request)
14
+ new(request.session)
15
+ end
16
+
17
+ # Converts time to a timestamp that represents milliseconds since
18
+ # epoch.
19
+ def self.convert_time_to_timestamp(time)
20
+ time.to_i * 1000 + time.usec / 1000
21
+ end
22
+
23
+ # Converts milliseconds since epoch timestamp into a time object.
24
+ def self.convert_timestamp_to_time(timestamp)
25
+ timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
26
+ end
27
+
28
+ def initialize(session)
29
+ @session = session
30
+ end
31
+
32
+ attr_reader :session
33
+
34
+ def last_write_timestamp
35
+ self.class.convert_timestamp_to_time(session[:last_write])
36
+ end
37
+
38
+ def update_last_write_timestamp
39
+ session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,1397 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "set"
5
+ require "zlib"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
8
+
9
+ module ActiveRecord
10
+ class MigrationError < ActiveRecordError #:nodoc:
11
+ def initialize(message = nil)
12
+ message = "\n\n#{message}\n\n" if message
13
+ super
14
+ end
15
+ end
16
+
17
+ # Exception that can be raised to stop migrations from being rolled back.
18
+ # For example the following migration is not reversible.
19
+ # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
20
+ #
21
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[5.0]
22
+ # def change
23
+ # create_table :distributors do |t|
24
+ # t.string :zipcode
25
+ # end
26
+ #
27
+ # execute <<~SQL
28
+ # ALTER TABLE distributors
29
+ # ADD CONSTRAINT zipchk
30
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
31
+ # SQL
32
+ # end
33
+ # end
34
+ #
35
+ # There are two ways to mitigate this problem.
36
+ #
37
+ # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
38
+ #
39
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
40
+ # def up
41
+ # create_table :distributors do |t|
42
+ # t.string :zipcode
43
+ # end
44
+ #
45
+ # execute <<~SQL
46
+ # ALTER TABLE distributors
47
+ # ADD CONSTRAINT zipchk
48
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
49
+ # SQL
50
+ # end
51
+ #
52
+ # def down
53
+ # execute <<~SQL
54
+ # ALTER TABLE distributors
55
+ # DROP CONSTRAINT zipchk
56
+ # SQL
57
+ #
58
+ # drop_table :distributors
59
+ # end
60
+ # end
61
+ #
62
+ # 2. Use the #reversible method in <tt>#change</tt> method:
63
+ #
64
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
65
+ # def change
66
+ # create_table :distributors do |t|
67
+ # t.string :zipcode
68
+ # end
69
+ #
70
+ # reversible do |dir|
71
+ # dir.up do
72
+ # execute <<~SQL
73
+ # ALTER TABLE distributors
74
+ # ADD CONSTRAINT zipchk
75
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
76
+ # SQL
77
+ # end
78
+ #
79
+ # dir.down do
80
+ # execute <<~SQL
81
+ # ALTER TABLE distributors
82
+ # DROP CONSTRAINT zipchk
83
+ # SQL
84
+ # end
85
+ # end
86
+ # end
87
+ # end
88
+ class IrreversibleMigration < MigrationError
89
+ end
90
+
91
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
92
+ def initialize(version = nil)
93
+ if version
94
+ super("Multiple migrations have the version number #{version}.")
95
+ else
96
+ super("Duplicate migration version error.")
97
+ end
98
+ end
99
+ end
100
+
101
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
102
+ def initialize(name = nil)
103
+ if name
104
+ super("Multiple migrations have the name #{name}.")
105
+ else
106
+ super("Duplicate migration name.")
107
+ end
108
+ end
109
+ end
110
+
111
+ class UnknownMigrationVersionError < MigrationError #:nodoc:
112
+ def initialize(version = nil)
113
+ if version
114
+ super("No migration with version number #{version}.")
115
+ else
116
+ super("Unknown migration version.")
117
+ end
118
+ end
119
+ end
120
+
121
+ class IllegalMigrationNameError < MigrationError #:nodoc:
122
+ def initialize(name = nil)
123
+ if name
124
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
125
+ else
126
+ super("Illegal name for migration.")
127
+ end
128
+ end
129
+ end
130
+
131
+ class PendingMigrationError < MigrationError #:nodoc:
132
+ include ActiveSupport::ActionableError
133
+
134
+ action "Run pending migrations" do
135
+ ActiveRecord::Tasks::DatabaseTasks.migrate
136
+ end
137
+
138
+ def initialize(message = nil)
139
+ if !message && defined?(Rails.env)
140
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
141
+ elsif !message
142
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate")
143
+ else
144
+ super
145
+ end
146
+ end
147
+ end
148
+
149
+ class ConcurrentMigrationError < MigrationError #:nodoc:
150
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
151
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
152
+
153
+ def initialize(message = DEFAULT_MESSAGE)
154
+ super
155
+ end
156
+ end
157
+
158
+ class NoEnvironmentInSchemaError < MigrationError #:nodoc:
159
+ def initialize
160
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n rails db:environment:set"
161
+ if defined?(Rails.env)
162
+ super("#{msg} RAILS_ENV=#{::Rails.env}")
163
+ else
164
+ super(msg)
165
+ end
166
+ end
167
+ end
168
+
169
+ class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
170
+ def initialize(env = "production")
171
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
172
+ msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
173
+ msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
174
+ super(msg)
175
+ end
176
+ end
177
+
178
+ class EnvironmentMismatchError < ActiveRecordError
179
+ def initialize(current: nil, stored: nil)
180
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
181
+ msg << "You are running in `#{ current }` environment. "
182
+ msg << "If you are sure you want to continue, first set the environment using:\n\n"
183
+ msg << " rails db:environment:set"
184
+ if defined?(Rails.env)
185
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
186
+ else
187
+ super("#{msg}\n\n")
188
+ end
189
+ end
190
+ end
191
+
192
+ # = Active Record Migrations
193
+ #
194
+ # Migrations can manage the evolution of a schema used by several physical
195
+ # databases. It's a solution to the common problem of adding a field to make
196
+ # a new feature work in your local database, but being unsure of how to
197
+ # push that change to other developers and to the production server. With
198
+ # migrations, you can describe the transformations in self-contained classes
199
+ # that can be checked into version control systems and executed against
200
+ # another database that might be one, two, or five versions behind.
201
+ #
202
+ # Example of a simple migration:
203
+ #
204
+ # class AddSsl < ActiveRecord::Migration[5.0]
205
+ # def up
206
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
207
+ # end
208
+ #
209
+ # def down
210
+ # remove_column :accounts, :ssl_enabled
211
+ # end
212
+ # end
213
+ #
214
+ # This migration will add a boolean flag to the accounts table and remove it
215
+ # if you're backing out of the migration. It shows how all migrations have
216
+ # two methods +up+ and +down+ that describes the transformations
217
+ # required to implement or remove the migration. These methods can consist
218
+ # of both the migration specific methods like +add_column+ and +remove_column+,
219
+ # but may also contain regular Ruby code for generating data needed for the
220
+ # transformations.
221
+ #
222
+ # Example of a more complex migration that also needs to initialize data:
223
+ #
224
+ # class AddSystemSettings < ActiveRecord::Migration[5.0]
225
+ # def up
226
+ # create_table :system_settings do |t|
227
+ # t.string :name
228
+ # t.string :label
229
+ # t.text :value
230
+ # t.string :type
231
+ # t.integer :position
232
+ # end
233
+ #
234
+ # SystemSetting.create name: 'notice',
235
+ # label: 'Use notice?',
236
+ # value: 1
237
+ # end
238
+ #
239
+ # def down
240
+ # drop_table :system_settings
241
+ # end
242
+ # end
243
+ #
244
+ # This migration first adds the +system_settings+ table, then creates the very
245
+ # first row in it using the Active Record model that relies on the table. It
246
+ # also uses the more advanced +create_table+ syntax where you can specify a
247
+ # complete table schema in one block call.
248
+ #
249
+ # == Available transformations
250
+ #
251
+ # === Creation
252
+ #
253
+ # * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
254
+ # table having its name as the lexical order of the first two
255
+ # arguments. See
256
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
257
+ # details.
258
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
259
+ # makes the table object available to a block that can then add columns to it,
260
+ # following the same format as +add_column+. See example above. The options hash
261
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
262
+ # table definition.
263
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
264
+ # to the table called +table_name+
265
+ # named +column_name+ specified to be one of the following types:
266
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
267
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
268
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
269
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
270
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
271
+ # <tt>{ limit: 50, null: false }</tt>) -- see
272
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
273
+ # * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
274
+ # foreign key. +from_table+ is the table with the key column, +to_table+ contains
275
+ # the referenced primary key.
276
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
277
+ # with the name of the column. Other options include
278
+ # <tt>:name</tt>, <tt>:unique</tt> (e.g.
279
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
280
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
281
+ # * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
282
+ # +reference_name_id+ by default an integer. See
283
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
284
+ # * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
285
+ # and +updated_at+) columns to +table_name+.
286
+ #
287
+ # === Modification
288
+ #
289
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
290
+ # the column to a different type using the same parameters as add_column.
291
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
292
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
293
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
294
+ # as +default_or_changes+ will make this change reversible in the migration.
295
+ # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
296
+ # Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
297
+ # indicates whether the value can be +NULL+. See
298
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
299
+ # details.
300
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
301
+ # the table called +name+. It makes the table object available to a block that
302
+ # can then add/remove columns, indexes or foreign keys to it.
303
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
304
+ # a column but keeps the type and content.
305
+ # * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
306
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
307
+ # to +new_name+.
308
+ #
309
+ # === Deletion
310
+ #
311
+ # * <tt>drop_table(name)</tt>: Drops the table called +name+.
312
+ # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
313
+ # specified by the given arguments.
314
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
315
+ # named +column_name+ from the table called +table_name+.
316
+ # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
317
+ # columns from the table definition.
318
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
319
+ # given foreign key from the table called +table_name+.
320
+ # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
321
+ # specified by +column_names+.
322
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
323
+ # specified by +index_name+.
324
+ # * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
325
+ # reference(s) on +table_name+ specified by +ref_name+.
326
+ # * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
327
+ # columns (+created_at+ and +updated_at+) from the table definition.
328
+ #
329
+ # == Irreversible transformations
330
+ #
331
+ # Some transformations are destructive in a manner that cannot be reversed.
332
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
333
+ # exception in their +down+ method.
334
+ #
335
+ # == Running migrations from within Rails
336
+ #
337
+ # The Rails package has several tools to help create and apply migrations.
338
+ #
339
+ # To generate a new migration, you can use
340
+ # rails generate migration MyNewMigration
341
+ #
342
+ # where MyNewMigration is the name of your migration. The generator will
343
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
344
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
345
+ # UTC formatted date and time that the migration was generated.
346
+ #
347
+ # There is a special syntactic shortcut to generate migrations that add fields to a table.
348
+ #
349
+ # rails generate migration add_fieldname_to_tablename fieldname:string
350
+ #
351
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
352
+ # class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
353
+ # def change
354
+ # add_column :tablenames, :fieldname, :string
355
+ # end
356
+ # end
357
+ #
358
+ # To run migrations against the currently configured database, use
359
+ # <tt>rails db:migrate</tt>. This will update the database by running all of the
360
+ # pending migrations, creating the <tt>schema_migrations</tt> table
361
+ # (see "About the schema_migrations table" section below) if missing. It will also
362
+ # invoke the db:schema:dump command, which will update your db/schema.rb file
363
+ # to match the structure of your database.
364
+ #
365
+ # To roll the database back to a previous migration version, use
366
+ # <tt>rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
367
+ # you wish to downgrade. Alternatively, you can also use the STEP option if you
368
+ # wish to rollback last few migrations. <tt>rails db:rollback STEP=2</tt> will rollback
369
+ # the latest two migrations.
370
+ #
371
+ # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
372
+ # that step will fail and you'll have some manual work to do.
373
+ #
374
+ # == Database support
375
+ #
376
+ # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
377
+ # SQL Server, and Oracle (all supported databases except DB2).
378
+ #
379
+ # == More examples
380
+ #
381
+ # Not all migrations change the schema. Some just fix the data:
382
+ #
383
+ # class RemoveEmptyTags < ActiveRecord::Migration[5.0]
384
+ # def up
385
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
386
+ # end
387
+ #
388
+ # def down
389
+ # # not much we can do to restore deleted data
390
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
391
+ # end
392
+ # end
393
+ #
394
+ # Others remove columns when they migrate up instead of down:
395
+ #
396
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
397
+ # def up
398
+ # remove_column :items, :incomplete_items_count
399
+ # remove_column :items, :completed_items_count
400
+ # end
401
+ #
402
+ # def down
403
+ # add_column :items, :incomplete_items_count
404
+ # add_column :items, :completed_items_count
405
+ # end
406
+ # end
407
+ #
408
+ # And sometimes you need to do something in SQL not abstracted directly by migrations:
409
+ #
410
+ # class MakeJoinUnique < ActiveRecord::Migration[5.0]
411
+ # def up
412
+ # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
413
+ # end
414
+ #
415
+ # def down
416
+ # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
417
+ # end
418
+ # end
419
+ #
420
+ # == Using a model after changing its table
421
+ #
422
+ # Sometimes you'll want to add a column in a migration and populate it
423
+ # immediately after. In that case, you'll need to make a call to
424
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
425
+ # latest column data from after the new column was added. Example:
426
+ #
427
+ # class AddPeopleSalary < ActiveRecord::Migration[5.0]
428
+ # def up
429
+ # add_column :people, :salary, :integer
430
+ # Person.reset_column_information
431
+ # Person.all.each do |p|
432
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
433
+ # end
434
+ # end
435
+ # end
436
+ #
437
+ # == Controlling verbosity
438
+ #
439
+ # By default, migrations will describe the actions they are taking, writing
440
+ # them to the console as they happen, along with benchmarks describing how
441
+ # long each step took.
442
+ #
443
+ # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
444
+ #
445
+ # You can also insert your own messages and benchmarks by using the +say_with_time+
446
+ # method:
447
+ #
448
+ # def up
449
+ # ...
450
+ # say_with_time "Updating salaries..." do
451
+ # Person.all.each do |p|
452
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
453
+ # end
454
+ # end
455
+ # ...
456
+ # end
457
+ #
458
+ # The phrase "Updating salaries..." would then be printed, along with the
459
+ # benchmark for the block when the block completes.
460
+ #
461
+ # == Timestamped Migrations
462
+ #
463
+ # By default, Rails generates migrations that look like:
464
+ #
465
+ # 20080717013526_your_migration_name.rb
466
+ #
467
+ # The prefix is a generation timestamp (in UTC).
468
+ #
469
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
470
+ # off by setting:
471
+ #
472
+ # config.active_record.timestamped_migrations = false
473
+ #
474
+ # In application.rb.
475
+ #
476
+ # == Reversible Migrations
477
+ #
478
+ # Reversible migrations are migrations that know how to go +down+ for you.
479
+ # You simply supply the +up+ logic, and the Migration system figures out
480
+ # how to execute the down commands for you.
481
+ #
482
+ # To define a reversible migration, define the +change+ method in your
483
+ # migration like this:
484
+ #
485
+ # class TenderloveMigration < ActiveRecord::Migration[5.0]
486
+ # def change
487
+ # create_table(:horses) do |t|
488
+ # t.column :content, :text
489
+ # t.column :remind_at, :datetime
490
+ # end
491
+ # end
492
+ # end
493
+ #
494
+ # This migration will create the horses table for you on the way up, and
495
+ # automatically figure out how to drop the table on the way down.
496
+ #
497
+ # Some commands cannot be reversed. If you care to define how to move up
498
+ # and down in these cases, you should define the +up+ and +down+ methods
499
+ # as before.
500
+ #
501
+ # If a command cannot be reversed, an
502
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
503
+ # the migration is moving down.
504
+ #
505
+ # For a list of commands that are reversible, please see
506
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
507
+ #
508
+ # == Transactional Migrations
509
+ #
510
+ # If the database adapter supports DDL transactions, all migrations will
511
+ # automatically be wrapped in a transaction. There are queries that you
512
+ # can't execute inside a transaction though, and for these situations
513
+ # you can turn the automatic transactions off.
514
+ #
515
+ # class ChangeEnum < ActiveRecord::Migration[5.0]
516
+ # disable_ddl_transaction!
517
+ #
518
+ # def up
519
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
520
+ # end
521
+ # end
522
+ #
523
+ # Remember that you can still open your own transactions, even if you
524
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
525
+ class Migration
526
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
527
+ autoload :Compatibility, "active_record/migration/compatibility"
528
+
529
+ # This must be defined before the inherited hook, below
530
+ class Current < Migration #:nodoc:
531
+ end
532
+
533
+ def self.inherited(subclass) #:nodoc:
534
+ super
535
+ if subclass.superclass == Migration
536
+ raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
537
+ "Please specify the Rails release the migration was written for:\n" \
538
+ "\n" \
539
+ " class #{subclass} < ActiveRecord::Migration[4.2]"
540
+ end
541
+ end
542
+
543
+ def self.[](version)
544
+ Compatibility.find(version)
545
+ end
546
+
547
+ def self.current_version
548
+ ActiveRecord::VERSION::STRING.to_f
549
+ end
550
+
551
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
552
+
553
+ # This class is used to verify that all migrations have been run before
554
+ # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
555
+ class CheckPending
556
+ def initialize(app)
557
+ @app = app
558
+ @last_check = 0
559
+ end
560
+
561
+ def call(env)
562
+ mtime = ActiveRecord::Base.connection.migration_context.last_migration.mtime.to_i
563
+ if @last_check < mtime
564
+ ActiveRecord::Migration.check_pending!(connection)
565
+ @last_check = mtime
566
+ end
567
+ @app.call(env)
568
+ end
569
+
570
+ private
571
+
572
+ def connection
573
+ ActiveRecord::Base.connection
574
+ end
575
+ end
576
+
577
+ class << self
578
+ attr_accessor :delegate #:nodoc:
579
+ attr_accessor :disable_ddl_transaction #:nodoc:
580
+
581
+ def nearest_delegate #:nodoc:
582
+ delegate || superclass.nearest_delegate
583
+ end
584
+
585
+ # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
586
+ def check_pending!(connection = Base.connection)
587
+ raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
588
+ end
589
+
590
+ def load_schema_if_pending!
591
+ current_config = Base.connection_config
592
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
593
+
594
+ needs_update = !all_configs.all? do |db_config|
595
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
596
+ end
597
+
598
+ if needs_update
599
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
600
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
601
+ FileUtils.cd(root) do
602
+ Base.clear_all_connections!
603
+ system("bin/rails db:test:prepare")
604
+ end
605
+ end
606
+
607
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
608
+ Base.establish_connection(current_config)
609
+
610
+ check_pending!
611
+ end
612
+
613
+ def maintain_test_schema! #:nodoc:
614
+ if ActiveRecord::Base.maintain_test_schema
615
+ suppress_messages { load_schema_if_pending! }
616
+ end
617
+ end
618
+
619
+ def method_missing(name, *args, &block) #:nodoc:
620
+ nearest_delegate.send(name, *args, &block)
621
+ end
622
+
623
+ def migrate(direction)
624
+ new.migrate direction
625
+ end
626
+
627
+ # Disable the transaction wrapping this migration.
628
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
629
+ #
630
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
631
+ def disable_ddl_transaction!
632
+ @disable_ddl_transaction = true
633
+ end
634
+ end
635
+
636
+ def disable_ddl_transaction #:nodoc:
637
+ self.class.disable_ddl_transaction
638
+ end
639
+
640
+ cattr_accessor :verbose
641
+ attr_accessor :name, :version
642
+
643
+ def initialize(name = self.class.name, version = nil)
644
+ @name = name
645
+ @version = version
646
+ @connection = nil
647
+ end
648
+
649
+ self.verbose = true
650
+ # instantiate the delegate object after initialize is defined
651
+ self.delegate = new
652
+
653
+ # Reverses the migration commands for the given block and
654
+ # the given migrations.
655
+ #
656
+ # The following migration will remove the table 'horses'
657
+ # and create the table 'apples' on the way up, and the reverse
658
+ # on the way down.
659
+ #
660
+ # class FixTLMigration < ActiveRecord::Migration[5.0]
661
+ # def change
662
+ # revert do
663
+ # create_table(:horses) do |t|
664
+ # t.text :content
665
+ # t.datetime :remind_at
666
+ # end
667
+ # end
668
+ # create_table(:apples) do |t|
669
+ # t.string :variety
670
+ # end
671
+ # end
672
+ # end
673
+ #
674
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
675
+ # documentation for Migration:
676
+ #
677
+ # require_relative '20121212123456_tenderlove_migration'
678
+ #
679
+ # class FixupTLMigration < ActiveRecord::Migration[5.0]
680
+ # def change
681
+ # revert TenderloveMigration
682
+ #
683
+ # create_table(:apples) do |t|
684
+ # t.string :variety
685
+ # end
686
+ # end
687
+ # end
688
+ #
689
+ # This command can be nested.
690
+ def revert(*migration_classes)
691
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
692
+ if block_given?
693
+ if connection.respond_to? :revert
694
+ connection.revert { yield }
695
+ else
696
+ recorder = command_recorder
697
+ @connection = recorder
698
+ suppress_messages do
699
+ connection.revert { yield }
700
+ end
701
+ @connection = recorder.delegate
702
+ recorder.replay(self)
703
+ end
704
+ end
705
+ end
706
+
707
+ def reverting?
708
+ connection.respond_to?(:reverting) && connection.reverting
709
+ end
710
+
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
712
+ def up
713
+ yield unless reverting
714
+ end
715
+
716
+ def down
717
+ yield if reverting
718
+ end
719
+ end
720
+
721
+ # Used to specify an operation that can be run in one direction or another.
722
+ # Call the methods +up+ and +down+ of the yielded object to run a block
723
+ # only in one given direction.
724
+ # The whole block will be called in the right order within the migration.
725
+ #
726
+ # In the following example, the looping on users will always be done
727
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
728
+ # even when migrating down:
729
+ #
730
+ # class SplitNameMigration < ActiveRecord::Migration[5.0]
731
+ # def change
732
+ # add_column :users, :first_name, :string
733
+ # add_column :users, :last_name, :string
734
+ #
735
+ # reversible do |dir|
736
+ # User.reset_column_information
737
+ # User.all.each do |u|
738
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
739
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
740
+ # u.save
741
+ # end
742
+ # end
743
+ #
744
+ # revert { add_column :users, :full_name, :string }
745
+ # end
746
+ # end
747
+ def reversible
748
+ helper = ReversibleBlockHelper.new(reverting?)
749
+ execute_block { yield helper }
750
+ end
751
+
752
+ # Used to specify an operation that is only run when migrating up
753
+ # (for example, populating a new column with its initial values).
754
+ #
755
+ # In the following example, the new column +published+ will be given
756
+ # the value +true+ for all existing records.
757
+ #
758
+ # class AddPublishedToPosts < ActiveRecord::Migration[5.2]
759
+ # def change
760
+ # add_column :posts, :published, :boolean, default: false
761
+ # up_only do
762
+ # execute "update posts set published = 'true'"
763
+ # end
764
+ # end
765
+ # end
766
+ def up_only
767
+ execute_block { yield } unless reverting?
768
+ end
769
+
770
+ # Runs the given migration classes.
771
+ # Last argument can specify options:
772
+ # - :direction (default is :up)
773
+ # - :revert (default is false)
774
+ def run(*migration_classes)
775
+ opts = migration_classes.extract_options!
776
+ dir = opts[:direction] || :up
777
+ dir = (dir == :down ? :up : :down) if opts[:revert]
778
+ if reverting?
779
+ # If in revert and going :up, say, we want to execute :down without reverting, so
780
+ revert { run(*migration_classes, direction: dir, revert: true) }
781
+ else
782
+ migration_classes.each do |migration_class|
783
+ migration_class.new.exec_migration(connection, dir)
784
+ end
785
+ end
786
+ end
787
+
788
+ def up
789
+ self.class.delegate = self
790
+ return unless self.class.respond_to?(:up)
791
+ self.class.up
792
+ end
793
+
794
+ def down
795
+ self.class.delegate = self
796
+ return unless self.class.respond_to?(:down)
797
+ self.class.down
798
+ end
799
+
800
+ # Execute this migration in the named direction
801
+ def migrate(direction)
802
+ return unless respond_to?(direction)
803
+
804
+ case direction
805
+ when :up then announce "migrating"
806
+ when :down then announce "reverting"
807
+ end
808
+
809
+ time = nil
810
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
811
+ time = Benchmark.measure do
812
+ exec_migration(conn, direction)
813
+ end
814
+ end
815
+
816
+ case direction
817
+ when :up then announce "migrated (%.4fs)" % time.real; write
818
+ when :down then announce "reverted (%.4fs)" % time.real; write
819
+ end
820
+ end
821
+
822
+ def exec_migration(conn, direction)
823
+ @connection = conn
824
+ if respond_to?(:change)
825
+ if direction == :down
826
+ revert { change }
827
+ else
828
+ change
829
+ end
830
+ else
831
+ send(direction)
832
+ end
833
+ ensure
834
+ @connection = nil
835
+ end
836
+
837
+ def write(text = "")
838
+ puts(text) if verbose
839
+ end
840
+
841
+ def announce(message)
842
+ text = "#{version} #{name}: #{message}"
843
+ length = [0, 75 - text.length].max
844
+ write "== %s %s" % [text, "=" * length]
845
+ end
846
+
847
+ # Takes a message argument and outputs it as is.
848
+ # A second boolean argument can be passed to specify whether to indent or not.
849
+ def say(message, subitem = false)
850
+ write "#{subitem ? " ->" : "--"} #{message}"
851
+ end
852
+
853
+ # Outputs text along with how long it took to run its block.
854
+ # If the block returns an integer it assumes it is the number of rows affected.
855
+ def say_with_time(message)
856
+ say(message)
857
+ result = nil
858
+ time = Benchmark.measure { result = yield }
859
+ say "%.4fs" % time.real, :subitem
860
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
861
+ result
862
+ end
863
+
864
+ # Takes a block as an argument and suppresses any output generated by the block.
865
+ def suppress_messages
866
+ save, self.verbose = verbose, false
867
+ yield
868
+ ensure
869
+ self.verbose = save
870
+ end
871
+
872
+ def connection
873
+ @connection || ActiveRecord::Base.connection
874
+ end
875
+
876
+ def method_missing(method, *arguments, &block)
877
+ arg_list = arguments.map(&:inspect) * ", "
878
+
879
+ say_with_time "#{method}(#{arg_list})" do
880
+ unless connection.respond_to? :revert
881
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
882
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
883
+ if [:rename_table, :add_foreign_key].include?(method) ||
884
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
885
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
886
+ end
887
+ end
888
+ end
889
+ return super unless connection.respond_to?(method)
890
+ connection.send(method, *arguments, &block)
891
+ end
892
+ end
893
+
894
+ def copy(destination, sources, options = {})
895
+ copied = []
896
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
897
+
898
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
899
+
900
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
901
+ last = destination_migrations.last
902
+ sources.each do |scope, path|
903
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
904
+
905
+ source_migrations.each do |migration|
906
+ source = File.binread(migration.filename)
907
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
908
+ magic_comments = +""
909
+ loop do
910
+ # If we have a magic comment in the original migration,
911
+ # insert our comment after the first newline(end of the magic comment line)
912
+ # so the magic keep working.
913
+ # Note that magic comments must be at the first line(except sh-bang).
914
+ source.sub!(/\A(?:#.*\b(?:en)?coding:\s*\S+|#\s*frozen_string_literal:\s*(?:true|false)).*\n/) do |magic_comment|
915
+ magic_comments << magic_comment; ""
916
+ end || break
917
+ end
918
+ source = "#{magic_comments}#{inserted_comment}#{source}"
919
+
920
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
921
+ if options[:on_skip] && duplicate.scope != scope.to_s
922
+ options[:on_skip].call(scope, migration)
923
+ end
924
+ next
925
+ end
926
+
927
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
928
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
929
+ old_path, migration.filename = migration.filename, new_path
930
+ last = migration
931
+
932
+ File.binwrite(migration.filename, source)
933
+ copied << migration
934
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
935
+ destination_migrations << migration
936
+ end
937
+ end
938
+
939
+ copied
940
+ end
941
+
942
+ # Finds the correct table name given an Active Record object.
943
+ # Uses the Active Record object's own table_name, or pre/suffix from the
944
+ # options passed in.
945
+ def proper_table_name(name, options = {})
946
+ if name.respond_to? :table_name
947
+ name.table_name
948
+ else
949
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
950
+ end
951
+ end
952
+
953
+ # Determines the version number of the next migration.
954
+ def next_migration_number(number)
955
+ if ActiveRecord::Base.timestamped_migrations
956
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
957
+ else
958
+ SchemaMigration.normalize_migration_number(number)
959
+ end
960
+ end
961
+
962
+ # Builds a hash for use in ActiveRecord::Migration#proper_table_name using
963
+ # the Active Record object's table_name prefix and suffix
964
+ def table_name_options(config = ActiveRecord::Base) #:nodoc:
965
+ {
966
+ table_name_prefix: config.table_name_prefix,
967
+ table_name_suffix: config.table_name_suffix
968
+ }
969
+ end
970
+
971
+ private
972
+ def execute_block
973
+ if connection.respond_to? :execute_block
974
+ super # use normal delegation to record the block
975
+ else
976
+ yield
977
+ end
978
+ end
979
+
980
+ def command_recorder
981
+ CommandRecorder.new(connection)
982
+ end
983
+ end
984
+
985
+ # MigrationProxy is used to defer loading of the actual migration classes
986
+ # until they are needed
987
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
988
+ def initialize(name, version, filename, scope)
989
+ super
990
+ @migration = nil
991
+ end
992
+
993
+ def basename
994
+ File.basename(filename)
995
+ end
996
+
997
+ def mtime
998
+ File.mtime filename
999
+ end
1000
+
1001
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
1002
+
1003
+ private
1004
+
1005
+ def migration
1006
+ @migration ||= load_migration
1007
+ end
1008
+
1009
+ def load_migration
1010
+ require(File.expand_path(filename))
1011
+ name.constantize.new(name, version)
1012
+ end
1013
+ end
1014
+
1015
+ class NullMigration < MigrationProxy #:nodoc:
1016
+ def initialize
1017
+ super(nil, 0, nil, nil)
1018
+ end
1019
+
1020
+ def mtime
1021
+ 0
1022
+ end
1023
+ end
1024
+
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
1027
+
1028
+ def initialize(migrations_paths, schema_migration)
1029
+ @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1031
+ end
1032
+
1033
+ def migrate(target_version = nil, &block)
1034
+ case
1035
+ when target_version.nil?
1036
+ up(target_version, &block)
1037
+ when current_version == 0 && target_version == 0
1038
+ []
1039
+ when current_version > target_version
1040
+ down(target_version, &block)
1041
+ else
1042
+ up(target_version, &block)
1043
+ end
1044
+ end
1045
+
1046
+ def rollback(steps = 1)
1047
+ move(:down, steps)
1048
+ end
1049
+
1050
+ def forward(steps = 1)
1051
+ move(:up, steps)
1052
+ end
1053
+
1054
+ def up(target_version = nil)
1055
+ selected_migrations = if block_given?
1056
+ migrations.select { |m| yield m }
1057
+ else
1058
+ migrations
1059
+ end
1060
+
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1062
+ end
1063
+
1064
+ def down(target_version = nil)
1065
+ selected_migrations = if block_given?
1066
+ migrations.select { |m| yield m }
1067
+ else
1068
+ migrations
1069
+ end
1070
+
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1072
+ end
1073
+
1074
+ def run(direction, target_version)
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1076
+ end
1077
+
1078
+ def open
1079
+ Migrator.new(:up, migrations, schema_migration)
1080
+ end
1081
+
1082
+ def get_all_versions
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1085
+ else
1086
+ []
1087
+ end
1088
+ end
1089
+
1090
+ def current_version
1091
+ get_all_versions.max || 0
1092
+ rescue ActiveRecord::NoDatabaseError
1093
+ end
1094
+
1095
+ def needs_migration?
1096
+ (migrations.collect(&:version) - get_all_versions).size > 0
1097
+ end
1098
+
1099
+ def any_migrations?
1100
+ migrations.any?
1101
+ end
1102
+
1103
+ def last_migration #:nodoc:
1104
+ migrations.last || NullMigration.new
1105
+ end
1106
+
1107
+ def migrations
1108
+ migrations = migration_files.map do |file|
1109
+ version, name, scope = parse_migration_filename(file)
1110
+ raise IllegalMigrationNameError.new(file) unless version
1111
+ version = version.to_i
1112
+ name = name.camelize
1113
+
1114
+ MigrationProxy.new(name, version, file, scope)
1115
+ end
1116
+
1117
+ migrations.sort_by(&:version)
1118
+ end
1119
+
1120
+ def migrations_status
1121
+ db_list = schema_migration.normalized_versions
1122
+
1123
+ file_list = migration_files.map do |file|
1124
+ version, name, scope = parse_migration_filename(file)
1125
+ raise IllegalMigrationNameError.new(file) unless version
1126
+ version = schema_migration.normalize_migration_number(version)
1127
+ status = db_list.delete(version) ? "up" : "down"
1128
+ [status, version, (name + scope).humanize]
1129
+ end.compact
1130
+
1131
+ db_list.map! do |version|
1132
+ ["up", version, "********** NO FILE **********"]
1133
+ end
1134
+
1135
+ (db_list + file_list).sort_by { |_, version, _| version }
1136
+ end
1137
+
1138
+ def current_environment
1139
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1140
+ end
1141
+
1142
+ def protected_environment?
1143
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1144
+ end
1145
+
1146
+ def last_stored_environment
1147
+ return nil if current_version == 0
1148
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
1149
+
1150
+ environment = ActiveRecord::InternalMetadata[:environment]
1151
+ raise NoEnvironmentInSchemaError unless environment
1152
+ environment
1153
+ end
1154
+
1155
+ private
1156
+ def migration_files
1157
+ paths = Array(migrations_paths)
1158
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1159
+ end
1160
+
1161
+ def parse_migration_filename(filename)
1162
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1163
+ end
1164
+
1165
+ def move(direction, steps)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
1167
+
1168
+ if current_version != 0 && !migrator.current_migration
1169
+ raise UnknownMigrationVersionError.new(current_version)
1170
+ end
1171
+
1172
+ start_index =
1173
+ if current_version == 0
1174
+ 0
1175
+ else
1176
+ migrator.migrations.index(migrator.current_migration)
1177
+ end
1178
+
1179
+ finish = migrator.migrations[start_index + steps]
1180
+ version = finish ? finish.version : 0
1181
+ send(direction, version)
1182
+ end
1183
+ end
1184
+
1185
+ class Migrator # :nodoc:
1186
+ class << self
1187
+ attr_accessor :migrations_paths
1188
+
1189
+ # For cases where a table doesn't exist like loading from schema cache
1190
+ def current_version
1191
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
1192
+ end
1193
+ end
1194
+
1195
+ self.migrations_paths = ["db/migrate"]
1196
+
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1198
+ @direction = direction
1199
+ @target_version = target_version
1200
+ @migrated_versions = nil
1201
+ @migrations = migrations
1202
+ @schema_migration = schema_migration
1203
+
1204
+ validate(@migrations)
1205
+
1206
+ @schema_migration.create_table
1207
+ ActiveRecord::InternalMetadata.create_table
1208
+ end
1209
+
1210
+ def current_version
1211
+ migrated.max || 0
1212
+ end
1213
+
1214
+ def current_migration
1215
+ migrations.detect { |m| m.version == current_version }
1216
+ end
1217
+ alias :current :current_migration
1218
+
1219
+ def run
1220
+ if use_advisory_lock?
1221
+ with_advisory_lock { run_without_lock }
1222
+ else
1223
+ run_without_lock
1224
+ end
1225
+ end
1226
+
1227
+ def migrate
1228
+ if use_advisory_lock?
1229
+ with_advisory_lock { migrate_without_lock }
1230
+ else
1231
+ migrate_without_lock
1232
+ end
1233
+ end
1234
+
1235
+ def runnable
1236
+ runnable = migrations[start..finish]
1237
+ if up?
1238
+ runnable.reject { |m| ran?(m) }
1239
+ else
1240
+ # skip the last migration if we're headed down, but not ALL the way down
1241
+ runnable.pop if target
1242
+ runnable.find_all { |m| ran?(m) }
1243
+ end
1244
+ end
1245
+
1246
+ def migrations
1247
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
1248
+ end
1249
+
1250
+ def pending_migrations
1251
+ already_migrated = migrated
1252
+ migrations.reject { |m| already_migrated.include?(m.version) }
1253
+ end
1254
+
1255
+ def migrated
1256
+ @migrated_versions || load_migrated
1257
+ end
1258
+
1259
+ def load_migrated
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1261
+ end
1262
+
1263
+ private
1264
+
1265
+ # Used for running a specific migration.
1266
+ def run_without_lock
1267
+ migration = migrations.detect { |m| m.version == @target_version }
1268
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1269
+ result = execute_migration_in_transaction(migration, @direction)
1270
+
1271
+ record_environment
1272
+ result
1273
+ end
1274
+
1275
+ # Used for running multiple migrations up to or down to a certain value.
1276
+ def migrate_without_lock
1277
+ if invalid_target?
1278
+ raise UnknownMigrationVersionError.new(@target_version)
1279
+ end
1280
+
1281
+ result = runnable.each do |migration|
1282
+ execute_migration_in_transaction(migration, @direction)
1283
+ end
1284
+
1285
+ record_environment
1286
+ result
1287
+ end
1288
+
1289
+ # Stores the current environment in the database.
1290
+ def record_environment
1291
+ return if down?
1292
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.migration_context.current_environment
1293
+ end
1294
+
1295
+ def ran?(migration)
1296
+ migrated.include?(migration.version.to_i)
1297
+ end
1298
+
1299
+ # Return true if a valid version is not provided.
1300
+ def invalid_target?
1301
+ @target_version && @target_version != 0 && !target
1302
+ end
1303
+
1304
+ def execute_migration_in_transaction(migration, direction)
1305
+ return if down? && !migrated.include?(migration.version.to_i)
1306
+ return if up? && migrated.include?(migration.version.to_i)
1307
+
1308
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1309
+
1310
+ ddl_transaction(migration) do
1311
+ migration.migrate(direction)
1312
+ record_version_state_after_migrating(migration.version)
1313
+ end
1314
+ rescue => e
1315
+ msg = +"An error has occurred, "
1316
+ msg << "this and " if use_transaction?(migration)
1317
+ msg << "all later migrations canceled:\n\n#{e}"
1318
+ raise StandardError, msg, e.backtrace
1319
+ end
1320
+
1321
+ def target
1322
+ migrations.detect { |m| m.version == @target_version }
1323
+ end
1324
+
1325
+ def finish
1326
+ migrations.index(target) || migrations.size - 1
1327
+ end
1328
+
1329
+ def start
1330
+ up? ? 0 : (migrations.index(current) || 0)
1331
+ end
1332
+
1333
+ def validate(migrations)
1334
+ name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
1335
+ raise DuplicateMigrationNameError.new(name) if name
1336
+
1337
+ version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
1338
+ raise DuplicateMigrationVersionError.new(version) if version
1339
+ end
1340
+
1341
+ def record_version_state_after_migrating(version)
1342
+ if down?
1343
+ migrated.delete(version)
1344
+ @schema_migration.delete_by(version: version.to_s)
1345
+ else
1346
+ migrated << version
1347
+ @schema_migration.create!(version: version.to_s)
1348
+ end
1349
+ end
1350
+
1351
+ def up?
1352
+ @direction == :up
1353
+ end
1354
+
1355
+ def down?
1356
+ @direction == :down
1357
+ end
1358
+
1359
+ # Wrap the migration in a transaction only if supported by the adapter.
1360
+ def ddl_transaction(migration)
1361
+ if use_transaction?(migration)
1362
+ Base.transaction { yield }
1363
+ else
1364
+ yield
1365
+ end
1366
+ end
1367
+
1368
+ def use_transaction?(migration)
1369
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1370
+ end
1371
+
1372
+ def use_advisory_lock?
1373
+ Base.connection.advisory_locks_enabled?
1374
+ end
1375
+
1376
+ def with_advisory_lock
1377
+ lock_id = generate_migrator_advisory_lock_id
1378
+ connection = Base.connection
1379
+ got_lock = connection.get_advisory_lock(lock_id)
1380
+ raise ConcurrentMigrationError unless got_lock
1381
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1382
+ yield
1383
+ ensure
1384
+ if got_lock && !connection.release_advisory_lock(lock_id)
1385
+ raise ConcurrentMigrationError.new(
1386
+ ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
1387
+ )
1388
+ end
1389
+ end
1390
+
1391
+ MIGRATOR_SALT = 2053462845
1392
+ def generate_migrator_advisory_lock_id
1393
+ db_name_hash = Zlib.crc32(Base.connection.current_database)
1394
+ MIGRATOR_SALT * db_name_hash
1395
+ end
1396
+ end
1397
+ end