activerecord 6.0.6 → 6.1.0

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 +783 -910
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +43 -26
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +32 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +223 -66
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +27 -10
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +2 -2
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +34 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +16 -7
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -13
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +266 -95
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +318 -195
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +39 -51
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +28 -30
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -12,26 +12,24 @@ module ActiveRecord
12
12
  delegate :connection, :establish_connection, :clear_active_connections!,
13
13
  to: ActiveRecord::Base
14
14
 
15
- def initialize(configuration)
16
- @configuration = configuration
15
+ def self.using_database_configurations?
16
+ true
17
+ end
18
+
19
+ def initialize(db_config)
20
+ @db_config = db_config
21
+ @configuration_hash = db_config.configuration_hash
17
22
  end
18
23
 
19
24
  def create(master_established = false)
20
25
  establish_master_connection unless master_established
21
- connection.create_database configuration["database"],
22
- configuration.merge("encoding" => encoding)
23
- establish_connection configuration
24
- rescue ActiveRecord::StatementInvalid => error
25
- if error.cause.is_a?(PG::DuplicateDatabase)
26
- raise DatabaseAlreadyExists
27
- else
28
- raise
29
- end
26
+ connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
27
+ establish_connection(db_config)
30
28
  end
31
29
 
32
30
  def drop
33
31
  establish_master_connection
34
- connection.drop_database configuration["database"]
32
+ connection.drop_database(db_config.database)
35
33
  end
36
34
 
37
35
  def charset
@@ -54,14 +52,14 @@ module ActiveRecord
54
52
  search_path = \
55
53
  case ActiveRecord::Base.dump_schemas
56
54
  when :schema_search_path
57
- configuration["schema_search_path"]
55
+ configuration_hash[:schema_search_path]
58
56
  when :all
59
57
  nil
60
58
  when String
61
59
  ActiveRecord::Base.dump_schemas
62
60
  end
63
61
 
64
- args = ["-s", "-x", "-O", "-f", filename]
62
+ args = ["--schema-only", "--no-privileges", "--no-owner", "--file", filename]
65
63
  args.concat(Array(extra_flags)) if extra_flags
66
64
  unless search_path.blank?
67
65
  args += search_path.split(",").map do |part|
@@ -74,7 +72,7 @@ module ActiveRecord
74
72
  args += ignore_tables.flat_map { |table| ["-T", table] }
75
73
  end
76
74
 
77
- args << configuration["database"]
75
+ args << db_config.database
78
76
  run_cmd("pg_dump", args, "dumping")
79
77
  remove_sql_header_comments(filename)
80
78
  File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
@@ -82,31 +80,31 @@ module ActiveRecord
82
80
 
83
81
  def structure_load(filename, extra_flags)
84
82
  set_psql_env
85
- args = ["-v", ON_ERROR_STOP_1, "-q", "-X", "-f", filename]
83
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--file", filename]
86
84
  args.concat(Array(extra_flags)) if extra_flags
87
- args << configuration["database"]
85
+ args << db_config.database
88
86
  run_cmd("psql", args, "loading")
89
87
  end
90
88
 
91
89
  private
92
- attr_reader :configuration
90
+ attr_reader :db_config, :configuration_hash
93
91
 
94
92
  def encoding
95
- configuration["encoding"] || DEFAULT_ENCODING
93
+ configuration_hash[:encoding] || DEFAULT_ENCODING
96
94
  end
97
95
 
98
96
  def establish_master_connection
99
- establish_connection configuration.merge(
100
- "database" => "postgres",
101
- "schema_search_path" => "public"
97
+ establish_connection configuration_hash.merge(
98
+ database: "postgres",
99
+ schema_search_path: "public"
102
100
  )
103
101
  end
104
102
 
105
103
  def set_psql_env
106
- ENV["PGHOST"] = configuration["host"] if configuration["host"]
107
- ENV["PGPORT"] = configuration["port"].to_s if configuration["port"]
108
- ENV["PGPASSWORD"] = configuration["password"].to_s if configuration["password"]
109
- ENV["PGUSER"] = configuration["username"].to_s if configuration["username"]
104
+ ENV["PGHOST"] = db_config.host if db_config.host
105
+ ENV["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
106
+ ENV["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
107
+ ENV["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
110
108
  end
111
109
 
112
110
  def run_cmd(cmd, args, action)
@@ -5,20 +5,25 @@ module ActiveRecord
5
5
  class SQLiteDatabaseTasks # :nodoc:
6
6
  delegate :connection, :establish_connection, to: ActiveRecord::Base
7
7
 
8
- def initialize(configuration, root = ActiveRecord::Tasks::DatabaseTasks.root)
9
- @configuration, @root = configuration, root
8
+ def self.using_database_configurations?
9
+ true
10
+ end
11
+
12
+ def initialize(db_config, root = ActiveRecord::Tasks::DatabaseTasks.root)
13
+ @db_config = db_config
14
+ @root = root
10
15
  end
11
16
 
12
17
  def create
13
- raise DatabaseAlreadyExists if File.exist?(configuration["database"])
18
+ raise DatabaseAlreadyExists if File.exist?(db_config.database)
14
19
 
15
- establish_connection configuration
20
+ establish_connection(db_config)
16
21
  connection
17
22
  end
18
23
 
19
24
  def drop
20
25
  require "pathname"
21
- path = Pathname.new configuration["database"]
26
+ path = Pathname.new(db_config.database)
22
27
  file = path.absolute? ? path.to_s : File.join(root, path)
23
28
 
24
29
  FileUtils.rm(file)
@@ -40,7 +45,7 @@ module ActiveRecord
40
45
  def structure_dump(filename, extra_flags)
41
46
  args = []
42
47
  args.concat(Array(extra_flags)) if extra_flags
43
- args << configuration["database"]
48
+ args << db_config.database
44
49
 
45
50
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
46
51
  if ignore_tables.any?
@@ -53,13 +58,12 @@ module ActiveRecord
53
58
  end
54
59
 
55
60
  def structure_load(filename, extra_flags)
56
- dbfile = configuration["database"]
57
61
  flags = extra_flags.join(" ") if extra_flags
58
- `sqlite3 #{flags} #{dbfile} < "#{filename}"`
62
+ `sqlite3 #{flags} #{db_config.database} < "#{filename}"`
59
63
  end
60
64
 
61
65
  private
62
- attr_reader :configuration, :root
66
+ attr_reader :db_config, :root
63
67
 
64
68
  def run_cmd(cmd, args, out)
65
69
  fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
@@ -5,18 +5,19 @@ require "active_support/testing/parallelization"
5
5
  module ActiveRecord
6
6
  module TestDatabases # :nodoc:
7
7
  ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
8
- create_and_load_schema(i, env_name: Rails.env)
8
+ create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
9
9
  end
10
10
 
11
11
  def self.create_and_load_schema(i, env_name:)
12
12
  old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
13
13
 
14
14
  ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
- db_config.config["database"] += "-#{i}"
16
- ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
15
+ db_config._database = "#{db_config.database}-#{i}"
16
+
17
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord::Base.schema_format, nil)
17
18
  end
18
19
  ensure
19
- ActiveRecord::Base.establish_connection(Rails.env.to_sym)
20
+ ActiveRecord::Base.establish_connection
20
21
  ENV["VERBOSE"] = old
21
22
  end
22
23
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  module TestFixtures
5
7
  extend ActiveSupport::Concern
@@ -21,7 +23,6 @@ module ActiveRecord
21
23
  class_attribute :use_transactional_tests, default: true
22
24
  class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
23
25
  class_attribute :pre_loaded_fixtures, default: false
24
- class_attribute :config, default: ActiveRecord::Base
25
26
  class_attribute :lock_threads, default: true
26
27
  end
27
28
 
@@ -41,8 +42,9 @@ module ActiveRecord
41
42
  def fixtures(*fixture_set_names)
42
43
  if fixture_set_names.first == :all
43
44
  raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
44
- fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
45
- fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
45
+ fixture_set_names = Dir[::File.join(fixture_path, "{**,*}/*.{yml}")].uniq
46
+ fixture_set_names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
47
+ fixture_set_names.map! { |f| f[fixture_path.to_s.size..-5].delete_prefix("/") }
46
48
  else
47
49
  fixture_set_names = fixture_set_names.flatten.map(&:to_s)
48
50
  end
@@ -97,7 +99,7 @@ module ActiveRecord
97
99
 
98
100
  def run_in_transaction?
99
101
  use_transactional_tests &&
100
- !self.class.uses_transaction?(method_name)
102
+ !self.class.uses_transaction?(name)
101
103
  end
102
104
 
103
105
  def setup_fixtures(config = ActiveRecord::Base)
@@ -112,8 +114,6 @@ module ActiveRecord
112
114
 
113
115
  # Load fixtures once and begin transaction.
114
116
  if run_in_transaction?
115
- @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
116
-
117
117
  if @@already_loaded_fixtures[self.class]
118
118
  @loaded_fixtures = @@already_loaded_fixtures[self.class]
119
119
  else
@@ -131,11 +131,12 @@ module ActiveRecord
131
131
  # When connections are established in the future, begin a transaction too
132
132
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
133
133
  spec_name = payload[:spec_name] if payload.key?(:spec_name)
134
+ shard = payload[:shard] if payload.key?(:shard)
134
135
  setup_shared_connection_pool
135
136
 
136
137
  if spec_name
137
138
  begin
138
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
139
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
139
140
  rescue ConnectionNotEstablished
140
141
  connection = nil
141
142
  end
@@ -168,7 +169,6 @@ module ActiveRecord
168
169
  connection.pool.lock_thread = false
169
170
  end
170
171
  @fixture_connections.clear
171
- teardown_shared_connection_pool
172
172
  else
173
173
  ActiveRecord::FixtureSet.reset_cache
174
174
  end
@@ -190,38 +190,41 @@ module ActiveRecord
190
190
  # need to share a connection pool so that the reading connection
191
191
  # can see data in the open transaction on the writing connection.
192
192
  def setup_shared_connection_pool
193
- writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role]
194
-
195
- ActiveRecord::Base.connection_handlers.values.each do |handler|
196
- if handler != writing_handler
197
- handler.connection_pool_list.each do |pool|
198
- name = pool.spec.name
199
- writing_connection = writing_handler.retrieve_connection_pool(name)
200
- return unless writing_connection
201
-
202
- reading_connection = handler.send(:owner_to_pool)[name]
203
- next if reading_connection == writing_connection
204
-
205
- @saved_pool_configs[handler][name] = reading_connection
206
- handler.send(:owner_to_pool)[name] = writing_connection
193
+ if ActiveRecord::Base.legacy_connection_handling
194
+ writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role]
195
+
196
+ ActiveRecord::Base.connection_handlers.values.each do |handler|
197
+ if handler != writing_handler
198
+ handler.connection_pool_names.each do |name|
199
+ writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
200
+ return unless writing_pool_manager
201
+
202
+ pool_manager = handler.send(:owner_to_pool_manager)[name]
203
+ pool_manager.shard_names.each do |shard_name|
204
+ writing_pool_config = writing_pool_manager.get_pool_config(nil, shard_name)
205
+ pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
206
+ end
207
+ end
207
208
  end
208
209
  end
209
- end
210
- end
211
-
212
- def teardown_shared_connection_pool
213
- @saved_pool_configs.each_pair do |handler, pools|
214
- pools.each_pair do |name, pool|
215
- handler.send(:owner_to_pool)[name] = pool
210
+ else
211
+ handler = ActiveRecord::Base.connection_handler
212
+
213
+ handler.connection_pool_names.each do |name|
214
+ pool_manager = handler.send(:owner_to_pool_manager)[name]
215
+ pool_manager.shard_names.each do |shard_name|
216
+ writing_pool_config = pool_manager.get_pool_config(ActiveRecord::Base.writing_role, shard_name)
217
+ pool_manager.role_names.each do |role|
218
+ next unless pool_manager.get_pool_config(role, shard_name)
219
+ pool_manager.set_pool_config(role, shard_name, writing_pool_config)
220
+ end
221
+ end
216
222
  end
217
223
  end
218
-
219
- @saved_pool_configs.clear
220
224
  end
221
225
 
222
226
  def load_fixtures(config)
223
- fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
224
- Hash[fixtures.map { |f| [f.name, f] }]
227
+ ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config).index_by(&:name)
225
228
  end
226
229
 
227
230
  def instantiate_fixtures
@@ -80,11 +80,11 @@ module ActiveRecord
80
80
 
81
81
  private
82
82
  def timestamp_attributes_for_create
83
- ["created_at", "created_on"]
83
+ ["created_at", "created_on"].map! { |name| attribute_aliases[name] || name }
84
84
  end
85
85
 
86
86
  def timestamp_attributes_for_update
87
- ["updated_at", "updated_on"]
87
+ ["updated_at", "updated_on"].map! { |name| attribute_aliases[name] || name }
88
88
  end
89
89
 
90
90
  def reload_schema_from_cache
@@ -101,9 +101,7 @@ module ActiveRecord
101
101
  current_time = current_time_from_proper_timezone
102
102
 
103
103
  all_timestamp_attributes_in_model.each do |column|
104
- if !attribute_present?(column)
105
- _write_attribute(column, current_time)
106
- end
104
+ _write_attribute(column, current_time) unless _read_attribute(column)
107
105
  end
108
106
  end
109
107
 
@@ -159,7 +157,7 @@ module ActiveRecord
159
157
  def clear_timestamp_attributes
160
158
  all_timestamp_attributes_in_model.each do |attribute_name|
161
159
  self[attribute_name] = nil
162
- clear_attribute_changes([attribute_name])
160
+ clear_attribute_change(attribute_name)
163
161
  end
164
162
  end
165
163
  end
@@ -3,22 +3,20 @@
3
3
  module ActiveRecord
4
4
  # = Active Record Touch Later
5
5
  module TouchLater # :nodoc:
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- before_commit_without_transaction_enrollment :touch_deferred_attributes
6
+ def before_committed!
7
+ touch_deferred_attributes if has_defer_touch_attrs? && persisted?
8
+ super
10
9
  end
11
10
 
12
- def touch_later(*names, **) # :nodoc:
13
- unless persisted?
14
- raise ActiveRecordError, <<-MSG.squish
15
- cannot touch on a new or destroyed record object. Consider using
16
- persisted?, new_record?, or destroyed? before touching
17
- MSG
18
- end
11
+ def touch_later(*names) # :nodoc:
12
+ _raise_record_not_touched_error unless persisted?
19
13
 
20
14
  @_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
21
- @_defer_touch_attrs |= names
15
+ @_defer_touch_attrs |= names.map! do |name|
16
+ name = name.to_s
17
+ self.class.attribute_aliases[name] || name
18
+ end unless names.empty?
19
+
22
20
  @_touch_time = current_time_from_proper_timezone
23
21
 
24
22
  surreptitiously_touch @_defer_touch_attrs
@@ -36,22 +34,24 @@ module ActiveRecord
36
34
  def touch(*names, time: nil) # :nodoc:
37
35
  if has_defer_touch_attrs?
38
36
  names |= @_defer_touch_attrs
37
+ super(*names, time: time)
38
+ @_defer_touch_attrs, @_touch_time = nil, nil
39
+ else
40
+ super
39
41
  end
40
- super(*names, time: time)
41
42
  end
42
43
 
43
44
  private
44
- def surreptitiously_touch(attrs)
45
- attrs.each { |attr| write_attribute attr, @_touch_time }
46
- clear_attribute_changes attrs
45
+ def surreptitiously_touch(attr_names)
46
+ attr_names.each do |attr_name|
47
+ _write_attribute(attr_name, @_touch_time)
48
+ clear_attribute_change(attr_name)
49
+ end
47
50
  end
48
51
 
49
52
  def touch_deferred_attributes
50
- if has_defer_touch_attrs? && persisted?
51
- @_skip_dirty_tracking = true
52
- touch(*@_defer_touch_attrs, time: @_touch_time)
53
- @_defer_touch_attrs, @_touch_time = nil, nil
54
- end
53
+ @_skip_dirty_tracking = true
54
+ touch(time: @_touch_time)
55
55
  end
56
56
 
57
57
  def has_defer_touch_attrs?
@@ -10,9 +10,6 @@ module ActiveRecord
10
10
  included do
11
11
  define_callbacks :commit, :rollback,
12
12
  :before_commit,
13
- :before_commit_without_transaction_enrollment,
14
- :commit_without_transaction_enrollment,
15
- :rollback_without_transaction_enrollment,
16
13
  scope: [:kind, :name]
17
14
  end
18
15
 
@@ -164,13 +161,13 @@ module ActiveRecord
164
161
  # end
165
162
  # end
166
163
  #
167
- # only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
164
+ # only "Kotori" is created.
168
165
  #
169
166
  # Most databases don't support true nested transactions. At the time of
170
167
  # writing, the only database that we're aware of that supports true nested
171
168
  # transactions, is MS-SQL. Because of this, Active Record emulates nested
172
- # transactions by using savepoints on MySQL and PostgreSQL. See
173
- # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
169
+ # transactions by using savepoints. See
170
+ # https://dev.mysql.com/doc/refman/en/savepoint.html
174
171
  # for more information about savepoints.
175
172
  #
176
173
  # === \Callbacks
@@ -266,21 +263,6 @@ module ActiveRecord
266
263
  set_callback(:rollback, :after, *args, &block)
267
264
  end
268
265
 
269
- def before_commit_without_transaction_enrollment(*args, &block) # :nodoc:
270
- set_options_for_callbacks!(args)
271
- set_callback(:before_commit_without_transaction_enrollment, :before, *args, &block)
272
- end
273
-
274
- def after_commit_without_transaction_enrollment(*args, &block) # :nodoc:
275
- set_options_for_callbacks!(args)
276
- set_callback(:commit_without_transaction_enrollment, :after, *args, &block)
277
- end
278
-
279
- def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc:
280
- set_options_for_callbacks!(args)
281
- set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
282
- end
283
-
284
266
  private
285
267
  def set_options_for_callbacks!(args, enforced_options = {})
286
268
  options = args.extract_options!.merge!(enforced_options)
@@ -302,7 +284,7 @@ module ActiveRecord
302
284
  end
303
285
 
304
286
  # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
305
- def transaction(options = {}, &block)
287
+ def transaction(**options, &block)
306
288
  self.class.transaction(**options, &block)
307
289
  end
308
290
 
@@ -310,11 +292,11 @@ module ActiveRecord
310
292
  with_transaction_returning_status { super }
311
293
  end
312
294
 
313
- def save(*, **) #:nodoc:
295
+ def save(**) #:nodoc:
314
296
  with_transaction_returning_status { super }
315
297
  end
316
298
 
317
- def save!(*, **) #:nodoc:
299
+ def save!(**) #:nodoc:
318
300
  with_transaction_returning_status { super }
319
301
  end
320
302
 
@@ -323,7 +305,6 @@ module ActiveRecord
323
305
  end
324
306
 
325
307
  def before_committed! # :nodoc:
326
- _run_before_commit_without_transaction_enrollment_callbacks
327
308
  _run_before_commit_callbacks
328
309
  end
329
310
 
@@ -335,7 +316,6 @@ module ActiveRecord
335
316
  force_clear_transaction_record_state
336
317
  if should_run_callbacks
337
318
  @_committed_already_called = true
338
- _run_commit_without_transaction_enrollment_callbacks
339
319
  _run_commit_callbacks
340
320
  end
341
321
  ensure
@@ -347,7 +327,6 @@ module ActiveRecord
347
327
  def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
348
328
  if should_run_callbacks
349
329
  _run_rollback_callbacks
350
- _run_rollback_without_transaction_enrollment_callbacks
351
330
  end
352
331
  ensure
353
332
  restore_transaction_record_state(force_restore_state)
@@ -363,13 +342,11 @@ module ActiveRecord
363
342
  # instance.
364
343
  def with_transaction_returning_status
365
344
  status = nil
366
- self.class.transaction do
367
- if has_transactional_callbacks?
368
- add_to_transaction
369
- else
370
- sync_with_transaction_state if @transaction_state&.finalized?
371
- @transaction_state = self.class.connection.transaction_state
372
- end
345
+ connection = self.class.connection
346
+ ensure_finalize = !connection.transaction_open?
347
+
348
+ connection.transaction do
349
+ add_to_transaction(ensure_finalize || has_transactional_callbacks?)
373
350
  remember_transaction_record_state
374
351
 
375
352
  status = yield
