activerecord 7.0.8.7 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1944
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +54 -12
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -6,19 +6,6 @@ module ActiveRecord
6
6
 
7
7
  class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
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
9
  def strict_loading_violation(event)
23
10
  debug do
24
11
  owner = event.payload[:owner]
@@ -26,11 +13,9 @@ module ActiveRecord
26
13
  color(reflection.strict_loading_violation_message(owner), RED)
27
14
  end
28
15
  end
16
+ subscribe_log_level :strict_loading_violation, :debug
29
17
 
30
18
  def sql(event)
31
- self.class.runtime += event.duration
32
- return unless logger.debug?
33
-
34
19
  payload = event.payload
35
20
 
36
21
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
@@ -66,10 +51,11 @@ module ActiveRecord
66
51
  end
67
52
 
68
53
  name = colorize_payload_name(name, payload[:name])
69
- sql = color(sql, sql_color(sql), true) if colorize_logging
54
+ sql = color(sql, sql_color(sql), bold: true) if colorize_logging
70
55
 
71
56
  debug " #{name} #{sql}#{binds}"
72
57
  end
58
+ subscribe_log_level :sql, :debug
73
59
 
74
60
  private
75
61
  def type_casted_binds(casted_binds)
@@ -93,9 +79,9 @@ module ActiveRecord
93
79
 
94
80
  def colorize_payload_name(name, payload_name)
95
81
  if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
96
- color(name, MAGENTA, true)
82
+ color(name, MAGENTA, bold: true)
97
83
  else
98
- color(name, CYAN, true)
84
+ color(name, CYAN, bold: true)
99
85
  end
100
86
  end
101
87
 
@@ -133,15 +119,25 @@ module ActiveRecord
133
119
  end
134
120
 
135
121
  def log_query_source
136
- source = extract_query_source_location(caller)
122
+ source = query_source_location
137
123
 
138
124
  if source
139
125
  logger.debug(" ↳ #{source}")
140
126
  end
141
127
  end
142
128
 
143
- def extract_query_source_location(locations)
144
- backtrace_cleaner.clean(locations.lazy).first
129
+ if Thread.respond_to?(:each_caller_location)
130
+ def query_source_location
131
+ Thread.each_caller_location do |location|
132
+ frame = backtrace_cleaner.clean_frame(location)
133
+ return frame if frame
134
+ end
135
+ nil
136
+ end
137
+ else
138
+ def query_source_location
139
+ backtrace_cleaner.clean(caller(1).lazy).first
140
+ end
145
141
  end
146
142
 
147
143
  def filter(name, value)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Marshalling
5
+ @format_version = 6.1
6
+
7
+ class << self
8
+ attr_reader :format_version
9
+
10
+ def format_version=(version)
11
+ case version
12
+ when 6.1
13
+ Methods.remove_method(:marshal_dump) if Methods.method_defined?(:marshal_dump)
14
+ when 7.1
15
+ Methods.alias_method(:marshal_dump, :_marshal_dump_7_1)
16
+ else
17
+ raise ArgumentError, "Unknown marshalling format: #{version.inspect}"
18
+ end
19
+ @format_version = version
20
+ end
21
+ end
22
+
23
+ module Methods
24
+ def _marshal_dump_7_1
25
+ payload = [attributes_for_database, new_record?]
26
+
27
+ cached_associations = self.class.reflect_on_all_associations.select do |reflection|
28
+ if association_cached?(reflection.name)
29
+ association = association(reflection.name)
30
+ association.loaded? || association.target.present?
31
+ end
32
+ end
33
+
34
+ unless cached_associations.empty?
35
+ payload << cached_associations.map do |reflection|
36
+ [reflection.name, association(reflection.name).target]
37
+ end
38
+ end
39
+
40
+ payload
41
+ end
42
+
43
+ def marshal_load(state)
44
+ attributes_from_database, new_record, associations = state
45
+
46
+ attributes = self.class.attributes_builder.build_from_database(attributes_from_database)
47
+ init_with_attributes(attributes, new_record)
48
+
49
+ if associations
50
+ associations.each do |name, target|
51
+ association(name).target = target
52
+ rescue ActiveRecord::AssociationNotFoundError
53
+ # the association no longer exist, we can just skip it.
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module MessagePack # :nodoc:
5
+ FORMAT_VERSION = 1
6
+
7
+ class << self
8
+ def dump(input)
9
+ encoder = Encoder.new
10
+ [FORMAT_VERSION, encoder.encode(input), encoder.entries]
11
+ end
12
+
13
+ def load(dumped)
14
+ format_version, top_level, entries = dumped
15
+ unless format_version == FORMAT_VERSION
16
+ raise "Invalid format version: #{format_version.inspect}"
17
+ end
18
+ Decoder.new(entries).decode(top_level)
19
+ end
20
+ end
21
+
22
+ module Extensions
23
+ extend self
24
+
25
+ def install(registry)
26
+ registry.register_type 119, ActiveModel::Type::Binary::Data,
27
+ packer: :to_s,
28
+ unpacker: :new
29
+
30
+ registry.register_type 120, ActiveRecord::Base,
31
+ packer: method(:write_record),
32
+ unpacker: method(:read_record),
33
+ recursive: true
34
+ end
35
+
36
+ def write_record(record, packer)
37
+ packer.write(ActiveRecord::MessagePack.dump(record))
38
+ end
39
+
40
+ def read_record(unpacker)
41
+ ActiveRecord::MessagePack.load(unpacker.read)
42
+ end
43
+ end
44
+
45
+ class Encoder
46
+ attr_reader :entries
47
+
48
+ def initialize
49
+ @entries = []
50
+ @refs = {}.compare_by_identity
51
+ end
52
+
53
+ def encode(input)
54
+ if input.is_a?(Array)
55
+ input.map { |record| encode_record(record) }
56
+ elsif input
57
+ encode_record(input)
58
+ end
59
+ end
60
+
61
+ def encode_record(record)
62
+ ref = @refs[record]
63
+
64
+ if !ref
65
+ ref = @refs[record] = @entries.size
66
+ @entries << build_entry(record)
67
+ add_cached_associations(record, @entries.last)
68
+ end
69
+
70
+ ref
71
+ end
72
+
73
+ def build_entry(record)
74
+ [
75
+ ActiveSupport::MessagePack::Extensions.dump_class(record.class),
76
+ record.attributes_for_database,
77
+ record.new_record?
78
+ ]
79
+ end
80
+
81
+ def add_cached_associations(record, entry)
82
+ record.class.normalized_reflections.each_value do |reflection|
83
+ if record.association_cached?(reflection.name) && record.association(reflection.name).loaded?
84
+ entry << reflection.name << encode(record.association(reflection.name).target)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ class Decoder
91
+ def initialize(entries)
92
+ @records = entries.map { |entry| build_record(entry) }
93
+ @records.zip(entries) { |record, entry| resolve_cached_associations(record, entry) }
94
+ end
95
+
96
+ def decode(ref)
97
+ if ref.is_a?(Array)
98
+ ref.map { |r| @records[r] }
99
+ elsif ref
100
+ @records[ref]
101
+ end
102
+ end
103
+
104
+ def build_record(entry)
105
+ class_name, attributes_hash, is_new_record, * = entry
106
+ klass = ActiveSupport::MessagePack::Extensions.load_class(class_name)
107
+ attributes = klass.attributes_builder.build_from_database(attributes_hash)
108
+ klass.allocate.init_with_attributes(attributes, is_new_record)
109
+ end
110
+
111
+ def resolve_cached_associations(record, entry)
112
+ i = 3 # entry == [class_name, attributes_hash, is_new_record, *associations]
113
+ while i < entry.length
114
+ begin
115
+ record.association(entry[i]).target = decode(entry[i + 1])
116
+ rescue ActiveRecord::AssociationNotFoundError
117
+ # The association no longer exists, so just skip it.
118
+ end
119
+ i += 2
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -48,6 +48,10 @@ module ActiveRecord
48
48
  context.save(response)
