activerecord 5.2.2.1 → 6.0.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 (269) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -508
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +15 -6
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +61 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  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 +5 -15
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  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 +16 -28
  22. data/lib/active_record/associations/collection_proxy.rb +19 -48
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +20 -25
  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.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +40 -32
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +22 -8
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +5 -19
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +137 -26
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +114 -130
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +7 -11
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -77
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
  89. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  90. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  95. data/lib/active_record/connection_handling.rb +155 -26
  96. data/lib/active_record/core.rb +104 -59
  97. data/lib/active_record/counter_cache.rb +4 -29
  98. data/lib/active_record/database_configurations.rb +233 -0
  99. data/lib/active_record/database_configurations/database_config.rb +37 -0
  100. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  101. data/lib/active_record/database_configurations/url_config.rb +79 -0
  102. data/lib/active_record/dynamic_matchers.rb +1 -1
  103. data/lib/active_record/enum.rb +37 -7
  104. data/lib/active_record/errors.rb +30 -16
  105. data/lib/active_record/explain.rb +1 -1
  106. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  107. data/lib/active_record/fixture_set/render_context.rb +17 -0
  108. data/lib/active_record/fixture_set/table_row.rb +153 -0
  109. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  110. data/lib/active_record/fixtures.rb +145 -472
  111. data/lib/active_record/gem_version.rb +3 -3
  112. data/lib/active_record/inheritance.rb +13 -3
  113. data/lib/active_record/insert_all.rb +179 -0
  114. data/lib/active_record/integration.rb +68 -16
  115. data/lib/active_record/internal_metadata.rb +10 -2
  116. data/lib/active_record/locking/optimistic.rb +5 -6
  117. data/lib/active_record/locking/pessimistic.rb +3 -3
  118. data/lib/active_record/log_subscriber.rb +7 -26
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  121. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/migration/command_recorder.rb +50 -6
  124. data/lib/active_record/migration/compatibility.rb +91 -64
  125. data/lib/active_record/model_schema.rb +33 -9
  126. data/lib/active_record/nested_attributes.rb +2 -2
  127. data/lib/active_record/no_touching.rb +7 -0
  128. data/lib/active_record/persistence.rb +231 -25
  129. data/lib/active_record/query_cache.rb +11 -4
  130. data/lib/active_record/querying.rb +33 -22
  131. data/lib/active_record/railtie.rb +80 -43
  132. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  133. data/lib/active_record/railties/controller_runtime.rb +30 -35
  134. data/lib/active_record/railties/databases.rake +199 -46
  135. data/lib/active_record/reflection.rb +42 -44
  136. data/lib/active_record/relation.rb +311 -80
  137. data/lib/active_record/relation/batches.rb +13 -10
  138. data/lib/active_record/relation/calculations.rb +67 -57
  139. data/lib/active_record/relation/delegation.rb +26 -43
  140. data/lib/active_record/relation/finder_methods.rb +28 -28
  141. data/lib/active_record/relation/merger.rb +17 -23
  142. data/lib/active_record/relation/predicate_builder.rb +18 -15
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  145. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  148. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  149. data/lib/active_record/relation/query_attribute.rb +17 -10
  150. data/lib/active_record/relation/query_methods.rb +247 -73
  151. data/lib/active_record/relation/spawn_methods.rb +1 -1
  152. data/lib/active_record/relation/where_clause.rb +14 -10
  153. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  154. data/lib/active_record/result.rb +30 -11
  155. data/lib/active_record/sanitization.rb +32 -40
  156. data/lib/active_record/schema.rb +2 -11
  157. data/lib/active_record/schema_dumper.rb +22 -7
  158. data/lib/active_record/schema_migration.rb +5 -1
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/scoping/default.rb +6 -7
  161. data/lib/active_record/scoping/named.rb +20 -15
  162. data/lib/active_record/statement_cache.rb +32 -5
  163. data/lib/active_record/store.rb +87 -8
  164. data/lib/active_record/table_metadata.rb +10 -17
  165. data/lib/active_record/tasks/database_tasks.rb +194 -25
  166. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  167. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  168. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  169. data/lib/active_record/test_databases.rb +23 -0
  170. data/lib/active_record/test_fixtures.rb +225 -0
  171. data/lib/active_record/timestamp.rb +39 -25
  172. data/lib/active_record/touch_later.rb +4 -2
  173. data/lib/active_record/transactions.rb +57 -66
  174. data/lib/active_record/translation.rb +1 -1
  175. data/lib/active_record/type.rb +3 -4
  176. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  177. data/lib/active_record/type_caster/connection.rb +15 -14
  178. data/lib/active_record/type_caster/map.rb +1 -4
  179. data/lib/active_record/validations.rb +1 -0
  180. data/lib/active_record/validations/uniqueness.rb +15 -27
  181. data/lib/arel.rb +58 -0
  182. data/lib/arel/alias_predication.rb +9 -0
  183. data/lib/arel/attributes.rb +22 -0
  184. data/lib/arel/attributes/attribute.rb +37 -0
  185. data/lib/arel/collectors/bind.rb +24 -0
  186. data/lib/arel/collectors/composite.rb +31 -0
  187. data/lib/arel/collectors/plain_string.rb +20 -0
  188. data/lib/arel/collectors/sql_string.rb +20 -0
  189. data/lib/arel/collectors/substitute_binds.rb +28 -0
  190. data/lib/arel/crud.rb +42 -0
  191. data/lib/arel/delete_manager.rb +18 -0
  192. data/lib/arel/errors.rb +9 -0
  193. data/lib/arel/expressions.rb +29 -0
  194. data/lib/arel/factory_methods.rb +49 -0
  195. data/lib/arel/insert_manager.rb +49 -0
  196. data/lib/arel/math.rb +45 -0
  197. data/lib/arel/nodes.rb +68 -0
  198. data/lib/arel/nodes/and.rb +32 -0
  199. data/lib/arel/nodes/ascending.rb +23 -0
  200. data/lib/arel/nodes/binary.rb +52 -0
  201. data/lib/arel/nodes/bind_param.rb +36 -0
  202. data/lib/arel/nodes/case.rb +55 -0
  203. data/lib/arel/nodes/casted.rb +50 -0
  204. data/lib/arel/nodes/comment.rb +29 -0
  205. data/lib/arel/nodes/count.rb +12 -0
  206. data/lib/arel/nodes/delete_statement.rb +45 -0
  207. data/lib/arel/nodes/descending.rb +23 -0
  208. data/lib/arel/nodes/equality.rb +18 -0
  209. data/lib/arel/nodes/extract.rb +24 -0
  210. data/lib/arel/nodes/false.rb +16 -0
  211. data/lib/arel/nodes/full_outer_join.rb +8 -0
  212. data/lib/arel/nodes/function.rb +44 -0
  213. data/lib/arel/nodes/grouping.rb +8 -0
  214. data/lib/arel/nodes/in.rb +8 -0
  215. data/lib/arel/nodes/infix_operation.rb +80 -0
  216. data/lib/arel/nodes/inner_join.rb +8 -0
  217. data/lib/arel/nodes/insert_statement.rb +37 -0
  218. data/lib/arel/nodes/join_source.rb +20 -0
  219. data/lib/arel/nodes/matches.rb +18 -0
  220. data/lib/arel/nodes/named_function.rb +23 -0
  221. data/lib/arel/nodes/node.rb +50 -0
  222. data/lib/arel/nodes/node_expression.rb +13 -0
  223. data/lib/arel/nodes/outer_join.rb +8 -0
  224. data/lib/arel/nodes/over.rb +15 -0
  225. data/lib/arel/nodes/regexp.rb +16 -0
  226. data/lib/arel/nodes/right_outer_join.rb +8 -0
  227. data/lib/arel/nodes/select_core.rb +67 -0
  228. data/lib/arel/nodes/select_statement.rb +41 -0
  229. data/lib/arel/nodes/sql_literal.rb +16 -0
  230. data/lib/arel/nodes/string_join.rb +11 -0
  231. data/lib/arel/nodes/table_alias.rb +27 -0
  232. data/lib/arel/nodes/terminal.rb +16 -0
  233. data/lib/arel/nodes/true.rb +16 -0
  234. data/lib/arel/nodes/unary.rb +45 -0
  235. data/lib/arel/nodes/unary_operation.rb +20 -0
  236. data/lib/arel/nodes/unqualified_column.rb +22 -0
  237. data/lib/arel/nodes/update_statement.rb +41 -0
  238. data/lib/arel/nodes/values_list.rb +9 -0
  239. data/lib/arel/nodes/window.rb +126 -0
  240. data/lib/arel/nodes/with.rb +11 -0
  241. data/lib/arel/order_predications.rb +13 -0
  242. data/lib/arel/predications.rb +257 -0
  243. data/lib/arel/select_manager.rb +271 -0
  244. data/lib/arel/table.rb +110 -0
  245. data/lib/arel/tree_manager.rb +72 -0
  246. data/lib/arel/update_manager.rb +34 -0
  247. data/lib/arel/visitors.rb +20 -0
  248. data/lib/arel/visitors/depth_first.rb +204 -0
  249. data/lib/arel/visitors/dot.rb +297 -0
  250. data/lib/arel/visitors/ibm_db.rb +34 -0
  251. data/lib/arel/visitors/informix.rb +62 -0
  252. data/lib/arel/visitors/mssql.rb +157 -0
  253. data/lib/arel/visitors/mysql.rb +83 -0
  254. data/lib/arel/visitors/oracle.rb +159 -0
  255. data/lib/arel/visitors/oracle12.rb +66 -0
  256. data/lib/arel/visitors/postgresql.rb +110 -0
  257. data/lib/arel/visitors/sqlite.rb +39 -0
  258. data/lib/arel/visitors/to_sql.rb +889 -0
  259. data/lib/arel/visitors/visitor.rb +46 -0
  260. data/lib/arel/visitors/where_sql.rb +23 -0
  261. data/lib/arel/window_predications.rb +9 -0
  262. data/lib/rails/generators/active_record/migration.rb +14 -1
  263. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  264. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  265. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  266. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  267. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  268. metadata +111 -26
  269. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -40,24 +40,6 @@ module ActiveRecord
