activerecord 5.2.8.1 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -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,19 @@ 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_writer :visitor
81
+ deprecate :visitor=
82
+
83
+ attr_accessor :pool
84
+ attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
81
85
  alias :in_use? :owner
82
86
 
87
+ set_callback :checkin, :after, :enable_lazy_transactions!
88
+
83
89
  def self.type_cast_config_to_integer(config)
84
90
  if config.is_a?(Integer)
85
91
  config
86
- elsif config =~ SIMPLE_INT
92
+ elsif SIMPLE_INT.match?(config)
87
93
  config.to_i
88
94
  else
89
95
  config
@@ -98,6 +104,11 @@ module ActiveRecord
98
104
  end
99
105
  end
100
106
 
107
+ def self.build_read_query_regexp(*parts) # :nodoc:
108
+ parts = parts.map { |part| /\A\s*#{part}/i }
109
+ Regexp.union(*parts)
110
+ end
111
+
101
112
  def initialize(connection, logger = nil, config = {}) # :nodoc:
102
113
  super()
103
114
 
@@ -110,6 +121,7 @@ module ActiveRecord
110
121
  @idle_since = Concurrent.monotonic_time
111
122
  @schema_cache = SchemaCache.new self
112
123
  @quoted_column_names, @quoted_table_names = {}, {}
124
+ @prevent_writes = false
113
125
  @visitor = arel_visitor
114
126
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
115
127
 
@@ -119,6 +131,36 @@ module ActiveRecord
119
131
  else
120
132
  @prepared_statements = false
121
133
  end
134
+
135
+ @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
136
+ config.fetch(:advisory_locks, true)
137
+ )
138
+
139
+ check_version
140
+ end
141
+
142
+ def replica?
143
+ @config[:replica] || false
144
+ end
145
+
146
+ # Determines whether writes are currently being prevents.
147
+ #
148
+ # Returns true if the connection is a replica, or if +prevent_writes+
149
+ # is set to true.
150
+ def preventing_writes?
151
+ replica? || prevent_writes
152
+ end
153
+
154
+ # Prevent writing to the database regardless of role.
155
+ #
156
+ # In some cases you may want to prevent writes to the database
157
+ # even if you are on a database that can write. `while_preventing_writes`
158
+ # will prevent writes to the database for the duration of the block.
159
+ def while_preventing_writes
160
+ original, @prevent_writes = @prevent_writes, true
161
+ yield
162
+ ensure
163
+ @prevent_writes = original
122
164
  end
123
165
 
124
166
  def migrations_paths # :nodoc:
@@ -139,6 +181,10 @@ module ActiveRecord
139
181
  def <=>(version_string)
140
182
  @version <=> version_string.split(".").map(&:to_i)
141
183
  end
184
+
185
+ def to_s
186
+ @version.join(".")
187
+ end
142
188
  end
143
189
 
144
190
  def valid_type?(type) # :nodoc:
@@ -148,7 +194,7 @@ module ActiveRecord
148
194
  # this method must only be called while holding connection pool's mutex
149
195
  def lease
150
196
  if in_use?
151
- msg = "Cannot lease connection, ".dup
197
+ msg = +"Cannot lease connection, "
152
198
  if @owner == Thread.current
153
199
  msg << "it is already leased by the current thread."
154
200
  else
@@ -298,6 +344,11 @@ module ActiveRecord
298
344
  false
299
345
  end
300
346
 
347
+ # Does this adapter support materialized views?
348
+ def supports_materialized_views?
349
+ false
350
+ end
351
+
301
352
  # Does this adapter support datetime with precision?
302
353
  def supports_datetime_with_precision?
303
354
  false
@@ -322,6 +373,7 @@ module ActiveRecord
322
373
  def supports_multi_insert?
323
374
  true
324
375
  end
376
+ deprecate :supports_multi_insert?
325
377
 
326
378
  # Does this adapter support virtual columns?
327
379
  def supports_virtual_columns?
@@ -333,6 +385,10 @@ module ActiveRecord
333
385
  false
334
386
  end
335
387
 
