activerecord 5.2.7 → 6.0.6.1

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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +987 -584
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/advisory_lock_base.rb +18 -0
  7. data/lib/active_record/aggregations.rb +4 -3
  8. data/lib/active_record/association_relation.rb +10 -8
  9. data/lib/active_record/associations/alias_tracker.rb +0 -1
  10. data/lib/active_record/associations/association.rb +55 -19
  11. data/lib/active_record/associations/association_scope.rb +11 -7
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +19 -23
  22. data/lib/active_record/associations/collection_proxy.rb +14 -17
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -11
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +4 -4
  30. data/lib/active_record/associations/join_dependency.rb +47 -30
  31. data/lib/active_record/associations/preloader/association.rb +61 -41
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/preloader.rb +44 -33
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/associations/through_association.rb +1 -1
  36. data/lib/active_record/associations.rb +21 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  40. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  42. data/lib/active_record/attribute_methods/query.rb +2 -3
  43. data/lib/active_record/attribute_methods/read.rb +15 -54
  44. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  46. data/lib/active_record/attribute_methods/write.rb +17 -25
  47. data/lib/active_record/attribute_methods.rb +28 -100
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +12 -14
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +23 -2
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +77 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +105 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -217
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +8 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +137 -147
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +117 -66
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations/database_config.rb +37 -0
  112. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  113. data/lib/active_record/database_configurations/url_config.rb +78 -0
  114. data/lib/active_record/database_configurations.rb +233 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +44 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +14 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector.rb +74 -0
  135. data/lib/active_record/migration/command_recorder.rb +54 -22
  136. data/lib/active_record/migration/compatibility.rb +79 -52
  137. data/lib/active_record/migration/join_table.rb +0 -1
  138. data/lib/active_record/migration.rb +104 -85
  139. data/lib/active_record/model_schema.rb +62 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +51 -51
  151. data/lib/active_record/relation/batches.rb +13 -11
  152. data/lib/active_record/relation/calculations.rb +55 -49
  153. data/lib/active_record/relation/delegation.rb +35 -50
  154. data/lib/active_record/relation/finder_methods.rb +23 -28
  155. data/lib/active_record/relation/from_clause.rb +4 -0
  156. data/lib/active_record/relation/merger.rb +12 -17
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  159. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  160. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  161. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  162. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  163. data/lib/active_record/relation/predicate_builder.rb +5 -11
  164. data/lib/active_record/relation/query_attribute.rb +13 -8
  165. data/lib/active_record/relation/query_methods.rb +234 -69
  166. data/lib/active_record/relation/spawn_methods.rb +1 -2
  167. data/lib/active_record/relation/where_clause.rb +14 -11
  168. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  169. data/lib/active_record/relation.rb +326 -81
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping/default.rb +4 -6
  176. data/lib/active_record/scoping/named.rb +25 -16
  177. data/lib/active_record/scoping.rb +8 -9
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +243 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  193. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  194. data/lib/active_record/type/serialized.rb +0 -1
  195. data/lib/active_record/type/time.rb +10 -0
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type.rb +3 -5
  199. data/lib/active_record/type_caster/connection.rb +15 -14
  200. data/lib/active_record/type_caster/map.rb +1 -4
  201. data/lib/active_record/validations/associated.rb +0 -1
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/active_record/validations.rb +3 -3
  204. data/lib/active_record.rb +10 -2
  205. data/lib/arel/alias_predication.rb +9 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/attributes.rb +22 -0
  208. data/lib/arel/collectors/bind.rb +24 -0
  209. data/lib/arel/collectors/composite.rb +31 -0
  210. data/lib/arel/collectors/plain_string.rb +20 -0
  211. data/lib/arel/collectors/sql_string.rb +20 -0
  212. data/lib/arel/collectors/substitute_binds.rb +28 -0
  213. data/lib/arel/crud.rb +42 -0
  214. data/lib/arel/delete_manager.rb +18 -0
  215. data/lib/arel/errors.rb +9 -0
  216. data/lib/arel/expressions.rb +29 -0
  217. data/lib/arel/factory_methods.rb +49 -0
  218. data/lib/arel/insert_manager.rb +49 -0
  219. data/lib/arel/math.rb +45 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/nodes.rb +68 -0
  264. data/lib/arel/order_predications.rb +13 -0
  265. data/lib/arel/predications.rb +256 -0
  266. data/lib/arel/select_manager.rb +271 -0
  267. data/lib/arel/table.rb +110 -0
  268. data/lib/arel/tree_manager.rb +72 -0
  269. data/lib/arel/update_manager.rb +34 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/visitors.rb +20 -0
  284. data/lib/arel/window_predications.rb +9 -0
  285. data/lib/arel.rb +62 -0
  286. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  287. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  288. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  289. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  290. data/lib/rails/generators/active_record/migration.rb +14 -2
  291. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  292. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  293. metadata +116 -29
  294. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class DatabaseConfigurations
