activerecord 5.2.8.1 → 6.0.0.beta1

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

Potentially problematic release.


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

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
  def initialize(values)
45
45
  @values = values
46
46
  @indexes = values.each_with_index.find_all { |thing, i|
47
- Arel::Nodes::BindParam === thing
47
+ Substitute === thing
48
48
  }.map(&:last)
49
49
  end
50
50
 
@@ -56,6 +56,28 @@ module ActiveRecord
56
56
  end
57
57
  end
58
58
 
59
+ class PartialQueryCollector
60
+ def initialize
61
+ @parts = []
62
+ @binds = []
63
+ end
64
+
65
+ def <<(str)
66
+ @parts << str
67
+ self
68
+ end
69
+
70
+ def add_bind(obj)
71
+ @binds << obj
72
+ @parts << Substitute.new
73
+ self
74
+ end
75
+
76
+ def value
77
+ [@parts, @binds]
78
+ end
79
+ end
80
+
59
81
  def self.query(sql)
60
82
  Query.new(sql)
61
83
  end
@@ -64,6 +86,10 @@ module ActiveRecord
64
86
  PartialQuery.new(values)
65
87
  end
66
88
 
89
+ def self.partial_query_collector
90
+ PartialQueryCollector.new
91
+ end
92
+
67
93
  class Params # :nodoc:
68
94
  def bind; Substitute.new; end
69
95
  end
@@ -87,8 +113,8 @@ module ActiveRecord
87
113
  end
88
114
  end
89
115
 
90
- def self.create(connection, callable = nil, &block)
91
- relation = (callable || block).call Params.new
116
+ def self.create(connection, block = Proc.new)
117
+ relation = block.call Params.new
92
118
  query_builder, binds = connection.cacheable_query(self, relation.arel)
93
119
  bind_map = BindMap.new(binds)
94
120
  new(query_builder, bind_map, relation.klass)
@@ -106,6 +132,8 @@ module ActiveRecord
106
132
  sql = query_builder.sql_for bind_values, connection
107
133
 
108
134
  klass.find_by_sql(sql, bind_values, preparable: true, &block)
135
+ rescue ::RangeError
136
+ nil
109
137
  end
110
138
 
111
139
  def self.unsupported_value?(value)
@@ -114,8 +142,7 @@ module ActiveRecord
114
142
  end
115
143
  end
116
144
 
117
- protected
118
-
145
+ private
119
146
  attr_reader :query_builder, :bind_map, :klass
120
147
  end
121
148
  end
@@ -17,8 +17,8 @@ module ActiveRecord
17
17
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
18
18
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
19
19
  #
20
- # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
21
- # the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
20
+ # NOTE: If you are using structured database data types (eg. PostgreSQL +hstore+/+json+, or MySQL 5.7+
21
+ # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
22
22
  # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
23
23
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
24
24
  # using a symbol.
@@ -31,10 +31,18 @@ module ActiveRecord
31
31
  #
32
32
  # class User < ActiveRecord::Base
33
33
  # store :settings, accessors: [ :color, :homepage ], coder: JSON
34
+ # store :parent, accessors: [ :name ], coder: JSON, prefix: true
35
+ # store :spouse, accessors: [ :name ], coder: JSON, prefix: :partner
36
+ # store :settings, accessors: [ :two_factor_auth ], suffix: true
37
+ # store :settings, accessors: [ :login_retry ], suffix: :config
34
38
  # end
35
39
  #
36
- # u = User.new(color: 'black', homepage: '37signals.com')
40
+ # u = User.new(color: 'black', homepage: '37signals.com', parent_name: 'Mary', partner_name: 'Lily')
37
41
  # u.color # Accessor stored attribute
42
+ # u.parent_name # Accessor stored attribute with prefix
43
+ # u.partner_name # Accessor stored attribute with custom prefix
44
+ # u.two_factor_auth_settings # Accessor stored attribute with suffix
45
+ # u.login_retry_config # Accessor stored attribute with custom suffix
38
46
  # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
