activerecord 7.0.4 → 7.1.5.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1971 -1243
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +20 -4
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +20 -14
  15. data/lib/active_record/associations/collection_proxy.rb +20 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  21. data/lib/active_record/associations/join_dependency.rb +10 -10
  22. data/lib/active_record/associations/preloader/association.rb +31 -7
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader.rb +13 -10
  25. data/lib/active_record/associations/singular_association.rb +1 -1
  26. data/lib/active_record/associations/through_association.rb +22 -11
  27. data/lib/active_record/associations.rb +333 -222
  28. data/lib/active_record/attribute_assignment.rb +0 -2
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  30. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  31. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  32. data/lib/active_record/attribute_methods/query.rb +28 -16
  33. data/lib/active_record/attribute_methods/read.rb +21 -8
  34. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
  36. data/lib/active_record/attribute_methods/write.rb +6 -6
  37. data/lib/active_record/attribute_methods.rb +148 -26
  38. data/lib/active_record/attributes.rb +3 -3
  39. data/lib/active_record/autosave_association.rb +59 -10
  40. data/lib/active_record/base.rb +7 -2
  41. data/lib/active_record/callbacks.rb +16 -32
  42. data/lib/active_record/coders/column_serializer.rb +61 -0
  43. data/lib/active_record/coders/json.rb +1 -1
  44. data/lib/active_record/coders/yaml_column.rb +70 -42
  45. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  46. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  47. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -7
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +297 -127
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -103
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +254 -125
  60. data/lib/active_record/connection_adapters/column.rb +9 -0
  61. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
  64. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  65. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  71. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  81. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  82. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  83. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +365 -61
  85. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
  87. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  88. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  91. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +213 -85
  94. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  97. data/lib/active_record/connection_adapters.rb +3 -1
  98. data/lib/active_record/connection_handling.rb +72 -95
  99. data/lib/active_record/core.rb +181 -154
  100. data/lib/active_record/counter_cache.rb +52 -27
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  102. data/lib/active_record/database_configurations/database_config.rb +9 -3
  103. data/lib/active_record/database_configurations/hash_config.rb +28 -14
  104. data/lib/active_record/database_configurations/url_config.rb +17 -11
  105. data/lib/active_record/database_configurations.rb +86 -33
  106. data/lib/active_record/delegated_type.rb +15 -10
  107. data/lib/active_record/deprecator.rb +7 -0
  108. data/lib/active_record/destroy_association_async_job.rb +3 -1
  109. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  110. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  111. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  112. data/lib/active_record/encryption/config.rb +25 -1
  113. data/lib/active_record/encryption/configurable.rb +12 -19
  114. data/lib/active_record/encryption/context.rb +10 -3
  115. data/lib/active_record/encryption/contexts.rb +5 -1
  116. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  117. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  118. data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  121. data/lib/active_record/encryption/key_generator.rb +12 -1
  122. data/lib/active_record/encryption/message_serializer.rb +2 -0
  123. data/lib/active_record/encryption/properties.rb +3 -3
  124. data/lib/active_record/encryption/scheme.rb +22 -21
  125. data/lib/active_record/encryption.rb +3 -0
  126. data/lib/active_record/enum.rb +112 -28
  127. data/lib/active_record/errors.rb +112 -18
  128. data/lib/active_record/explain.rb +23 -3
  129. data/lib/active_record/explain_subscriber.rb +1 -1
  130. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  131. data/lib/active_record/fixture_set/render_context.rb +2 -0
  132. data/lib/active_record/fixture_set/table_row.rb +29 -8
  133. data/lib/active_record/fixtures.rb +135 -71
  134. data/lib/active_record/future_result.rb +40 -5
  135. data/lib/active_record/gem_version.rb +4 -4
  136. data/lib/active_record/inheritance.rb +30 -16
  137. data/lib/active_record/insert_all.rb +57 -10
  138. data/lib/active_record/integration.rb +8 -8
  139. data/lib/active_record/internal_metadata.rb +120 -30
  140. data/lib/active_record/locking/optimistic.rb +33 -19
  141. data/lib/active_record/locking/pessimistic.rb +5 -2
  142. data/lib/active_record/log_subscriber.rb +29 -12
  143. data/lib/active_record/marshalling.rb +59 -0
  144. data/lib/active_record/message_pack.rb +124 -0
  145. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  146. data/lib/active_record/middleware/database_selector.rb +9 -11
  147. data/lib/active_record/middleware/shard_selector.rb +3 -1
  148. data/lib/active_record/migration/command_recorder.rb +105 -7
  149. data/lib/active_record/migration/compatibility.rb +163 -58
  150. data/lib/active_record/migration/default_strategy.rb +23 -0
  151. data/lib/active_record/migration/execution_strategy.rb +19 -0
  152. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  153. data/lib/active_record/migration.rb +271 -114
  154. data/lib/active_record/model_schema.rb +69 -44
  155. data/lib/active_record/nested_attributes.rb +37 -8
  156. data/lib/active_record/normalization.rb +167 -0
  157. data/lib/active_record/persistence.rb +195 -42
  158. data/lib/active_record/promise.rb +84 -0
  159. data/lib/active_record/query_cache.rb +4 -22
  160. data/lib/active_record/query_logs.rb +87 -51
  161. data/lib/active_record/query_logs_formatter.rb +41 -0
  162. data/lib/active_record/querying.rb +15 -2
  163. data/lib/active_record/railtie.rb +107 -45
  164. data/lib/active_record/railties/controller_runtime.rb +14 -9
  165. data/lib/active_record/railties/databases.rake +144 -150
  166. data/lib/active_record/railties/job_runtime.rb +23 -0
  167. data/lib/active_record/readonly_attributes.rb +32 -5
  168. data/lib/active_record/reflection.rb +189 -45
  169. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  170. data/lib/active_record/relation/batches.rb +190 -61
  171. data/lib/active_record/relation/calculations.rb +232 -81
  172. data/lib/active_record/relation/delegation.rb +23 -9
  173. data/lib/active_record/relation/finder_methods.rb +77 -16
  174. data/lib/active_record/relation/merger.rb +2 -0
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  178. data/lib/active_record/relation/predicate_builder.rb +26 -14
  179. data/lib/active_record/relation/query_attribute.rb +25 -1
  180. data/lib/active_record/relation/query_methods.rb +408 -76
  181. data/lib/active_record/relation/spawn_methods.rb +18 -1
  182. data/lib/active_record/relation.rb +103 -37
  183. data/lib/active_record/result.rb +25 -9
  184. data/lib/active_record/runtime_registry.rb +24 -1
  185. data/lib/active_record/sanitization.rb +51 -11
  186. data/lib/active_record/schema.rb +2 -3
  187. data/lib/active_record/schema_dumper.rb +50 -7
  188. data/lib/active_record/schema_migration.rb +68 -33
  189. data/lib/active_record/scoping/default.rb +15 -5
  190. data/lib/active_record/scoping/named.rb +2 -2
  191. data/lib/active_record/scoping.rb +2 -1
  192. data/lib/active_record/secure_password.rb +60 -0
  193. data/lib/active_record/secure_token.rb +21 -3
  194. data/lib/active_record/signed_id.rb +7 -5
  195. data/lib/active_record/store.rb +9 -9
  196. data/lib/active_record/suppressor.rb +3 -1
  197. data/lib/active_record/table_metadata.rb +16 -3
  198. data/lib/active_record/tasks/database_tasks.rb +152 -108
  199. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  200. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  201. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  202. data/lib/active_record/test_fixtures.rb +114 -96
  203. data/lib/active_record/timestamp.rb +30 -16
  204. data/lib/active_record/token_for.rb +113 -0
  205. data/lib/active_record/touch_later.rb +11 -6
  206. data/lib/active_record/transactions.rb +39 -13
  207. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  208. data/lib/active_record/type/internal/timezone.rb +7 -2
  209. data/lib/active_record/type/serialized.rb +8 -4
  210. data/lib/active_record/type/time.rb +4 -0
  211. data/lib/active_record/validations/absence.rb +1 -1
  212. data/lib/active_record/validations/numericality.rb +5 -4
  213. data/lib/active_record/validations/presence.rb +5 -28
  214. data/lib/active_record/validations/uniqueness.rb +47 -2
  215. data/lib/active_record/validations.rb +8 -4
  216. data/lib/active_record/version.rb +1 -1
  217. data/lib/active_record.rb +130 -17
  218. data/lib/arel/errors.rb +10 -0
  219. data/lib/arel/factory_methods.rb +4 -0
  220. data/lib/arel/filter_predications.rb +1 -1
  221. data/lib/arel/nodes/and.rb +4 -0
  222. data/lib/arel/nodes/binary.rb +6 -1
  223. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  224. data/lib/arel/nodes/cte.rb +36 -0
  225. data/lib/arel/nodes/filter.rb +1 -1
  226. data/lib/arel/nodes/fragments.rb +35 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  228. data/lib/arel/nodes/leading_join.rb +8 -0
  229. data/lib/arel/nodes/node.rb +111 -2
  230. data/lib/arel/nodes/sql_literal.rb +6 -0
  231. data/lib/arel/nodes/table_alias.rb +4 -0
  232. data/lib/arel/nodes.rb +4 -0
  233. data/lib/arel/predications.rb +2 -0
  234. data/lib/arel/table.rb +9 -5
  235. data/lib/arel/tree_manager.rb +5 -1
  236. data/lib/arel/visitors/mysql.rb +8 -1
  237. data/lib/arel/visitors/to_sql.rb +83 -18
  238. data/lib/arel/visitors/visitor.rb +2 -2
  239. data/lib/arel.rb +16 -2
  240. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  241. data/lib/rails/generators/active_record/migration.rb +3 -1
  242. data/lib/rails/generators/active_record/model/USAGE +113 -0
  243. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  244. metadata +51 -15
  245. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  246. data/lib/active_record/null_relation.rb +0 -63
