omg-activerecord 8.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -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/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,1626 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "active_support/core_ext/array/access"
5
+ require "active_support/core_ext/enumerable"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
8
+ require "active_record/migration/pending_migration_connection"
9
+
10
+ module ActiveRecord
11
+ class MigrationError < ActiveRecordError # :nodoc:
12
+ def initialize(message = nil)
13
+ message = "\n\n#{message}\n\n" if message
14
+ super
15
+ end
16
+ end
17
+
18
+ # Exception that can be raised to stop migrations from being rolled back.
19
+ # For example the following migration is not reversible.
20
+ # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
21
+ #
22
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[8.0]
23
+ # def change
24
+ # create_table :distributors do |t|
25
+ # t.string :zipcode
26
+ # end
27
+ #
28
+ # execute <<~SQL
29
+ # ALTER TABLE distributors
30
+ # ADD CONSTRAINT zipchk
31
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
32
+ # SQL
33
+ # end
34
+ # end
35
+ #
36
+ # There are two ways to mitigate this problem.
37
+ #
38
+ # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
39
+ #
40
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.0]
41
+ # def up
42
+ # create_table :distributors do |t|
43
+ # t.string :zipcode
44
+ # end
45
+ #
46
+ # execute <<~SQL
47
+ # ALTER TABLE distributors
48
+ # ADD CONSTRAINT zipchk
49
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
50
+ # SQL
51
+ # end
52
+ #
53
+ # def down
54
+ # execute <<~SQL
55
+ # ALTER TABLE distributors
56
+ # DROP CONSTRAINT zipchk
57
+ # SQL
58
+ #
59
+ # drop_table :distributors
60
+ # end
61
+ # end
62
+ #
63
+ # 2. Use the #reversible method in <tt>#change</tt> method:
64
+ #
65
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.0]
66
+ # def change
67
+ # create_table :distributors do |t|
68
+ # t.string :zipcode
69
+ # end
70
+ #
71
+ # reversible do |dir|
72
+ # dir.up do
73
+ # execute <<~SQL
74
+ # ALTER TABLE distributors
75
+ # ADD CONSTRAINT zipchk
76
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
77
+ # SQL
78
+ # end
79
+ #
80
+ # dir.down do
81
+ # execute <<~SQL
82
+ # ALTER TABLE distributors
83
+ # DROP CONSTRAINT zipchk
84
+ # SQL
85
+ # end
86
+ # end
87
+ # end
88
+ # end
89
+ class IrreversibleMigration < MigrationError
90
+ end
91
+
92
+ class DuplicateMigrationVersionError < MigrationError # :nodoc:
93
+ def initialize(version = nil)
94
+ if version
95
+ super("Multiple migrations have the version number #{version}.")
96
+ else
97
+ super("Duplicate migration version error.")
98
+ end
99
+ end
100
+ end
101
+
102
+ class DuplicateMigrationNameError < MigrationError # :nodoc:
103
+ def initialize(name = nil)
104
+ if name
105
+ super("Multiple migrations have the name #{name}.")
106
+ else
107
+ super("Duplicate migration name.")
108
+ end
109
+ end
110
+ end
111
+
112
+ class UnknownMigrationVersionError < MigrationError # :nodoc:
113
+ def initialize(version = nil)
114
+ if version
115
+ super("No migration with version number #{version}.")
116
+ else
117
+ super("Unknown migration version.")
118
+ end
119
+ end
120
+ end
121
+
122
+ class IllegalMigrationNameError < MigrationError # :nodoc:
123
+ def initialize(name = nil)
124
+ if name
125
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
126
+ else
127
+ super("Illegal name for migration.")
128
+ end
129
+ end
130
+ end
131
+
132
+ class InvalidMigrationTimestampError < MigrationError # :nodoc:
133
+ def initialize(version = nil, name = nil)
134
+ if version && name
135
+ super(<<~MSG)
136
+ Invalid timestamp #{version} for migration file: #{name}.
137
+ Timestamp must be in form YYYYMMDDHHMMSS, and less than #{(Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S")}.
138
+ MSG
139
+ else
140
+ super(<<~MSG)
141
+ Invalid timestamp for migration.
142
+ Timestamp must be in form YYYYMMDDHHMMSS, and less than #{(Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S")}.
143
+ MSG
144
+ end
145
+ end
146
+ end
147
+
148
+ class PendingMigrationError < MigrationError # :nodoc:
149
+ include ActiveSupport::ActionableError
150
+
151
+ action "Run pending migrations" do
152
+ ActiveRecord::Tasks::DatabaseTasks.migrate
153
+
154
+ if ActiveRecord.dump_schema_after_migration
155
+ connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
156
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(connection.pool.db_config)
157
+ end
158
+ end
159
+
160
+ def initialize(message = nil, pending_migrations: nil)
161
+ if pending_migrations.nil?
162
+ pending_migrations = connection_pool.migration_context.open.pending_migrations
163
+ end
164
+
165
+ super(message || detailed_migration_message(pending_migrations))
166
+ end
167
+
168
+ private
169
+ def detailed_migration_message(pending_migrations)
170
+ message = "Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate"
171
+ message += " RAILS_ENV=#{::Rails.env}" if defined?(Rails.env) && !Rails.env.local?
172
+ message += "\n\n"
173
+
174
+ message += "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}\n\n"
175
+
176
+ pending_migrations.each do |pending_migration|
177
+ message += "#{pending_migration.filename}\n"
178
+ end
179
+
180
+ message
181
+ end
182
+
183
+ def connection_pool
184
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
185
+ end
186
+ end
187
+
188
+ class ConcurrentMigrationError < MigrationError # :nodoc:
189
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
190
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
191
+
192
+ def initialize(message = DEFAULT_MESSAGE)
193
+ super
194
+ end
195
+ end
196
+
197
+ class NoEnvironmentInSchemaError < MigrationError # :nodoc:
198
+ def initialize
199
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
200
+ if defined?(Rails.env)
201
+ super("#{msg} RAILS_ENV=#{::Rails.env}")
202
+ else
203
+ super(msg)
204
+ end
205
+ end
206
+ end
207
+
208
+ class ProtectedEnvironmentError < ActiveRecordError # :nodoc:
209
+ def initialize(env = "production")
210
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
211
+ msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
212
+ msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
213
+ super(msg)
214
+ end
215
+ end
216
+
217
+ class EnvironmentMismatchError < ActiveRecordError
218
+ def initialize(current: nil, stored: nil)
219
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
220
+ msg << "You are running in `#{ current }` environment. "
221
+ msg << "If you are sure you want to continue, first set the environment using:\n\n"
222
+ msg << " bin/rails db:environment:set"
223
+ if defined?(Rails.env)
224
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
225
+ else
226
+ super("#{msg}\n\n")
227
+ end
228
+ end
229
+ end
230
+
231
+ class EnvironmentStorageError < ActiveRecordError # :nodoc:
232
+ def initialize
233
+ msg = +"You are attempting to store the environment in a database where metadata is disabled.\n"
234
+ msg << "Check your database configuration to see if this is intended."
235
+ super(msg)
236
+ end
237
+ end
238
+
239
+ # = Active Record Migrations
240
+ #
241
+ # Migrations can manage the evolution of a schema used by several physical
242
+ # databases. It's a solution to the common problem of adding a field to make
243
+ # a new feature work in your local database, but being unsure of how to
244
+ # push that change to other developers and to the production server. With
245
+ # migrations, you can describe the transformations in self-contained classes
246
+ # that can be checked into version control systems and executed against
247
+ # another database that might be one, two, or five versions behind.
248
+ #
249
+ # Example of a simple migration:
250
+ #
251
+ # class AddSsl < ActiveRecord::Migration[8.0]
252
+ # def up
253
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
254
+ # end
255
+ #
256
+ # def down
257
+ # remove_column :accounts, :ssl_enabled
258
+ # end
259
+ # end
260
+ #
261
+ # This migration will add a boolean flag to the accounts table and remove it
262
+ # if you're backing out of the migration. It shows how all migrations have
263
+ # two methods +up+ and +down+ that describes the transformations
264
+ # required to implement or remove the migration. These methods can consist
265
+ # of both the migration specific methods like +add_column+ and +remove_column+,
266
+ # but may also contain regular Ruby code for generating data needed for the
267
+ # transformations.
268
+ #
269
+ # Example of a more complex migration that also needs to initialize data:
270
+ #
271
+ # class AddSystemSettings < ActiveRecord::Migration[8.0]
272
+ # def up
273
+ # create_table :system_settings do |t|
274
+ # t.string :name
275
+ # t.string :label
276
+ # t.text :value
277
+ # t.string :type
278
+ # t.integer :position
279
+ # end
280
+ #
281
+ # SystemSetting.create name: 'notice',
282
+ # label: 'Use notice?',
283
+ # value: 1
284
+ # end
285
+ #
286
+ # def down
287
+ # drop_table :system_settings
288
+ # end
289
+ # end
290
+ #
291
+ # This migration first adds the +system_settings+ table, then creates the very
292
+ # first row in it using the Active Record model that relies on the table. It
293
+ # also uses the more advanced +create_table+ syntax where you can specify a
294
+ # complete table schema in one block call.
295
+ #
296
+ # == Available transformations
297
+ #
298
+ # === Creation
299
+ #
300
+ # * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
301
+ # table having its name as the lexical order of the first two
302
+ # arguments. See
303
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
304
+ # details.
305
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
306
+ # makes the table object available to a block that can then add columns to it,
307
+ # following the same format as +add_column+. See example above. The options hash
308
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
309
+ # table definition.
310
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
311
+ # to the table called +table_name+
312
+ # named +column_name+ specified to be one of the following types:
313
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
314
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
315
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
316
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
317
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
318
+ # <tt>{ limit: 50, null: false }</tt>) -- see
319
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
320
+ # * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
321
+ # foreign key. +from_table+ is the table with the key column, +to_table+ contains
322
+ # the referenced primary key.
323
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
324
+ # with the name of the column. Other options include
325
+ # <tt>:name</tt>, <tt>:unique</tt> (e.g.
326
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
327
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
328
+ # * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
329
+ # +reference_name_id+ by default an integer. See
330
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
331
+ # * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
332
+ # and +updated_at+) columns to +table_name+.
333
+ #
334
+ # === Modification
335
+ #
336
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
337
+ # the column to a different type using the same parameters as add_column.
338
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
339
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
340
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
341
+ # as +default_or_changes+ will make this change reversible in the migration.
342
+ # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
343
+ # Sets or removes a <tt>NOT NULL</tt> constraint on +column_name+. The +null+ flag
344
+ # indicates whether the value can be +NULL+. See
345
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
346
+ # details.
347
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
348
+ # the table called +name+. It makes the table object available to a block that
349
+ # can then add/remove columns, indexes, or foreign keys to it.
350
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
351
+ # a column but keeps the type and content.
352
+ # * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
353
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
354
+ # to +new_name+.
355
+ #
356
+ # === Deletion
357
+ #
358
+ # * <tt>drop_table(*names)</tt>: Drops the given tables.
359
+ # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
360
+ # specified by the given arguments.
361
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
362
+ # named +column_name+ from the table called +table_name+.
363
+ # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
364
+ # columns from the table definition.
365
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
366
+ # given foreign key from the table called +table_name+.
367
+ # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
368
+ # specified by +column_names+.
369
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
370
+ # specified by +index_name+.
371
+ # * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
372
+ # reference(s) on +table_name+ specified by +ref_name+.
373
+ # * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
374
+ # columns (+created_at+ and +updated_at+) from the table definition.
375
+ #
376
+ # == Irreversible transformations
377
+ #
378
+ # Some transformations are destructive in a manner that cannot be reversed.
379
+ # Migrations of that kind should raise an ActiveRecord::IrreversibleMigration
380
+ # exception in their +down+ method.
381
+ #
382
+ # == Running migrations from within \Rails
383
+ #
384
+ # The \Rails package has several tools to help create and apply migrations.
385
+ #
386
+ # To generate a new migration, you can use
387
+ #
388
+ # $ bin/rails generate migration MyNewMigration
389
+ #
390
+ # where MyNewMigration is the name of your migration. The generator will
391
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
392
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
393
+ # UTC formatted date and time that the migration was generated.
394
+ #
395
+ # There is a special syntactic shortcut to generate migrations that add fields to a table.
396
+ #
397
+ # $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
398
+ #
399
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
400
+ # class AddFieldnameToTablename < ActiveRecord::Migration[8.0]
401
+ # def change
402
+ # add_column :tablenames, :fieldname, :string
403
+ # end
404
+ # end
405
+ #
406
+ # To run migrations against the currently configured database, use
407
+ # <tt>bin/rails db:migrate</tt>. This will update the database by running all of the
408
+ # pending migrations, creating the <tt>schema_migrations</tt> table
409
+ # (see "About the schema_migrations table" section below) if missing. It will also
410
+ # invoke the db:schema:dump command, which will update your db/schema.rb file
411
+ # to match the structure of your database.
412
+ #
413
+ # To roll the database back to a previous migration version, use
414
+ # <tt>bin/rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
415
+ # you wish to downgrade. Alternatively, you can also use the STEP option if you
416
+ # wish to rollback last few migrations. <tt>bin/rails db:rollback STEP=2</tt> will rollback
417
+ # the latest two migrations.
418
+ #
419
+ # If any of the migrations throw an ActiveRecord::IrreversibleMigration exception,
420
+ # that step will fail and you'll have some manual work to do.
421
+ #
422
+ # == More examples
423
+ #
424
+ # Not all migrations change the schema. Some just fix the data:
425
+ #
426
+ # class RemoveEmptyTags < ActiveRecord::Migration[8.0]
427
+ # def up
428
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
429
+ # end
430
+ #
431
+ # def down
432
+ # # not much we can do to restore deleted data
433
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
434
+ # end
435
+ # end
436
+ #
437
+ # Others remove columns when they migrate up instead of down:
438
+ #
439
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[8.0]
440
+ # def up
441
+ # remove_column :items, :incomplete_items_count
442
+ # remove_column :items, :completed_items_count
443
+ # end
444
+ #
445
+ # def down
446
+ # add_column :items, :incomplete_items_count
447
+ # add_column :items, :completed_items_count
448
+ # end
449
+ # end
450
+ #
451
+ # And sometimes you need to do something in SQL not abstracted directly by migrations:
452
+ #
453
+ # class MakeJoinUnique < ActiveRecord::Migration[8.0]
454
+ # def up
455
+ # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
456
+ # end
457
+ #
458
+ # def down
459
+ # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
460
+ # end
461
+ # end
462
+ #
463
+ # == Using a model after changing its table
464
+ #
465
+ # Sometimes you'll want to add a column in a migration and populate it
466
+ # immediately after. In that case, you'll need to make a call to
467
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
468
+ # latest column data from after the new column was added. Example:
469
+ #
470
+ # class AddPeopleSalary < ActiveRecord::Migration[8.0]
471
+ # def up
472
+ # add_column :people, :salary, :integer
473
+ # Person.reset_column_information
474
+ # Person.all.each do |p|
475
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
476
+ # end
477
+ # end
478
+ # end
479
+ #
480
+ # == Controlling verbosity
481
+ #
482
+ # By default, migrations will describe the actions they are taking, writing
483
+ # them to the console as they happen, along with benchmarks describing how
484
+ # long each step took.
485
+ #
486
+ # You can quiet them down by setting <tt>ActiveRecord::Migration.verbose = false</tt>.
487
+ #
488
+ # You can also insert your own messages and benchmarks by using the +say_with_time+
489
+ # method:
490
+ #
491
+ # def up
492
+ # ...
493
+ # say_with_time "Updating salaries..." do
494
+ # Person.all.each do |p|
495
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
496
+ # end
497
+ # end
498
+ # ...
499
+ # end
500
+ #
501
+ # The phrase "Updating salaries..." would then be printed, along with the
502
+ # benchmark for the block when the block completes.
503
+ #
504
+ # == Timestamped Migrations
505
+ #
506
+ # By default, \Rails generates migrations that look like:
507
+ #
508
+ # 20080717013526_your_migration_name.rb
509
+ #
510
+ # The prefix is a generation timestamp (in UTC). Timestamps should not be
511
+ # modified manually. To validate that migration timestamps adhere to the
512
+ # format Active Record expects, you can use the following configuration option:
513
+ #
514
+ # config.active_record.validate_migration_timestamps = true
515
+ #
516
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
517
+ # off by setting:
518
+ #
519
+ # config.active_record.timestamped_migrations = false
520
+ #
521
+ # In application.rb.
522
+ #
523
+ # == Reversible Migrations
524
+ #
525
+ # Reversible migrations are migrations that know how to go +down+ for you.
526
+ # You simply supply the +up+ logic, and the Migration system figures out
527
+ # how to execute the down commands for you.
528
+ #
529
+ # To define a reversible migration, define the +change+ method in your
530
+ # migration like this:
531
+ #
532
+ # class TenderloveMigration < ActiveRecord::Migration[8.0]
533
+ # def change
534
+ # create_table(:horses) do |t|
535
+ # t.column :content, :text
536
+ # t.column :remind_at, :datetime
537
+ # end
538
+ # end
539
+ # end
540
+ #
541
+ # This migration will create the horses table for you on the way up, and
542
+ # automatically figure out how to drop the table on the way down.
543
+ #
544
+ # Some commands cannot be reversed. If you care to define how to move up
545
+ # and down in these cases, you should define the +up+ and +down+ methods
546
+ # as before.
547
+ #
548
+ # If a command cannot be reversed, an
549
+ # ActiveRecord::IrreversibleMigration exception will be raised when
550
+ # the migration is moving down.
551
+ #
552
+ # For a list of commands that are reversible, please see
553
+ # +ActiveRecord::Migration::CommandRecorder+.
554
+ #
555
+ # == Transactional Migrations
556
+ #
557
+ # If the database adapter supports DDL transactions, all migrations will
558
+ # automatically be wrapped in a transaction. There are queries that you
559
+ # can't execute inside a transaction though, and for these situations
560
+ # you can turn the automatic transactions off.
561
+ #
562
+ # class ChangeEnum < ActiveRecord::Migration[8.0]
563
+ # disable_ddl_transaction!
564
+ #
565
+ # def up
566
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
567
+ # end
568
+ # end
569
+ #
570
+ # Remember that you can still open your own transactions, even if you
571
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
572
+ class Migration
573
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
574
+ autoload :Compatibility, "active_record/migration/compatibility"
575
+ autoload :JoinTable, "active_record/migration/join_table"
576
+ autoload :ExecutionStrategy, "active_record/migration/execution_strategy"
577
+ autoload :DefaultStrategy, "active_record/migration/default_strategy"
578
+
579
+ # This must be defined before the inherited hook, below
580
+ class Current < Migration # :nodoc:
581
+ def create_table(table_name, **options)
582
+ if block_given?
583
+ super { |t| yield compatible_table_definition(t) }
584
+ else
585
+ super
586
+ end
587
+ end
588
+
589
+ def change_table(table_name, **options)
590
+ if block_given?
591
+ super { |t| yield compatible_table_definition(t) }
592
+ else
593
+ super
594
+ end
595
+ end
596
+
597
+ def create_join_table(table_1, table_2, **options)
598
+ if block_given?
599
+ super { |t| yield compatible_table_definition(t) }
600
+ else
601
+ super
602
+ end
603
+ end
604
+
605
+ def drop_table(*table_names, **options)
606
+ if block_given?
607
+ super { |t| yield compatible_table_definition(t) }
608
+ else
609
+ super
610
+ end
611
+ end
612
+
613
+ def compatible_table_definition(t)
614
+ t
615
+ end
616
+ end
617
+
618
+ def self.inherited(subclass) # :nodoc:
619
+ super
620
+ if subclass.superclass == Migration
621
+ major = ActiveRecord::VERSION::MAJOR
622
+ minor = ActiveRecord::VERSION::MINOR
623
+ raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
624
+ "Please specify the Active Record release the migration was written for:\n" \
625
+ "\n" \
626
+ " class #{subclass} < ActiveRecord::Migration[#{major}.#{minor}]"
627
+ end
628
+ end
629
+
630
+ def self.[](version)
631
+ Compatibility.find(version)
632
+ end
633
+
634
+ def self.current_version
635
+ ActiveRecord::VERSION::STRING.to_f
636
+ end
637
+
638
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
639
+
640
+ def self.valid_version_format?(version_string) # :nodoc:
641
+ [
642
+ MigrationFilenameRegexp,
643
+ /\A\d(_?\d)*\z/ # integer with optional underscores
644
+ ].any? { |pattern| pattern.match?(version_string) }
645
+ end
646
+
647
+ # This class is used to verify that all migrations have been run before
648
+ # loading a web page if <tt>config.active_record.migration_error</tt> is set to +:page_load+.
649
+ class CheckPending
650
+ def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker)
651
+ @app = app
652
+ @needs_check = true
653
+ @mutex = Mutex.new
654
+ @file_watcher = file_watcher
655
+ end
656
+
657
+ def call(env)
658
+ @mutex.synchronize do
659
+ @watcher ||= build_watcher do
660
+ @needs_check = true
661
+ ActiveRecord::Migration.check_pending_migrations
662
+ @needs_check = false
663
+ end
664
+
665
+ if @needs_check
666
+ @watcher.execute
667
+ else
668
+ @watcher.execute_if_updated
669
+ end
670
+ end
671
+
672
+ @app.call(env)
673
+ end
674
+
675
+ private
676
+ def build_watcher(&block)
677
+ current_environment = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
678
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: current_environment)
679
+ paths = all_configs.flat_map { |config| config.migrations_paths || Migrator.migrations_paths }.uniq
680
+ @file_watcher.new([], paths.index_with(["rb"]), &block)
681
+ end
682
+
683
+ def connection
684
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection
685
+ end
686
+ end
687
+
688
+ class << self
689
+ attr_accessor :delegate # :nodoc:
690
+ attr_accessor :disable_ddl_transaction # :nodoc:
691
+
692
+ def nearest_delegate # :nodoc:
693
+ delegate || superclass.nearest_delegate
694
+ end
695
+
696
+ # Raises ActiveRecord::PendingMigrationError error if any migrations are pending
697
+ # for all database configurations in an environment.
698
+ def check_all_pending!
699
+ pending_migrations = []
700
+
701
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: env) do |pool|
702
+ if pending = pool.migration_context.open.pending_migrations
703
+ pending_migrations << pending
704
+ end
705
+ end
706
+
707
+ migrations = pending_migrations.flatten
708
+
709
+ if migrations.any?
710
+ raise ActiveRecord::PendingMigrationError.new(pending_migrations: migrations)
711
+ end
712
+ end
713
+
714
+ def load_schema_if_pending!
715
+ if any_schema_needs_update?
716
+ load_schema!
717
+ end
718
+
719
+ check_pending_migrations
720
+ end
721
+
722
+ def maintain_test_schema! # :nodoc:
723
+ if ActiveRecord.maintain_test_schema
724
+ suppress_messages { load_schema_if_pending! }
725
+ end
726
+ end
727
+
728
+ def method_missing(name, ...) # :nodoc:
729
+ nearest_delegate.send(name, ...)
730
+ end
731
+
732
+ def migrate(direction)
733
+ new.migrate direction
734
+ end
735
+
736
+ # Disable the transaction wrapping this migration.
737
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
738
+ #
739
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
740
+ def disable_ddl_transaction!
741
+ @disable_ddl_transaction = true
742
+ end
743
+
744
+ def check_pending_migrations # :nodoc:
745
+ migrations = pending_migrations
746
+
747
+ if migrations.any?
748
+ raise ActiveRecord::PendingMigrationError.new(pending_migrations: migrations)
749
+ end
750
+ end
751
+
752
+ private
753
+ def any_schema_needs_update?
754
+ !db_configs_in_current_env.all? do |db_config|
755
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord.schema_format)
756
+ end
757
+ end
758
+
759
+ def db_configs_in_current_env
760
+ ActiveRecord::Base.configurations.configs_for(env_name: env)
761
+ end
762
+
763
+ def pending_migrations
764
+ pending_migrations = []
765
+
766
+ ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
767
+ ActiveRecord::PendingMigrationConnection.with_temporary_pool(db_config) do |pool|
768
+ if pending = pool.migration_context.open.pending_migrations
769
+ pending_migrations << pending
770
+ end
771
+ end
772
+ end
773
+
774
+ pending_migrations.flatten
775
+ end
776
+
777
+ def env
778
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
779
+ end
780
+
781
+ def load_schema!
782
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
783
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
784
+
785
+ FileUtils.cd(root) do
786
+ Base.connection_handler.clear_all_connections!(:all)
787
+ system("bin/rails db:test:prepare")
788
+ end
789
+ end
790
+ end
791
+
792
+ def disable_ddl_transaction # :nodoc:
793
+ self.class.disable_ddl_transaction
794
+ end
795
+
796
+ ##
797
+ # :singleton-method: verbose
798
+ #
799
+ # Specifies if migrations will write the actions they are taking to the console as they
800
+ # happen, along with benchmarks describing how long each step took. Defaults to
801
+ # true.
802
+ cattr_accessor :verbose
803
+ attr_accessor :name, :version
804
+
805
+ def initialize(name = self.class.name, version = nil)
806
+ @name = name
807
+ @version = version
808
+ @connection = nil
809
+ @pool = nil
810
+ end
811
+
812
+ def execution_strategy
813
+ @execution_strategy ||= ActiveRecord.migration_strategy.new(self)
814
+ end
815
+
816
+ self.verbose = true
817
+ # instantiate the delegate object after initialize is defined
818
+ self.delegate = new
819
+
820
+ # Reverses the migration commands for the given block and
821
+ # the given migrations.
822
+ #
823
+ # The following migration will remove the table 'horses'
824
+ # and create the table 'apples' on the way up, and the reverse
825
+ # on the way down.
826
+ #
827
+ # class FixTLMigration < ActiveRecord::Migration[8.0]
828
+ # def change
829
+ # revert do
830
+ # create_table(:horses) do |t|
831
+ # t.text :content
832
+ # t.datetime :remind_at
833
+ # end
834
+ # end
835
+ # create_table(:apples) do |t|
836
+ # t.string :variety
837
+ # end
838
+ # end
839
+ # end
840
+ #
841
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
842
+ # documentation for Migration:
843
+ #
844
+ # require_relative "20121212123456_tenderlove_migration"
845
+ #
846
+ # class FixupTLMigration < ActiveRecord::Migration[8.0]
847
+ # def change
848
+ # revert TenderloveMigration
849
+ #
850
+ # create_table(:apples) do |t|
851
+ # t.string :variety
852
+ # end
853
+ # end
854
+ # end
855
+ #
856
+ # This command can be nested.
857
+ def revert(*migration_classes, &block)
858
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
859
+ if block_given?
860
+ if connection.respond_to? :revert
861
+ connection.revert(&block)
862
+ else
863
+ recorder = command_recorder
864
+ @connection = recorder
865
+ suppress_messages do
866
+ connection.revert(&block)
867
+ end
868
+ @connection = recorder.delegate
869
+ recorder.replay(self)
870
+ end
871
+ end
872
+ end
873
+
874
+ def reverting?
875
+ connection.respond_to?(:reverting) && connection.reverting
876
+ end
877
+
878
+ ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc:
879
+ def up
880
+ yield unless reverting
881
+ end
882
+
883
+ def down
884
+ yield if reverting
885
+ end
886
+ end
887
+
888
+ # Used to specify an operation that can be run in one direction or another.
889
+ # Call the methods +up+ and +down+ of the yielded object to run a block
890
+ # only in one given direction.
891
+ # The whole block will be called in the right order within the migration.
892
+ #
893
+ # In the following example, the looping on users will always be done
894
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
895
+ # even when migrating down:
896
+ #
897
+ # class SplitNameMigration < ActiveRecord::Migration[8.0]
898
+ # def change
899
+ # add_column :users, :first_name, :string
900
+ # add_column :users, :last_name, :string
901
+ #
902
+ # reversible do |dir|
903
+ # User.reset_column_information
904
+ # User.all.each do |u|
905
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
906
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
907
+ # u.save
908
+ # end
909
+ # end
910
+ #
911
+ # revert { add_column :users, :full_name, :string }
912
+ # end
913
+ # end
914
+ def reversible
915
+ helper = ReversibleBlockHelper.new(reverting?)
916
+ execute_block { yield helper }
917
+ end
918
+
919
+ # Used to specify an operation that is only run when migrating up
920
+ # (for example, populating a new column with its initial values).
921
+ #
922
+ # In the following example, the new column +published+ will be given
923
+ # the value +true+ for all existing records.
924
+ #
925
+ # class AddPublishedToPosts < ActiveRecord::Migration[8.0]
926
+ # def change
927
+ # add_column :posts, :published, :boolean, default: false
928
+ # up_only do
929
+ # execute "update posts set published = 'true'"
930
+ # end
931
+ # end
932
+ # end
933
+ def up_only(&block)
934
+ execute_block(&block) unless reverting?
935
+ end
936
+
937
+ # Runs the given migration classes.
938
+ # Last argument can specify options:
939
+ #
940
+ # - +:direction+ - Default is +:up+.
941
+ # - +:revert+ - Default is +false+.
942
+ def run(*migration_classes)
943
+ opts = migration_classes.extract_options!
944
+ dir = opts[:direction] || :up
945
+ dir = (dir == :down ? :up : :down) if opts[:revert]
946
+ if reverting?
947
+ # If in revert and going :up, say, we want to execute :down without reverting, so
948
+ revert { run(*migration_classes, direction: dir, revert: true) }
949
+ else
950
+ migration_classes.each do |migration_class|
951
+ migration_class.new.exec_migration(connection, dir)
952
+ end
953
+ end
954
+ end
955
+
956
+ def up
957
+ self.class.delegate = self
958
+ return unless self.class.respond_to?(:up)
959
+ self.class.up
960
+ end
961
+
962
+ def down
963
+ self.class.delegate = self
964
+ return unless self.class.respond_to?(:down)
965
+ self.class.down
966
+ end
967
+
968
+ # Execute this migration in the named direction
969
+ def migrate(direction)
970
+ return unless respond_to?(direction)
971
+
972
+ case direction
973
+ when :up then announce "migrating"
974
+ when :down then announce "reverting"
975
+ end
976
+
977
+ time_elapsed = nil
978
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection.pool.with_connection do |conn|
979
+ time_elapsed = ActiveSupport::Benchmark.realtime do
980
+ exec_migration(conn, direction)
981
+ end
982
+ end
983
+
984
+ case direction
985
+ when :up then announce "migrated (%.4fs)" % time_elapsed; write
986
+ when :down then announce "reverted (%.4fs)" % time_elapsed; write
987
+ end
988
+ end
989
+
990
+ def exec_migration(conn, direction)
991
+ @connection = conn
992
+ if respond_to?(:change)
993
+ if direction == :down
994
+ revert { change }
995
+ else
996
+ change
997
+ end
998
+ else
999
+ public_send(direction)
1000
+ end
1001
+ ensure
1002
+ @connection = nil
1003
+ @execution_strategy = nil
1004
+ end
1005
+
1006
+ def write(text = "")
1007
+ puts(text) if verbose
1008
+ end
1009
+
1010
+ def announce(message)
1011
+ text = "#{version} #{name}: #{message}"
1012
+ length = [0, 75 - text.length].max
1013
+ write "== %s %s" % [text, "=" * length]
1014
+ end
1015
+
1016
+ # Takes a message argument and outputs it as is.
1017
+ # A second boolean argument can be passed to specify whether to indent or not.
1018
+ def say(message, subitem = false)
1019
+ write "#{subitem ? " ->" : "--"} #{message}"
1020
+ end
1021
+
1022
+ # Outputs text along with how long it took to run its block.
1023
+ # If the block returns an integer it assumes it is the number of rows affected.
1024
+ def say_with_time(message)
1025
+ say(message)
1026
+ result = nil
1027
+ time_elapsed = ActiveSupport::Benchmark.realtime { result = yield }
1028
+ say "%.4fs" % time_elapsed, :subitem
1029
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
1030
+ result
1031
+ end
1032
+
1033
+ # Takes a block as an argument and suppresses any output generated by the block.
1034
+ def suppress_messages
1035
+ save, self.verbose = verbose, false
1036
+ yield
1037
+ ensure
1038
+ self.verbose = save
1039
+ end
1040
+
1041
+ def connection
1042
+ @connection || ActiveRecord::Tasks::DatabaseTasks.migration_connection
1043
+ end
1044
+
1045
+ def connection_pool
1046
+ @pool || ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
1047
+ end
1048
+
1049
+ def method_missing(method, *arguments, &block)
1050
+ say_with_time "#{method}(#{format_arguments(arguments)})" do
1051
+ unless connection.respond_to? :revert
1052
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
1053
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
1054
+ if method == :rename_table ||
1055
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
1056
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
1057
+ end
1058
+ end
1059
+ end
1060
+ return super unless execution_strategy.respond_to?(method)
1061
+ execution_strategy.send(method, *arguments, &block)
1062
+ end
1063
+ end
1064
+ ruby2_keywords(:method_missing)
1065
+
1066
+ def copy(destination, sources, options = {})
1067
+ copied = []
1068
+
1069
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
1070
+ schema_migration = SchemaMigration::NullSchemaMigration.new
1071
+ internal_metadata = InternalMetadata::NullInternalMetadata.new
1072
+
1073
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration, internal_metadata).migrations
1074
+ last = destination_migrations.last
1075
+ sources.each do |scope, path|
1076
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration, internal_metadata).migrations
1077
+
1078
+ source_migrations.each do |migration|
1079
+ source = File.binread(migration.filename)
1080
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
1081
+ magic_comments = +""
1082
+ loop do
1083
+ # If we have a magic comment in the original migration,
1084
+ # insert our comment after the first newline(end of the magic comment line)
1085
+ # so the magic keep working.
1086
+ # Note that magic comments must be at the first line(except sh-bang).
1087
+ source.sub!(/\A(?:#.*\b(?:en)?coding:\s*\S+|#\s*frozen_string_literal:\s*(?:true|false)).*\n/) do |magic_comment|
1088
+ magic_comments << magic_comment; ""
1089
+ end || break
1090
+ end
1091
+
1092
+ if !magic_comments.empty? && source.start_with?("\n")
1093
+ magic_comments << "\n"
1094
+ source = source[1..-1]
1095
+ end
1096
+
1097
+ source = "#{magic_comments}#{inserted_comment}#{source}"
1098
+
1099
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
1100
+ if options[:on_skip] && duplicate.scope != scope.to_s
1101
+ options[:on_skip].call(scope, migration)
1102
+ end
1103
+ next
1104
+ end
1105
+
1106
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
1107
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
1108
+ old_path, migration.filename = migration.filename, new_path
1109
+ last = migration
1110
+
1111
+ File.binwrite(migration.filename, source)
1112
+ copied << migration
1113
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
1114
+ destination_migrations << migration
1115
+ end
1116
+ end
1117
+
1118
+ copied
1119
+ end
1120
+
1121
+ # Finds the correct table name given an Active Record object.
1122
+ # Uses the Active Record object's own table_name, or pre/suffix from the
1123
+ # options passed in.
1124
+ def proper_table_name(name, options = {})
1125
+ if name.respond_to? :table_name
1126
+ name.table_name
1127
+ else
1128
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
1129
+ end
1130
+ end
1131
+
1132
+ # Determines the version number of the next migration.
1133
+ def next_migration_number(number)
1134
+ if ActiveRecord.timestamped_migrations
1135
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
1136
+ else
1137
+ "%.3d" % number.to_i
1138
+ end
1139
+ end
1140
+
1141
+ # Builds a hash for use in ActiveRecord::Migration#proper_table_name using
1142
+ # the Active Record object's table_name prefix and suffix
1143
+ def table_name_options(config = ActiveRecord::Base) # :nodoc:
1144
+ {
1145
+ table_name_prefix: config.table_name_prefix,
1146
+ table_name_suffix: config.table_name_suffix
1147
+ }
1148
+ end
1149
+
1150
+ private
1151
+ def execute_block
1152
+ if connection.respond_to? :execute_block
1153
+ super # use normal delegation to record the block
1154
+ else
1155
+ yield
1156
+ end
1157
+ end
1158
+
1159
+ def format_arguments(arguments)
1160
+ arg_list = arguments[0...-1].map(&:inspect)
1161
+ last_arg = arguments.last
1162
+ if last_arg.is_a?(Hash)
1163
+ last_arg = last_arg.reject { |k, _v| internal_option?(k) }
1164
+ arg_list << last_arg.inspect unless last_arg.empty?
1165
+ else
1166
+ arg_list << last_arg.inspect
1167
+ end
1168
+ arg_list.join(", ")
1169
+ end
1170
+
1171
+ def internal_option?(option_name)
1172
+ option_name.start_with?("_")
1173
+ end
1174
+
1175
+ def command_recorder
1176
+ CommandRecorder.new(connection)
1177
+ end
1178
+ end
1179
+
1180
+ # MigrationProxy is used to defer loading of the actual migration classes
1181
+ # until they are needed
1182
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
1183
+ def initialize(name, version, filename, scope)
1184
+ super
1185
+ @migration = nil
1186
+ end
1187
+
1188
+ def basename
1189
+ File.basename(filename)
1190
+ end
1191
+
1192
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
1193
+
1194
+ private
1195
+ def migration
1196
+ @migration ||= load_migration
1197
+ end
1198
+
1199
+ def load_migration
1200
+ Object.send(:remove_const, name) rescue nil
1201
+
1202
+ load(File.expand_path(filename))
1203
+ name.constantize.new(name, version)
1204
+ end
1205
+ end
1206
+
1207
+ # = \Migration \Context
1208
+ #
1209
+ # MigrationContext sets the context in which a migration is run.
1210
+ #
1211
+ # A migration context requires the path to the migrations is set
1212
+ # in the +migrations_paths+ parameter. Optionally a +schema_migration+
1213
+ # class can be provided. Multiple database applications will instantiate
1214
+ # a +SchemaMigration+ object per database. From the Rake tasks, \Rails will
1215
+ # handle this for you.
1216
+ class MigrationContext
1217
+ attr_reader :migrations_paths, :schema_migration, :internal_metadata
1218
+
1219
+ def initialize(migrations_paths, schema_migration = nil, internal_metadata = nil)
1220
+ @migrations_paths = migrations_paths
1221
+ @schema_migration = schema_migration || SchemaMigration.new(connection_pool)
1222
+ @internal_metadata = internal_metadata || InternalMetadata.new(connection_pool)
1223
+ end
1224
+
1225
+ # Runs the migrations in the +migrations_path+.
1226
+ #
1227
+ # If +target_version+ is +nil+, +migrate+ will run +up+.
1228
+ #
1229
+ # If the +current_version+ and +target_version+ are both
1230
+ # 0 then an empty array will be returned and no migrations
1231
+ # will be run.
1232
+ #
1233
+ # If the +current_version+ in the schema is greater than
1234
+ # the +target_version+, then +down+ will be run.
1235
+ #
1236
+ # If none of the conditions are met, +up+ will be run with
1237
+ # the +target_version+.
1238
+ def migrate(target_version = nil, &block)
1239
+ case
1240
+ when target_version.nil?
1241
+ up(target_version, &block)
1242
+ when current_version == 0 && target_version == 0
1243
+ []
1244
+ when current_version > target_version
1245
+ down(target_version, &block)
1246
+ else
1247
+ up(target_version, &block)
1248
+ end
1249
+ end
1250
+
1251
+ def rollback(steps = 1) # :nodoc:
1252
+ move(:down, steps)
1253
+ end
1254
+
1255
+ def forward(steps = 1) # :nodoc:
1256
+ move(:up, steps)
1257
+ end
1258
+
1259
+ def up(target_version = nil, &block) # :nodoc:
1260
+ selected_migrations = if block_given?
1261
+ migrations.select(&block)
1262
+ else
1263
+ migrations
1264
+ end
1265
+
1266
+ Migrator.new(:up, selected_migrations, schema_migration, internal_metadata, target_version).migrate
1267
+ end
1268
+
1269
+ def down(target_version = nil, &block) # :nodoc:
1270
+ selected_migrations = if block_given?
1271
+ migrations.select(&block)
1272
+ else
1273
+ migrations
1274
+ end
1275
+
1276
+ Migrator.new(:down, selected_migrations, schema_migration, internal_metadata, target_version).migrate
1277
+ end
1278
+
1279
+ def run(direction, target_version) # :nodoc:
1280
+ Migrator.new(direction, migrations, schema_migration, internal_metadata, target_version).run
1281
+ end
1282
+
1283
+ def open # :nodoc:
1284
+ Migrator.new(:up, migrations, schema_migration, internal_metadata)
1285
+ end
1286
+
1287
+ def get_all_versions # :nodoc:
1288
+ if schema_migration.table_exists?
1289
+ schema_migration.integer_versions
1290
+ else
1291
+ []
1292
+ end
1293
+ end
1294
+
1295
+ def current_version # :nodoc:
1296
+ get_all_versions.max || 0
1297
+ rescue ActiveRecord::NoDatabaseError
1298
+ end
1299
+
1300
+ def needs_migration? # :nodoc:
1301
+ pending_migration_versions.size > 0
1302
+ end
1303
+
1304
+ def pending_migration_versions # :nodoc:
1305
+ migrations.collect(&:version) - get_all_versions
1306
+ end
1307
+
1308
+ def migrations # :nodoc:
1309
+ migrations = migration_files.map do |file|
1310
+ version, name, scope = parse_migration_filename(file)
1311
+ raise IllegalMigrationNameError.new(file) unless version
1312
+ if validate_timestamp? && !valid_migration_timestamp?(version)
1313
+ raise InvalidMigrationTimestampError.new(version, name)
1314
+ end
1315
+ version = version.to_i
1316
+ name = name.camelize
1317
+
1318
+ MigrationProxy.new(name, version, file, scope)
1319
+ end
1320
+
1321
+ migrations.sort_by(&:version)
1322
+ end
1323
+
1324
+ def migrations_status # :nodoc:
1325
+ db_list = schema_migration.normalized_versions
1326
+
1327
+ file_list = migration_files.filter_map do |file|
1328
+ version, name, scope = parse_migration_filename(file)
1329
+ raise IllegalMigrationNameError.new(file) unless version
1330
+ if validate_timestamp? && !valid_migration_timestamp?(version)
1331
+ raise InvalidMigrationTimestampError.new(version, name)
1332
+ end
1333
+ version = schema_migration.normalize_migration_number(version)
1334
+ status = db_list.delete(version) ? "up" : "down"
1335
+ [status, version, (name + scope).humanize]
1336
+ end
1337
+
1338
+ db_list.map! do |version|
1339
+ ["up", version, "********** NO FILE **********"]
1340
+ end
1341
+
1342
+ (db_list + file_list).sort_by { |_, version, _| version.to_i }
1343
+ end
1344
+
1345
+ def current_environment # :nodoc:
1346
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1347
+ end
1348
+
1349
+ def protected_environment? # :nodoc:
1350
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1351
+ end
1352
+
1353
+ def last_stored_environment # :nodoc:
1354
+ internal_metadata = connection_pool.internal_metadata
1355
+ return nil unless internal_metadata.enabled?
1356
+ return nil if current_version == 0
1357
+ raise NoEnvironmentInSchemaError unless internal_metadata.table_exists?
1358
+
1359
+ environment = internal_metadata[:environment]
1360
+ raise NoEnvironmentInSchemaError unless environment
1361
+ environment
1362
+ end
1363
+
1364
+ private
1365
+ def connection
1366
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection
1367
+ end
1368
+
1369
+ def connection_pool
1370
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
1371
+ end
1372
+
1373
+ def migration_files
1374
+ paths = Array(migrations_paths)
1375
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1376
+ end
1377
+
1378
+ def parse_migration_filename(filename)
1379
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1380
+ end
1381
+
1382
+ def validate_timestamp?
1383
+ ActiveRecord.timestamped_migrations && ActiveRecord.validate_migration_timestamps
1384
+ end
1385
+
1386
+ def valid_migration_timestamp?(version)
1387
+ version.to_i < (Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S").to_i
1388
+ end
1389
+
1390
+ def move(direction, steps)
1391
+ migrator = Migrator.new(direction, migrations, schema_migration, internal_metadata)
1392
+
1393
+ if current_version != 0 && !migrator.current_migration
1394
+ raise UnknownMigrationVersionError.new(current_version)
1395
+ end
1396
+
1397
+ start_index =
1398
+ if current_version == 0
1399
+ 0
1400
+ else
1401
+ migrator.migrations.index(migrator.current_migration)
1402
+ end
1403
+
1404
+ finish = migrator.migrations[start_index + steps]
1405
+ version = finish ? finish.version : 0
1406
+ public_send(direction, version)
1407
+ end
1408
+ end
1409
+
1410
+ class Migrator # :nodoc:
1411
+ class << self
1412
+ attr_accessor :migrations_paths
1413
+
1414
+ # For cases where a table doesn't exist like loading from schema cache
1415
+ def current_version
1416
+ connection_pool = ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
1417
+ schema_migration = SchemaMigration.new(connection_pool)
1418
+ internal_metadata = InternalMetadata.new(connection_pool)
1419
+
1420
+ MigrationContext.new(migrations_paths, schema_migration, internal_metadata).current_version
1421
+ end
1422
+ end
1423
+
1424
+ self.migrations_paths = ["db/migrate"]
1425
+
1426
+ def initialize(direction, migrations, schema_migration, internal_metadata, target_version = nil)
1427
+ @direction = direction
1428
+ @target_version = target_version
1429
+ @migrated_versions = nil
1430
+ @migrations = migrations
1431
+ @schema_migration = schema_migration
1432
+ @internal_metadata = internal_metadata
1433
+
1434
+ validate(@migrations)
1435
+
1436
+ @schema_migration.create_table
1437
+ @internal_metadata.create_table
1438
+ end
1439
+
1440
+ def current_version
1441
+ migrated.max || 0
1442
+ end
1443
+
1444
+ def current_migration
1445
+ migrations.detect { |m| m.version == current_version }
1446
+ end
1447
+ alias :current :current_migration
1448
+
1449
+ def run
1450
+ if use_advisory_lock?
1451
+ with_advisory_lock { run_without_lock }
1452
+ else
1453
+ run_without_lock
1454
+ end
1455
+ end
1456
+
1457
+ def migrate
1458
+ if use_advisory_lock?
1459
+ with_advisory_lock { migrate_without_lock }
1460
+ else
1461
+ migrate_without_lock
1462
+ end
1463
+ end
1464
+
1465
+ def runnable
1466
+ runnable = migrations[start..finish]
1467
+ if up?
1468
+ runnable.reject { |m| ran?(m) }
1469
+ else
1470
+ # skip the last migration if we're headed down, but not ALL the way down
1471
+ runnable.pop if target
1472
+ runnable.find_all { |m| ran?(m) }
1473
+ end
1474
+ end
1475
+
1476
+ def migrations
1477
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
1478
+ end
1479
+
1480
+ def pending_migrations
1481
+ already_migrated = migrated
1482
+ migrations.reject { |m| already_migrated.include?(m.version) }
1483
+ end
1484
+
1485
+ def migrated
1486
+ @migrated_versions || load_migrated
1487
+ end
1488
+
1489
+ def load_migrated
1490
+ @migrated_versions = Set.new(@schema_migration.integer_versions)
1491
+ end
1492
+
1493
+ private
1494
+ def connection
1495
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection
1496
+ end
1497
+
1498
+ # Used for running a specific migration.
1499
+ def run_without_lock
1500
+ migration = migrations.detect { |m| m.version == @target_version }
1501
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1502
+
1503
+ record_environment
1504
+ execute_migration_in_transaction(migration)
1505
+ end
1506
+
1507
+ # Used for running multiple migrations up to or down to a certain value.
1508
+ def migrate_without_lock
1509
+ if invalid_target?
1510
+ raise UnknownMigrationVersionError.new(@target_version)
1511
+ end
1512
+
1513
+ record_environment
1514
+ runnable.each(&method(:execute_migration_in_transaction))
1515
+ end
1516
+
1517
+ # Stores the current environment in the database.
1518
+ def record_environment
1519
+ return if down?
1520
+
1521
+ @internal_metadata[:environment] = connection.pool.db_config.env_name
1522
+ end
1523
+
1524
+ def ran?(migration)
1525
+ migrated.include?(migration.version.to_i)
1526
+ end
1527
+
1528
+ # Return true if a valid version is not provided.
1529
+ def invalid_target?
1530
+ @target_version && @target_version != 0 && !target
1531
+ end
1532
+
1533
+ def execute_migration_in_transaction(migration)
1534
+ return if down? && !migrated.include?(migration.version.to_i)
1535
+ return if up? && migrated.include?(migration.version.to_i)
1536
+
1537
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1538
+
1539
+ ddl_transaction(migration) do
1540
+ migration.migrate(@direction)
1541
+ record_version_state_after_migrating(migration.version)
1542
+ end
1543
+ rescue => e
1544
+ msg = +"An error has occurred, "
1545
+ msg << "this and " if use_transaction?(migration)
1546
+ msg << "all later migrations canceled:\n\n#{e}"
1547
+ raise StandardError, msg, e.backtrace
1548
+ end
1549
+
1550
+ def target
1551
+ migrations.detect { |m| m.version == @target_version }
1552
+ end
1553
+
1554
+ def finish
1555
+ migrations.index(target) || migrations.size - 1
1556
+ end
1557
+
1558
+ def start
1559
+ up? ? 0 : (migrations.index(current) || 0)
1560
+ end
1561
+
1562
+ def validate(migrations)
1563
+ name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
1564
+ raise DuplicateMigrationNameError.new(name) if name
1565
+
1566
+ version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
1567
+ raise DuplicateMigrationVersionError.new(version) if version
1568
+ end
1569
+
1570
+ def record_version_state_after_migrating(version)
1571
+ if down?
1572
+ migrated.delete(version)
1573
+ @schema_migration.delete_version(version.to_s)
1574
+ else
1575
+ migrated << version
1576
+ @schema_migration.create_version(version.to_s)
1577
+ end
1578
+ end
1579
+
1580
+ def up?
1581
+ @direction == :up
1582
+ end
1583
+
1584
+ def down?
1585
+ @direction == :down
1586
+ end
1587
+
1588
+ # Wrap the migration in a transaction only if supported by the adapter.
1589
+ def ddl_transaction(migration, &block)
1590
+ if use_transaction?(migration)
1591
+ connection.transaction(&block)
1592
+ else
1593
+ yield
1594
+ end
1595
+ end
1596
+
1597
+ def use_transaction?(migration)
1598
+ !migration.disable_ddl_transaction && connection.supports_ddl_transactions?
1599
+ end
1600
+
1601
+ def use_advisory_lock?
1602
+ connection.advisory_locks_enabled?
1603
+ end
1604
+
1605
+ def with_advisory_lock
1606
+ lock_id = generate_migrator_advisory_lock_id
1607
+
1608
+ got_lock = connection.get_advisory_lock(lock_id)
1609
+ raise ConcurrentMigrationError unless got_lock
1610
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1611
+ yield
1612
+ ensure
1613
+ if got_lock && !connection.release_advisory_lock(lock_id)
1614
+ raise ConcurrentMigrationError.new(
1615
+ ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
1616
+ )
1617
+ end
1618
+ end
1619
+
1620
+ MIGRATOR_SALT = 2053462845
1621
+ def generate_migrator_advisory_lock_id
1622
+ db_name_hash = Zlib.crc32(connection.current_database)
1623
+ MIGRATOR_SALT * db_name_hash
1624
+ end
1625
+ end
1626
+ end