40
40
  committed? || rolledback?
41
41
  end
42
42
 
43
- def set_state(state)
44
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
45
- The set_state method is deprecated and will be removed in
46
- Rails 6.0. Please use rollback! or commit! to set transaction
47
- state directly.
48
- MSG
49
- case state
50
- when :rolledback
51
- rollback!
52
- when :committed
53
- commit!
54
- when nil
55
- nullify!
56
- else
57
- raise ArgumentError, "Invalid transaction state: #{state}"
58
- end
59
- end
60
-
61
43
  def rollback!
62
44
  @children.each { |c| c.rollback! }
63
45
  @state = :rolledback
@@ -91,13 +73,14 @@ module ActiveRecord
91
73
  end
92
74
 
93
75
  class Transaction #:nodoc:
94
- attr_reader :connection, :state, :records, :savepoint_name
95
- attr_writer :joinable
76
+ attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
96
77
 
97
78
  def initialize(connection, options, run_commit_callbacks: false)
98
79
  @connection = connection
99
80
  @state = TransactionState.new
100
81
  @records = []
82
+ @isolation_level = options[:isolation]
83
+ @materialized = false
101
84
  @joinable = options.fetch(:joinable, true)
102
85
  @run_commit_callbacks = run_commit_callbacks
103
86
  end