49
49
  end
50
50
 
51
+ def reading_request?(request)
52
+ request.get? || request.head?
53
+ end
54
+
51
55
  private
52
56
  def read_from_primary(&blk)
53
57
  ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: true) do
@@ -4,8 +4,10 @@ require "active_record/middleware/database_selector/resolver"
4
4
 
5
5
  module ActiveRecord
6
6
  module Middleware
7
+ # = Database Selector \Middleware
8
+ #
7
9
  # The DatabaseSelector Middleware provides a framework for automatically
8
- # swapping from the primary to the replica database connection. Rails
10
+ # swapping from the primary to the replica database connection. \Rails
9
11
  # provides a basic framework to determine when to swap and allows for
10
12
  # applications to write custom strategy classes to override the default
11
13
  # behavior.
@@ -15,14 +17,14 @@ module ActiveRecord
15
17
  # resolver context class that sets a value that helps the resolver class
16
18
  # decide when to switch.
17
19
  #
18
- # Rails default middleware uses the request's session to set a timestamp
20
+ # \Rails default middleware uses the request's session to set a timestamp
19
21
  # that informs the application when to read from a primary or read from a
20
22
  # replica.
21
23
  #
22
24
  # To use the DatabaseSelector in your application with default settings,
23
25
  # run the provided generator.
24
26
  #
25
- # bin/rails g active_record:multi_db
27
+ # $ bin/rails g active_record:multi_db
26
28
  #
27
29
  # This will create a file named +config/initializers/multi_db.rb+ with the
28
30
  # following contents:
@@ -71,7 +73,7 @@ module ActiveRecord
71
73
  context = context_klass.call(request)
72
74
  resolver = resolver_klass.call(context, options)
73
75
 
74
- response = if reading_request?(request)
76
+ response = if resolver.reading_request?(request)
75
77
  resolver.read(&blk)
76
78
  else
77
79
  resolver.write(&blk)
@@ -80,10 +82,6 @@ module ActiveRecord
80
82
  resolver.update_context(response)
81
83
  response
82
84
  end
83
-
84
- def reading_request?(request)
85
- request.get? || request.head?
86
- end
87
85
  end
88
86
  end
89
87
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Middleware
5
+ # = Shard Selector \Middleware
6
+ #
5
7
  # The ShardSelector Middleware provides a framework for automatically
6
- # swapping shards. Rails provides a basic framework to determine which
8
+ # swapping shards. \Rails provides a basic framework to determine which
7
9
  # shard to switch to and allows for applications to write custom strategies
8
10
  # for swapping if needed.
9
11
  #
@@ -2,13 +2,17 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class Migration
5
- # <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
5
+ # = \Migration Command Recorder
6
+ #
7
+ # +ActiveRecord::Migration::CommandRecorder+ records commands done during
6
8
  # a migration and knows how to reverse those commands. The CommandRecorder
7
9
  # knows how to invert the following commands:
8
10
  #
9
11
  # * add_column
10
12
  # * add_foreign_key
11
13
  # * add_check_constraint
14
+ # * add_exclusion_constraint
15
+ # * add_unique_constraint
12
16
  # * add_index
13
17
  # * add_reference
14
18
  # * add_timestamps
@@ -16,9 +20,11 @@ module ActiveRecord
16
20
  # * change_column_null
17
21
  # * change_column_comment (must supply a +:from+ and +:to+ option)
18
22
  # * change_table_comment (must supply a +:from+ and +:to+ option)
23
+ # * create_enum
19
24
  # * create_join_table