5
+ # A HashConfig object is created for each database configuration entry that
6
+ # is created from a hash.
7
+ #
8
+ # A hash config:
9
+ #
10
+ # { "development" => { "database" => "db_name" } }
11
+ #
12
+ # Becomes:
13
+ #
14
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
+ # @env_name="development", @spec_name="primary", @config={"database"=>"db_name"}>
16
+ #
17
+ # ==== Options
18
+ #
19
+ # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
20
+ # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
21
+ # database configuration this will default to "primary". In a multiple
22
+ # database three-tier database configuration this corresponds to the name
23
+ # used in the second tier, for example "primary_readonly".
24
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
25
+ # database adapter, name, and other important information for database
26
+ # connections.
27
+ class HashConfig < DatabaseConfig
28
+ attr_reader :config
29
+
30
+ def initialize(env_name, spec_name, config)
31
+ super(env_name, spec_name)
32
+ @config = config
33
+ end
34
+
35
+ # Determines whether a database configuration is for a replica / readonly
36
+ # connection. If the +replica+ key is present in the config, +replica?+ will
37
+ # return +true+.
38
+ def replica?
39
+ config["replica"]
40
+ end
41
+
42
+ # The migrations paths for a database configuration. If the
43
+ # +migrations_paths+ key is present in the config, +migrations_paths+
44
+ # will return its value.
45
+ def migrations_paths
46
+ config["migrations_paths"]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class DatabaseConfigurations
5
+ # A UrlConfig object is created for each database configuration
6
+ # entry that is created from a URL. This can either be a URL string
7
+ # or a hash with a URL in place of the config hash.
8
+ #
9
+ # A URL config:
10
+ #
11
+ # postgres://localhost/foo
12
+ #
13
+ # Becomes:
14
+ #
15
+ # #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fdc3238f340
16
+ # @env_name="default_env", @spec_name="primary",
17
+ # @config={"adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost"},
18
+ # @url="postgres://localhost/foo">
19
+ #
20
+ # ==== Options
21
+ #
22
+ # * <tt>:env_name</tt> - The Rails environment, ie "development".
23
+ # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
24
+ # database configuration this will default to "primary". In a multiple
25
+ # database three-tier database configuration this corresponds to the name
26
+ # used in the second tier, for example "primary_readonly".
27
+ # * <tt>:url</tt> - The database URL.
28
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
29
+ # database adapter, name, and other important information for database
30
+ # connections.
31
+ class UrlConfig < DatabaseConfig
32
+ attr_reader :url, :config
33
+
34
+ def initialize(env_name, spec_name, url, config = {})
35
+ super(env_name, spec_name)
36
+ @config = build_config(config, url)
37
+ @url = url
38
+ end
39
+
40
+ def url_config? # :nodoc:
41
+ true
42
+ end
43
+
44
+ # Determines whether a database configuration is for a replica / readonly
45
+ # connection. If the +replica+ key is present in the config, +replica?+ will
46
+ # return +true+.
47
+ def replica?
48
+ config["replica"]
49
+ end
50
+
51
+ # The migrations paths for a database configuration. If the
52
+ # +migrations_paths+ key is present in the config, +migrations_paths+
53
+ # will return its value.
54
+ def migrations_paths
55
+ config["migrations_paths"]
56
+ end
57
+
58
+ private
59
+ def build_url_hash(url)
60
+ if url.nil? || /^jdbc:/.match?(url)
61
+ { "url" => url }
62
+ else
63
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
64
+ end
65
+ end
66
+
67
+ def build_config(original_config, url)
68
+ hash = build_url_hash(url)
69
+
70
+ if original_config[env_name]
71
+ original_config[env_name].merge(hash)
72
+ else
73
+ original_config.merge(hash)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/database_configurations/database_config"
4
+ require "active_record/database_configurations/hash_config"
5
+ require "active_record/database_configurations/url_config"
6
+
7
+ module ActiveRecord
8
+ # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
9
+ # objects (either a HashConfig or UrlConfig) that are constructed from the
10
+ # application's database configuration hash or URL string.
11
+ class DatabaseConfigurations
12
+ class InvalidConfigurationError < StandardError; end
13
+
14
+ attr_reader :configurations
15
+ delegate :any?, to: :configurations
16
+
17
+ def initialize(configurations = {})
18
+ @configurations = build_configs(configurations)
19
+ end
20
+
21
+ # Collects the configs for the environment and optionally the specification
22
+ # name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
23
+ #
24
+ # If a spec name is provided a single DatabaseConfig object will be
25
+ # returned, otherwise an array of DatabaseConfig objects will be
26
+ # returned that corresponds with the environment and type requested.
27
+ #
28
+ # ==== Options
29
+ #
30
+ # * <tt>env_name:</tt> The environment name. Defaults to +nil+ which will collect
31
+ # configs for all environments.
32
+ # * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
33
+ # to +nil+.
34
+ # * <tt>include_replicas:</tt> Determines whether to include replicas in
35
+ # the returned list. Most of the time we're only iterating over the write
36
+ # connection (i.e. migrations don't need to run for the write and read connection).
37
+ # Defaults to +false+.
38
+ def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
39
+ configs = env_with_configs(env_name)
40
+
41
+ unless include_replicas
42
+ configs = configs.select do |db_config|
43
+ !db_config.replica?
44
+ end
45
+ end
46
+
47
+ if spec_name
48
+ configs.find do |db_config|
49
+ db_config.spec_name == spec_name
50
+ end
51
+ else
52
+ configs
53
+ end
54
+ end
55
+
56
+ # Returns the config hash that corresponds with the environment
57
+ #
58
+ # If the application has multiple databases +default_hash+ will
59
+ # return the first config hash for the environment.
60
+ #
61
+ # { database: "my_db", adapter: "mysql2" }
62
+ def default_hash(env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s)
63
+ default = find_db_config(env)
64
+ default.config if default
65
+ end
66
+ alias :[] :default_hash
67
+
68
+ # Returns a single DatabaseConfig object based on the requested environment.
69
+ #
70
+ # If the application has multiple databases +find_db_config+ will return
71
+ # the first DatabaseConfig for the environment.
72
+ def find_db_config(env)
73
+ configurations.find do |db_config|
74
+ db_config.env_name == env.to_s ||
75
+ (db_config.for_current_env? && db_config.spec_name == env.to_s)
76
+ end
77
+ end
78
+
79
+ # Returns the DatabaseConfigurations object as a Hash.
80
+ def to_h
81
+ configs = configurations.reverse.inject({}) do |memo, db_config|
82
+ memo.merge(db_config.to_legacy_hash)
83
+ end
84
+
85
+ Hash[configs.to_a.reverse]
86
+ end
87
+
88
+ # Checks if the application's configurations are empty.
89
+ #
90
+ # Aliased to blank?
91
+ def empty?
92
+ configurations.empty?
93
+ end
94
+ alias :blank? :empty?
95
+
96
+ def each
97
+ throw_getter_deprecation(:each)
98
+ configurations.each { |config|
99
+ yield [config.env_name, config.config]
100
+ }
101
+ end
102
+
103
+ def first
104
+ throw_getter_deprecation(:first)
105
+ config = configurations.first
106
+ [config.env_name, config.config]
107
+ end
108
+
109
+ private
110
+ def env_with_configs(env = nil)
111
+ if env
112
+ configurations.select { |db_config| db_config.env_name == env }
113
+ else
114
+ configurations
115
+ end
116
+ end
117
+
118
+ def build_configs(configs)
119
+ return configs.configurations if configs.is_a?(DatabaseConfigurations)
120
+ return configs if configs.is_a?(Array)
121
+
122
+ db_configs = configs.flat_map do |env_name, config|
123
+ if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
124
+ walk_configs(env_name.to_s, config)
125
+ else
126
+ build_db_config_from_raw_config(env_name.to_s, "primary", config)
127
+ end
128
+ end
129
+
130
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
131
+
132
+ unless db_configs.find(&:for_current_env?)
133
+ db_configs << environment_url_config(current_env, "primary", {})
134
+ end
135
+
136
+ merge_db_environment_variables(current_env, db_configs.compact)
137
+ end
138
+
139
+ def walk_configs(env_name, config)
140
+ config.map do |spec_name, sub_config|
141
+ build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
142
+ end
143
+ end
144
+
145
+ def build_db_config_from_raw_config(env_name, spec_name, config)
146
+ case config
147
+ when String
148
+ build_db_config_from_string(env_name, spec_name, config)
149
+ when Hash
150
+ build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
151
+ else
152
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
153
+ end
154
+ end
155
+
156
+ def build_db_config_from_string(env_name, spec_name, config)
157
+ url = config
158
+ uri = URI.parse(url)
159
+ if uri.scheme
160
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
161
+ else
162
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
163
+ end
164
+ end
165
+
166
+ def build_db_config_from_hash(env_name, spec_name, config)
167
+ if config.has_key?("url")
168
+ url = config["url"]
169
+ config_without_url = config.dup
170
+ config_without_url.delete "url"
171
+
172
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
173
+ else
174
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
175
+ end
176
+ end
177
+
178
+ def merge_db_environment_variables(current_env, configs)
179
+ configs.map do |config|
180
+ next config if config.url_config? || config.env_name != current_env
181
+
182
+ url_config = environment_url_config(current_env, config.spec_name, config.config)
183
+ url_config || config
184
+ end
185
+ end
186
+
187
+ def environment_url_config(env, spec_name, config)
188
+ url = environment_value_for(spec_name)
189
+ return unless url
190
+
191
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
192
+ end
193
+
194
+ def environment_value_for(spec_name)
195
+ spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
196
+ url = ENV[spec_env_key]
197
+ url ||= ENV["DATABASE_URL"] if spec_name == "primary"
198
+ url
199
+ end
200
+
201
+ def method_missing(method, *args, &blk)
202
+ case method
203
+ when :fetch
204
+ throw_getter_deprecation(method)
205
+ configs_for(env_name: args.first)
206
+ when :values
207
+ throw_getter_deprecation(method)
208
+ configurations.map(&:config)
209
+ when :[]=
210
+ throw_setter_deprecation(method)
211
+
212
+ env_name = args[0]
213
+ config = args[1]
214
+
215
+ remaining_configs = configurations.reject { |db_config| db_config.env_name == env_name }
216
+ new_config = build_configs(env_name => config)
217
+ new_configs = remaining_configs + new_config
218
+
219
+ ActiveRecord::Base.configurations = new_configs
220
+ else
221
+ raise NotImplementedError, "`ActiveRecord::Base.configurations` in Rails 6 now returns an object instead of a hash. The `#{method}` method is not supported. Please use `configs_for` or consult the documentation for supported methods."
222
+ end
223
+ end
224
+
225
+ def throw_setter_deprecation(method)
226
+ ActiveSupport::Deprecation.warn("Setting `ActiveRecord::Base.configurations` with `#{method}` is deprecated. Use `ActiveRecord::Base.configurations=` directly to set the configurations instead.")
227
+ end
228
+
229
+ def throw_getter_deprecation(method)
230
+ ActiveSupport::Deprecation.warn("`ActiveRecord::Base.configurations` no longer returns a hash. Methods that act on the hash like `#{method}` are deprecated and will be removed in Rails 6.1. Use the `configs_for` method to collect and iterate over the database configurations.")
231
+ end
232
+ end
233
+ end
@@ -49,11 +49,11 @@ module ActiveRecord
49
49
 
