activerecord 5.2.3 → 6.0.0

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

Potentially problematic release.


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

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +624 -548
  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 +5 -15
  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 +18 -25
  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 +27 -7
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +39 -31
  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 +16 -6
  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 +116 -19
  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 +20 -11
  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 +180 -47
  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 +26 -9
  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/money.rb +2 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  83. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  86. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  87. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  88. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  89. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  90. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  91. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -143
  94. data/lib/active_record/connection_handling.rb +149 -27
  95. data/lib/active_record/core.rb +100 -60
  96. data/lib/active_record/counter_cache.rb +4 -29
  97. data/lib/active_record/database_configurations.rb +233 -0
  98. data/lib/active_record/database_configurations/database_config.rb +37 -0
  99. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  100. data/lib/active_record/database_configurations/url_config.rb +79 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/enum.rb +37 -7
  103. data/lib/active_record/errors.rb +15 -7
  104. data/lib/active_record/explain.rb +1 -1
  105. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  106. data/lib/active_record/fixture_set/render_context.rb +17 -0
  107. data/lib/active_record/fixture_set/table_row.rb +153 -0
  108. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  109. data/lib/active_record/fixtures.rb +145 -472
  110. data/lib/active_record/gem_version.rb +3 -3
  111. data/lib/active_record/inheritance.rb +13 -3
  112. data/lib/active_record/insert_all.rb +179 -0
  113. data/lib/active_record/integration.rb +68 -16
  114. data/lib/active_record/internal_metadata.rb +10 -2
  115. data/lib/active_record/locking/optimistic.rb +5 -6
  116. data/lib/active_record/locking/pessimistic.rb +3 -3
  117. data/lib/active_record/log_subscriber.rb +7 -26
  118. data/lib/active_record/middleware/database_selector.rb +75 -0
  119. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  120. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  121. data/lib/active_record/migration.rb +100 -81
  122. data/lib/active_record/migration/command_recorder.rb +50 -6
  123. data/lib/active_record/migration/compatibility.rb +76 -49
  124. data/lib/active_record/model_schema.rb +30 -9
  125. data/lib/active_record/nested_attributes.rb +2 -2
  126. data/lib/active_record/no_touching.rb +7 -0
  127. data/lib/active_record/persistence.rb +228 -24
  128. data/lib/active_record/query_cache.rb +11 -4
  129. data/lib/active_record/querying.rb +32 -20
  130. data/lib/active_record/railtie.rb +80 -43
  131. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  132. data/lib/active_record/railties/controller_runtime.rb +30 -35
  133. data/lib/active_record/railties/databases.rake +196 -46
  134. data/lib/active_record/reflection.rb +42 -44
  135. data/lib/active_record/relation.rb +310 -80
  136. data/lib/active_record/relation/batches.rb +13 -10
  137. data/lib/active_record/relation/calculations.rb +58 -51
  138. data/lib/active_record/relation/delegation.rb +26 -43
  139. data/lib/active_record/relation/finder_methods.rb +14 -27
  140. data/lib/active_record/relation/merger.rb +11 -20
  141. data/lib/active_record/relation/predicate_builder.rb +4 -6
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  147. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  148. data/lib/active_record/relation/query_attribute.rb +13 -8
  149. data/lib/active_record/relation/query_methods.rb +206 -78
  150. data/lib/active_record/relation/spawn_methods.rb +1 -1
  151. data/lib/active_record/relation/where_clause.rb +14 -10
  152. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  153. data/lib/active_record/result.rb +30 -11
  154. data/lib/active_record/sanitization.rb +32 -40
  155. data/lib/active_record/schema.rb +2 -11
  156. data/lib/active_record/schema_dumper.rb +22 -7
  157. data/lib/active_record/schema_migration.rb +5 -1
  158. data/lib/active_record/scoping.rb +8 -8
  159. data/lib/active_record/scoping/default.rb +6 -7
  160. data/lib/active_record/scoping/named.rb +19 -15
  161. data/lib/active_record/statement_cache.rb +32 -5
  162. data/lib/active_record/store.rb +87 -8
  163. data/lib/active_record/table_metadata.rb +10 -17
  164. data/lib/active_record/tasks/database_tasks.rb +194 -25
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  168. data/lib/active_record/test_databases.rb +23 -0
  169. data/lib/active_record/test_fixtures.rb +224 -0
  170. data/lib/active_record/timestamp.rb +39 -25
  171. data/lib/active_record/touch_later.rb +4 -2
  172. data/lib/active_record/transactions.rb +56 -65
  173. data/lib/active_record/translation.rb +1 -1
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  176. data/lib/active_record/type_caster/connection.rb +15 -14
  177. data/lib/active_record/type_caster/map.rb +1 -4
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record/validations/uniqueness.rb +15 -27
  180. data/lib/arel.rb +51 -0
  181. data/lib/arel/alias_predication.rb +9 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/attributes/attribute.rb +37 -0
  184. data/lib/arel/collectors/bind.rb +24 -0
  185. data/lib/arel/collectors/composite.rb +31 -0
  186. data/lib/arel/collectors/plain_string.rb +20 -0
  187. data/lib/arel/collectors/sql_string.rb +20 -0
  188. data/lib/arel/collectors/substitute_binds.rb +28 -0
  189. data/lib/arel/crud.rb +42 -0
  190. data/lib/arel/delete_manager.rb +18 -0
  191. data/lib/arel/errors.rb +9 -0
  192. data/lib/arel/expressions.rb +29 -0
  193. data/lib/arel/factory_methods.rb +49 -0
  194. data/lib/arel/insert_manager.rb +49 -0
  195. data/lib/arel/math.rb +45 -0
  196. data/lib/arel/nodes.rb +68 -0
  197. data/lib/arel/nodes/and.rb +32 -0
  198. data/lib/arel/nodes/ascending.rb +23 -0
  199. data/lib/arel/nodes/binary.rb +52 -0
  200. data/lib/arel/nodes/bind_param.rb +36 -0
  201. data/lib/arel/nodes/case.rb +55 -0
  202. data/lib/arel/nodes/casted.rb +50 -0
  203. data/lib/arel/nodes/comment.rb +29 -0
  204. data/lib/arel/nodes/count.rb +12 -0
  205. data/lib/arel/nodes/delete_statement.rb +45 -0
  206. data/lib/arel/nodes/descending.rb +23 -0
  207. data/lib/arel/nodes/equality.rb +18 -0
  208. data/lib/arel/nodes/extract.rb +24 -0
  209. data/lib/arel/nodes/false.rb +16 -0
  210. data/lib/arel/nodes/full_outer_join.rb +8 -0
  211. data/lib/arel/nodes/function.rb +44 -0
  212. data/lib/arel/nodes/grouping.rb +8 -0
  213. data/lib/arel/nodes/in.rb +8 -0
  214. data/lib/arel/nodes/infix_operation.rb +80 -0
  215. data/lib/arel/nodes/inner_join.rb +8 -0
  216. data/lib/arel/nodes/insert_statement.rb +37 -0
  217. data/lib/arel/nodes/join_source.rb +20 -0
  218. data/lib/arel/nodes/matches.rb +18 -0
  219. data/lib/arel/nodes/named_function.rb +23 -0
  220. data/lib/arel/nodes/node.rb +50 -0
  221. data/lib/arel/nodes/node_expression.rb +13 -0
  222. data/lib/arel/nodes/outer_join.rb +8 -0
  223. data/lib/arel/nodes/over.rb +15 -0
  224. data/lib/arel/nodes/regexp.rb +16 -0
  225. data/lib/arel/nodes/right_outer_join.rb +8 -0
  226. data/lib/arel/nodes/select_core.rb +67 -0
  227. data/lib/arel/nodes/select_statement.rb +41 -0
  228. data/lib/arel/nodes/sql_literal.rb +16 -0
  229. data/lib/arel/nodes/string_join.rb +11 -0
  230. data/lib/arel/nodes/table_alias.rb +27 -0
  231. data/lib/arel/nodes/terminal.rb +16 -0
  232. data/lib/arel/nodes/true.rb +16 -0
  233. data/lib/arel/nodes/unary.rb +45 -0
  234. data/lib/arel/nodes/unary_operation.rb +20 -0
  235. data/lib/arel/nodes/unqualified_column.rb +22 -0
  236. data/lib/arel/nodes/update_statement.rb +41 -0
  237. data/lib/arel/nodes/values_list.rb +9 -0
  238. data/lib/arel/nodes/window.rb +126 -0
  239. data/lib/arel/nodes/with.rb +11 -0
  240. data/lib/arel/order_predications.rb +13 -0
  241. data/lib/arel/predications.rb +257 -0
  242. data/lib/arel/select_manager.rb +271 -0
  243. data/lib/arel/table.rb +110 -0
  244. data/lib/arel/tree_manager.rb +72 -0
  245. data/lib/arel/update_manager.rb +34 -0
  246. data/lib/arel/visitors.rb +20 -0
  247. data/lib/arel/visitors/depth_first.rb +204 -0
  248. data/lib/arel/visitors/dot.rb +297 -0
  249. data/lib/arel/visitors/ibm_db.rb +34 -0
  250. data/lib/arel/visitors/informix.rb +62 -0
  251. data/lib/arel/visitors/mssql.rb +157 -0
  252. data/lib/arel/visitors/mysql.rb +83 -0
  253. data/lib/arel/visitors/oracle.rb +159 -0
  254. data/lib/arel/visitors/oracle12.rb +66 -0
  255. data/lib/arel/visitors/postgresql.rb +110 -0
  256. data/lib/arel/visitors/sqlite.rb +39 -0
  257. data/lib/arel/visitors/to_sql.rb +889 -0
  258. data/lib/arel/visitors/visitor.rb +46 -0
  259. data/lib/arel/visitors/where_sql.rb +23 -0
  260. data/lib/arel/window_predications.rb +9 -0
  261. data/lib/rails/generators/active_record/migration.rb +14 -1
  262. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  263. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  264. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +107 -25
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -6,10 +6,12 @@ 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"
12
13
  require "arel/collectors/substitute_binds"