@@ -391,6 +368,7 @@ module ActiveRecord
391
368
  @_start_transaction_state ||= {
392
369
  id: id,
393
370
  new_record: @new_record,
371
+ previously_new_record: @previously_new_record,
394
372
  destroyed: @destroyed,
395
373
  attributes: @attributes,
396
374
  frozen?: frozen?,
@@ -415,7 +393,6 @@ module ActiveRecord
415
393
  # Force to clear the transaction record state.
416
394
  def force_clear_transaction_record_state
417
395
  @_start_transaction_state = nil
418
- @transaction_state = nil
419
396
  end
420
397
 
421
398
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
@@ -423,6 +400,7 @@ module ActiveRecord
423
400
  if restore_state = @_start_transaction_state
424
401
  if force_restore_state || restore_state[:level] <= 1
425
402
  @new_record = restore_state[:new_record]
403
+ @previously_new_record = restore_state[:previously_new_record]
426
404
  @destroyed = restore_state[:destroyed]
427
405
  @attributes = restore_state[:attributes].map do |attr|
428
406
  value = @attributes.fetch_value(attr.name)
@@ -455,39 +433,12 @@ module ActiveRecord
455
433
 
456
434
  # Add the record to the current transaction so that the #after_rollback and #after_commit
457
435
  # callbacks can be called.
458
- def add_to_transaction
459
- self.class.connection.add_transaction_record(self)
436
+ def add_to_transaction(ensure_finalize = true)
437
+ self.class.connection.add_transaction_record(self, ensure_finalize)
460
438
  end
461
439
 
462
440
  def has_transactional_callbacks?
463
441
  !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
464
442
  end
465
-
466
- # Updates the attributes on this particular Active Record object so that
467
- # if it's associated with a transaction, then the state of the Active Record
468
- # object will be updated to reflect the current state of the transaction.
469
- #
470
- # The <tt>@transaction_state</tt> variable stores the states of the associated
471
- # transaction. This relies on the fact that a transaction can only be in
472
- # one rollback or commit (otherwise a list of states would be required).
473
- # Each Active Record object inside of a transaction carries that transaction's
474
- # TransactionState.
475
- #
476
- # This method checks to see if the ActiveRecord object's state reflects
477
- # the TransactionState, and rolls back or commits the Active Record object
478
- # as appropriate.
479
- def sync_with_transaction_state
480
- if transaction_state = @transaction_state
481
- if transaction_state.fully_committed?
482
- force_clear_transaction_record_state
483
- elsif transaction_state.committed?
484
- clear_transaction_record_state
485
- elsif transaction_state.rolledback?
486
- force_restore_state = transaction_state.fully_rolledback?
487
- restore_transaction_record_state(force_restore_state)
488
- clear_transaction_record_state
489
- end
490
- end
491
- end
492
443
  end
493
444
  end
@@ -61,9 +61,13 @@ module ActiveRecord
61
61
  end
62
62
 
63
63
  def encoded(value)
64
- unless default_value?(value)
65
- coder.dump(value)
64
+ return if default_value?(value)
65
+ payload = coder.dump(value)
66
+ if payload && binary? && payload.encoding != Encoding::BINARY
67
+ payload = payload.dup if payload.frozen?
68
+ payload.force_encoding(Encoding::BINARY)
66
69
  end
70
+ payload
67
71
  end
68
72
  end
69
73
  end
@@ -46,9 +46,14 @@ module ActiveRecord
46
46
  @default_value ||= Value.new
47
47
  end
48
48
 
49
+ def adapter_name_from(model) # :nodoc:
50
+ # TODO: this shouldn't depend on a connection to the database
51
+ model.connection.adapter_name.downcase.to_sym
52
+ end
53
+
49
54
  private
50
55
  def current_adapter_name
51
- ActiveRecord::Base.connection.adapter_name.downcase.to_sym
56
+ adapter_name_from(ActiveRecord::Base)
52
57
  end
53
58
  end
54
59
 
@@ -58,6 +63,7 @@ module ActiveRecord
58
63
  Decimal = ActiveModel::Type::Decimal
59
64
  Float = ActiveModel::Type::Float
60
65
  Integer = ActiveModel::Type::Integer
66
+ ImmutableString = ActiveModel::Type::ImmutableString
61
67
  String = ActiveModel::Type::String
62
68
  Value = ActiveModel::Type::Value
63
69
 
@@ -69,6 +75,7 @@ module ActiveRecord
69
75
  register(:decimal, Type::Decimal, override: false)
70
76
  register(:float, Type::Float, override: false)
71
77
  register(:integer, Type::Integer, override: false)
78
+ register(:immutable_string, Type::ImmutableString, override: false)
72
79
  register(:json, Type::Json, override: false)
73
80
  register(:string, Type::String, override: false)
74
81
  register(:text, Type::Text, override: false)
@@ -9,7 +9,6 @@ module ActiveRecord
9
9
  end
10
10
 
11
11
  def type_cast_for_database(attr_name, value)
12
- return value if value.is_a?(Arel::Nodes::BindParam)
13
12
  type = type_for_attribute(attr_name)
14
13
  type.serialize(value)
15
14
  end