50
50
  attr_reader :model, :name, :attribute_names
51
51
 
52
- def initialize(model, name)
52
+ def initialize(model, method_name)
53
53
  @model = model
54
- @name = name.to_s
54
+ @name = method_name.to_s
55
55
  @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
56
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
56
+ @attribute_names.map! { |name| @model.attribute_aliases[name] || name }
57
57
  end
58
58
 
59
59
  def valid?
@@ -69,7 +69,6 @@ module ActiveRecord
69
69
  end
70
70
 
71
71
  private
72
-
73
72
  def body
74
73
  "#{finder}(#{attributes_hash})"
75
74
  end
@@ -31,7 +31,9 @@ module ActiveRecord
31
31
  # as well. With the above example:
32
32
  #
33
33
  # Conversation.active
34
+ # Conversation.not_active
34
35
  # Conversation.archived
36
+ # Conversation.not_archived
35
37
  #
36
38
  # Of course, you can also query them directly if the scopes don't fit your
37
39
  # needs:
@@ -141,10 +143,7 @@ module ActiveRecord
141
143
  end
142
144
  end
143
145
 
144
- # TODO Change this to private once we've dropped Ruby 2.2 support.
145
- # Workaround for Ruby 2.2 "private attribute?" warning.
146
- protected
147
-
146
+ private
148
147
  attr_reader :name, :mapping, :subtype