14
+ require "concurrent/atomic/thread_local_var"
13
15
 
14
16
  module ActiveRecord
15
17
  module ConnectionAdapters # :nodoc:
@@ -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,19 +123,34 @@ 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 })
117
- @prepared_statements = true
133
+ @prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
118
134
  @visitor.extend(DetermineIfPreparableVisitor)
119
135
  else
120
- @prepared_statements = false
136
+ @prepared_statement_status = Concurrent::ThreadLocalVar.new(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,45 @@ 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_statement_status.value
130
181
  end
131
182
 
132
183
  class Version
133
184
  include Comparable
134
185
 
135
- def initialize(version_string)
186
+ attr_reader :full_version_string
187
+
188
+ def initialize(version_string, full_version_string = nil)
136
189
  @version = version_string.split(".").map(&:to_i)
190
+ @full_version_string = full_version_string
137
191
  end
138
192
 
139
193
  def <=>(version_string)
140
194
  @version <=> version_string.split(".").map(&:to_i)
141
195
  end
196
+
197
+ def to_s
198
+ @version.join(".")
199
+ end
142
200
  end
143
201
 
144
202
  def valid_type?(type) # :nodoc:
@@ -148,7 +206,7 @@ module ActiveRecord
148
206
  # this method must only be called while holding connection pool's mutex
149
207
  def lease
150
208
  if in_use?
151
- msg = "Cannot lease connection, ".dup
209
+ msg = +"Cannot lease connection, "
152
210
  if @owner == Thread.current
153
211
  msg << "it is already leased by the current thread."
154
212
  else
@@ -161,9 +219,13 @@ module ActiveRecord
161
219
  @owner = Thread.current
162
220
  end
163
221
 
222
+ def schema_cache
223
+ @pool.get_schema_cache(self)
224
+ end
225
+
164
226
  def schema_cache=(cache)
165
227
  cache.connection = self
166
- @schema_cache = cache
228
+ @pool.set_schema_cache(cache)
167
229
  end
168
230
 
169
231
  # this method must only be called while holding connection pool's mutex
@@ -202,10 +264,7 @@ module ActiveRecord
202
264
  end
203
265
 
204
266
  def unprepared_statement
205
- old_prepared_statements, @prepared_statements = @prepared_statements, false
206
- yield
207
- ensure
208
- @prepared_statements = old_prepared_statements
267
+ @prepared_statement_status.bind(false) { yield }
209
268
  end
210
269
 
211
270
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -214,6 +273,11 @@ module ActiveRecord
214
273
  self.class::ADAPTER_NAME
215
274
  end
216
275
 
276
+ # Does the database for this adapter exist?
277
+ def self.database_exists?(config)
278
+ raise NotImplementedError
279
+ end
280
+
217
281
  # Does this adapter support DDL rollbacks in transactions? That is, would
218
282
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
219
283
  def supports_ddl_transactions?
@@ -292,12 +356,18 @@ module ActiveRecord
292
356
  def supports_foreign_keys_in_create?
293
357
  supports_foreign_keys?
294
358
  end
359
+ deprecate :supports_foreign_keys_in_create?
295
360
 
296
361
  # Does this adapter support views?
297
362
  def supports_views?
298
363
  false
299
364
  end
300
365
 
366
+ # Does this adapter support materialized views?
367
+ def supports_materialized_views?
368
+ false
369
+ end
370
+
301
371
  # Does this adapter support datetime with precision?
302
372
  def supports_datetime_with_precision?
303
373
  false
@@ -322,6 +392,7 @@ module ActiveRecord
322
392
  def supports_multi_insert?
323
393
  true
324
394
  end
395
+ deprecate :supports_multi_insert?
325
396
 
326
397
  # Does this adapter support virtual columns?
327
398
  def supports_virtual_columns?
@@ -333,6 +404,31 @@ module ActiveRecord
333
404
  false
334
405
  end
335
406
 
407
+ # Does this adapter support optimizer hints?
408
+ def supports_optimizer_hints?
409
+ false
410
+ end
411
+
412
+ def supports_lazy_transactions?
413
+ false
414
+ end
415
+
416
+ def supports_insert_returning?
417
+ false
418
+ end
419
+
420
+ def supports_insert_on_duplicate_skip?
421
+ false
422
+ end
423
+
424
+ def supports_insert_on_duplicate_update?
425
+ false
426
+ end
427
+
428
+ def supports_insert_conflict_target?
429
+ false
430
+ end
431
+
336
432
  # This is meant to be implemented by the adapters that support extensions
337
433
  def disable_extension(name)
338
434
  end
@@ -341,6 +437,10 @@ module ActiveRecord
341
437
  def enable_extension(name)
342
438
  end
343
439
 
440
+ def advisory_locks_enabled? # :nodoc:
441
+ supports_advisory_locks? && @advisory_locks_enabled
442
+ end
443
+
344
444
  # This is meant to be implemented by the adapters that support advisory
345
445
  # locks
346
446
  #
@@ -406,6 +506,9 @@ module ActiveRecord
406
506
  #
407
507
  # Prevent @connection's finalizer from touching the socket, or
408
508
  # otherwise communicating with its server, when it is collected.
509
+ if schema_cache.connection == self
510
+ schema_cache.connection = nil
511
+ end
409
512
  end
410
513
 
411
514
  # Reset the state of this connection, directing the DBMS to clear
@@ -418,11 +521,9 @@ module ActiveRecord
418
521
  # this should be overridden by concrete adapters
419
522
  end
420
523
 
421
- ###
422
- # Clear any caching the database adapter may be doing, for example
423
- # clearing the prepared statement cache. This is database specific.
524
+ # Clear any caching the database adapter may be doing.
424
525
  def clear_cache!
425
- # this should be overridden by concrete adapters
526
+ @lock.synchronize { @statements.clear } if @statements
426
527
  end
427
528
 
428
529
  # Returns true if its required to reload the connection between requests for development mode.
@@ -444,18 +545,25 @@ module ActiveRecord
444
545
  # This is useful for when you need to call a proprietary method such as
445
546
  # PostgreSQL's lo_* methods.
446
547
  def raw_connection
548
+ disable_lazy_transactions!
447
549
  @connection
448
550
  end
449
551
 
450
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
- table[attribute].eq(value)
552
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
553
+ attribute.eq(value)
554
+ end
555
+
556
+ def case_sensitive_comparison(attribute, value) # :nodoc:
557
+ attribute.eq(value)
452
558
  end
453
559
 
454
- def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
560
+ def case_insensitive_comparison(attribute, value) # :nodoc:
561
+ column = column_for_attribute(attribute)
562
+
455
563
  if can_perform_case_insensitive_comparison_for?(column)
456
- table[attribute].lower.eq(table.lower(value))
564
+ attribute.lower.eq(attribute.relation.lower(value))
457
565
  else
458
- table[attribute].eq(value)
566
+ attribute.eq(value)
459
567
  end
460
568
  end
461
569
 
@@ -470,18 +578,38 @@ module ActiveRecord
470
578
  end
471
579
 
472
580
  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
581
+ visitor.compile(node)
478
582
  end
479
583
 
480
584
  def default_index_type?(index) # :nodoc:
481
585
  index.using.nil?
482
586
  end
483
587
 
588
+ # Called by ActiveRecord::InsertAll,
589
+ # Passed an instance of ActiveRecord::InsertAll::Builder,
590
+ # This method implements standard bulk inserts for all databases, but
591
+ # should be overridden by adapters to implement common features with
592
+ # non-standard syntax like handling duplicates or returning values.
593
+ def build_insert_sql(insert) # :nodoc:
594
+ if insert.skip_duplicates? || insert.update_duplicates?
595
+ raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
596
+ end
597
+
598
+ "INSERT #{insert.into} #{insert.values_list}"
599
+ end
600
+
601
+ def get_database_version # :nodoc:
602
+ end
603
+
604
+ def database_version # :nodoc:
605
+ schema_cache.database_version
606
+ end
607
+
608
+ def check_version # :nodoc:
609
+ end
610
+
484
611
  private
612
+
485
613
  def type_map
486
614
  @type_map ||= Type::TypeMap.new.tap do |mapping|
487
615
  initialize_type_map(mapping)
@@ -555,14 +683,12 @@ module ActiveRecord
555
683
  $1.to_i if sql_type =~ /\((.*)\)/
556
684
  end
557
685
 
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
686
+ def translate_exception_class(e, sql, binds)
687
+ message = "#{e.class.name}: #{e.message}"
564
688
 
565
- exception = translate_exception(e, message)
689
+ exception = translate_exception(
690
+ e, message: message, sql: sql, binds: binds
691
+ )
566
692
  exception.set_backtrace e.backtrace
567
693
  exception
568
694
  end
@@ -575,24 +701,23 @@ module ActiveRecord
575
701
  binds: binds,
576
702
  type_casted_binds: type_casted_binds,
577
703
  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)
704
+ connection_id: object_id,
705
+ connection: self) do
706
+ @lock.synchronize do
707
+ yield
585
708
  end
709
+ rescue => e
710
+ raise translate_exception_class(e, sql, binds)
586
711
  end
587
712
  end
588
713
 
589
- def translate_exception(exception, message)
714
+ def translate_exception(exception, message:, sql:, binds:)
590
715
  # override in derived class
591
716
  case exception
592
717
  when RuntimeError
593
718
  exception
594
719
  else
595
- ActiveRecord::StatementInvalid.new(message)
720
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
596
721
  end
597
722
  end
598
723
 
@@ -606,6 +731,11 @@ module ActiveRecord
606
731
  raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
607
732
  end
608
733
 
734
+ def column_for_attribute(attribute)
735
+ table_name = attribute.relation.name
736
+ schema_cache.columns_hash(table_name)[attribute.name.to_s]
737
+ end
738
+
609
739
  def collector
610
740
  if prepared_statements
611
741
  Arel::Collectors::Composite.new(
@@ -623,6 +753,9 @@ module ActiveRecord
623
753
  def arel_visitor
624
754
  Arel::Visitors::ToSql.new(self)
625
755
  end
756
+
757
+ def build_statement_pool
758
+ end
626
759
  end
627
760
  end
628
761
  end
@@ -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
@@ -31,7 +29,7 @@ module ActiveRecord
31
29
  NATIVE_DATABASE_TYPES = {
32
30
  primary_key: "bigint auto_increment PRIMARY KEY",
33
31
  string: { name: "varchar", limit: 255 },
34
- text: { name: "text", limit: 65535 },
32
+ text: { name: "text" },
35
33
  integer: { name: "int", limit: 4 },
36
34
  float: { name: "float", limit: 24 },
37
35
  decimal: { name: "decimal" },
@@ -39,41 +37,44 @@ module ActiveRecord
39
37
  timestamp: { name: "timestamp" },
40
38
  time: { name: "time" },
41
39
  date: { name: "date" },
42
- binary: { name: "blob", limit: 65535 },
40
+ binary: { name: "blob" },
41
+ blob: { name: "blob" },
43
42
  boolean: { name: "tinyint", limit: 1 },
44
43
  json: { name: "json" },
45
44
  }
46
45
 
47
46
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
48
- private def dealloc(stmt)
49
- stmt[:stmt].close
50
- end
47
+ private
48
+
49
+ def dealloc(stmt)
50
+ stmt.close
51
+ end
51
52
  end
52
53
 
53
54
  def initialize(connection, logger, connection_options, config)
54
55
  super(connection, logger, config)
55
-
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
56
  end
62
57
 
63
- def version #:nodoc:
64
- @version ||= Version.new(version_string)
58
+ def get_database_version #:nodoc:
59
+ full_version_string = get_full_version
60
+ version_string = version_string(full_version_string)
61
+ Version.new(version_string, full_version_string)
65
62
  end
66
63
 
67
64
  def mariadb? # :nodoc:
68
65
  /mariadb/i.match?(full_version)
69
66
  end
70
67
 
71
- def supports_bulk_alter? #:nodoc:
68
+ def supports_bulk_alter?
72
69
  true
73
70
  end
74
71
 
75
72
  def supports_index_sort_order?
76
- !mariadb? && version >= "8.0.1"
73
+ !mariadb? && database_version >= "8.0.1"
74
+ end
75
+
76
+ def supports_expression_index?
77
+ !mariadb? && database_version >= "8.0.13"
77
78
  end
78
79
 
79
80
  def supports_transaction_isolation?
@@ -97,25 +98,30 @@ module ActiveRecord
97
98
  end
98
99
 
99
100
  def supports_datetime_with_precision?
100
- if mariadb?
101
- version >= "5.3.0"
102
- else
103
- version >= "5.6.4"
104
- end
101
+ mariadb? || database_version >= "5.6.4"
105
102
  end
106
103
 
107
104
  def supports_virtual_columns?
108
- if mariadb?
109
- version >= "5.2.0"
110
- else
111
- version >= "5.7.5"
112
- end
105
+ mariadb? || database_version >= "5.7.5"
106
+ end
107
+
108
+ # See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
109
+ def supports_optimizer_hints?
110
+ !mariadb? && database_version >= "5.7.7"
113
111
  end
114
112
 
115
113
  def supports_advisory_locks?
116
114
  true
117
115
  end
118
116
 
117
+ def supports_insert_on_duplicate_skip?
118
+ true
119
+ end
120
+
121
+ def supports_insert_on_duplicate_update?
122
+ true
123
+ end
124
+
119
125
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
120
126
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
121
127
  end
@@ -129,7 +135,7 @@ module ActiveRecord
129
135
  end
130
136
 
131
137
  def index_algorithms
132
- { default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
138
+ { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
133
139
  end
134
140
 
135
141
  # HELPER METHODS ===========================================
@@ -161,10 +167,9 @@ module ActiveRecord
161
167
 
162
168
  # CONNECTION MANAGEMENT ====================================
163
169
 
164
- # Clears the prepared statements cache.
165
- def clear_cache!
170
+ def clear_cache! # :nodoc:
166
171
  reload_type_map
167
- @statements.clear
172
+ super
168
173
  end
169
174
 
170
175
  #--
@@ -173,15 +178,17 @@ module ActiveRecord
173
178
 
174
179
  def explain(arel, binds = [])
175
180
  sql = "EXPLAIN #{to_sql(arel, binds)}"
176
- start = Time.now
181
+ start = Concurrent.monotonic_time
177
182
  result = exec_query(sql, "EXPLAIN", binds)
178
- elapsed = Time.now - start
183
+ elapsed = Concurrent.monotonic_time - start
179
184
 
180
185
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
181
186
  end
182
187
 
183
188
  # Executes the SQL statement in the context of this connection.
184
189
  def execute(sql, name = nil)
190
+ materialize_transactions
191
+
185
192
  log(sql, name) do
186
193
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
187
194
  @connection.query(sql)
@@ -213,19 +220,7 @@ module ActiveRecord
213
220
  execute "ROLLBACK"
214
221
  end
215
222
 
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
223
+ def empty_insert_statement_value(primary_key = nil)
229
224
  "VALUES ()"
230
225
  end
231
226
 
@@ -241,7 +236,7 @@ module ActiveRecord
241
236
  end
242
237
 
243
238
  # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
244
- # Charset defaults to utf8.
239
+ # Charset defaults to utf8mb4.
245
240
  #
246
241
  # Example:
247
242
  # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -250,8 +245,12 @@ module ActiveRecord
250
245
  def create_database(name, options = {})
251
246
  if options[:collation]
252
247
  execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
248
+ elsif options[:charset]
249
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
250
+ elsif row_format_dynamic_by_default?
251
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
253
252
  else
254
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
253
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
255
254
  end
256
255
  end
257
256
 
@@ -277,14 +276,10 @@ module ActiveRecord
277
276
  show_variable "collation_database"
278
277
  end
279
278
 
280
- def truncate(table_name, name = nil)
281
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
282
- end
283
-
284
279
  def table_comment(table_name) # :nodoc:
285
280
  scope = quoted_scope(table_name)
286
281
 
287
- query_value(<<-SQL.strip_heredoc, "SCHEMA").presence
282
+ query_value(<<~SQL, "SCHEMA").presence
288
283
  SELECT table_comment
289
284
  FROM information_schema.tables
290
285
  WHERE table_schema = #{scope[:schema]}
@@ -292,22 +287,8 @@ module ActiveRecord
292
287
  SQL
293
288
  end
294
289
 
295
- def bulk_change_table(table_name, operations) #:nodoc:
296
- sqls = operations.flat_map do |command, args|
297
- table, arguments = args.shift, args
298
- method = :"#{command}_for_alter"
299
-
300
- if respond_to?(method, true)
301
- send(method, table, *arguments)
302
- else
303
- raise "Unknown method called : #{method}(#{arguments.inspect})"
304
- end
305
- end.join(", ")
306
-
307
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
308
- end
309
-
310
- def change_table_comment(table_name, comment) #:nodoc:
290
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
291
+ comment = extract_new_comment_value(comment_or_changes)
311
292
  comment = "" if comment.nil?
312
293
  execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
313
294
  end
@@ -363,7 +344,8 @@ module ActiveRecord
363
344
  change_column table_name, column_name, nil, null: null
364
345
  end
365
346
 
366
- def change_column_comment(table_name, column_name, comment) #:nodoc:
347
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
348
+ comment = extract_new_comment_value(comment_or_changes)
367
349
  change_column table_name, column_name, nil, comment: comment
368
350
  end
369
351
 
@@ -378,7 +360,7 @@ module ActiveRecord
378
360
 
379
361
  def add_index(table_name, column_name, options = {}) #:nodoc:
380
362
  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
363
+ sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
382
364
  execute add_sql_comment!(sql, comment)
383
365
  end
384
366
 
@@ -392,7 +374,7 @@ module ActiveRecord
392
374
 
393
375
  scope = quoted_scope(table_name)
394
376
 
395
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
377
+ fk_info = exec_query(<<~SQL, "SCHEMA")
396
378
  SELECT fk.referenced_table_name AS 'to_table',
397
379
  fk.referenced_column_name AS 'primary_key',
398
380
  fk.column_name AS 'column',
@@ -444,30 +426,6 @@ module ActiveRecord
444
426
  table_options
445
427
  end
446
428
 
447
- # Maps logical Rails types to MySQL-specific data types.
448
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
449
- sql = \
450
- case type.to_s
451
- when "integer"
452
- integer_to_sql(limit)
453
- when "text"
454
- text_to_sql(limit)
455
- when "blob"
456
- binary_to_sql(limit)
457
- when "binary"
458
- if (0..0xfff) === limit
459
- "varbinary(#{limit})"
460
- else
461
- binary_to_sql(limit)
462
- end
463
- else
464
- super
465
- end
466
-
467
- sql = "#{sql} unsigned" if unsigned && type != :primary_key
468
- sql
469
- end
470
-
471
429
  # SHOW VARIABLES LIKE 'name'
472
430
  def show_variable(name)
473
431
  query_value("SELECT @@#{name}", "SCHEMA")
@@ -480,7 +438,7 @@ module ActiveRecord
480
438
 
481
439
  scope = quoted_scope(table_name)
482
440
 
483
- query_values(<<-SQL.strip_heredoc, "SCHEMA")
441
+ query_values(<<~SQL, "SCHEMA")
484
442
  SELECT column_name
485
443
  FROM information_schema.key_column_usage
486
444
  WHERE constraint_name = 'PRIMARY'
@@ -490,9 +448,26 @@ module ActiveRecord
490
448
  SQL
491
449
  end
492
450
 
493
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
452
+ column = column_for_attribute(attribute)
453
+
454
+ if column.collation && !column.case_sensitive? && !value.nil?
455
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
456
+ Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
457
+ To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
458
+ pass `case_sensitive: true` option explicitly to the uniqueness validator.
459
+ MSG
460
+ attribute.eq(Arel::Nodes::Bin.new(value))
461
+ else
462
+ super
463
+ end
464
+ end
465
+
466
+ def case_sensitive_comparison(attribute, value) # :nodoc:
467
+ column = column_for_attribute(attribute)
468
+
494
469
  if column.collation && !column.case_sensitive?
495
- table[attribute].eq(Arel::Nodes::Bin.new(value))
470
+ attribute.eq(Arel::Nodes::Bin.new(value))
496
471
  else
497
472
  super
498
473
  end
@@ -526,39 +501,27 @@ module ActiveRecord
526
501
  index.using == :btree || super
527
502
  end
528
503
 
529
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
530
- with_multi_statements do
531
- super { discard_remaining_results }
532
- end
533
- end
504
+ def build_insert_sql(insert) # :nodoc:
505
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
534
506
 
535
- private
536
- def combine_multi_statements(total_sql)
537
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
538
- previous_packet = total_sql_chunks.last
539
- sql << ";\n"
540
- if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
541
- total_sql_chunks << sql
542
- else
543
- previous_packet << sql
544
- end
545
- end
507
+ if insert.skip_duplicates?
508
+ no_op_column = quote_column_name(insert.keys.first)
509
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
510
+ elsif insert.update_duplicates?
511
+ sql << " ON DUPLICATE KEY UPDATE "
512
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
546
513
  end
547
514
 
548
- def max_allowed_packet_reached?(current_packet, previous_packet)
549
- if current_packet.bytesize > max_allowed_packet
550
- raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
551
- elsif previous_packet.nil?
552
- false
553
- else
554
- (current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
555
- end
556
- end
515
+ sql
516
+ end
557
517
 
558
- def max_allowed_packet
559
- bytes_margin = 2
560
- @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
518
+ def check_version # :nodoc:
519
+ if database_version < "5.5.8"
520
+ raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
561
521
  end
522
+ end
523
+
524
+ private
562
525
 
563
526
  def initialize_type_map(m = type_map)
564
527
  super
@@ -587,13 +550,13 @@ module ActiveRecord
587
550
  m.alias_type %r(bit)i, "binary"
588
551
 
589
552
  m.register_type(%r(enum)i) do |sql_type|
590
- limit = sql_type[/^enum\((.+)\)/i, 1]
553
+ limit = sql_type[/^enum\s*\((.+)\)/i, 1]
591
554
  .split(",").map { |enum| enum.strip.length - 2 }.max
592
555
  MysqlString.new(limit: limit)
593
556
  end
594
557
 
595
558
  m.register_type(%r(^set)i) do |sql_type|
596
- limit = sql_type[/^set\((.+)\)/i, 1]
559
+ limit = sql_type[/^set\s*\((.+)\)/i, 1]
597
560
  .split(",").map { |set| set.strip.length - 1 }.sum - 1
598
561
  MysqlString.new(limit: limit)
599
562
  end
@@ -620,7 +583,10 @@ module ActiveRecord
620
583
  # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
621
584
  ER_DUP_ENTRY = 1062
622
585
  ER_NOT_NULL_VIOLATION = 1048
586
+ ER_NO_REFERENCED_ROW = 1216
587
+ ER_ROW_IS_REFERENCED = 1217
623
588
  ER_DO_NOT_HAVE_DEFAULT = 1364
589
+ ER_ROW_IS_REFERENCED_2 = 1451
624
590
  ER_NO_REFERENCED_ROW_2 = 1452
625
591
  ER_DATA_TOO_LONG = 1406
626
592
  ER_OUT_OF_RANGE = 1264
@@ -630,35 +596,36 @@ module ActiveRecord
630
596
  ER_LOCK_WAIT_TIMEOUT = 1205
631
597
  ER_QUERY_INTERRUPTED = 1317
632
598
  ER_QUERY_TIMEOUT = 3024
599
+ ER_FK_INCOMPATIBLE_COLUMNS = 3780
633
600
 
634
- def translate_exception(exception, message)
601
+ def translate_exception(exception, message:, sql:, binds:)
635
602
  case error_number(exception)
636
603
  when ER_DUP_ENTRY
637
- RecordNotUnique.new(message)
638
- when ER_NO_REFERENCED_ROW_2
639
- InvalidForeignKey.new(message)
640
- when ER_CANNOT_ADD_FOREIGN
641
- mismatched_foreign_key(message)
604
+ RecordNotUnique.new(message, sql: sql, binds: binds)
605
+ when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
606
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
607
+ when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
608
+ mismatched_foreign_key(message, sql: sql, binds: binds)
642
609
  when ER_CANNOT_CREATE_TABLE
643
610
  if message.include?("errno: 150")
644
- mismatched_foreign_key(message)
611
+ mismatched_foreign_key(message, sql: sql, binds: binds)
645
612
  else
646
613
  super
647
614
  end
648
615
  when ER_DATA_TOO_LONG
649
- ValueTooLong.new(message)
616
+ ValueTooLong.new(message, sql: sql, binds: binds)
650
617
  when ER_OUT_OF_RANGE
651
- RangeError.new(message)
618
+ RangeError.new(message, sql: sql, binds: binds)
652
619
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
653
- NotNullViolation.new(message)
620
+ NotNullViolation.new(message, sql: sql, binds: binds)
654
621
  when ER_LOCK_DEADLOCK
655
- Deadlocked.new(message)
622
+ Deadlocked.new(message, sql: sql, binds: binds)
656
623
  when ER_LOCK_WAIT_TIMEOUT
657
- LockWaitTimeout.new(message)
624
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
658
625
  when ER_QUERY_TIMEOUT
659
- StatementTimeout.new(message)
626
+ StatementTimeout.new(message, sql: sql, binds: binds)
660
627
  when ER_QUERY_INTERRUPTED
661
- QueryCanceled.new(message)
628
+ QueryCanceled.new(message, sql: sql, binds: binds)
662
629
  else
663
630
  super
664
631
  end
@@ -711,6 +678,12 @@ module ActiveRecord
711
678
  end
712
679
 
713
680
  def add_timestamps_for_alter(table_name, options = {})
681
+ options[:null] = false if options[:null].nil?
682
+
683
+ if !options.key?(:precision) && supports_datetime_with_precision?
684
+ options[:precision] = 6
685
+ end
686
+
714
687
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
715
688
  end
716
689
 
@@ -718,22 +691,8 @@ module ActiveRecord
718
691
  [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
719
692
  end
720
693
 
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
694
  def supports_rename_index?
736
- mariadb? ? false : version >= "5.7.6"
695
+ mariadb? ? false : database_version >= "5.7.6"
737
696
  end
738
697
 
739
698
  def configure_connection
@@ -770,7 +729,7 @@ module ActiveRecord
770
729
  # https://dev.mysql.com/doc/refman/5.7/en/set-names.html
771
730
  # (trailing comma because variable_assignments will always have content)
772
731
  if @config[:encoding]
773
- encoding = "NAMES #{@config[:encoding]}".dup
732
+ encoding = +"NAMES #{@config[:encoding]}"
774
733
  encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
775
734
  encoding << ", "
776
735
  end
@@ -803,15 +762,21 @@ module ActiveRecord
803
762
  Arel::Visitors::MySQL.new(self)
804
763
  end
805
764
 
806
- def mismatched_foreign_key(message)
765
+ def build_statement_pool
766
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
767
+ end
768
+
769
+ def mismatched_foreign_key(message, sql:, binds:)
807
770
  match = %r/
808
771
  (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
809
772
  FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
810
773
  REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
811
- /xmi.match(message)
774
+ /xmi.match(sql)
812
775
 
813
776
  options = {
814
777
  message: message,
778
+ sql: sql,
779
+ binds: binds,
815
780
  }
816
781
 
817
782
  if match
@@ -825,39 +790,8 @@ module ActiveRecord
825
790
  MismatchedForeignKey.new(options)
826
791
  end
827
792
 
828
- def integer_to_sql(limit) # :nodoc:
829
- case limit
830
- when 1; "tinyint"
831
- when 2; "smallint"
832
- when 3; "mediumint"
833
- when nil, 4; "int"
834
- when 5..8; "bigint"
835
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
836
- end
837
- end
838
-
839
- def text_to_sql(limit) # :nodoc:
840
- case limit
841
- when 0..0xff; "tinytext"
842
- when nil, 0x100..0xffff; "text"
843
- when 0x10000..0xffffff; "mediumtext"
844
- when 0x1000000..0xffffffff; "longtext"
845
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
846
- end
847
- end
848
-
849
- def binary_to_sql(limit) # :nodoc:
850
- case limit
851
- when 0..0xff; "tinyblob"
852
- when nil, 0x100..0xffff; "blob"
853
- when 0x10000..0xffffff; "mediumblob"
854
- when 0x1000000..0xffffffff; "longblob"
855
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
856
- end
857
- end
858
-
859
- def version_string
860
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
793
+ def version_string(full_version_string)
794
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
861
795
  end
862
796
 
863
797
  class MysqlString < Type::String # :nodoc: