activerecord 5.2.4.2 → 6.0.2.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 +715 -566
  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 +3 -13
  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 +12 -23
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  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 +2 -2
  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 +104 -16
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -8
  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 +132 -53
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
  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 +6 -10
  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 +12 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  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 +42 -6
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +129 -141
  95. data/lib/active_record/connection_handling.rb +155 -26
  96. data/lib/active_record/core.rb +103 -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 +15 -7
  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 +76 -49
  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 +228 -24
  129. data/lib/active_record/query_cache.rb +11 -4
  130. data/lib/active_record/querying.rb +32 -20
  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 +32 -30
  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 +53 -47
  139. data/lib/active_record/relation/delegation.rb +26 -43
  140. data/lib/active_record/relation/finder_methods.rb +23 -27
  141. data/lib/active_record/relation/merger.rb +11 -20
  142. data/lib/active_record/relation/predicate_builder.rb +4 -6
  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 +13 -8
  150. data/lib/active_record/relation/query_methods.rb +213 -64
  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 +4 -5
  161. data/lib/active_record/scoping/named.rb +20 -15
  162. data/lib/active_record/statement_cache.rb +30 -3
  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 +56 -65
  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 +109 -24
  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,14 +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
88
  if config.is_a?(Integer)
85
89
  config
86
- elsif config =~ SIMPLE_INT
90
+ elsif SIMPLE_INT.match?(config)
87
91
  config.to_i
88
92
  else
89
93
  config
@@ -98,6 +102,19 @@ module ActiveRecord
98
102
  end
99
103
  end
100
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
+
101
118
  def initialize(connection, logger = nil, config = {}) # :nodoc:
102
119
  super()
103
120
 
@@ -106,11 +123,10 @@ module ActiveRecord
106
123
  @instrumenter = ActiveSupport::Notifications.instrumenter
107
124
  @logger = logger
108
125
  @config = config
109
- @pool = nil
126
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
110
127
  @idle_since = Concurrent.monotonic_time
111
- @schema_cache = SchemaCache.new self
112
- @quoted_column_names, @quoted_table_names = {}, {}
113
128
  @visitor = arel_visitor
129
+ @statements = build_statement_pool
114
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
115
131
 
116
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -119,6 +135,22 @@ module ActiveRecord
119
135
  else
120
136
  @prepared_statements = false
121
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
122
154
  end
123
155
 
124
156
  def migrations_paths # :nodoc:
@@ -126,19 +158,49 @@ module ActiveRecord
126
158
  end
127
159
 
128
160
  def migration_context # :nodoc:
129
- 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
130
185
  end
131
186
 
132
187
  class Version
133
188
  include Comparable
134
189
 
135
- def initialize(version_string)
190
+ attr_reader :full_version_string
191
+
192
+ def initialize(version_string, full_version_string = nil)
136
193
  @version = version_string.split(".").map(&:to_i)
194
+ @full_version_string = full_version_string
137
195
  end
138
196
 
139
197
  def <=>(version_string)
140
198
  @version <=> version_string.split(".").map(&:to_i)
141
199
  end
200
+
201
+ def to_s
202
+ @version.join(".")
203
+ end
142
204
  end
143
205
 
144
206
  def valid_type?(type) # :nodoc:
@@ -148,7 +210,7 @@ module ActiveRecord
148
210
  # this method must only be called while holding connection pool's mutex
149
211
  def lease
150
212
  if in_use?
151
- msg = "Cannot lease connection, ".dup
213
+ msg = +"Cannot lease connection, "
152
214
  if @owner == Thread.current
153
215
  msg << "it is already leased by the current thread."
154
216
  else
@@ -161,9 +223,13 @@ module ActiveRecord
161
223
  @owner = Thread.current
162
224
  end
163
225
 
226
+ def schema_cache
227
+ @pool.get_schema_cache(self)
228
+ end
229
+
164
230
  def schema_cache=(cache)
165
231
  cache.connection = self
166
- @schema_cache = cache
232
+ @pool.set_schema_cache(cache)
167
233
  end
168
234
 
169
235
  # this method must only be called while holding connection pool's mutex
@@ -202,10 +268,10 @@ module ActiveRecord
202
268
  end
203
269
 
204
270
  def unprepared_statement
205
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
206
272
  yield
207
273
  ensure
208
- @prepared_statements = old_prepared_statements
274
+ cache&.delete(object_id)
209
275
  end
210
276
 
211
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -214,6 +280,11 @@ module ActiveRecord
214
280
  self.class::ADAPTER_NAME
215
281
  end
216
282
 
283
+ # Does the database for this adapter exist?
284
+ def self.database_exists?(config)
285
+ raise NotImplementedError
286
+ end
287
+
217
288
  # Does this adapter support DDL rollbacks in transactions? That is, would
218
289
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
219
290
  def supports_ddl_transactions?
@@ -292,12 +363,18 @@ module ActiveRecord
292
363
  def supports_foreign_keys_in_create?
293
364
  supports_foreign_keys?
294
365
  end
366
+ deprecate :supports_foreign_keys_in_create?
295
367
 
296
368
  # Does this adapter support views?
297
369
  def supports_views?
298
370
  false
299
371
  end
300
372
 
373
+ # Does this adapter support materialized views?
374
+ def supports_materialized_views?
375
+ false
376
+ end
377
+
301
378
  # Does this adapter support datetime with precision?
302
379
  def supports_datetime_with_precision?
303
380
  false
@@ -322,6 +399,7 @@ module ActiveRecord
322
399
  def supports_multi_insert?
323
400
  true
324
401
  end