20
25
  # * create_table
21
26
  # * disable_extension
27
+ # * drop_enum (must supply a list of values)
22
28
  # * drop_join_table
23
29
  # * drop_table (must supply a block)
24
30
  # * enable_extension
@@ -26,10 +32,14 @@ module ActiveRecord
26
32
  # * remove_columns (must supply a +:type+ option)
27
33
  # * remove_foreign_key (must supply a second table)
28
34
  # * remove_check_constraint
35
+ # * remove_exclusion_constraint
36
+ # * remove_unique_constraint
29
37
  # * remove_index
30
38
  # * remove_reference
31
39
  # * remove_timestamps
32
40
  # * rename_column
41
+ # * rename_enum (must supply a +:to+ option)
42
+ # * rename_enum_value (must supply a +:from+ and +:to+ option)
33
43
  # * rename_index
34
44
  # * rename_table
35
45
  class CommandRecorder
@@ -41,7 +51,10 @@ module ActiveRecord
41
51
  :change_column, :execute, :remove_columns, :change_column_null,
42
52
  :add_foreign_key, :remove_foreign_key,
43
53
  :change_column_comment, :change_table_comment,
44
- :add_check_constraint, :remove_check_constraint
54
+ :add_check_constraint, :remove_check_constraint,
55
+ :add_exclusion_constraint, :remove_exclusion_constraint,
56
+ :add_unique_constraint, :remove_unique_constraint,
57
+ :create_enum, :drop_enum, :rename_enum, :add_enum_value, :rename_enum_value,
45
58
  ]
46
59
  include JoinTable
47
60
 
@@ -117,7 +130,15 @@ module ActiveRecord
117
130
  alias :remove_belongs_to :remove_reference
118
131
 
119
132
  def change_table(table_name, **options) # :nodoc:
120
- yield delegate.update_table_definition(table_name, self)
133
+ if delegate.supports_bulk_alter? && options[:bulk]
134
+ recorder = self.class.new(self.delegate)
135
+ recorder.reverting = @reverting
136
+ yield recorder.delegate.update_table_definition(table_name, recorder)
137
+ commands = recorder.commands
138
+ @commands << [:change_table, [table_name], -> t { bulk_change_table(table_name, commands) }]
139
+ else
140
+ yield delegate.update_table_definition(table_name, self)
141
+ end
121
142
  end
122
143
 
123
144
  def replay(migration)
@@ -139,7 +160,10 @@ module ActiveRecord
139
160
  add_reference: :remove_reference,
140
161
  add_foreign_key: :remove_foreign_key,
141
162
  add_check_constraint: :remove_check_constraint,
142
- enable_extension: :disable_extension
163
+ add_exclusion_constraint: :remove_exclusion_constraint,
164
+ add_unique_constraint: :remove_unique_constraint,
165
+ enable_extension: :disable_extension,
166
+ create_enum: :drop_enum
143
167
  }.each do |cmd, inv|
144
168
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
145
169
  class_eval <<-EOV, __FILE__, __LINE__ + 1
@@ -164,7 +188,17 @@ module ActiveRecord
164
188
  [:transaction, args, invertions_proc]
165
189
  end
166
190
 
191
+ def invert_create_table(args, &block)
192
+ if args.last.is_a?(Hash)
193
+ args.last.delete(:if_not_exists)
194
+ end
195
+ super
196
+ end
197
+
167
198
  def invert_drop_table(args, &block)
199
+ if args.last.is_a?(Hash)
200
+ args.last.delete(:if_exists)
201
+ end
168
202
  if args.size == 1 && block == nil
169
203
  raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
170
204
  end
@@ -172,7 +206,10 @@ module ActiveRecord
172
206
  end
173
207
 
174
208
  def invert_rename_table(args)
175
- [:rename_table, args.reverse]
209
+ old_name, new_name, options = args
210
+ args = [new_name, old_name]
211
+ args << options if options
212
+ [:rename_table, args]
176
213
  end