149
148
  end
150
149
 
@@ -152,14 +151,16 @@ module ActiveRecord
152
151
  klass = self
153
152
  enum_prefix = definitions.delete(:_prefix)
154
153
  enum_suffix = definitions.delete(:_suffix)
154
+ enum_scopes = definitions.delete(:_scopes)
155
155
  definitions.each do |name, values|
156
+ assert_valid_enum_definition_values(values)
156
157
  # statuses = { }
157
158
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
158
159
  name = name.to_s
159
160
 
160
161
  # def self.statuses() statuses end
161
162
  detect_enum_conflict!(name, name.pluralize, true)
162
- singleton_class.send(:define_method, name.pluralize) { enum_values }
163
+ singleton_class.define_method(name.pluralize) { enum_values }
163
164
  defined_enums[name] = enum_values
164
165
 
165
166
  detect_enum_conflict!(name, name)
@@ -172,6 +173,7 @@ module ActiveRecord
172
173
 
173
174
  _enum_methods_module.module_eval do
174
175
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
176
+ value_method_names = []
175
177
  pairs.each do |label, value|
176
178
  if enum_prefix == true
177
179
  prefix = "#{name}_"
@@ -185,6 +187,7 @@ module ActiveRecord
185
187
  end
186
188
 
187
189
  value_method_name = "#{prefix}#{label}#{suffix}"