388
+ def supports_lazy_transactions?
389
+ false
390
+ end
391
+
336
392
  # This is meant to be implemented by the adapters that support extensions
337
393
  def disable_extension(name)
338
394
  end
@@ -341,6 +397,10 @@ module ActiveRecord
341
397
  def enable_extension(name)
342
398
  end
343
399
 
400
+ def advisory_locks_enabled? # :nodoc:
401
+ supports_advisory_locks? && @advisory_locks_enabled
402
+ end
403
+
344
404
  # This is meant to be implemented by the adapters that support advisory
345
405
  # locks
346
406
  #
@@ -444,18 +504,21 @@ module ActiveRecord
444
504
  # This is useful for when you need to call a proprietary method such as
445
505
  # PostgreSQL's lo_* methods.
446
506
  def raw_connection
507
+ disable_lazy_transactions!
447
508
  @connection
448
509
  end
449
510
 
450
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
- table[attribute].eq(value)
511
+ def case_sensitive_comparison(attribute, value) # :nodoc:
512
+ attribute.eq(value)
452
513
  end
453
514
 
454
- def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
515
+ def case_insensitive_comparison(attribute, value) # :nodoc:
516
+ column = column_for_attribute(attribute)
517
+
455
518
  if can_perform_case_insensitive_comparison_for?(column)
456
- table[attribute].lower.eq(table.lower(value))
519
+ attribute.lower.eq(attribute.relation.lower(value))
457
520
  else
458
- table[attribute].eq(value)
521
+ attribute.eq(value)
459
522
  end
460
523
  end
461
524
 
@@ -470,11 +533,7 @@ module ActiveRecord
470
533
  end
471
534
 
472
535
  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
536
+ visitor.compile(node)
478
537
  end
479
538
 
480
539
  def default_index_type?(index) # :nodoc:
@@ -482,6 +541,9 @@ module ActiveRecord
482
541
  end
483
542
 
484
543
  private
544
+ def check_version
545
+ end
546
+
485
547
  def type_map
486
548
  @type_map ||= Type::TypeMap.new.tap do |mapping|
487
549
  initialize_type_map(mapping)
@@ -555,14 +617,12 @@ module ActiveRecord
555
617
  $1.to_i if sql_type =~ /\((.*)\)/
556
618
  end
557
619
 
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
620
+ def translate_exception_class(e, sql, binds)
621
+ message = "#{e.class.name}: #{e.message}"
564
622
 
565
- exception = translate_exception(e, message)
623
+ exception = translate_exception(
624
+ e, message: message, sql: sql, binds: binds
625
+ )
566
626
  exception.set_backtrace e.backtrace
567
627
  exception
568
628
  end
@@ -575,24 +635,23 @@ module ActiveRecord
575
635
  binds: binds,
576
636
  type_casted_binds: type_casted_binds,
577
637
  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)
638
+ connection_id: object_id,
639
+ connection: self) do
640
+ @lock.synchronize do
641
+ yield
585
642
  end
643
+ rescue => e
644
+ raise translate_exception_class(e, sql, binds)
586
645
  end
587
646
  end
588
647
 
589
- def translate_exception(exception, message)
648
+ def translate_exception(exception, message:, sql:, binds:)
590
649
  # override in derived class
591
650
  case exception
592
651
  when RuntimeError
593
652
  exception
594
653
  else
595
- ActiveRecord::StatementInvalid.new(message)
654
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
596
655
  end
597
656
  end
598
657
 
@@ -606,6 +665,11 @@ module ActiveRecord
606
665
  raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
607
666
  end
608
667
 
668
+ def column_for_attribute(attribute)
669
+ table_name = attribute.relation.name
670
+ schema_cache.columns_hash(table_name)[attribute.name.to_s]
671
+ end
672
+
609
673
  def collector
610
674
  if prepared_statements
611
675
  Arel::Collectors::Composite.new(
@@ -11,8 +11,6 @@ require "active_record/connection_adapters/mysql/schema_dumper"
11
11
  require "active_record/connection_adapters/mysql/schema_statements"
12
12
  require "active_record/connection_adapters/mysql/type_metadata"
13
13
 
14
- require "active_support/core_ext/string/strip"
15
-
16
14
  module ActiveRecord
17
15
  module ConnectionAdapters
18
16
  class AbstractMysqlAdapter < AbstractAdapter
@@ -45,19 +43,17 @@ module ActiveRecord
45
43
  }