@@ -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.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:
@@ -43,9 +45,9 @@ module ActiveRecord
43
45
  # config.active_record.database_resolver = MyResolver
44
46
  # config.active_record.database_resolver_context = MyResolver::MySession
45
47
  #
46
- # Note: If you are using `rails new my_app --minimal` you will need to call
47
- # `require "active_support/core_ext/integer/time"` to load the libraries
48
- # for +Time+.
48
+ # Note: If you are using <tt>rails new my_app --minimal</tt> you will need
49
+ # to call <tt>require "active_support/core_ext/integer/time"</tt> to load
50
+ # the core extension in order to use +2.seconds+
49
51
  class DatabaseSelector
50
52
  def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
51
53
  @app = app
@@ -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,35 +2,44 @@
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
15
- # * change_column
16
19
  # * change_column_default (must supply a +:from+ and +:to+ option)
17
20
  # * change_column_null
18
21
  # * change_column_comment (must supply a +:from+ and +:to+ option)
19
22
  # * change_table_comment (must supply a +:from+ and +:to+ option)
23
+ # * create_enum
20
24
  # * create_join_table
21
25
  # * create_table
22
26
  # * disable_extension
27
+ # * drop_enum (must supply a list of values)
23
28
  # * drop_join_table
24
29
  # * drop_table (must supply a block)
