activerecord 5.2.4.rc1 → 6.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (266) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +597 -586
  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/associations.rb +19 -14
  9. data/lib/active_record/associations/association.rb +52 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/association.rb +14 -18
  14. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  15. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  17. data/lib/active_record/associations/builder/has_many.rb +2 -0
  18. data/lib/active_record/associations/builder/has_one.rb +35 -1
  19. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  20. data/lib/active_record/associations/collection_association.rb +6 -21
  21. data/lib/active_record/associations/collection_proxy.rb +12 -15
  22. data/lib/active_record/associations/foreign_association.rb +7 -0
  23. data/lib/active_record/associations/has_many_association.rb +2 -10
  24. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  25. data/lib/active_record/associations/has_one_association.rb +28 -30
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/preloader/association.rb +38 -36
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/singular_association.rb +2 -16
  34. data/lib/active_record/attribute_assignment.rb +7 -10
  35. data/lib/active_record/attribute_methods.rb +28 -100
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  37. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  38. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  39. data/lib/active_record/attribute_methods/query.rb +2 -3
  40. data/lib/active_record/attribute_methods/read.rb +15 -53
  41. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  43. data/lib/active_record/attribute_methods/write.rb +17 -24
  44. data/lib/active_record/attributes.rb +13 -0
  45. data/lib/active_record/autosave_association.rb +5 -9
  46. data/lib/active_record/base.rb +2 -3
  47. data/lib/active_record/callbacks.rb +5 -19
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +89 -16
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +172 -41
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  60. data/lib/active_record/connection_adapters/column.rb +17 -13
  61. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  62. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  69. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  70. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  75. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  80. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  81. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  85. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  87. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  88. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  93. data/lib/active_record/connection_handling.rb +149 -27
  94. data/lib/active_record/core.rb +100 -60
  95. data/lib/active_record/counter_cache.rb +4 -29
  96. data/lib/active_record/database_configurations.rb +204 -0
  97. data/lib/active_record/database_configurations/database_config.rb +37 -0
  98. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  99. data/lib/active_record/database_configurations/url_config.rb +79 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/enum.rb +28 -7
  102. data/lib/active_record/errors.rb +15 -7
  103. data/lib/active_record/explain.rb +1 -1
  104. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  105. data/lib/active_record/fixture_set/render_context.rb +17 -0
  106. data/lib/active_record/fixture_set/table_row.rb +153 -0
  107. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  108. data/lib/active_record/fixtures.rb +140 -472
  109. data/lib/active_record/gem_version.rb +4 -4
  110. data/lib/active_record/inheritance.rb +13 -3
  111. data/lib/active_record/insert_all.rb +180 -0
  112. data/lib/active_record/integration.rb +68 -16
  113. data/lib/active_record/internal_metadata.rb +10 -2
  114. data/lib/active_record/locking/optimistic.rb +5 -6
  115. data/lib/active_record/locking/pessimistic.rb +3 -3
  116. data/lib/active_record/log_subscriber.rb +7 -26
  117. data/lib/active_record/middleware/database_selector.rb +75 -0
  118. data/lib/active_record/middleware/database_selector/resolver.rb +90 -0
  119. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  120. data/lib/active_record/migration.rb +87 -76
  121. data/lib/active_record/migration/command_recorder.rb +50 -6
  122. data/lib/active_record/migration/compatibility.rb +76 -49
  123. data/lib/active_record/model_schema.rb +30 -9
  124. data/lib/active_record/nested_attributes.rb +2 -2
  125. data/lib/active_record/no_touching.rb +7 -0
  126. data/lib/active_record/persistence.rb +228 -24
  127. data/lib/active_record/query_cache.rb +11 -4
  128. data/lib/active_record/querying.rb +32 -20
  129. data/lib/active_record/railtie.rb +80 -43
  130. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  131. data/lib/active_record/railties/controller_runtime.rb +30 -35
  132. data/lib/active_record/railties/databases.rake +193 -46
  133. data/lib/active_record/reflection.rb +32 -30
  134. data/lib/active_record/relation.rb +310 -80
  135. data/lib/active_record/relation/batches.rb +13 -10
  136. data/lib/active_record/relation/calculations.rb +53 -45
  137. data/lib/active_record/relation/delegation.rb +26 -43
  138. data/lib/active_record/relation/finder_methods.rb +13 -26
  139. data/lib/active_record/relation/merger.rb +11 -20
  140. data/lib/active_record/relation/predicate_builder.rb +4 -6
  141. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  142. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  143. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  144. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  146. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  147. data/lib/active_record/relation/query_attribute.rb +13 -8
  148. data/lib/active_record/relation/query_methods.rb +165 -53
  149. data/lib/active_record/relation/spawn_methods.rb +1 -1
  150. data/lib/active_record/relation/where_clause.rb +14 -10
  151. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  152. data/lib/active_record/result.rb +30 -11
  153. data/lib/active_record/sanitization.rb +32 -40
  154. data/lib/active_record/schema.rb +2 -11
  155. data/lib/active_record/schema_dumper.rb +22 -7
  156. data/lib/active_record/schema_migration.rb +5 -1
  157. data/lib/active_record/scoping.rb +8 -8
  158. data/lib/active_record/scoping/default.rb +4 -5
  159. data/lib/active_record/scoping/named.rb +19 -15
  160. data/lib/active_record/statement_cache.rb +30 -3
  161. data/lib/active_record/store.rb +87 -8
  162. data/lib/active_record/table_metadata.rb +10 -17
  163. data/lib/active_record/tasks/database_tasks.rb +159 -25
  164. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  165. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  166. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  167. data/lib/active_record/test_databases.rb +38 -0
  168. data/lib/active_record/test_fixtures.rb +224 -0
  169. data/lib/active_record/timestamp.rb +39 -25
  170. data/lib/active_record/touch_later.rb +4 -2
  171. data/lib/active_record/transactions.rb +57 -66
  172. data/lib/active_record/translation.rb +1 -1
  173. data/lib/active_record/type.rb +3 -4
  174. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  175. data/lib/active_record/type_caster/connection.rb +15 -14
  176. data/lib/active_record/type_caster/map.rb +1 -4
  177. data/lib/active_record/validations/uniqueness.rb +15 -27
  178. data/lib/arel.rb +51 -0
  179. data/lib/arel/alias_predication.rb +9 -0
  180. data/lib/arel/attributes.rb +22 -0
  181. data/lib/arel/attributes/attribute.rb +37 -0
  182. data/lib/arel/collectors/bind.rb +24 -0
  183. data/lib/arel/collectors/composite.rb +31 -0
  184. data/lib/arel/collectors/plain_string.rb +20 -0
  185. data/lib/arel/collectors/sql_string.rb +20 -0
  186. data/lib/arel/collectors/substitute_binds.rb +28 -0
  187. data/lib/arel/crud.rb +42 -0
  188. data/lib/arel/delete_manager.rb +18 -0
  189. data/lib/arel/errors.rb +9 -0
  190. data/lib/arel/expressions.rb +29 -0
  191. data/lib/arel/factory_methods.rb +49 -0
  192. data/lib/arel/insert_manager.rb +49 -0
  193. data/lib/arel/math.rb +45 -0
  194. data/lib/arel/nodes.rb +68 -0
  195. data/lib/arel/nodes/and.rb +32 -0
  196. data/lib/arel/nodes/ascending.rb +23 -0
  197. data/lib/arel/nodes/binary.rb +52 -0
  198. data/lib/arel/nodes/bind_param.rb +36 -0
  199. data/lib/arel/nodes/case.rb +55 -0
  200. data/lib/arel/nodes/casted.rb +50 -0
  201. data/lib/arel/nodes/comment.rb +29 -0
  202. data/lib/arel/nodes/count.rb +12 -0
  203. data/lib/arel/nodes/delete_statement.rb +45 -0
  204. data/lib/arel/nodes/descending.rb +23 -0
  205. data/lib/arel/nodes/equality.rb +18 -0
  206. data/lib/arel/nodes/extract.rb +24 -0
  207. data/lib/arel/nodes/false.rb +16 -0
  208. data/lib/arel/nodes/full_outer_join.rb +8 -0
  209. data/lib/arel/nodes/function.rb +44 -0
  210. data/lib/arel/nodes/grouping.rb +8 -0
  211. data/lib/arel/nodes/in.rb +8 -0
  212. data/lib/arel/nodes/infix_operation.rb +80 -0
  213. data/lib/arel/nodes/inner_join.rb +8 -0
  214. data/lib/arel/nodes/insert_statement.rb +37 -0
  215. data/lib/arel/nodes/join_source.rb +20 -0
  216. data/lib/arel/nodes/matches.rb +18 -0
  217. data/lib/arel/nodes/named_function.rb +23 -0
  218. data/lib/arel/nodes/node.rb +50 -0
  219. data/lib/arel/nodes/node_expression.rb +13 -0
  220. data/lib/arel/nodes/outer_join.rb +8 -0
  221. data/lib/arel/nodes/over.rb +15 -0
  222. data/lib/arel/nodes/regexp.rb +16 -0
  223. data/lib/arel/nodes/right_outer_join.rb +8 -0
  224. data/lib/arel/nodes/select_core.rb +67 -0
  225. data/lib/arel/nodes/select_statement.rb +41 -0
  226. data/lib/arel/nodes/sql_literal.rb +16 -0
  227. data/lib/arel/nodes/string_join.rb +11 -0
  228. data/lib/arel/nodes/table_alias.rb +27 -0
  229. data/lib/arel/nodes/terminal.rb +16 -0
  230. data/lib/arel/nodes/true.rb +16 -0
  231. data/lib/arel/nodes/unary.rb +45 -0
  232. data/lib/arel/nodes/unary_operation.rb +20 -0
  233. data/lib/arel/nodes/unqualified_column.rb +22 -0
  234. data/lib/arel/nodes/update_statement.rb +41 -0
  235. data/lib/arel/nodes/values_list.rb +9 -0
  236. data/lib/arel/nodes/window.rb +126 -0
  237. data/lib/arel/nodes/with.rb +11 -0
  238. data/lib/arel/order_predications.rb +13 -0
  239. data/lib/arel/predications.rb +257 -0
  240. data/lib/arel/select_manager.rb +271 -0
  241. data/lib/arel/table.rb +110 -0
  242. data/lib/arel/tree_manager.rb +72 -0
  243. data/lib/arel/update_manager.rb +34 -0
  244. data/lib/arel/visitors.rb +20 -0
  245. data/lib/arel/visitors/depth_first.rb +204 -0
  246. data/lib/arel/visitors/dot.rb +297 -0
  247. data/lib/arel/visitors/ibm_db.rb +34 -0
  248. data/lib/arel/visitors/informix.rb +62 -0
  249. data/lib/arel/visitors/mssql.rb +157 -0
  250. data/lib/arel/visitors/mysql.rb +83 -0
  251. data/lib/arel/visitors/oracle.rb +159 -0
  252. data/lib/arel/visitors/oracle12.rb +66 -0
  253. data/lib/arel/visitors/postgresql.rb +110 -0
  254. data/lib/arel/visitors/sqlite.rb +39 -0
  255. data/lib/arel/visitors/to_sql.rb +889 -0
  256. data/lib/arel/visitors/visitor.rb +46 -0
  257. data/lib/arel/visitors/where_sql.rb +23 -0
  258. data/lib/arel/window_predications.rb +9 -0
  259. data/lib/rails/generators/active_record/migration.rb +14 -1
  260. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  261. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  262. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  263. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  264. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  265. metadata +108 -26
  266. 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