46
44
 
47
45
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
48
- private def dealloc(stmt)
49
- stmt[:stmt].close
50
- end
46
+ private
47
+
48
+ def dealloc(stmt)
49
+ stmt.close
50
+ end
51
51
  end
52
52
 
53
53
  def initialize(connection, logger, connection_options, config)
54
54
  super(connection, logger, config)
55
55
 
56
56
  @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
57
-
58
- if version < "5.1.10"
59
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
60
- end
61
57
  end
62
58
 
63
59
  def version #:nodoc:
@@ -76,6 +72,10 @@ module ActiveRecord
76
72
  !mariadb? && version >= "8.0.1"
77
73
  end
78
74
 
75
+ def supports_expression_index?
76
+ !mariadb? && version >= "8.0.13"
77
+ end
78
+
79
79
  def supports_transaction_isolation?
80
80
  true
81
81
  end
@@ -97,19 +97,11 @@ module ActiveRecord
97
97
  end
98
98
 
99
99
  def supports_datetime_with_precision?
100
- if mariadb?
101
- version >= "5.3.0"
102
- else
103
- version >= "5.6.4"
104
- end
100
+ mariadb? || version >= "5.6.4"
105
101
  end
106
102
 
107
103
  def supports_virtual_columns?
108
- if mariadb?
109
- version >= "5.2.0"
110
- else
111
- version >= "5.7.5"
112
- end
104
+ mariadb? || version >= "5.7.5"
113
105
  end
114
106
 
115
107
  def supports_advisory_locks?
@@ -129,7 +121,7 @@ module ActiveRecord
129
121
  end
130
122
 
131
123
  def index_algorithms
132
- { default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
124
+ { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
133
125
  end
134
126
 
135
127
  # HELPER METHODS ===========================================
@@ -182,6 +174,8 @@ module ActiveRecord
182
174
 
183
175
  # Executes the SQL statement in the context of this connection.
184
176
  def execute(sql, name = nil)
177
+ materialize_transactions
178
+
185
179
  log(sql, name) do
186
180
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
187
181
  @connection.query(sql)
@@ -213,19 +207,7 @@ module ActiveRecord
213
207
  execute "ROLLBACK"
214
208
  end
215
209
 
216
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
217
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
218
- # these, we must use a subquery.
219
- def join_to_update(update, select, key) # :nodoc:
220
- if select.limit || select.offset || select.orders.any?
221
- super
222
- else
223
- update.table select.source
224
- update.wheres = select.constraints
225
- end
226
- end
227
-
228
- def empty_insert_statement_value
210
+ def empty_insert_statement_value(primary_key = nil)
229
211
  "VALUES ()"
230
212
  end
231
213
 
@@ -241,7 +223,7 @@ module ActiveRecord
241
223
  end
242
224
 
243
225
  # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
244
- # Charset defaults to utf8.
226
+ # Charset defaults to utf8mb4.
245
227
  #
246
228
  # Example:
247
229
  # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -250,8 +232,12 @@ module ActiveRecord
250
232
  def create_database(name, options = {})
251
233
  if options[:collation]
252
234
  execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
235
+ elsif options[:charset]
236
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
237
+ elsif row_format_dynamic_by_default?
238
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
253
239
  else
254
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
240
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
255
241
  end
256
242
  end
257
243
 
@@ -284,7 +270,7 @@ module ActiveRecord
284
270
  def table_comment(table_name) # :nodoc:
285
271
  scope = quoted_scope(table_name)
286
272
 
287
- query_value(<<-SQL.strip_heredoc, "SCHEMA").presence
273
+ query_value(<<~SQL, "SCHEMA").presence
288
274
  SELECT table_comment
289
275
  FROM information_schema.tables
290
276
  WHERE table_schema = #{scope[:schema]}
@@ -378,7 +364,7 @@ module ActiveRecord
378
364
 
379
365
  def add_index(table_name, column_name, options = {}) #:nodoc:
380
366
  index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
381
- sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}".dup
367
+ sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
382
368
  execute add_sql_comment!(sql, comment)
383
369
  end
384
370
 
@@ -392,7 +378,7 @@ module ActiveRecord
392
378
 
393
379
  scope = quoted_scope(table_name)
394
380
 
395
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
381
+ fk_info = exec_query(<<~SQL, "SCHEMA")
396
382
  SELECT fk.referenced_table_name AS 'to_table',
397
383
  fk.referenced_column_name AS 'primary_key',
398
384
  fk.column_name AS 'column',
@@ -480,7 +466,7 @@ module ActiveRecord
480
466
 
481
467
  scope = quoted_scope(table_name)
482
468
 
483
- query_values(<<-SQL.strip_heredoc, "SCHEMA")
469
+ query_values(<<~SQL, "SCHEMA")
484
470
  SELECT column_name
485
471
  FROM information_schema.key_column_usage
486
472
  WHERE constraint_name = 'PRIMARY'
@@ -490,9 +476,11 @@ module ActiveRecord
490
476
  SQL
491
477
  end
492
478
 
493
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
479
+ def case_sensitive_comparison(attribute, value) # :nodoc:
480
+ column = column_for_attribute(attribute)
481
+
494
482
  if column.collation && !column.case_sensitive?
495
- table[attribute].eq(Arel::Nodes::Bin.new(value))
483
+ attribute.eq(Arel::Nodes::Bin.new(value))
496
484
  else
497
485
  super
498
486
  end
@@ -533,6 +521,12 @@ module ActiveRecord
533
521
  end
534
522
 
535
523
  private
524
+ def check_version
525
+ if version < "5.5.8"
526
+ raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
527
+ end
528
+ end
529
+
536
530
  def combine_multi_statements(total_sql)
537
531
  total_sql.each_with_object([]) do |sql, total_sql_chunks|
538
532
  previous_packet = total_sql_chunks.last
@@ -587,13 +581,13 @@ module ActiveRecord
587
581
  m.alias_type %r(bit)i, "binary"
588
582
 
589
583
  m.register_type(%r(enum)i) do |sql_type|
590
- limit = sql_type[/^enum\((.+)\)/i, 1]
584
+ limit = sql_type[/^enum\s*\((.+)\)/i, 1]
591
585
  .split(",").map { |enum| enum.strip.length - 2 }.max
592
586
  MysqlString.new(limit: limit)
593
587
  end
594
588
 
595
589
  m.register_type(%r(^set)i) do |sql_type|
596
- limit = sql_type[/^set\((.+)\)/i, 1]
590
+ limit = sql_type[/^set\s*\((.+)\)/i, 1]
597
591
  .split(",").map { |set| set.strip.length - 1 }.sum - 1
598
592
  MysqlString.new(limit: limit)
599
593
  end
@@ -620,7 +614,10 @@ module ActiveRecord
620
614
  # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
621
615
  ER_DUP_ENTRY = 1062
622
616
  ER_NOT_NULL_VIOLATION = 1048
617
+ ER_NO_REFERENCED_ROW = 1216
618
+ ER_ROW_IS_REFERENCED = 1217
623
619
  ER_DO_NOT_HAVE_DEFAULT = 1364
620
+ ER_ROW_IS_REFERENCED_2 = 1451
624
621
  ER_NO_REFERENCED_ROW_2 = 1452
625
622
  ER_DATA_TOO_LONG = 1406
626
623
  ER_OUT_OF_RANGE = 1264
@@ -631,34 +628,34 @@ module ActiveRecord
631
628
  ER_QUERY_INTERRUPTED = 1317
632
629
  ER_QUERY_TIMEOUT = 3024
633
630
 
634
- def translate_exception(exception, message)
631
+ def translate_exception(exception, message:, sql:, binds:)
635
632
  case error_number(exception)
636
633
  when ER_DUP_ENTRY
637
- RecordNotUnique.new(message)
638
- when ER_NO_REFERENCED_ROW_2
639
- InvalidForeignKey.new(message)
634
+ RecordNotUnique.new(message, sql: sql, binds: binds)
635
+ when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
636
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
640
637
  when ER_CANNOT_ADD_FOREIGN
641
- mismatched_foreign_key(message)
638
+ mismatched_foreign_key(message, sql: sql, binds: binds)
642
639
  when ER_CANNOT_CREATE_TABLE
643
640
  if message.include?("errno: 150")
644
- mismatched_foreign_key(message)
641
+ mismatched_foreign_key(message, sql: sql, binds: binds)
645
642
  else
646
643
  super
647
644
  end
648
645
  when ER_DATA_TOO_LONG
649
- ValueTooLong.new(message)
646
+ ValueTooLong.new(message, sql: sql, binds: binds)
650
647
  when ER_OUT_OF_RANGE
651
- RangeError.new(message)
648
+ RangeError.new(message, sql: sql, binds: binds)
652
649
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
653
- NotNullViolation.new(message)
650
+ NotNullViolation.new(message, sql: sql, binds: binds)
654
651
  when ER_LOCK_DEADLOCK
655
- Deadlocked.new(message)
652
+ Deadlocked.new(message, sql: sql, binds: binds)
656
653
  when ER_LOCK_WAIT_TIMEOUT
657
- LockWaitTimeout.new(message)
654
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
658
655
  when ER_QUERY_TIMEOUT
659
- StatementTimeout.new(message)
656
+ StatementTimeout.new(message, sql: sql, binds: binds)
660
657
  when ER_QUERY_INTERRUPTED
661
- QueryCanceled.new(message)
658
+ QueryCanceled.new(message, sql: sql, binds: binds)
662
659
  else
663
660
  super
664
661
  end
@@ -718,20 +715,6 @@ module ActiveRecord
718
715
  [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
719
716
  end
720
717
 
721
- # MySQL is too stupid to create a temporary table for use subquery, so we have
722
- # to give it some prompting in the form of a subsubquery. Ugh!
723
- def subquery_for(key, select)
724
- subselect = select.clone
725
- subselect.projections = [key]
726
-
727
- # Materialize subquery by adding distinct
728
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
729
- subselect.distinct unless select.limit || select.offset || select.orders.any?
730
-
731
- key_name = quote_column_name(key.name)
732
- Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
733
- end
734
-
735
718
  def supports_rename_index?
736
719
  mariadb? ? false : version >= "5.7.6"
737
720
  end
@@ -770,7 +753,7 @@ module ActiveRecord
770
753
  # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
771
754
  # (trailing comma because variable_assignments will always have content)
772
755
  if @config[:encoding]
773
- encoding = "NAMES #{@config[:encoding]}".dup
756
+ encoding = +"NAMES #{@config[:encoding]}"
774
757
  encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
775
758
  encoding << ", "
776
759
  end
@@ -803,26 +786,18 @@ module ActiveRecord
803
786
  Arel::Visitors::MySQL.new(self)
804
787
  end
805
788
 
806
- def mismatched_foreign_key(message)
807
- match = %r/
808
- (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
809
- FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
810
- REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
811
- /xmi.match(message)
812
-
813
- options = {
789
+ def mismatched_foreign_key(message, sql:, binds:)
790
+ parts = sql.scan(/`(\w+)`[ $)]/).flatten
791
+ MismatchedForeignKey.new(
792
+ self,
814
793
  message: message,
815
- }
816
-
817
- if match
818
- options[:table] = match[:table]
819
- options[:foreign_key] = match[:foreign_key]
820
- options[:target_table] = match[:target_table]
821
- options[:primary_key] = match[:primary_key]
822
- options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
823
- end
824
-
825
- MismatchedForeignKey.new(options)
794
+ sql: sql,
795
+ binds: binds,
796
+ table: parts[0],
797
+ foreign_key: parts[1],
798
+ target_table: parts[2],
799
+ primary_key: parts[3],
800
+ )
826
801
  end
827
802
 
828
803
  def integer_to_sql(limit) # :nodoc: