activerecord 5.2.6 → 6.1.3.2

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1038 -571
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +13 -12
  7. data/lib/active_record/aggregations.rb +9 -8
  8. data/lib/active_record/association_relation.rb +30 -10
  9. data/lib/active_record/associations.rb +137 -25
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +95 -42
  12. data/lib/active_record/associations/association_scope.rb +23 -21
  13. data/lib/active_record/associations/belongs_to_association.rb +54 -46
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  15. data/lib/active_record/associations/builder/association.rb +45 -22
  16. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  17. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  19. data/lib/active_record/associations/builder/has_many.rb +8 -2
  20. data/lib/active_record/associations/builder/has_one.rb +33 -2
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  22. data/lib/active_record/associations/collection_association.rb +31 -29
  23. data/lib/active_record/associations/collection_proxy.rb +25 -21
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +26 -13
  26. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  27. data/lib/active_record/associations/has_one_association.rb +43 -31
  28. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  31. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/preloader/association.rb +71 -43
  34. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  35. data/lib/active_record/associations/singular_association.rb +3 -17
  36. data/lib/active_record/associations/through_association.rb +1 -1
  37. data/lib/active_record/attribute_assignment.rb +17 -19
  38. data/lib/active_record/attribute_methods.rb +81 -143
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  42. data/lib/active_record/attribute_methods/query.rb +4 -8
  43. data/lib/active_record/attribute_methods/read.rb +14 -56
  44. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  46. data/lib/active_record/attribute_methods/write.rb +18 -34
  47. data/lib/active_record/attributes.rb +46 -9
  48. data/lib/active_record/autosave_association.rb +57 -42
  49. data/lib/active_record/base.rb +4 -17
  50. data/lib/active_record/callbacks.rb +158 -43
  51. data/lib/active_record/coders/yaml_column.rb +1 -2
  52. data/lib/active_record/connection_adapters.rb +50 -0
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  58. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  60. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
  61. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  63. data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
  64. data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
  65. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  66. data/lib/active_record/connection_adapters/column.rb +30 -12
  67. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  68. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  69. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
  71. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  77. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  79. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  80. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
  83. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  101. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  103. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  104. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  106. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  107. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  108. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  109. data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
  110. data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
  111. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  112. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  113. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  114. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  116. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  117. data/lib/active_record/connection_handling.rb +293 -33
  118. data/lib/active_record/core.rb +323 -97
  119. data/lib/active_record/counter_cache.rb +8 -30
  120. data/lib/active_record/database_configurations.rb +272 -0
  121. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  122. data/lib/active_record/database_configurations/database_config.rb +80 -0
  123. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  124. data/lib/active_record/database_configurations/url_config.rb +53 -0
  125. data/lib/active_record/delegated_type.rb +209 -0
  126. data/lib/active_record/destroy_association_async_job.rb +36 -0
  127. data/lib/active_record/dynamic_matchers.rb +3 -4
  128. data/lib/active_record/enum.rb +111 -37
  129. data/lib/active_record/errors.rb +62 -19
  130. data/lib/active_record/explain.rb +10 -6
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +10 -17
  133. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  134. data/lib/active_record/fixture_set/render_context.rb +17 -0
  135. data/lib/active_record/fixture_set/table_row.rb +152 -0
  136. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  137. data/lib/active_record/fixtures.rb +200 -481
  138. data/lib/active_record/gem_version.rb +4 -4
  139. data/lib/active_record/inheritance.rb +53 -24
  140. data/lib/active_record/insert_all.rb +208 -0
  141. data/lib/active_record/integration.rb +67 -17
  142. data/lib/active_record/internal_metadata.rb +26 -9
  143. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  144. data/lib/active_record/locking/optimistic.rb +37 -23
  145. data/lib/active_record/locking/pessimistic.rb +9 -5
  146. data/lib/active_record/log_subscriber.rb +35 -35
  147. data/lib/active_record/middleware/database_selector.rb +77 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  149. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  150. data/lib/active_record/migration.rb +206 -157
  151. data/lib/active_record/migration/command_recorder.rb +96 -44
  152. data/lib/active_record/migration/compatibility.rb +142 -64
  153. data/lib/active_record/migration/join_table.rb +0 -1
  154. data/lib/active_record/model_schema.rb +148 -22
  155. data/lib/active_record/nested_attributes.rb +4 -7
  156. data/lib/active_record/no_touching.rb +8 -1
  157. data/lib/active_record/null_relation.rb +0 -1
  158. data/lib/active_record/persistence.rb +267 -59
  159. data/lib/active_record/query_cache.rb +21 -4
  160. data/lib/active_record/querying.rb +40 -23
  161. data/lib/active_record/railtie.rb +115 -58
  162. data/lib/active_record/railties/console_sandbox.rb +2 -4
  163. data/lib/active_record/railties/controller_runtime.rb +30 -35
  164. data/lib/active_record/railties/databases.rake +408 -78
  165. data/lib/active_record/readonly_attributes.rb +4 -0
  166. data/lib/active_record/reflection.rb +109 -93
  167. data/lib/active_record/relation.rb +374 -104
  168. data/lib/active_record/relation/batches.rb +44 -35
  169. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  170. data/lib/active_record/relation/calculations.rb +153 -90
  171. data/lib/active_record/relation/delegation.rb +35 -50
  172. data/lib/active_record/relation/finder_methods.rb +64 -39
  173. data/lib/active_record/relation/from_clause.rb +5 -1
  174. data/lib/active_record/relation/merger.rb +32 -40
  175. data/lib/active_record/relation/predicate_builder.rb +62 -45
  176. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  177. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  182. data/lib/active_record/relation/query_attribute.rb +13 -8
  183. data/lib/active_record/relation/query_methods.rb +475 -186
  184. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  185. data/lib/active_record/relation/spawn_methods.rb +9 -9
  186. data/lib/active_record/relation/where_clause.rb +111 -61
  187. data/lib/active_record/result.rb +64 -38
  188. data/lib/active_record/runtime_registry.rb +2 -2
  189. data/lib/active_record/sanitization.rb +22 -41
  190. data/lib/active_record/schema.rb +2 -11
  191. data/lib/active_record/schema_dumper.rb +54 -9
  192. data/lib/active_record/schema_migration.rb +7 -9
  193. data/lib/active_record/scoping.rb +8 -9
  194. data/lib/active_record/scoping/default.rb +4 -6
  195. data/lib/active_record/scoping/named.rb +17 -24
  196. data/lib/active_record/secure_token.rb +16 -8
  197. data/lib/active_record/serialization.rb +5 -3
  198. data/lib/active_record/signed_id.rb +116 -0
  199. data/lib/active_record/statement_cache.rb +49 -6
  200. data/lib/active_record/store.rb +88 -9
  201. data/lib/active_record/suppressor.rb +2 -2
  202. data/lib/active_record/table_metadata.rb +42 -43
  203. data/lib/active_record/tasks/database_tasks.rb +277 -81
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  207. data/lib/active_record/test_databases.rb +24 -0
  208. data/lib/active_record/test_fixtures.rb +246 -0
  209. data/lib/active_record/timestamp.rb +43 -32
  210. data/lib/active_record/touch_later.rb +23 -22
  211. data/lib/active_record/transactions.rb +62 -118
  212. data/lib/active_record/translation.rb +1 -1
  213. data/lib/active_record/type.rb +10 -5
  214. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  215. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  216. data/lib/active_record/type/serialized.rb +6 -3
  217. data/lib/active_record/type/time.rb +10 -0
  218. data/lib/active_record/type/type_map.rb +0 -1
  219. data/lib/active_record/type/unsigned_integer.rb +0 -1
  220. data/lib/active_record/type_caster/connection.rb +15 -15
  221. data/lib/active_record/type_caster/map.rb +8 -8
  222. data/lib/active_record/validations.rb +4 -3
  223. data/lib/active_record/validations/associated.rb +1 -2
  224. data/lib/active_record/validations/numericality.rb +35 -0
  225. data/lib/active_record/validations/uniqueness.rb +38 -30
  226. data/lib/arel.rb +54 -0
  227. data/lib/arel/alias_predication.rb +9 -0
  228. data/lib/arel/attributes/attribute.rb +41 -0
  229. data/lib/arel/collectors/bind.rb +29 -0
  230. data/lib/arel/collectors/composite.rb +39 -0
  231. data/lib/arel/collectors/plain_string.rb +20 -0
  232. data/lib/arel/collectors/sql_string.rb +27 -0
  233. data/lib/arel/collectors/substitute_binds.rb +35 -0
  234. data/lib/arel/crud.rb +42 -0
  235. data/lib/arel/delete_manager.rb +18 -0
  236. data/lib/arel/errors.rb +9 -0
  237. data/lib/arel/expressions.rb +29 -0
  238. data/lib/arel/factory_methods.rb +49 -0
  239. data/lib/arel/insert_manager.rb +49 -0
  240. data/lib/arel/math.rb +45 -0
  241. data/lib/arel/nodes.rb +70 -0
  242. data/lib/arel/nodes/and.rb +32 -0
  243. data/lib/arel/nodes/ascending.rb +23 -0
  244. data/lib/arel/nodes/binary.rb +126 -0
  245. data/lib/arel/nodes/bind_param.rb +44 -0
  246. data/lib/arel/nodes/case.rb +55 -0
  247. data/lib/arel/nodes/casted.rb +62 -0
  248. data/lib/arel/nodes/comment.rb +29 -0
  249. data/lib/arel/nodes/count.rb +12 -0
  250. data/lib/arel/nodes/delete_statement.rb +45 -0
  251. data/lib/arel/nodes/descending.rb +23 -0
  252. data/lib/arel/nodes/equality.rb +15 -0
  253. data/lib/arel/nodes/extract.rb +24 -0
  254. data/lib/arel/nodes/false.rb +16 -0
  255. data/lib/arel/nodes/full_outer_join.rb +8 -0
  256. data/lib/arel/nodes/function.rb +44 -0
  257. data/lib/arel/nodes/grouping.rb +11 -0
  258. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  259. data/lib/arel/nodes/in.rb +15 -0
  260. data/lib/arel/nodes/infix_operation.rb +92 -0
  261. data/lib/arel/nodes/inner_join.rb +8 -0
  262. data/lib/arel/nodes/insert_statement.rb +37 -0
  263. data/lib/arel/nodes/join_source.rb +20 -0
  264. data/lib/arel/nodes/matches.rb +18 -0
  265. data/lib/arel/nodes/named_function.rb +23 -0
  266. data/lib/arel/nodes/node.rb +51 -0
  267. data/lib/arel/nodes/node_expression.rb +13 -0
  268. data/lib/arel/nodes/ordering.rb +27 -0
  269. data/lib/arel/nodes/outer_join.rb +8 -0
  270. data/lib/arel/nodes/over.rb +15 -0
  271. data/lib/arel/nodes/regexp.rb +16 -0
  272. data/lib/arel/nodes/right_outer_join.rb +8 -0
  273. data/lib/arel/nodes/select_core.rb +67 -0
  274. data/lib/arel/nodes/select_statement.rb +41 -0
  275. data/lib/arel/nodes/sql_literal.rb +19 -0
  276. data/lib/arel/nodes/string_join.rb +11 -0
  277. data/lib/arel/nodes/table_alias.rb +31 -0
  278. data/lib/arel/nodes/terminal.rb +16 -0
  279. data/lib/arel/nodes/true.rb +16 -0
  280. data/lib/arel/nodes/unary.rb +44 -0
  281. data/lib/arel/nodes/unary_operation.rb +20 -0
  282. data/lib/arel/nodes/unqualified_column.rb +22 -0
  283. data/lib/arel/nodes/update_statement.rb +41 -0
  284. data/lib/arel/nodes/values_list.rb +9 -0
  285. data/lib/arel/nodes/window.rb +126 -0
  286. data/lib/arel/nodes/with.rb +11 -0
  287. data/lib/arel/order_predications.rb +13 -0
  288. data/lib/arel/predications.rb +250 -0
  289. data/lib/arel/select_manager.rb +270 -0
  290. data/lib/arel/table.rb +118 -0
  291. data/lib/arel/tree_manager.rb +72 -0
  292. data/lib/arel/update_manager.rb +34 -0
  293. data/lib/arel/visitors.rb +13 -0
  294. data/lib/arel/visitors/dot.rb +308 -0
  295. data/lib/arel/visitors/mysql.rb +93 -0
  296. data/lib/arel/visitors/postgresql.rb +120 -0
  297. data/lib/arel/visitors/sqlite.rb +38 -0
  298. data/lib/arel/visitors/to_sql.rb +899 -0
  299. data/lib/arel/visitors/visitor.rb +45 -0
  300. data/lib/arel/window_predications.rb +9 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration.rb +19 -2
  303. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  304. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  305. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +119 -34
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -7,10 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 6
13
- PRE = nil
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 3
13
+ PRE = "2"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -38,6 +38,8 @@ module ActiveRecord
38
38
  extend ActiveSupport::Concern
39
39
 
40
40
  included do
41
+ class_attribute :store_full_class_name, instance_writer: false, default: true
42
+
41
43
  # Determines whether to store the full constant name including namespace when using STI.
42
44
  # This is true, by default.
43
45
  class_attribute :store_full_sti_class, instance_writer: false, default: true
@@ -52,10 +54,14 @@ module ActiveRecord
52
54
  raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
53
55
  end
54
56
 
55
- if has_attribute?(inheritance_column)
57
+ if _has_attribute?(inheritance_column)
56
58
  subclass = subclass_from_attributes(attributes)
57
59
 
58
- if subclass.nil? && base_class == self
60
+ if subclass.nil? && scope_attributes = current_scope&.scope_for_create
61
+ subclass = subclass_from_attributes(scope_attributes)
62
+ end
63
+
64
+ if subclass.nil? && base_class?
59
65
  subclass = subclass_from_attributes(column_defaults)
60
66
  end
61
67
  end
@@ -104,6 +110,12 @@ module ActiveRecord
104
110
  end
105
111
  end
106
112
 
113
+ # Returns whether the class is a base class.
114
+ # See #base_class for more information.
115
+ def base_class?
116
+ base_class == self
117
+ end
118
+
107
119
  # Set this to +true+ if this is an abstract class (see
108
120
  # <tt>abstract_class?</tt>).
109
121
  # If you are using inheritance with Active Record and don't want a class
@@ -152,12 +164,42 @@ module ActiveRecord
152
164
  defined?(@abstract_class) && @abstract_class == true
153
165
  end
154
166
 
167
+ # Returns the value to be stored in the inheritance column for STI.
155
168
  def sti_name
156
- store_full_sti_class ? name : name.demodulize
169
+ store_full_sti_class && store_full_class_name ? name : name.demodulize
157
170
  end
158
171
 
172
+ # Returns the class for the provided +type_name+.
173
+ #
174
+ # It is used to find the class correspondent to the value stored in the inheritance column.
175
+ def sti_class_for(type_name)
176
+ if store_full_sti_class && store_full_class_name
177
+ ActiveSupport::Dependencies.constantize(type_name)
178
+ else
179
+ compute_type(type_name)
180
+ end
181
+ rescue NameError
182
+ raise SubclassNotFound,
183
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
184
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
185
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
186
+ "or overwrite #{name}.inheritance_column to use another column for that information."
187
+ end
188
+
189
+ # Returns the value to be stored in the polymorphic type column for Polymorphic Associations.
159
190
  def polymorphic_name
160
- base_class.name
191
+ store_full_class_name ? base_class.name : base_class.name.demodulize
192
+ end
193
+
194
+ # Returns the class for the provided +name+.
195
+ #
196
+ # It is used to find the class correspondent to the value stored in the polymorphic type column.
197
+ def polymorphic_class_for(name)
198
+ if store_full_class_name
199
+ ActiveSupport::Dependencies.constantize(name)
200
+ else
201
+ compute_type(name)
202
+ end
161
203
  end
162
204
 
163
205
  def inherited(subclass)
@@ -166,11 +208,10 @@ module ActiveRecord
166
208
  end
167
209
 
168
210
  protected
169
-
170
211
  # Returns the class type of the record using the current module as a prefix. So descendants of
171
212
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
172
213
  def compute_type(type_name)
173
- if type_name.start_with?("::".freeze)
214
+ if type_name.start_with?("::")
174
215
  # If the type is prefixed with a scope operator then we assume that
175
216
  # the type_name is an absolute reference.
176
217
  ActiveSupport::Dependencies.constantize(type_name)
@@ -198,7 +239,6 @@ module ActiveRecord
198
239
  end
199
240
 
200
241
  private
201
-
202
242
  # Called by +instantiate+ to decide which class to use for a new
203
243
  # record instance. For single-table inheritance, we check the record
204
244
  # for a +type+ column and return the corresponding class.
@@ -211,35 +251,25 @@ module ActiveRecord
211
251
  end
212
252
 
213
253
  def using_single_table_inheritance?(record)
214
- record[inheritance_column].present? && has_attribute?(inheritance_column)
254
+ record[inheritance_column].present? && _has_attribute?(inheritance_column)
215
255
  end
216
256
 
217
257
  def find_sti_class(type_name)
218
258
  type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
219
- subclass = begin
220
- if store_full_sti_class
221
- ActiveSupport::Dependencies.constantize(type_name)
222
- else
223
- compute_type(type_name)
224
- end
225
- rescue NameError
226
- raise SubclassNotFound,
227
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
228
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
229
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
230
- "or overwrite #{name}.inheritance_column to use another column for that information."
231
- end
259
+ subclass = sti_class_for(type_name)
260
+
232
261
  unless subclass == self || descendants.include?(subclass)
233
262
  raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
234
263
  end
264
+
235
265
  subclass
236
266
  end
237
267
 
238
268
  def type_condition(table = arel_table)
239
- sti_column = arel_attribute(inheritance_column, table)
269
+ sti_column = table[inheritance_column]
240
270
  sti_names = ([self] + descendants).map(&:sti_name)
241
271
 
242
- sti_column.in(sti_names)
272
+ predicate_builder.build(sti_column, sti_names)
243
273
  end
244
274
 
245
275
  # Detect the subclass from the inheritance column of attrs. If the inheritance column value
@@ -262,7 +292,6 @@ module ActiveRecord
262
292
  end
263
293
 
264
294
  private
265
-
266
295
  def initialize_internals_callback
267
296
  super
268
297
  ensure_proper_type
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
5
+ module ActiveRecord
6
+ class InsertAll # :nodoc:
7
+ attr_reader :model, :connection, :inserts, :keys
8
+ attr_reader :on_duplicate, :returning, :unique_by
9
+
10
+ def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
11
+ raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
12
+
13
+ @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
14
+ @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
15
+
16
+ if model.scope_attributes?
17
+ @scope_attributes = model.scope_attributes
18
+ @keys |= @scope_attributes.keys
19
+ end
20
+ @keys = @keys.to_set
21
+
22
+ @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
23
+ @returning = false if @returning == []
24
+
25
+ @unique_by = find_unique_index_for(unique_by)
26
+ @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
27
+
28
+ ensure_valid_options_for_connection!
29
+ end
30
+
31
+ def execute
32
+ message = +"#{model} "
33
+ message << "Bulk " if inserts.many?
34
+ message << (on_duplicate == :update ? "Upsert" : "Insert")
35
+ connection.exec_insert_all to_sql, message
36
+ end
37
+
38
+ def updatable_columns
39
+ keys - readonly_columns - unique_by_columns
40
+ end
41
+
42
+ def primary_keys
43
+ Array(connection.schema_cache.primary_keys(model.table_name))
44
+ end
45
+
46
+
47
+ def skip_duplicates?
48
+ on_duplicate == :skip
49
+ end
50
+
51
+ def update_duplicates?
52
+ on_duplicate == :update
53
+ end
54
+
55
+ def map_key_with_value
56
+ inserts.map do |attributes|
57
+ attributes = attributes.stringify_keys
58
+ attributes.merge!(scope_attributes) if scope_attributes
59
+
60
+ verify_attributes(attributes)
61
+
62
+ keys.map do |key|
63
+ yield key, attributes[key]
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+ attr_reader :scope_attributes
70
+
71
+ def find_unique_index_for(unique_by)
72
+ return unique_by if !connection.supports_insert_conflict_target?
73
+
74
+ name_or_columns = unique_by || model.primary_key
75
+ match = Array(name_or_columns).map(&:to_s)
76
+
77
+ if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
78
+ index
79
+ elsif match == primary_keys
80
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
81
+ else
82
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
83
+ end
84
+ end
85
+
86
+ def unique_indexes
87
+ connection.schema_cache.indexes(model.table_name).select(&:unique)
88
+ end
89
+
90
+
91
+ def ensure_valid_options_for_connection!
92
+ if returning && !connection.supports_insert_returning?
93
+ raise ArgumentError, "#{connection.class} does not support :returning"
94
+ end
95
+
96
+ if skip_duplicates? && !connection.supports_insert_on_duplicate_skip?
97
+ raise ArgumentError, "#{connection.class} does not support skipping duplicates"
98
+ end
99
+
100
+ if update_duplicates? && !connection.supports_insert_on_duplicate_update?
101
+ raise ArgumentError, "#{connection.class} does not support upsert"
102
+ end
103
+
104
+ if unique_by && !connection.supports_insert_conflict_target?
105
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
106
+ end
107
+ end
108
+
109
+
110
+ def to_sql
111
+ connection.build_insert_sql(ActiveRecord::InsertAll::Builder.new(self))
112
+ end
113
+
114
+
115
+ def readonly_columns
116
+ primary_keys + model.readonly_attributes.to_a
117
+ end
118
+
119
+ def unique_by_columns
120
+ Array(unique_by&.columns)
121
+ end
122
+
123
+
124
+ def verify_attributes(attributes)
125
+ if keys != attributes.keys.to_set
126
+ raise ArgumentError, "All objects being inserted must have the same keys"
127
+ end
128
+ end
129
+
130
+ class Builder # :nodoc:
131
+ attr_reader :model
132
+
133
+ delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
134
+
135
+ def initialize(insert_all)
136
+ @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
137
+ end
138
+
139
+ def into
140
+ "INTO #{model.quoted_table_name} (#{columns_list})"
141
+ end
142
+
143
+ def values_list
144
+ types = extract_types_from_columns_on(model.table_name, keys: keys)
145
+
146
+ values_list = insert_all.map_key_with_value do |key, value|
147
+ connection.with_yaml_fallback(types[key].serialize(value))
148
+ end
149
+
150
+ connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
151
+ end
152
+
153
+ def returning
154
+ format_columns(insert_all.returning) if insert_all.returning
155
+ end
156
+
157
+ def conflict_target
158
+ if index = insert_all.unique_by
159
+ sql = +"(#{format_columns(index.columns)})"
160
+ sql << " WHERE #{index.where}" if index.where
161
+ sql
162
+ elsif update_duplicates?
163
+ "(#{format_columns(insert_all.primary_keys)})"
164
+ end
165
+ end
166
+
167
+ def updatable_columns
168
+ quote_columns(insert_all.updatable_columns)
169
+ end
170
+
171
+ def touch_model_timestamps_unless(&block)
172
+ model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
173
+ if touch_timestamp_attribute?(column_name)
174
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
175
+ end
176
+ end.compact.join
177
+ end
178
+
179
+ private
180
+ attr_reader :connection, :insert_all
181
+
182
+ def touch_timestamp_attribute?(column_name)
183
+ update_duplicates? && !insert_all.updatable_columns.include?(column_name)
184
+ end
185
+
186
+ def columns_list
187
+ format_columns(insert_all.keys)
188
+ end
189
+
190
+ def extract_types_from_columns_on(table_name, keys:)
191
+ columns = connection.schema_cache.columns_hash(table_name)
192
+
193
+ unknown_column = (keys - columns.keys).first
194
+ raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
195
+
196
+ keys.index_with { |key| model.type_for_attribute(key) }
197
+ end
198
+
199
+ def format_columns(columns)
200
+ columns.respond_to?(:map) ? quote_columns(columns).join(",") : columns
201
+ end
202
+
203
+ def quote_columns(columns)
204
+ columns.map(&connection.method(:quote_column_name))
205
+ end
206
+ end
207
+ end
208
+ end
@@ -20,8 +20,16 @@ module ActiveRecord
20
20
  # Indicates whether to use a stable #cache_key method that is accompanied
21
21
  # by a changing version in the #cache_version method.
22
22
  #
23
- # This is +false+, by default until Rails 6.0.
23
+ # This is +true+, by default on Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Indicates whether to use a stable #cache_key method that is accompanied
29
+ # by a changing version in the #cache_version method on collections.
30
+ #
31
+ # This is +false+, by default until Rails 6.1.
32
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
25
33
  end
26
34
 
27
35
  # Returns a +String+, which Action Pack uses for constructing a URL to this
@@ -60,24 +68,15 @@ module ActiveRecord
60
68
  # the cache key will also include a version.
61
69
  #
62
70
  # Product.cache_versioning = false
63
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
64
- def cache_key(*timestamp_names)
71
+ # Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
72
+ def cache_key
65
73
  if new_record?
66
74
  "#{model_name.cache_key}/new"
67
75
  else
68
- if cache_version && timestamp_names.none?
76
+ if cache_version
69
77
  "#{model_name.cache_key}/#{id}"
70
78
  else
71
- timestamp = if timestamp_names.any?
72
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
- Specifying a timestamp name for #cache_key has been deprecated in favor of
74
- the explicit #cache_version method that can be overwritten.
75
- MSG
76
-
77
- max_updated_column_timestamp(timestamp_names)
78
- else
79
- max_updated_column_timestamp
80
- end
79
+ timestamp = max_updated_column_timestamp
81
80
 
82
81
  if timestamp
83
82
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
@@ -94,10 +93,19 @@ module ActiveRecord
94
93
  # cache_version, but this method can be overwritten to return something else.
95
94
  #
96
95
  # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
97
- # +false+ (which it is by default until Rails 6.0).
96
+ # +false+.
98
97
  def cache_version
99
- if cache_versioning && timestamp = try(:updated_at)
100
- timestamp.utc.to_s(:usec)
98
+ return unless cache_versioning
99
+
100
+ if has_attribute?("updated_at")
101
+ timestamp = updated_at_before_type_cast
102
+ if can_use_fast_cache_version?(timestamp)
103
+ raw_timestamp_to_cache_version(timestamp)
104
+ elsif timestamp = updated_at
105
+ timestamp.utc.to_s(cache_timestamp_format)
106
+ end
107
+ elsif self.class.has_attribute?("updated_at")
108
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
101
109
  end
102
110
  end
103
111
 
@@ -150,6 +158,48 @@ module ActiveRecord
150
158
  end
151
159
  end
152
160
  end
161
+
162
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
163
+ collection.send(:compute_cache_key, timestamp_column)
164
+ end
153
165
  end
166
+
167
+ private
168
+ # Detects if the value before type cast
169
+ # can be used to generate a cache_version.
170
+ #
171
+ # The fast cache version only works with a
172
+ # string value directly from the database.
173
+ #
174
+ # We also must check if the timestamp format has been changed
175
+ # or if the timezone is not set to UTC then
176
+ # we cannot apply our transformations correctly.
177
+ def can_use_fast_cache_version?(timestamp)
178
+ timestamp.is_a?(String) &&
179
+ cache_timestamp_format == :usec &&
180
+ default_timezone == :utc &&
181
+ !updated_at_came_from_user?
182
+ end
183
+
184
+ # Converts a raw database string to `:usec`
185
+ # format.
186
+ #
187
+ # Example:
188
+ #
189
+ # timestamp = "2018-10-15 20:02:15.266505"
190
+ # raw_timestamp_to_cache_version(timestamp)
191
+ # # => "20181015200215266505"
192
+ #
193
+ # PostgreSQL truncates trailing zeros,
194
+ # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
195
+ # to account for this we pad the output with zeros
196
+ def raw_timestamp_to_cache_version(timestamp)
197
+ key = timestamp.delete("- :.")
198
+ if key.length < 20
199
+ key.ljust(20, "0")
200
+ else
201
+ key
202
+ end
203
+ end
154
204
  end
155
205
  end