402
+ deprecate :supports_multi_insert?
325
403
 
326
404
  # Does this adapter support virtual columns?
327
405
  def supports_virtual_columns?
@@ -333,6 +411,35 @@ module ActiveRecord
333
411
  false
334
412
  end
335
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
+
336
443
  # This is meant to be implemented by the adapters that support extensions
337
444
  def disable_extension(name)
338
445
  end
@@ -341,6 +448,10 @@ module ActiveRecord
341
448
  def enable_extension(name)
342
449
  end
343
450
 
451
+ def advisory_locks_enabled? # :nodoc:
452
+ supports_advisory_locks? && @advisory_locks_enabled
453
+ end
454
+
344
455
  # This is meant to be implemented by the adapters that support advisory
345
456
  # locks
346
457
  #
@@ -406,6 +517,9 @@ module ActiveRecord
406
517
  #
407
518
  # Prevent @connection's finalizer from touching the socket, or
408
519
  # otherwise communicating with its server, when it is collected.
520
+ if schema_cache.connection == self
521
+ schema_cache.connection = nil
522
+ end
409
523
  end
410
524
 
411
525
  # Reset the state of this connection, directing the DBMS to clear
@@ -418,11 +532,9 @@ module ActiveRecord
418
532
  # this should be overridden by concrete adapters
419
533
  end
420
534
 
421
- ###
422
- # Clear any caching the database adapter may be doing, for example
423
- # clearing the prepared statement cache. This is database specific.
535
+ # Clear any caching the database adapter may be doing.
424
536
  def clear_cache!
425
- # this should be overridden by concrete adapters
537
+ @lock.synchronize { @statements.clear } if @statements
426
538
  end
427
539
 
428
540
  # Returns true if its required to reload the connection between requests for development mode.
@@ -444,18 +556,25 @@ module ActiveRecord
444
556
  # This is useful for when you need to call a proprietary method such as
445
557
  # PostgreSQL's lo_* methods.
446
558
  def raw_connection
559
+ disable_lazy_transactions!
447
560
  @connection
448
561
  end
449
562
 
450
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
- 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)
452
569
  end
453
570
 
454
- def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
571
+ def case_insensitive_comparison(attribute, value) # :nodoc:
572
+ column = column_for_attribute(attribute)
573
+
455
574
  if can_perform_case_insensitive_comparison_for?(column)
456
- table[attribute].lower.eq(table.lower(value))
575
+ attribute.lower.eq(attribute.relation.lower(value))
457
576
  else
458
- table[attribute].eq(value)
577
+ attribute.eq(value)
459
578
  end
460
579
  end
461
580
 
@@ -470,18 +589,38 @@ module ActiveRecord
470
589
  end
471
590
 
472
591
  def column_name_for_operation(operation, node) # :nodoc:
473
- column_name_from_arel_node(node)
474
- end
475
-
476
- def column_name_from_arel_node(node) # :nodoc:
477
- visitor.accept(node, Arel::Collectors::SQLString.new).value
592
+ visitor.compile(node)
478
593
  end
479
594
 
480
595
  def default_index_type?(index) # :nodoc:
481
596
  index.using.nil?
482
597
  end
483
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
+
484
622
  private
623
+
485
624
  def type_map
486
625
  @type_map ||= Type::TypeMap.new.tap do |mapping|
487
626
  initialize_type_map(mapping)
@@ -555,14 +694,12 @@ module ActiveRecord
555
694
  $1.to_i if sql_type =~ /\((.*)\)/
556
695
  end
557
696
 
558
- def translate_exception_class(e, sql)
559
- begin
560
- message = "#{e.class.name}: #{e.message}: #{sql}"
561
- rescue Encoding::CompatibilityError
562
- message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
563
- end
697
+ def translate_exception_class(e, sql, binds)
698
+ message = "#{e.class.name}: #{e.message}"
564
699
 
565
- exception = translate_exception(e, message)
700
+ exception = translate_exception(
701
+ e, message: message, sql: sql, binds: binds
702
+ )
566
703
  exception.set_backtrace e.backtrace
567
704
  exception
568
705
  end
@@ -575,24 +712,23 @@ module ActiveRecord
575
712
  binds: binds,
576
713
  type_casted_binds: type_casted_binds,
577
714
  statement_name: statement_name,
578
- connection_id: object_id) do
579
- begin
580
- @lock.synchronize do
581
- yield
582
- end
583
- rescue => e
584
- raise translate_exception_class(e, sql)
715
+ connection_id: object_id,
716
+ connection: self) do
717
+ @lock.synchronize do
718
+ yield
585
719
  end
720
+ rescue => e
721
+ raise translate_exception_class(e, sql, binds)
586
722
  end
587
723
  end
588
724
 
589
- def translate_exception(exception, message)
725
+ def translate_exception(exception, message:, sql:, binds:)
590
726
  # override in derived class
591
727
  case exception
592
728
  when RuntimeError
593
729
  exception
594
730
  else
595
- ActiveRecord::StatementInvalid.new(message)
731
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
596
732
  end
597
733
  end
598
734
 
@@ -606,6 +742,11 @@ module ActiveRecord
606
742
  raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
607
743
  end
608
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
+
609
750
  def collector
610
751
  if prepared_statements
611
752
  Arel::Collectors::Composite.new(
@@ -623,6 +764,9 @@ module ActiveRecord
623
764
  def arel_visitor
624
765
  Arel::Visitors::ToSql.new(self)
625
766
  end
767
+
768
+ def build_statement_pool
769
+ end
626
770
  end
627
771
  end
628
772
  end