25
30
  # * enable_extension
26
31
  # * remove_column (must supply a type)
27
- # * remove_columns (must specify at least one column name or more)
32
+ # * remove_columns (must supply a +:type+ option)
28
33
  # * remove_foreign_key (must supply a second table)
29
34
  # * remove_check_constraint
35
+ # * remove_exclusion_constraint
36
+ # * remove_unique_constraint
30
37
  # * remove_index
31
38
  # * remove_reference
32
39
  # * remove_timestamps
33
40
  # * rename_column
41
+ # * rename_enum (must supply a +:to+ option)
42
+ # * rename_enum_value (must supply a +:from+ and +:to+ option)
34
43
  # * rename_index
35
44
  # * rename_table
36
45
  class CommandRecorder
@@ -42,7 +51,10 @@ module ActiveRecord
42
51
  :change_column, :execute, :remove_columns, :change_column_null,
43
52
  :add_foreign_key, :remove_foreign_key,
44
53
  :change_column_comment, :change_table_comment,
45
- :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,
46
58
  ]
47
59
  include JoinTable
48
60
 
@@ -118,7 +130,15 @@ module ActiveRecord
118
130
  alias :remove_belongs_to :remove_reference
119
131
 
120
132
  def change_table(table_name, **options) # :nodoc:
121
- 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
122
142
  end
123
143
 
124
144
  def replay(migration)
@@ -140,7 +160,10 @@ module ActiveRecord
140
160
  add_reference: :remove_reference,
141
161
  add_foreign_key: :remove_foreign_key,
142
162
  add_check_constraint: :remove_check_constraint,
143
- 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
144
167
  }.each do |cmd, inv|
145
168
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
146
169
  class_eval <<-EOV, __FILE__, __LINE__ + 1
@@ -165,7 +188,17 @@ module ActiveRecord
165
188
  [:transaction, args, invertions_proc]
166
189
  end
167
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
+
168
198
  def invert_drop_table(args, &block)
199
+ if args.last.is_a?(Hash)
200
+ args.last.delete(:if_exists)
201
+ end
169
202
  if args.size == 1 && block == nil
170
203
  raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
171
204
  end
@@ -173,7 +206,10 @@ module ActiveRecord
173
206
  end
174
207
 
175
208
  def invert_rename_table(args)
176
- [: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]
177
213
  end
178
214
 
179
215
  def invert_remove_column(args)
@@ -235,6 +271,11 @@ module ActiveRecord
235
271
  [:change_column_null, args]
236
272
  end
237
273
 
274
+ def invert_add_foreign_key(args)
275
+ args.last.delete(:validate) if args.last.is_a?(Hash)
276
+ super
277
+ end
278
+
238
279
  def invert_remove_foreign_key(args)
239
280
  options = args.extract_options!
240
281
  from_table, to_table = args
@@ -269,11 +310,68 @@ module ActiveRecord
269
310
  [:change_table_comment, [table, from: options[:to], to: options[:from]]]
270
311
  end
271
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
+
272
321
  def invert_remove_check_constraint(args)
273
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]
274
339
  super
275
340
  end
276
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
+
277
375
  def respond_to_missing?(method, _)
278
376
  super || delegate.respond_to?(method)
279
377
  end