39
47
  #
40
48
  # # There is no difference between strings and symbols for accessing custom attributes
@@ -44,11 +52,13 @@ module ActiveRecord
44
52
  # # Add additional accessors to an existing store through store_accessor
45
53
  # class SuperUser < User
46
54
  # store_accessor :settings, :privileges, :servants
55
+ # store_accessor :parent, :birthday, prefix: true
56
+ # store_accessor :settings, :secret_question, suffix: :config
47
57
  # end
48
58
  #
49
59
  # The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
50
60
  #
51
- # User.stored_attributes[:settings] # [:color, :homepage]
61
+ # User.stored_attributes[:settings] # [:color, :homepage, :two_factor_auth, :login_retry]
52
62
  #
53
63
  # == Overwriting default accessors
54
64
  #
@@ -81,19 +91,40 @@ module ActiveRecord
81
91
  module ClassMethods
82
92
  def store(store_attribute, options = {})
83
93
  serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
84
- store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
94
+ store_accessor(store_attribute, options[:accessors], options.slice(:prefix, :suffix)) if options.has_key? :accessors
85
95
  end
86
96
 
87
- def store_accessor(store_attribute, *keys)
97
+ def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
88
98
  keys = keys.flatten
89
99
 
100
+ accessor_prefix =
101
+ case prefix
102
+ when String, Symbol
103
+ "#{prefix}_"
104
+ when TrueClass
105
+ "#{store_attribute}_"
106
+ else
107
+ ""
108
+ end
109
+ accessor_suffix =
110
+ case suffix
111
+ when String, Symbol
112
+ "_#{suffix}"
113
+ when TrueClass
114
+ "_#{store_attribute}"
115
+ else
116
+ ""
117
+ end
118
+
90
119
  _store_accessors_module.module_eval do
91
120
  keys.each do |key|
92
- define_method("#{key}=") do |value|
121
+ accessor_key = "#{accessor_prefix}#{key}#{accessor_suffix}"
122
+
123
+ define_method("#{accessor_key}=") do |value|
93
124
  write_store_attribute(store_attribute, key, value)
94
125
  end
95
126
 
96
- define_method(key) do
127
+ define_method(accessor_key) do
97
128
  read_store_attribute(store_attribute, key)
98
129
  end
99
130
  end
@@ -73,10 +73,7 @@ module ActiveRecord
73
73
  klass.reflect_on_aggregation(aggregation_name)
74
74
  end
75
75
 
76
- # TODO Change this to private once we've dropped Ruby 2.2 support.
77
- # Workaround for Ruby 2.2 "private attribute?" warning.
78
- protected
79
-
76
+ private
80
77
  attr_reader :klass, :arel_table, :association
81
78
  end
82
79
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/database_configurations"
4
+
3
5
  module ActiveRecord
4
6
  module Tasks # :nodoc:
5
7
  class DatabaseAlreadyExists < StandardError; end # :nodoc:
@@ -8,7 +10,7 @@ module ActiveRecord
8
10
  # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
9
11
  # logic behind common tasks used to manage database and migrations.
10
12
  #
11
- # The tasks defined here are used with Rake tasks provided by Active Record.
13
+ # The tasks defined here are used with Rails commands provided by Active Record.
12
14
  #
13
15
  # In order to use DatabaseTasks, a few config values need to be set. All the needed
14
16
  # config values are set by Rails already, so it's necessary to do it only if you
@@ -101,25 +103,30 @@ module ActiveRecord
101
103
  @env ||= Rails.env
102
104
  end
103
105
 
106
+ def spec
107
+ @spec ||= "primary"
108
+ end
109
+
104
110
  def seed_loader
105
111
  @seed_loader ||= Rails.application
106
112
  end
107
113
 
108
114
  def current_config(options = {})
109
115
  options.reverse_merge! env: env
116
+ options[:spec] ||= "primary"
110
117
  if options.has_key?(:config)
111
118
  @current_config = options[:config]
112
119
  else
113
- @current_config ||= ActiveRecord::Base.configurations[options[:env]]
120
+ @current_config ||= ActiveRecord::Base.configurations.configs_for(env_name: options[:env], spec_name: options[:spec]).config
114
121
  end
115
122
  end
116
123
 
117
124
  def create(*arguments)
118
125
  configuration = arguments.first
119
126
  class_for_adapter(configuration["adapter"]).new(*arguments).create
120
- $stdout.puts "Created database '#{configuration['database']}'"
127
+ $stdout.puts "Created database '#{configuration['database']}'" if verbose?
121
128
  rescue DatabaseAlreadyExists
122
- $stderr.puts "Database '#{configuration['database']}' already exists"
129
+ $stderr.puts "Database '#{configuration['database']}' already exists" if verbose?
123
130
  rescue Exception => error
124
131
  $stderr.puts error
125
132
  $stderr.puts "Couldn't create '#{configuration['database']}' database. Please check your configuration."
@@ -134,6 +141,18 @@ module ActiveRecord
134
141
  end
135
142
  end
136
143
 
144
+ def for_each
145
+ databases = Rails.application.config.database_configuration
146
+ database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
147
+
148
+ # if this is a single database application we don't want tasks for each primary database
149
+ return if database_configs.count == 1
150
+
151
+ database_configs.each do |db_config|
152
+ yield db_config.spec_name
153
+ end
154
+ end
155
+
137
156
  def create_current(environment = env)