@@ -6,6 +6,7 @@ require "active_record/connection_adapters/sql_type_metadata"
6
6
  require "active_record/connection_adapters/abstract/schema_dumper"
7
7
  require "active_record/connection_adapters/abstract/schema_creation"
8
8
  require "active_support/concurrency/load_interlock_aware_monitor"
9
+ require "active_support/deprecation"
9
10
  require "arel/collectors/bind"
10
11
  require "arel/collectors/composite"
11
12
  require "arel/collectors/sql_string"
@@ -65,7 +66,7 @@ module ActiveRecord
65
66
  # Most of the methods in the adapter are useful during migrations. Most
66
67
  # notably, the instance methods provided by SchemaStatements are very useful.
67
68
  class AbstractAdapter
68
- ADAPTER_NAME = "Abstract".freeze
69
+ ADAPTER_NAME = "Abstract"
69
70
  include ActiveSupport::Callbacks
70
71
  define_callbacks :checkout, :checkin
71
72
 
@@ -76,14 +77,16 @@ module ActiveRecord
76
77
 
77
78
  SIMPLE_INT = /\A\d+\z/
78
79
 
79
- attr_accessor :visitor, :pool
80
- attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
80
+ attr_accessor :pool
81
+ attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
81
82
  alias :in_use? :owner