190
+ value_method_names << value_method_name
188
191
  enum_values[label] = value
189
192
  label = label.to_s
190
193
 
@@ -197,9 +200,16 @@ module ActiveRecord
197
200
  define_method("#{value_method_name}!") { update!(attr => value) }
198
201
 
199
202
  # scope :active, -> { where(status: 0) }
200
- klass.send(:detect_enum_conflict!, name, value_method_name, true)
201
- klass.scope value_method_name, -> { where(attr => value) }
203
+ # scope :not_active, -> { where.not(status: 0) }
204
+ if enum_scopes != false
205
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
206
+ klass.scope value_method_name, -> { where(attr => value) }
207
+
208
+ klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
209
+ klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
210
+ end
202
211
  end
212
+ klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
203
213
  end
204
214
  enum_values.freeze
205
215
  end
@@ -214,10 +224,24 @@ module ActiveRecord
214
224
  end
215
225
  end
216
226
 
227
+ def assert_valid_enum_definition_values(values)
228
+ unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
229
+ error_message = <<~MSG
230
+ Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
231
+ MSG
232
+ raise ArgumentError, error_message
233
+ end
234
+
235
+ if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
236
+ raise ArgumentError, "Enum label name must not be blank."
237
+ end
238
+ end
239
+
217
240
  ENUM_CONFLICT_MESSAGE = \
218
241
  "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
219
242
  "this will generate a %{type} method \"%{method}\", which is already defined " \
220
243
  "by %{source}."
244
+ private_constant :ENUM_CONFLICT_MESSAGE
221
245
 
222
246
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
223
247
  if klass_method && dangerous_class_method?(method_name)
@@ -240,5 +264,18 @@ module ActiveRecord
240
264
  source: source
241
265
  }
242
266
  end
267
+
268
+ def detect_negative_enum_conditions!(method_names)
269
+ return unless logger
270
+
271
+ method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
272
+ inverted_form = potential_not.sub("not_", "")
273
+ if method_names.include?(inverted_form)
274
+ logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
275
+ " This has caused a conflict with auto generated negative scopes." \
276
+ " Avoid using enum elements starting with 'not' where the positive form is also an element."
277
+ end
278
+ end
279
+ end
243
280
  end
244
281
  end
@@ -49,6 +49,10 @@ module ActiveRecord
49
49
  class ConnectionNotEstablished < ActiveRecordError
50
50
  end
51
51
 
52
+ # Raised when a write to the database is attempted on a read only connection.
53
+ class ReadOnlyError < ActiveRecordError
54
+ end
55
+
52
56
  # Raised when Active Record cannot find a record by given id or set of ids.
53
57
  class RecordNotFound < ActiveRecordError
54
58
  attr_reader :model, :primary_key, :id
@@ -64,7 +68,7 @@ module ActiveRecord
64
68
 
65
69
  # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
66
70
  # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