@@ -106,10 +89,22 @@ module ActiveRecord
106
89
  records << record
107
90
  end
108
91
 
92
+ def materialize!
93
+ @materialized = true
94
+ end
95
+
96
+ def materialized?
97
+ @materialized
98
+ end
99
+
109
100
  def rollback_records
110
- ite = records.uniq
101
+ ite = records.uniq(&:object_id)
102
+ already_run_callbacks = {}
111
103
  while record = ite.shift
112
- record.rolledback!(force_restore_state: full_rollback?)
104
+ trigger_callbacks = record.trigger_transactional_callbacks?
105
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
106
+ already_run_callbacks[record] ||= trigger_callbacks
107
+ record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
113
108
  end
114
109
  ensure
115
110
  ite.each do |i|
@@ -122,13 +117,17 @@ module ActiveRecord
122
117
  end
123
118
 
124
119
  def commit_records
125
- ite = records.uniq
120
+ ite = records.uniq(&:object_id)
121
+ already_run_callbacks = {}
126
122
  while record = ite.shift
127
123
  if @run_commit_callbacks
128
- record.committed!
124
+ trigger_callbacks = record.trigger_transactional_callbacks?
125
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
126
+ already_run_callbacks[record] ||= trigger_callbacks
127
+ record.committed!(should_run_callbacks: should_run_callbacks)
129
128
  else