177
214
 
178
215
  def invert_remove_column(args)
@@ -234,6 +271,11 @@ module ActiveRecord
234
271
  [:change_column_null, args]
235
272
  end
236
273
 
274
+ def invert_add_foreign_key(args)
275
+ args.last.delete(:validate) if args.last.is_a?(Hash)
276
+ super
277
+ end
278
+
237
279
  def invert_remove_foreign_key(args)
238
280
  options = args.extract_options!
239
281
  from_table, to_table = args
@@ -268,24 +310,80 @@ module ActiveRecord
268
310
  [:change_table_comment, [table, from: options[:to], to: options[:from]]]
269
311
  end
270
312
 
313
+ def invert_add_check_constraint(args)
314
+ if (options = args.last).is_a?(Hash)
315
+ options.delete(:validate)
316
+ options[:if_exists] = options.delete(:if_not_exists) if options.key?(:if_not_exists)
317
+ end
318
+ super
319
+ end
320
+
271
321
  def invert_remove_check_constraint(args)
272
322
  raise ActiveRecord::IrreversibleMigration, "remove_check_constraint is only reversible if given an expression." if args.size < 2
323
+
324
+ if (options = args.last).is_a?(Hash)
325
+ options[:if_not_exists] = options.delete(:if_exists) if options.key?(:if_exists)
326
+ end
327
+ super
328
+ end
329
+
330
+ def invert_remove_exclusion_constraint(args)
331
+ raise ActiveRecord::IrreversibleMigration, "remove_exclusion_constraint is only reversible if given an expression." if args.size < 2
332
+ super
333
+ end
334
+
335
+ def invert_add_unique_constraint(args)
336
+ options = args.dup.extract_options!
337
+
338
+ raise ActiveRecord::IrreversibleMigration, "add_unique_constraint is not reversible if given an using_index." if options[:using_index]
273
339
  super
274
340
  end
275
341
 
342
+ def invert_remove_unique_constraint(args)
343
+ _table, columns = args.dup.tap(&:extract_options!)
344
+
345
+ raise ActiveRecord::IrreversibleMigration, "remove_unique_constraint is only reversible if given an column_name." if columns.blank?
346
+ super
347
+ end
348
+
349
+ def invert_drop_enum(args)
350
+ _enum, values = args.dup.tap(&:extract_options!)
351
+ raise ActiveRecord::IrreversibleMigration, "drop_enum is only reversible if given a list of enum values." unless values
352
+ super
353
+ end
354
+
355
+ def invert_rename_enum(args)
356
+ name, options = args
357
+
358
+ unless options.is_a?(Hash) && options.has_key?(:to)
359
+ raise ActiveRecord::IrreversibleMigration, "rename_enum is only reversible if given a :to option."
360
+ end
361
+
362
+ [:rename_enum, [options[:to], to: name]]
363
+ end
364
+
365
+ def invert_rename_enum_value(args)
366
+ type_name, options = args
367
+
368
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
369
+ raise ActiveRecord::IrreversibleMigration, "rename_enum_value is only reversible if given a :from and :to option."
370
+ end
371
+
372
+ [:rename_enum_value, [type_name, from: options[:to], to: options[:from]]]
373
+ end
374
+
276
375
  def respond_to_missing?(method, _)
277
376
  super || delegate.respond_to?(method)
278
377
  end
279
378
 
280
379
  # Forwards any missing method call to the \target.
281
- def method_missing(method, *args, &block)
380
+ def method_missing(method, ...)
282
381
  if delegate.respond_to?(method)
283
- delegate.public_send(method, *args, &block)
382
+ delegate.public_send(method, ...)
284
383
  else
285
384
  super
286
385
  end
287
386
  end
288
- ruby2_keywords(:method_missing)
289
387
  end
290
388
  end
291
389
  end