82
83
 
84
+ set_callback :checkin, :after, :enable_lazy_transactions!
85
+
83
86
  def self.type_cast_config_to_integer(config)
84
87
  if config.is_a?(Integer)
85
88
  config
86
- elsif config =~ SIMPLE_INT
89
+ elsif SIMPLE_INT.match?(config)
87
90
  config.to_i
88
91
  else
89
92
  config
@@ -98,6 +101,19 @@ module ActiveRecord
98
101
  end
99
102
  end
100
103
 
104
+ def self.build_read_query_regexp(*parts) # :nodoc:
105
+ parts = parts.map { |part| /\A[\(\s]*#{part}/i }
106
+ Regexp.union(*parts)
107
+ end
108
+
109
+ def self.quoted_column_names # :nodoc:
110
+ @quoted_column_names ||= {}
111
+ end
112
+
113
+ def self.quoted_table_names # :nodoc:
114
+ @quoted_table_names ||= {}
115
+ end
116
+
101
117
  def initialize(connection, logger = nil, config = {}) # :nodoc:
102
118
  super()
103
119
 
@@ -106,11 +122,10 @@ module ActiveRecord
106
122
  @instrumenter = ActiveSupport::Notifications.instrumenter
107
123
  @logger = logger
108
124
  @config = config
109
- @pool = nil
125
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
110
126
  @idle_since = Concurrent.monotonic_time
111
- @schema_cache = SchemaCache.new self
112
- @quoted_column_names, @quoted_table_names = {}, {}
113
127
  @visitor = arel_visitor
128
+ @statements = build_statement_pool
114
129
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
115
130
 
116
131
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -119,6 +134,22 @@ module ActiveRecord
119
134
  else
120
135
  @prepared_statements = false
121
136
  end
137
+
138
+ @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
139
+ config.fetch(:advisory_locks, true)
140
+ )
141
+ end
142
+
143
+ def replica?
144
+ @config[:replica] || false
145
+ end
146
+
147
+ # Determines whether writes are currently being prevents.
148
+ #
149
+ # Returns true if the connection is a replica, or if +prevent_writes+
150
+ # is set to true.
151
+ def preventing_writes?
152
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
122
153
  end
123
154
 
124
155
  def migrations_paths # :nodoc:
@@ -126,19 +157,41 @@ module ActiveRecord
126
157
  end
127
158
 
128
159
  def migration_context # :nodoc:
129
- MigrationContext.new(migrations_paths)
160
+ MigrationContext.new(migrations_paths, schema_migration)
161
+ end
162
+
163
+ def schema_migration # :nodoc:
164
+ @schema_migration ||= begin
165
+ conn = self
166
+ spec_name = conn.pool.spec.name
167
+ name = "#{spec_name}::SchemaMigration"
168
+
169
+ Class.new(ActiveRecord::SchemaMigration) do
170
+ define_singleton_method(:name) { name }
171
+ define_singleton_method(:to_s) { name }
172
+
173
+ self.connection_specification_name = spec_name
174
+ end
175
+ end
130
176
  end
131
177
 
132
178
  class Version
133
179
  include Comparable
134
180
 
135
- def initialize(version_string)
181
+ attr_reader :full_version_string
182
+
183
+ def initialize(version_string, full_version_string = nil)
136
184
  @version = version_string.split(".").map(&:to_i)
185
+ @full_version_string = full_version_string
137
186
  end
138
187
 
139
188
  def <=>(version_string)
140
189
  @version <=> version_string.split(".").map(&:to_i)
141
190
  end
191
+
192
+ def to_s
193
+ @version.join(".")
194
+ end
142
195
  end
143
196
 
144
197
  def valid_type?(type) # :nodoc:
@@ -148,7 +201,7 @@ module ActiveRecord
148
201
  # this method must only be called while holding connection pool's mutex
149
202
  def lease
150
203
  if in_use?
151
- msg = "Cannot lease connection, ".dup
204
+ msg = +"Cannot lease connection, "
152
205
  if @owner == Thread.current
153
206
  msg << "it is already leased by the current thread."
154
207
  else
@@ -161,9 +214,13 @@ module ActiveRecord
161
214
  @owner = Thread.current
162
215
  end
163
216
 
217
+ def schema_cache
218
+ @pool.get_schema_cache(self)
219
+ end
220
+
164
221
  def schema_cache=(cache)
165
222
  cache.connection = self
166
- @schema_cache = cache
223
+ @pool.set_schema_cache(cache)
167
224
  end
168
225
 
169
226
  # this method must only be called while holding connection pool's mutex
@@ -214,6 +271,11 @@ module ActiveRecord
214
271
  self.class::ADAPTER_NAME
215
272
  end
216
273
 
274
+ # Does the database for this adapter exist?
275
+ def self.database_exists?(config)
276
+ raise NotImplementedError
277
+ end
278
+
217
279
  # Does this adapter support DDL rollbacks in transactions? That is, would
218
280
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
219
281
  def supports_ddl_transactions?
@@ -292,12 +354,18 @@ module ActiveRecord
292
354
  def supports_foreign_keys_in_create?
293
355
  supports_foreign_keys?
294
356
  end
357
+ deprecate :supports_foreign_keys_in_create?
295
358
 
296
359
  # Does this adapter support views?
297
360
  def supports_views?
298
361
  false
299
362
  end
300
363
 
364
+ # Does this adapter support materialized views?
365
+ def supports_materialized_views?
366
+ false
367
+ end
368
+
301
369
  # Does this adapter support datetime with precision?
302
370
  def supports_datetime_with_precision?
303
371
  false
@@ -322,6 +390,7 @@ module ActiveRecord
322
390
  def supports_multi_insert?
323
391
  true
324
392
  end
393
+ deprecate :supports_multi_insert?
325
394
 
326
395
  # Does this adapter support virtual columns?
327
396
  def supports_virtual_columns?
@@ -333,6 +402,31 @@ module ActiveRecord
333
402
  false
334
403
  end
335
404
 
405
+ # Does this adapter support optimizer hints?
406
+ def supports_optimizer_hints?
407
+ false
408
+ end
409
+
410
+ def supports_lazy_transactions?
411
+ false
412
+ end
413
+
414
+ def supports_insert_returning?
415
+ false
416
+ end
417
+
418
+ def supports_insert_on_duplicate_skip?
419
+ false
420
+ end
421
+
422
+ def supports_insert_on_duplicate_update?
423
+ false
424
+ end
425
+
426
+ def supports_insert_conflict_target?
427
+ false
428
+ end
429
+
336
430
  # This is meant to be implemented by the adapters that support extensions
337
431
  def disable_extension(name)
338
432
  end
@@ -341,6 +435,10 @@ module ActiveRecord
341
435
  def enable_extension(name)
342
436
  end
343
437
 
438
+ def advisory_locks_enabled? # :nodoc:
439
+ supports_advisory_locks? && @advisory_locks_enabled
440
+ end
441
+
344
442
  # This is meant to be implemented by the adapters that support advisory
345
443
  # locks
346
444
  #
@@ -406,6 +504,9 @@ module ActiveRecord
406
504
  #
407
505
  # Prevent @connection's finalizer from touching the socket, or
408
506
  # otherwise communicating with its server, when it is collected.
507
+ if schema_cache.connection == self
508
+ schema_cache.connection = nil
509
+ end
409
510
  end
410
511
 
411
512
  # Reset the state of this connection, directing the DBMS to clear
@@ -418,11 +519,9 @@ module ActiveRecord
418
519
  # this should be overridden by concrete adapters
419
520
  end
420
521
 
421
- ###
422
- # Clear any caching the database adapter may be doing, for example
423
- # clearing the prepared statement cache. This is database specific.
522
+ # Clear any caching the database adapter may be doing.
424
523
  def clear_cache!
425
- # this should be overridden by concrete adapters
524
+ @lock.synchronize { @statements.clear } if @statements
426
525
  end
427
526
 
428
527
  # Returns true if its required to reload the connection between requests for development mode.
@@ -444,18 +543,25 @@ module ActiveRecord
444
543
  # This is useful for when you need to call a proprietary method such as
445
544
  # PostgreSQL's lo_* methods.
446
545
  def raw_connection
546
+ disable_lazy_transactions!
447
547
  @connection
448
548
  end
449
549
 
450
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
- table[attribute].eq(value)
550
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
551
+ attribute.eq(value)
452
552
  end
453
553
 
454
- def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
554
+ def case_sensitive_comparison(attribute, value) # :nodoc:
555
+ attribute.eq(value)
556
+ end
557
+
558
+ def case_insensitive_comparison(attribute, value) # :nodoc:
559
+ column = column_for_attribute(attribute)
560
+
455
561
  if can_perform_case_insensitive_comparison_for?(column)
456
- table[attribute].lower.eq(table.lower(value))
562
+ attribute.lower.eq(attribute.relation.lower(value))
457
563
  else
458
- table[attribute].eq(value)
564
+ attribute.eq(value)
459
565
  end
460
566
  end
461
567
 
@@ -470,18 +576,38 @@ module ActiveRecord
470
576
  end
471
577
 
472
578
  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
579
+ visitor.compile(node)
478
580
  end
479
581
 
480
582
  def default_index_type?(index) # :nodoc:
481
583
  index.using.nil?
482
584
  end
483
585
 
586
+ # Called by ActiveRecord::InsertAll,
587
+ # Passed an instance of ActiveRecord::InsertAll::Builder,
588
+ # This method implements standard bulk inserts for all databases, but
589
+ # should be overridden by adapters to implement common features with
590
+ # non-standard syntax like handling duplicates or returning values.
591
+ def build_insert_sql(insert) # :nodoc:
592
+ if insert.skip_duplicates? || insert.update_duplicates?
593
+ raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
594
+ end
595
+
596
+ "INSERT #{insert.into} #{insert.values_list}"
597
+ end
598
+
599
+ def get_database_version # :nodoc:
600
+ end
601
+
602
+ def database_version # :nodoc:
603
+ schema_cache.database_version
604
+ end
605
+
606
+ def check_version # :nodoc:
607
+ end
608
+
484
609
  private
610
+
485
611
  def type_map
486
612
  @type_map ||= Type::TypeMap.new.tap do |mapping|
487
613
  initialize_type_map(mapping)
@@ -555,14 +681,12 @@ module ActiveRecord
555
681
  $1.to_i if sql_type =~ /\((.*)\)/
556
682
  end
557
683
 
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
684
+ def translate_exception_class(e, sql, binds)
685
+ message = "#{e.class.name}: #{e.message}"
564
686
 
565
- exception = translate_exception(e, message)
687
+ exception = translate_exception(
688
+ e, message: message, sql: sql, binds: binds
689
+ )
566
690
  exception.set_backtrace e.backtrace
567
691
  exception
568
692
  end
@@ -575,24 +699,23 @@ module ActiveRecord
575
699
  binds: binds,
576
700
  type_casted_binds: type_casted_binds,
577
701
  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)
702
+ connection_id: object_id,
703
+ connection: self) do
704
+ @lock.synchronize do
705
+ yield
585
706
  end
707
+ rescue => e
708
+ raise translate_exception_class(e, sql, binds)
586
709
  end
587
710
  end
588
711
 
589
- def translate_exception(exception, message)
712
+ def translate_exception(exception, message:, sql:, binds:)
590
713
  # override in derived class
591
714
  case exception
592
715
  when RuntimeError
593
716
  exception
594
717
  else
595
- ActiveRecord::StatementInvalid.new(message)
718
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
596
719
  end
597
720
  end
598
721
 
@@ -606,6 +729,11 @@ module ActiveRecord
606
729
  raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
607
730
  end
608
731
 
732
+ def column_for_attribute(attribute)
733
+ table_name = attribute.relation.name
734
+ schema_cache.columns_hash(table_name)[attribute.name.to_s]
735
+ end
736
+
609
737
  def collector
610
738
  if prepared_statements
611
739
  Arel::Collectors::Composite.new(
@@ -623,6 +751,9 @@ module ActiveRecord
623
751
  def arel_visitor
624
752
  Arel::Visitors::ToSql.new(self)
625
753
  end
754
+
755
+ def build_statement_pool
756
+ end
626
757
  end
627
758
  end
628
759
  end