138
157
  each_current_configuration(environment) { |configuration|
139
158
  create configuration
@@ -144,7 +163,7 @@ module ActiveRecord
144
163
  def drop(*arguments)
145
164
  configuration = arguments.first
146
165
  class_for_adapter(configuration["adapter"]).new(*arguments).drop
147
- $stdout.puts "Dropped database '#{configuration['database']}'"
166
+ $stdout.puts "Dropped database '#{configuration['database']}'" if verbose?
148
167
  rescue ActiveRecord::NoDatabaseError
149
168
  $stderr.puts "Database '#{configuration['database']}' does not exist"
150
169
  rescue Exception => error
@@ -166,17 +185,33 @@ module ActiveRecord
166
185
  def migrate
167
186
  check_target_version
168
187
 
169
- verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
170
188
  scope = ENV["SCOPE"]
171
- verbose_was, Migration.verbose = Migration.verbose, verbose
189
+ verbose_was, Migration.verbose = Migration.verbose, verbose?
190
+
172
191
  Base.connection.migration_context.migrate(target_version) do |migration|
173
192
  scope.blank? || scope == migration.scope
174
193
  end
194
+
175
195
  ActiveRecord::Base.clear_cache!
176
196
  ensure
177
197
  Migration.verbose = verbose_was
178
198
  end
179
199
 
200
+ def migrate_status
201
+ unless ActiveRecord::SchemaMigration.table_exists?
202
+ Kernel.abort "Schema migrations table does not exist yet."
203
+ end
204
+
205
+ # output
206
+ puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
207
+ puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
208
+ puts "-" * 50
209
+ ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
210
+ puts "#{status.center(8)} #{version.ljust(14)} #{name}"
211
+ end
212
+ puts
213
+ end
214
+
180
215
  def check_target_version
181
216
  if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"]))
182
217
  raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`"
@@ -187,8 +222,8 @@ module ActiveRecord
187
222
  ENV["VERSION"].to_i if ENV["VERSION"] && !ENV["VERSION"].empty?
188
223
  end
189
224
 
190
- def charset_current(environment = env)
191
- charset ActiveRecord::Base.configurations[environment]
225
+ def charset_current(environment = env, specification_name = spec)
226
+ charset ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
192
227
  end
193
228
 
194
229
  def charset(*arguments)
@@ -196,8 +231,8 @@ module ActiveRecord
196
231
  class_for_adapter(configuration["adapter"]).new(*arguments).charset
197
232
  end
198
233
 
199
- def collation_current(environment = env)
200
- collation ActiveRecord::Base.configurations[environment]
234
+ def collation_current(environment = env, specification_name = spec)
235
+ collation ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
201
236
  end
202
237
 
203
238
  def collation(*arguments)
@@ -234,9 +269,10 @@ module ActiveRecord
234
269
  class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
235
270
  end
236
271
 
237
- def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
238
- file ||= schema_file(format)
272
+ def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
273
+ file ||= dump_filename(spec_name, format)
239
274
 
275
+ verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
240
276
  check_schema_file(file)
241
277
  ActiveRecord::Base.establish_connection(configuration)
242
278
 
@@ -250,27 +286,53 @@ module ActiveRecord
250
286
  end
251
287
  ActiveRecord::InternalMetadata.create_table
252
288
  ActiveRecord::InternalMetadata[:environment] = environment
289
+ ensure
290
+ Migration.verbose = verbose_was
253
291
  end
254
292
 
255
293
  def schema_file(format = ActiveRecord::Base.schema_format)
294
+ File.join(db_dir, schema_file_type(format))
295
+ end
296
+
297
+ def schema_file_type(format = ActiveRecord::Base.schema_format)
256
298
  case format
257
299
  when :ruby
258
- File.join(db_dir, "schema.rb")
300
+ "schema.rb"
259
301
  when :sql
260
- File.join(db_dir, "structure.sql")
302
+ "structure.sql"
303
+ end
304
+ end
305
+
306
+ def dump_filename(namespace, format = ActiveRecord::Base.schema_format)
307
+ filename = if namespace == "primary"
308
+ schema_file_type(format)
309
+ else
310
+ "#{namespace}_#{schema_file_type(format)}"
311
+ end
312
+
313
+ ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
314
+ end
315
+
316
+ def cache_dump_filename(namespace)
317
+ filename = if namespace == "primary"
318
+ "schema_cache.yml"
319
+ else
320
+ "#{namespace}_schema_cache.yml"
261
321
  end
322
+
323
+ ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
262
324
  end
263
325
 
264
326
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
265
- each_current_configuration(environment) { |configuration, configuration_environment|
266
- load_schema configuration, format, file, configuration_environment
327
+ each_current_configuration(environment) { |configuration, spec_name, env|
328
+ load_schema(configuration, format, file, env, spec_name)
267
329
  }
268
330
  ActiveRecord::Base.establish_connection(environment.to_sym)
269
331
  end
270
332
 
271
333
  def check_schema_file(filename)
272
334
  unless File.exist?(filename)
273
- message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}.dup
335
+ message = +%{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
274
336
  message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
275
337
  Kernel.abort message
276
338
  end
@@ -297,6 +359,9 @@ module ActiveRecord
297
359
  end
298
360
 
299
361
  private
362
+ def verbose?
363
+ ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
364
+ end
300
365
 
301
366
  def class_for_adapter(adapter)
302
367
  _key, task = @tasks.each_pair.detect { |pattern, _task| adapter[pattern] }
@@ -310,15 +375,16 @@ module ActiveRecord
310
375
  environments = [environment]
311
376
  environments << "test" if environment == "development"
312
377
 
313
- ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
314
- next unless configuration["database"]
315
-
316
- yield configuration, configuration_environment
378
+ environments.each do |env|
379
+ ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
380
+ yield db_config.config, db_config.spec_name, env
381
+ end
317
382
  end
318
383
  end
319
384
 
320
385
  def each_local_configuration
321
- ActiveRecord::Base.configurations.each_value do |configuration|
386
+ ActiveRecord::Base.configurations.configs_for.each do |db_config|
387
+ configuration = db_config.config
322
388
  next unless configuration["database"]
323
389
 
324
390
  if local_database?(configuration)
@@ -68,9 +68,7 @@ module ActiveRecord
68
68
 
69
69
  private
70
70
 
71
- def configuration
72
- @configuration
73
- end
71
+ attr_reader :configuration
74
72
 
75
73
  def configuration_without_database
76
74
  configuration.merge("database" => nil)
@@ -106,7 +104,7 @@ module ActiveRecord
106
104
  end
107
105
 
108
106
  def run_cmd_error(cmd, args, action)
109
- msg = "failed to execute: `#{cmd}`\n".dup
107
+ msg = +"failed to execute: `#{cmd}`\n"
110
108
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
111
109
  msg
112
110
  end
@@ -6,8 +6,8 @@ module ActiveRecord
6
6
  module Tasks # :nodoc:
7
7
  class PostgreSQLDatabaseTasks # :nodoc:
8
8
  DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
9
- ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
10
- SQL_COMMENT_BEGIN = "--".freeze
9
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
10
+ SQL_COMMENT_BEGIN = "--"
11
11
 
12
12
  delegate :connection, :establish_connection, :clear_active_connections!,
13
13
  to: ActiveRecord::Base
@@ -82,7 +82,7 @@ module ActiveRecord
82
82
 
83
83
  def structure_load(filename, extra_flags)
84
84
  set_psql_env
85
- args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename]
85
+ args = ["-v", ON_ERROR_STOP_1, "-q", "-X", "-f", filename]
86
86
  args.concat(Array(extra_flags)) if extra_flags
87
87
  args << configuration["database"]
88
88
  run_cmd("psql", args, "loading")
@@ -90,9 +90,7 @@ module ActiveRecord
90
90
 
91
91
  private
92
92
 
93
- def configuration
94
- @configuration
95
- end
93
+ attr_reader :configuration
96
94
 
97
95
  def encoding
98
96
  configuration["encoding"] || DEFAULT_ENCODING
@@ -117,7 +115,7 @@ module ActiveRecord
117
115
  end
118
116
 
119
117
  def run_cmd_error(cmd, args, action)
120
- msg = "failed to execute:\n".dup
118
+ msg = +"failed to execute:\n"
121
119
  msg << "#{cmd} #{args.join(' ')}\n\n"
122
120
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
123
121
  msg
@@ -60,20 +60,14 @@ module ActiveRecord
60
60
 
61
61
  private
62
62
 
63
- def configuration
64
- @configuration
65
- end
66
-
67
- def root
68
- @root
69
- end
63
+ attr_reader :configuration, :root
70
64
 
71
65
  def run_cmd(cmd, args, out)
72
66
  fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
73
67
  end
74
68
 
75
69
  def run_cmd_error(cmd, args)
76
- msg = "failed to execute:\n".dup
70
+ msg = +"failed to execute:\n"
77
71
  msg << "#{cmd} #{args.join(' ')}\n\n"
78
72
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
79
73
  msg
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/testing/parallelization"
4
+
5
+ module ActiveRecord
6
+ module TestDatabases # :nodoc:
7
+ ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
8
+ create_and_load_schema(i, env_name: Rails.env)
9
+ end
10
+
11
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook do
12
+ drop(env_name: Rails.env)
13
+ end
14
+
15
+ def self.create_and_load_schema(i, env_name:)
16
+ old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
17
+
18
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
19
+ db_config.config["database"] += "-#{i}"
20
+ ActiveRecord::Tasks::DatabaseTasks.create(db_config.config)
21
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
22
+ end
23
+ ensure
24
+ ActiveRecord::Base.establish_connection(Rails.env.to_sym)
25
+ ENV["VERBOSE"] = old
26
+ end
27
+
28
+ def self.drop(env_name:)
29
+ old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
30
+
31
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
32
+ ActiveRecord::Tasks::DatabaseTasks.drop(db_config.config)
33
+ end
34
+ ensure
35
+ ENV["VERBOSE"] = old
36
+ end
37
+ end
38
+ end