67
- # methods when a record is invalid and can not be saved.
71
+ # methods when a record is invalid and cannot be saved.
68
72
  class RecordNotSaved < ActiveRecordError
69
73
  attr_reader :record
70
74
 
@@ -97,9 +101,13 @@ module ActiveRecord
97
101
  #
98
102
  # Wraps the underlying database error as +cause+.
99
103
  class StatementInvalid < ActiveRecordError
100
- def initialize(message = nil)
104
+ def initialize(message = nil, sql: nil, binds: nil)
101
105
  super(message || $!.try(:message))
106
+ @sql = sql
107
+ @binds = binds
102
108
  end
109
+
110
+ attr_reader :sql, :binds
103
111
  end
104
112
 
105
113
  # Defunct wrapper class kept for compatibility.
@@ -111,14 +119,14 @@ module ActiveRecord
111
119
  class RecordNotUnique < WrappedDatabaseException
112
120
  end
113
121
 
114
- # Raised when a record cannot be inserted or updated because it references a non-existent record.
122
+ # Raised when a record cannot be inserted or updated because it references a non-existent record,
123
+ # or when a record cannot be deleted because a parent record references it.
115
124
  class InvalidForeignKey < WrappedDatabaseException
116
125
  end
117
126
 
118
127
  # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
119
128
  class MismatchedForeignKey < StatementInvalid
120
129
  def initialize(
121
- adapter = nil,
122
130
  message: nil,
123
131
  sql: nil,
124
132
  binds: nil,
@@ -130,14 +138,14 @@ module ActiveRecord
130
138
  )
131
139
  if table
132
140
  type = primary_key_column.bigint? ? :bigint : primary_key_column.type
133
- msg = <<-EOM.squish
141
+ msg = <<~EOM.squish
134
142
  Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
135
143
  which has type `#{primary_key_column.sql_type}`.
136
144
  To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
137
145
  (For example `t.#{type} :#{foreign_key}`).
138
146
  EOM
139
147
  else
140
- msg = <<-EOM.squish
148
+ msg = <<~EOM.squish
141
149
  There is a mismatch between the foreign key and primary key column types.
142
150
  Verify that the foreign key column type and the primary key of the associated table match types.
143
151
  EOM
@@ -145,7 +153,7 @@ module ActiveRecord
145
153
  if message
146
154
  msg << "\nOriginal message: #{message}"
147
155
  end
148
- super(msg)
156
+ super(msg, sql: sql, binds: binds)
149
157
  end
150
158
  end
151
159
 
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  # Returns a formatted string ready to be logged.
19
19
  def exec_explain(queries) # :nodoc:
20
20
  str = queries.map do |sql, binds|
21
- msg = "EXPLAIN for: #{sql}".dup
21
+ msg = +"EXPLAIN for: #{sql}"
22
22
  unless binds.empty?
23
23
  msg << " "
24
24
  msg << binds.map { |attr| render_bind(attr) }.inspect
@@ -36,7 +36,6 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  private
39
-
40
39
  def render_bind(attr)
41
40
  value = if attr.type.binary? && attr.value
42
41
  "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FixtureSet
5
+ class ModelMetadata # :nodoc:
6
+ def initialize(model_class)
7
+ @model_class = model_class
8
+ end
9
+
10
+ def primary_key_name
11
+ @primary_key_name ||= @model_class && @model_class.primary_key
12
+ end
13
+
14
+ def primary_key_type
15
+ @primary_key_type ||= @model_class && @model_class.type_for_attribute(@model_class.primary_key).type
16
+ end
17
+
18
+ def has_primary_key_column?
19
+ @has_primary_key_column ||= primary_key_name &&
20
+ @model_class.columns.any? { |col| col.name == primary_key_name }
21
+ end
22
+
23
+ def timestamp_column_names
24
+ @timestamp_column_names ||=
25
+ %w(created_at created_on updated_at updated_on) & @model_class.column_names
26
+ end
27
+
28
+ def inheritance_column_name
29
+ @inheritance_column_name ||= @model_class && @model_class.inheritance_column
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: This class has to be defined in compact style in
4
+ # order for rendering context subclassing to work correctly.
5
+ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
6
+ def self.create_subclass
7
+ Class.new(ActiveRecord::FixtureSet.context_class) do
8
+ def get_binding
9
+ binding()
10
+ end
11
+
12
+ def binary(path)
13
+ %(!!binary "#{Base64.strict_encode64(File.read(path, mode: 'rb'))}")
14
+ end
15
+ end
16
+ end
17
+ end