130
129
  # if not running callbacks, only adds the record to the parent transaction
131
- record.add_to_transaction
130
+ connection.add_transaction_record(record)
132
131
  end
133
132
  end
134
133
  ensure
@@ -142,24 +141,30 @@ module ActiveRecord
142
141
  end
143
142
 
144
143
  class SavepointTransaction < Transaction
145
- def initialize(connection, savepoint_name, parent_transaction, options, *args)
146
- super(connection, options, *args)
144
+ def initialize(connection, savepoint_name, parent_transaction, *args)
145
+ super(connection, *args)
147
146
 
148
147
  parent_transaction.state.add_child(@state)
149
148
 
150
- if options[:isolation]
149
+ if isolation_level
151
150
  raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
152
151
  end
153
- connection.create_savepoint(@savepoint_name = savepoint_name)
152
+
153
+ @savepoint_name = savepoint_name
154
+ end
155
+
156
+ def materialize!
157
+ connection.create_savepoint(savepoint_name)
158
+ super
154
159
  end
155
160
 
156
161
  def rollback
157
- connection.rollback_to_savepoint(savepoint_name)
162
+ connection.rollback_to_savepoint(savepoint_name) if materialized?
158
163
  @state.rollback!
159
164
  end
160
165
 
161
166
  def commit
162
- connection.release_savepoint(savepoint_name)
167
+ connection.release_savepoint(savepoint_name) if materialized?
163
168
  @state.commit!
164
169
  end
165
170
 
@@ -167,22 +172,23 @@ module ActiveRecord
167
172
  end
168
173
 
169
174
  class RealTransaction < Transaction
170
- def initialize(connection, options, *args)
171
- super
172
- if options[:isolation]
173
- connection.begin_isolated_db_transaction(options[:isolation])
175
+ def materialize!
176
+ if isolation_level
177
+ connection.begin_isolated_db_transaction(isolation_level)
174
178
  else
175
179
  connection.begin_db_transaction
176
180
  end
181
+
182
+ super
177
183
  end
178
184
 
179
185
  def rollback
180
- connection.rollback_db_transaction
186
+ connection.rollback_db_transaction if materialized?
181
187
  @state.full_rollback!
182
188
  end
183
189
 
184
190
  def commit
185
- connection.commit_db_transaction
191
+ connection.commit_db_transaction if materialized?
186
192
  @state.full_commit!
187
193
  end
188
194
  end
@@ -191,6 +197,9 @@ module ActiveRecord
191
197
  def initialize(connection)
192
198
  @stack = []
193
199
  @connection = connection
200
+ @has_unmaterialized_transactions = false
201
+ @materializing_transactions = false
202
+ @lazy_transactions_enabled = true
194
203
  end
195
204
 
196
205
  def begin_transaction(options = {})
@@ -204,11 +213,44 @@ module ActiveRecord
204
213
  run_commit_callbacks: run_commit_callbacks)
205
214
  end
206
215
 
216
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
217
+ @has_unmaterialized_transactions = true
218
+ else
219
+ transaction.materialize!
220
+ end
207
221
  @stack.push(transaction)
208
222
  transaction
209
223
  end
210
224
  end
211
225
 
226
+ def disable_lazy_transactions!
227
+ materialize_transactions
228
+ @lazy_transactions_enabled = false
229
+ end
230
+
231
+ def enable_lazy_transactions!
232
+ @lazy_transactions_enabled = true
233
+ end
234
+
235
+ def lazy_transactions_enabled?
236
+ @lazy_transactions_enabled
237
+ end
238
+
239
+ def materialize_transactions
240
+ return if @materializing_transactions
241
+ return unless @has_unmaterialized_transactions
242
+
243
+ @connection.lock.synchronize do
244
+ begin
245
+ @materializing_transactions = true
246
+ @stack.each { |t| t.materialize! unless t.materialized? }
247
+ ensure
248
+ @materializing_transactions = false
249
+ end
250
+ @has_unmaterialized_transactions = false
251
+ end
252
+ end
253
+
212
254
  def commit_transaction
213
255
  @connection.lock.synchronize do
214
256
  transaction = @stack.last
@@ -234,26 +276,24 @@ module ActiveRecord
234
276
 
235
277
  def within_new_transaction(options = {})
236
278
  @connection.lock.synchronize do
237
- begin
238
- transaction = begin_transaction options
239
- yield
240
- rescue Exception => error
241
- if transaction
279
+ transaction = begin_transaction options
280
+ yield
281
+ rescue Exception => error
282
+ if transaction
283
+ rollback_transaction
284
+ after_failure_actions(transaction, error)
285
+ end
286
+ raise
287
+ ensure
288
+ if !error && transaction
289
+ if Thread.current.status == "aborting"
242
290
  rollback_transaction
243
- after_failure_actions(transaction, error)
244
- end
245
- raise
246
- ensure
247
- unless error
248
- if Thread.current.status == "aborting"
249
- rollback_transaction if transaction
250
- else
251
- begin
252
- commit_transaction if transaction
253
- rescue Exception
254
- rollback_transaction(transaction) unless transaction.state.completed?
255
- raise
256
- end
291
+ else
292
+ begin
293
+ commit_transaction
294
+ rescue Exception
295
+ rollback_transaction(transaction) unless transaction.state.completed?
296
+ raise
257
297
  end
258
298
  end
259
299
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
3
4
  require "active_record/connection_adapters/determine_if_preparable_visitor"
4
5
  require "active_record/connection_adapters/schema_cache"
5
6
  require "active_record/connection_adapters/sql_type_metadata"
6
7
  require "active_record/connection_adapters/abstract/schema_dumper"
7
8
  require "active_record/connection_adapters/abstract/schema_creation"
8
9
  require "active_support/concurrency/load_interlock_aware_monitor"
10
+ require "active_support/deprecation"
9
11
  require "arel/collectors/bind"
10
12
  require "arel/collectors/composite"
11
13
  require "arel/collectors/sql_string"
@@ -65,7 +67,7 @@ module ActiveRecord
65
67
  # Most of the methods in the adapter are useful during migrations. Most
66
68
  # notably, the instance methods provided by SchemaStatements are very useful.
67
69
  class AbstractAdapter
68
- ADAPTER_NAME = "Abstract".freeze
70
+ ADAPTER_NAME = "Abstract"
69
71
  include ActiveSupport::Callbacks
70
72
  define_callbacks :checkout, :checkin
71
73
 
@@ -76,12 +78,16 @@ module ActiveRecord
76
78
 
77
79
  SIMPLE_INT = /\A\d+\z/
78
80
 
79
- attr_accessor :visitor, :pool
80
- attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
81
+ attr_accessor :pool
82
+ attr_reader :visitor, :owner, :logger, :lock
81
83
  alias :in_use? :owner
82
84
 
85
+ set_callback :checkin, :after, :enable_lazy_transactions!
86
+
83
87
  def self.type_cast_config_to_integer(config)
84
- if config =~ SIMPLE_INT
88
+ if config.is_a?(Integer)
89
+ config
90
+ elsif SIMPLE_INT.match?(config)
85
91
  config.to_i
86
92
  else
87
93
  config
@@ -96,6 +102,19 @@ module ActiveRecord
96
102
  end
97
103
  end
98
104
 
105
+ def self.build_read_query_regexp(*parts) # :nodoc:
106
+ parts = parts.map { |part| /\A[\(\s]*#{part}/i }
107
+ Regexp.union(*parts)
108
+ end
109
+
110
+ def self.quoted_column_names # :nodoc:
111
+ @quoted_column_names ||= {}
112
+ end
113
+
114
+ def self.quoted_table_names # :nodoc:
115
+ @quoted_table_names ||= {}
116
+ end
117
+
99
118
  def initialize(connection, logger = nil, config = {}) # :nodoc:
100
119
  super()
101
120
 
@@ -104,11 +123,10 @@ module ActiveRecord
104
123
  @instrumenter = ActiveSupport::Notifications.instrumenter
105
124
  @logger = logger
106
125
  @config = config
107
- @pool = nil
126
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
108
127
  @idle_since = Concurrent.monotonic_time
109
- @schema_cache = SchemaCache.new self
110
- @quoted_column_names, @quoted_table_names = {}, {}
111
128
  @visitor = arel_visitor
129
+ @statements = build_statement_pool
112
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
113
131
 
114
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -117,6 +135,22 @@ module ActiveRecord
117
135
  else
118
136
  @prepared_statements = false
119
137
  end
138
+
139
+ @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
140
+ config.fetch(:advisory_locks, true)
141
+ )
142
+ end
143
+
144
+ def replica?
145
+ @config[:replica] || false
146
+ end
147
+
148
+ # Determines whether writes are currently being prevents.
149
+ #
150
+ # Returns true if the connection is a replica, or if +prevent_writes+
151
+ # is set to true.
152
+ def preventing_writes?
153
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
120
154
  end
121
155
 
122
156
  def migrations_paths # :nodoc:
@@ -124,19 +158,49 @@ module ActiveRecord
124
158
  end
125
159
 
126
160
  def migration_context # :nodoc:
127
- MigrationContext.new(migrations_paths)
161
+ MigrationContext.new(migrations_paths, schema_migration)
162
+ end
163
+
164
+ def schema_migration # :nodoc:
165
+ @schema_migration ||= begin
166
+ conn = self
167
+ spec_name = conn.pool.spec.name
168
+ name = "#{spec_name}::SchemaMigration"
169
+
170
+ Class.new(ActiveRecord::SchemaMigration) do
171
+ define_singleton_method(:name) { name }
172
+ define_singleton_method(:to_s) { name }
173
+
174
+ self.connection_specification_name = spec_name
175
+ end
176
+ end
177
+ end
178
+
179
+ def prepared_statements
180
+ @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
181
+ end
182
+
183
+ def prepared_statements_disabled_cache # :nodoc:
184
+ Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
128
185
  end
129
186
 
130
187
  class Version
131
188
  include Comparable
132
189
 
133
- def initialize(version_string)
190
+ attr_reader :full_version_string
191
+
192
+ def initialize(version_string, full_version_string = nil)
134
193
  @version = version_string.split(".").map(&:to_i)
194
+ @full_version_string = full_version_string
135
195
  end
136
196
 
137
197
  def <=>(version_string)
138
198
  @version <=> version_string.split(".").map(&:to_i)
139
199
  end
200
+
201
+ def to_s
202
+ @version.join(".")
203
+ end
140
204
  end
141
205
 
142
206
  def valid_type?(type) # :nodoc:
@@ -146,7 +210,7 @@ module ActiveRecord
146
210
  # this method must only be called while holding connection pool's mutex
147
211
  def lease
148
212
  if in_use?
149
- msg = "Cannot lease connection, ".dup
213
+ msg = +"Cannot lease connection, "
150
214
  if @owner == Thread.current
151
215
  msg << "it is already leased by the current thread."
152
216
  else
@@ -159,9 +223,13 @@ module ActiveRecord
159
223
  @owner = Thread.current
160
224
  end
161
225
 
226
+ def schema_cache
227
+ @pool.get_schema_cache(self)
228
+ end
229
+
162
230
  def schema_cache=(cache)
163
231
  cache.connection = self
164
- @schema_cache = cache
232
+ @pool.set_schema_cache(cache)
165
233
  end
166
234
 
167
235
  # this method must only be called while holding connection pool's mutex
@@ -200,10 +268,10 @@ module ActiveRecord
200
268
  end
201
269
 
202
270
  def unprepared_statement
203
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
204
272
  yield
205
273
  ensure
206
- @prepared_statements = old_prepared_statements
274
+ cache&.delete(object_id)
207
275
  end
208
276
 
209
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -212,6 +280,11 @@ module ActiveRecord
212
280
  self.class::ADAPTER_NAME
213
281
  end
214
282
 
283
+ # Does the database for this adapter exist?
284
+ def self.database_exists?(config)
285
+ raise NotImplementedError
286
+ end
287
+
215
288
  # Does this adapter support DDL rollbacks in transactions? That is, would
216
289
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
217
290
  def supports_ddl_transactions?
@@ -290,12 +363,18 @@ module ActiveRecord
290
363
  def supports_foreign_keys_in_create?
291
364
  supports_foreign_keys?
292
365
  end
366
+ deprecate :supports_foreign_keys_in_create?
293
367
 
294
368
  # Does this adapter support views?
295
369
  def supports_views?
296
370
  false
297
371
  end
298
372
 
373
+ # Does this adapter support materialized views?
374
+ def supports_materialized_views?
375
+ false
376
+ end
377
+
299
378
  # Does this adapter support datetime with precision?
300
379
  def supports_datetime_with_precision?
301
380
  false
@@ -320,6 +399,7 @@ module ActiveRecord
320
399
  def supports_multi_insert?
321
400
  true
322
401
  end
402
+ deprecate :supports_multi_insert?
323
403
 
324
404
  # Does this adapter support virtual columns?
325
405
  def supports_virtual_columns?
@@ -331,6 +411,35 @@ module ActiveRecord
331
411
  false
332
412
  end
333
413
 
414
+ # Does this adapter support optimizer hints?
415
+ def supports_optimizer_hints?
416
+ false
417
+ end
418
+
419
+ def supports_common_table_expressions?
420
+ false
421
+ end
422
+
423
+ def supports_lazy_transactions?
424
+ false
425
+ end
426
+
427
+ def supports_insert_returning?
428
+ false
429
+ end
430
+
431
+ def supports_insert_on_duplicate_skip?
432
+ false
433
+ end
434
+
435
+ def supports_insert_on_duplicate_update?
436
+ false
437
+ end
438
+
439
+ def supports_insert_conflict_target?
440
+ false
441
+ end
442
+
334
443
  # This is meant to be implemented by the adapters that support extensions
335
444
  def disable_extension(name)
336
445
  end
@@ -339,6 +448,10 @@ module ActiveRecord
339
448
  def enable_extension(name)
340
449
  end
341
450
 
451
+ def advisory_locks_enabled? # :nodoc:
452
+ supports_advisory_locks? && @advisory_locks_enabled
453
+ end
454
+
342
455
  # This is meant to be implemented by the adapters that support advisory
343
456
  # locks
344
457
  #
@@ -404,6 +517,9 @@ module ActiveRecord
404
517
  #
405
518
  # Prevent @connection's finalizer from touching the socket, or
406
519
  # otherwise communicating with its server, when it is collected.
520
+ if schema_cache.connection == self
521
+ schema_cache.connection = nil
522
+ end
407
523
  end
408
524
 
409
525
  # Reset the state of this connection, directing the DBMS to clear
@@ -416,11 +532,9 @@ module ActiveRecord
416
532
  # this should be overridden by concrete adapters
417
533
  end
418
534
 
419
- ###
420
- # Clear any caching the database adapter may be doing, for example
421
- # clearing the prepared statement cache. This is database specific.
535
+ # Clear any caching the database adapter may be doing.
422
536
  def clear_cache!
423
- # this should be overridden by concrete adapters
537
+ @lock.synchronize { @statements.clear } if @statements
424
538
  end
425
539
 
426
540
  # Returns true if its required to reload the connection between requests for development mode.
@@ -442,18 +556,25 @@ module ActiveRecord
442
556
  # This is useful for when you need to call a proprietary method such as
443
557
  # PostgreSQL's lo_* methods.
444
558
  def raw_connection
559
+ disable_lazy_transactions!
445
560
  @connection
446
561
  end
447
562
 
448
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
449
- table[attribute].eq(value)
563
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
564
+ attribute.eq(value)
565
+ end
566
+
567
+ def case_sensitive_comparison(attribute, value) # :nodoc:
568
+ attribute.eq(value)
450
569
  end
451
570
 
452
- def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
571
+ def case_insensitive_comparison(attribute, value) # :nodoc:
572
+ column = column_for_attribute(attribute)
573
+
453
574
  if can_perform_case_insensitive_comparison_for?(column)
454
- table[attribute].lower.eq(table.lower(value))
575
+ attribute.lower.eq(attribute.relation.lower(value))
455
576
  else
456
- table[attribute].eq(value)
577
+ attribute.eq(value)
457
578
  end
458
579
  end
459
580
 
@@ -468,18 +589,38 @@ module ActiveRecord
468
589
  end
469
590
 
470
591
  def column_name_for_operation(operation, node) # :nodoc:
471
- column_name_from_arel_node(node)
472
- end
473
-
474
- def column_name_from_arel_node(node) # :nodoc:
475
- visitor.accept(node, Arel::Collectors::SQLString.new).value
592
+ visitor.compile(node)
476
593
  end
477
594
 
478
595
  def default_index_type?(index) # :nodoc:
479
596
  index.using.nil?
480
597
  end
481
598
 
599
+ # Called by ActiveRecord::InsertAll,
600
+ # Passed an instance of ActiveRecord::InsertAll::Builder,
601
+ # This method implements standard bulk inserts for all databases, but
602
+ # should be overridden by adapters to implement common features with
603
+ # non-standard syntax like handling duplicates or returning values.
604
+ def build_insert_sql(insert) # :nodoc:
605
+ if insert.skip_duplicates? || insert.update_duplicates?
606
+ raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
607
+ end
608
+
609
+ "INSERT #{insert.into} #{insert.values_list}"
610
+ end
611
+
612
+ def get_database_version # :nodoc:
613
+ end
614
+
615
+ def database_version # :nodoc:
616
+ schema_cache.database_version
617
+ end
618
+
619
+ def check_version # :nodoc:
620
+ end
621
+
482
622
  private
623
+
483
624
  def type_map
484
625
  @type_map ||= Type::TypeMap.new.tap do |mapping|
485
626
  initialize_type_map(mapping)
@@ -553,14 +694,12 @@ module ActiveRecord
553
694
  $1.to_i if sql_type =~ /\((.*)\)/
554
695
  end
555
696
 
556
- def translate_exception_class(e, sql)
557
- begin
558
- message = "#{e.class.name}: #{e.message}: #{sql}"
559
- rescue Encoding::CompatibilityError
560
- message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
561
- end
697
+ def translate_exception_class(e, sql, binds)
698
+ message = "#{e.class.name}: #{e.message}"
562
699
 
563
- exception = translate_exception(e, message)
700
+ exception = translate_exception(
701
+ e, message: message, sql: sql, binds: binds
702
+ )
564
703
  exception.set_backtrace e.backtrace
565
704
  exception
566
705
  end
@@ -573,24 +712,23 @@ module ActiveRecord
573
712
  binds: binds,
574
713
  type_casted_binds: type_casted_binds,
575
714
  statement_name: statement_name,
576
- connection_id: object_id) do
577
- begin
578
- @lock.synchronize do
579
- yield
580
- end
581
- rescue => e
582
- raise translate_exception_class(e, sql)
715
+ connection_id: object_id,
716
+ connection: self) do
717
+ @lock.synchronize do
718
+ yield
583
719
  end
720
+ rescue => e
721
+ raise translate_exception_class(e, sql, binds)
584
722
  end
585
723
  end
586
724
 
587
- def translate_exception(exception, message)
725
+ def translate_exception(exception, message:, sql:, binds:)
588
726
  # override in derived class
589
727
  case exception
590
728
  when RuntimeError
591
729
  exception
592
730
  else
593
- ActiveRecord::StatementInvalid.new(message)
731
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
594
732
  end
595
733
  end
596
734
 
@@ -604,6 +742,11 @@ module ActiveRecord
604
742
  raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
605
743
  end
606
744
 
745
+ def column_for_attribute(attribute)
746
+ table_name = attribute.relation.name
747
+ schema_cache.columns_hash(table_name)[attribute.name.to_s]
748
+ end
749
+
607
750
  def collector
608
751
  if prepared_statements
609
752
  Arel::Collectors::Composite.new(
@@ -621,6 +764,9 @@ module ActiveRecord
621
764
  def arel_visitor
622
765
  Arel::Visitors::ToSql.new(self)
623
766
  end
767
+
768
+ def build_statement_pool
769
+ end
624
770
  end
625
771
  end
626
772
  end