activerecord 6.0.0 → 6.1.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1045 -575
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +5 -6
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations.rb +120 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +33 -9
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -77
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  95. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  97. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  99. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  100. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  103. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  104. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  106. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  107. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  108. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  109. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  110. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  111. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  112. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  113. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  114. data/lib/active_record/connection_handling.rb +219 -81
  115. data/lib/active_record/core.rb +253 -67
  116. data/lib/active_record/counter_cache.rb +4 -1
  117. data/lib/active_record/database_configurations.rb +124 -85
  118. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  119. data/lib/active_record/database_configurations/database_config.rb +52 -9
  120. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  121. data/lib/active_record/database_configurations/url_config.rb +15 -41
  122. data/lib/active_record/delegated_type.rb +209 -0
  123. data/lib/active_record/destroy_association_async_job.rb +36 -0
  124. data/lib/active_record/dynamic_matchers.rb +2 -3
  125. data/lib/active_record/enum.rb +82 -38
  126. data/lib/active_record/errors.rb +47 -12
  127. data/lib/active_record/explain.rb +9 -5
  128. data/lib/active_record/explain_subscriber.rb +1 -1
  129. data/lib/active_record/fixture_set/file.rb +10 -17
  130. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  131. data/lib/active_record/fixture_set/render_context.rb +1 -1
  132. data/lib/active_record/fixture_set/table_row.rb +2 -3
  133. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  134. data/lib/active_record/fixtures.rb +58 -12
  135. data/lib/active_record/gem_version.rb +2 -2
  136. data/lib/active_record/inheritance.rb +40 -21
  137. data/lib/active_record/insert_all.rb +39 -10
  138. data/lib/active_record/integration.rb +3 -5
  139. data/lib/active_record/internal_metadata.rb +16 -7
  140. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  141. data/lib/active_record/locking/optimistic.rb +33 -18
  142. data/lib/active_record/locking/pessimistic.rb +6 -2
  143. data/lib/active_record/log_subscriber.rb +28 -9
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  147. data/lib/active_record/migration.rb +115 -85
  148. data/lib/active_record/migration/command_recorder.rb +53 -45
  149. data/lib/active_record/migration/compatibility.rb +71 -20
  150. data/lib/active_record/migration/join_table.rb +0 -1
  151. data/lib/active_record/model_schema.rb +120 -15
  152. data/lib/active_record/nested_attributes.rb +2 -5
  153. data/lib/active_record/no_touching.rb +1 -1
  154. data/lib/active_record/null_relation.rb +0 -1
  155. data/lib/active_record/persistence.rb +50 -46
  156. data/lib/active_record/query_cache.rb +15 -5
  157. data/lib/active_record/querying.rb +12 -7
  158. data/lib/active_record/railtie.rb +65 -45
  159. data/lib/active_record/railties/console_sandbox.rb +2 -4
  160. data/lib/active_record/railties/databases.rake +277 -97
  161. data/lib/active_record/readonly_attributes.rb +4 -0
  162. data/lib/active_record/reflection.rb +77 -63
  163. data/lib/active_record/relation.rb +107 -67
  164. data/lib/active_record/relation/batches.rb +38 -32
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  166. data/lib/active_record/relation/calculations.rb +102 -45
  167. data/lib/active_record/relation/delegation.rb +9 -7
  168. data/lib/active_record/relation/finder_methods.rb +55 -17
  169. data/lib/active_record/relation/from_clause.rb +5 -1
  170. data/lib/active_record/relation/merger.rb +27 -26
  171. data/lib/active_record/relation/predicate_builder.rb +59 -40
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  175. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  176. data/lib/active_record/relation/query_methods.rb +343 -180
  177. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  178. data/lib/active_record/relation/spawn_methods.rb +8 -8
  179. data/lib/active_record/relation/where_clause.rb +107 -61
  180. data/lib/active_record/result.rb +41 -34
  181. data/lib/active_record/runtime_registry.rb +2 -2
  182. data/lib/active_record/sanitization.rb +6 -17
  183. data/lib/active_record/schema_dumper.rb +34 -4
  184. data/lib/active_record/schema_migration.rb +2 -8
  185. data/lib/active_record/scoping.rb +0 -1
  186. data/lib/active_record/scoping/default.rb +0 -1
  187. data/lib/active_record/scoping/named.rb +7 -18
  188. data/lib/active_record/secure_token.rb +16 -8
  189. data/lib/active_record/serialization.rb +5 -3
  190. data/lib/active_record/signed_id.rb +116 -0
  191. data/lib/active_record/statement_cache.rb +20 -4
  192. data/lib/active_record/store.rb +3 -3
  193. data/lib/active_record/suppressor.rb +2 -2
  194. data/lib/active_record/table_metadata.rb +42 -36
  195. data/lib/active_record/tasks/database_tasks.rb +140 -113
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  199. data/lib/active_record/test_databases.rb +5 -4
  200. data/lib/active_record/test_fixtures.rb +38 -16
  201. data/lib/active_record/timestamp.rb +4 -7
  202. data/lib/active_record/touch_later.rb +20 -21
  203. data/lib/active_record/transactions.rb +26 -73
  204. data/lib/active_record/type.rb +8 -2
  205. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  206. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  207. data/lib/active_record/type/serialized.rb +6 -3
  208. data/lib/active_record/type/time.rb +10 -0
  209. data/lib/active_record/type/type_map.rb +0 -1
  210. data/lib/active_record/type/unsigned_integer.rb +0 -1
  211. data/lib/active_record/type_caster/connection.rb +0 -1
  212. data/lib/active_record/type_caster/map.rb +8 -5
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record/validations/associated.rb +1 -2
  215. data/lib/active_record/validations/numericality.rb +35 -0
  216. data/lib/active_record/validations/uniqueness.rb +24 -4
  217. data/lib/arel.rb +15 -12
  218. data/lib/arel/attributes/attribute.rb +4 -0
  219. data/lib/arel/collectors/bind.rb +5 -0
  220. data/lib/arel/collectors/composite.rb +8 -0
  221. data/lib/arel/collectors/sql_string.rb +7 -0
  222. data/lib/arel/collectors/substitute_binds.rb +7 -0
  223. data/lib/arel/nodes.rb +3 -1
  224. data/lib/arel/nodes/binary.rb +82 -8
  225. data/lib/arel/nodes/bind_param.rb +8 -0
  226. data/lib/arel/nodes/casted.rb +21 -9
  227. data/lib/arel/nodes/equality.rb +6 -9
  228. data/lib/arel/nodes/grouping.rb +3 -0
  229. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  230. data/lib/arel/nodes/in.rb +8 -1
  231. data/lib/arel/nodes/infix_operation.rb +13 -1
  232. data/lib/arel/nodes/join_source.rb +1 -1
  233. data/lib/arel/nodes/node.rb +7 -6
  234. data/lib/arel/nodes/ordering.rb +27 -0
  235. data/lib/arel/nodes/sql_literal.rb +3 -0
  236. data/lib/arel/nodes/table_alias.rb +7 -3
  237. data/lib/arel/nodes/unary.rb +0 -1
  238. data/lib/arel/predications.rb +17 -24
  239. data/lib/arel/select_manager.rb +1 -2
  240. data/lib/arel/table.rb +13 -5
  241. data/lib/arel/visitors.rb +0 -7
  242. data/lib/arel/visitors/dot.rb +14 -3
  243. data/lib/arel/visitors/mysql.rb +11 -1
  244. data/lib/arel/visitors/postgresql.rb +15 -5
  245. data/lib/arel/visitors/sqlite.rb +0 -1
  246. data/lib/arel/visitors/to_sql.rb +89 -79
  247. data/lib/arel/visitors/visitor.rb +0 -1
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration.rb +6 -2
  250. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  251. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  252. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -5,6 +5,8 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  # An abstract definition of a column in a table.
7
7
  class Column
8
+ include Deduplicable
9
+
8
10
  attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
9
11
 
10
12
  delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@@ -76,6 +78,7 @@ module ActiveRecord
76
78
  def hash
77
79
  Column.hash ^
78
80
  name.hash ^
81
+ name.encoding.hash ^
79
82
  default.hash ^
80
83
  sql_type_metadata.hash ^
81
84
  null.hash ^
@@ -83,10 +86,21 @@ module ActiveRecord
83
86
  collation.hash ^
84
87
  comment.hash
85
88
  end
89
+
90
+ private
91
+ def deduplicated
92
+ @name = -name
93
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
94
+ @default = -default if default
95
+ @default_function = -default_function if default_function
96
+ @collation = -collation if collation
97
+ @comment = -comment if comment
98
+ super
99
+ end
86
100
  end
87
101
 
88
102
  class NullColumn < Column
89
- def initialize(name)
103
+ def initialize(name, **)
90
104
  super(name, nil)
91
105
  end
92
106
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Deduplicable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def registry
10
+ @registry ||= {}
11
+ end
12
+
13
+ def new(*, **)
14
+ super.deduplicate
15
+ end
16
+ end
17
+
18
+ def deduplicate
19
+ self.class.registry[self] ||= deduplicated
20
+ end
21
+ alias :-@ :deduplicate
22
+
23
+ private
24
+ def deduplicated
25
+ freeze
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class LegacyPoolManager # :nodoc:
6
+ def initialize
7
+ @name_to_pool_config = {}
8
+ end
9
+
10
+ def shard_names
11
+ @name_to_pool_config.keys
12
+ end
13
+
14
+ def pool_configs(_ = nil)
15
+ @name_to_pool_config.values
16
+ end
17
+
18
+ def remove_pool_config(_, shard)
19
+ @name_to_pool_config.delete(shard)
20
+ end
21
+
22
+ def get_pool_config(_, shard)
23
+ @name_to_pool_config[shard]
24
+ end
25
+
26
+ def set_pool_config(_, shard, pool_config)
27
+ @name_to_pool_config[shard] = pool_config
28
+ end
29
+ end
30
+ end
31
+ end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  end
12
12
 
13
13
  def case_sensitive?
14
- collation && !/_ci\z/.match?(collation)
14
+ collation && !collation.end_with?("_ci")
15
15
  end
16
16
 
17
17
  def auto_increment?
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module MySQL
6
6
  module DatabaseStatements
7
7
  # Returns an ActiveRecord::Result instance.
8
- def select_all(*) # :nodoc:
8
+ def select_all(*, **) # :nodoc:
9
9
  result = if ExplainRegistry.collect? && prepared_statements
10
10
  unprepared_statement { super }
11
11
  else
@@ -19,13 +19,24 @@ module ActiveRecord
19
19
  execute(sql, name).to_a
20
20
  end
21
21
 
22
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
22
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
23
+ :desc, :describe, :set, :show, :use
24
+ ) # :nodoc:
23
25
  private_constant :READ_QUERY
24
26
 
25
27
  def write_query?(sql) # :nodoc:
26
28
  !READ_QUERY.match?(sql)
27
29
  end
28
30
 
31
+ def explain(arel, binds = [])
32
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
33
+ start = Concurrent.monotonic_time
34
+ result = exec_query(sql, "EXPLAIN", binds)
35
+ elapsed = Concurrent.monotonic_time - start
36
+
37
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
38
+ end
39
+
29
40
  # Executes the SQL statement in the context of this connection.
30
41
  def execute(sql, name = nil)
31
42
  if preventing_writes? && write_query?(sql)
@@ -43,17 +54,17 @@ module ActiveRecord
43
54
  if without_prepared_statement?(binds)
44
55
  execute_and_free(sql, name) do |result|
45
56
  if result
46
- ActiveRecord::Result.new(result.fields, result.to_a)
57
+ build_result(columns: result.fields, rows: result.to_a)
47
58
  else
48
- ActiveRecord::Result.new([], [])
59
+ build_result(columns: [], rows: [])
49
60
  end
50
61
  end
51
62
  else
52
63
  exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
53
64
  if result
54
- ActiveRecord::Result.new(result.fields, result.to_a)
65
+ build_result(columns: result.fields, rows: result.to_a)
55
66
  else
56
- ActiveRecord::Result.new([], [])
67
+ build_result(columns: [], rows: [])
57
68
  end
58
69
  end
59
70
  end
@@ -71,8 +82,10 @@ module ActiveRecord
71
82
  alias :exec_update :exec_delete
72
83
 
73
84
  private
74
- def execute_batch(sql, name = nil)
75
- super
85
+ def execute_batch(statements, name = nil)
86
+ combine_multi_statements(statements).each do |statement|
87
+ execute(statement, name)
88
+ end
76
89
  @connection.abandon_results!
77
90
  end
78
91
 
@@ -84,47 +97,27 @@ module ActiveRecord
84
97
  @connection.last_id
85
98
  end
86
99
 
87
- def supports_set_server_option?
88
- @connection.respond_to?(:set_server_option)
89
- end
90
-
91
- def build_truncate_statements(*table_names)
92
- if table_names.size == 1
93
- super.first
94
- else
95
- super
96
- end
97
- end
100
+ def multi_statements_enabled?
101
+ flags = @config[:flags]
98
102
 
99
- def multi_statements_enabled?(flags)
100
103
  if flags.is_a?(Array)
101
104
  flags.include?("MULTI_STATEMENTS")
102
105
  else
103
- (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
106
+ flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
104
107
  end
105
108
  end
106
109
 
107
110
  def with_multi_statements
108
- previous_flags = @config[:flags]
111
+ multi_statements_was = multi_statements_enabled?
109
112
 
110
- unless multi_statements_enabled?(previous_flags)
111
- if supports_set_server_option?
112
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
113
- else
114
- @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
115
- reconnect!
116
- end
113
+ unless multi_statements_was
114
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
117
115
  end
118
116
 
119
117
  yield
120
118
  ensure
121
- unless multi_statements_enabled?(previous_flags)
122
- if supports_set_server_option?
123
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
124
- else
125
- @config[:flags] = previous_flags
126
- reconnect!
127
- end
119
+ unless multi_statements_was
120
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
128
121
  end
129
122
  end
130
123
 
@@ -161,6 +154,7 @@ module ActiveRecord
161
154
  end
162
155
 
163
156
  materialize_transactions
157
+ mark_transaction_written_if_write(sql)
164
158
 
165
159
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
166
160
  # made since we established the connection
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  private
40
-
41
40
  def compute_column_widths(result)
42
41
  [].tap do |widths|
43
42
  result.columns.each_with_index do |column, i|
@@ -57,7 +56,7 @@ module ActiveRecord
57
56
  items.each_with_index do |item, i|
58
57
  item = "NULL" if item.nil?
59
58
  justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
60
- cells << item.to_s.send(justifier, widths[i])
59
+ cells << item.to_s.public_send(justifier, widths[i])
61
60
  end
62
61
  "| " + cells.join(" | ") + " |"
63
62
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/time_with_zone"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module MySQL
@@ -47,7 +49,7 @@ module ActiveRecord
47
49
  # `table_name`.`column_name` | function(one or no argument)
48
50
  ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
49
51
  )
50
- (?:\s+AS\s+(?:\w+|`\w+`))?
52
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
51
53
  )
52
54
  (?:\s*,\s*\g<1>)*
53
55
  \z
@@ -69,10 +71,23 @@ module ActiveRecord
69
71
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
70
72
 
71
73
  private
74
+ # Override +_type_cast+ we pass to mysql2 Date and Time objects instead
75
+ # of Strings since mysql2 is able to handle those classes more efficiently.
72
76
  def _type_cast(value)
73
77
  case value
74
- when Date, Time then value
75
- else super
78
+ when ActiveSupport::TimeWithZone
79
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
80
+ # we need to transform it to Time objects but we don't want to
81
+ # transform Time objects to themselves.
82
+ if ActiveRecord::Base.default_timezone == :utc
83
+ value.getutc
84
+ else
85
+ value.getlocal
86
+ end
87
+ when Date, Time
88
+ value
89
+ else
90
+ super
76
91
  end
77
92
  end
78
93
  end
@@ -3,15 +3,18 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
- class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
7
  delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
8
8
 
9
9
  private
10
-
11
10
  def visit_DropForeignKey(name)
12
11
  "DROP FOREIGN KEY #{name}"
13
12
  end
14
13
 
14
+ def visit_DropCheckConstraint(name)
15
+ "DROP #{mariadb? ? 'CONSTRAINT' : 'CHECK'} #{name}"
16
+ end
17
+
15
18
  def visit_AddColumnDefinition(o)
16
19
  add_column_position!(super, column_options(o.column))
17
20
  end
@@ -21,15 +24,37 @@ module ActiveRecord
21
24
  add_column_position!(change_column_sql, column_options(o.column))
22
25
  end
23
26
 
24
- def add_table_options!(create_sql, options)
25
- add_sql_comment!(super, options[:comment])
27
+ def visit_CreateIndexDefinition(o)
28
+ sql = visit_IndexDefinition(o.index, true)
29
+ sql << " #{o.algorithm}" if o.algorithm
30
+ sql
31
+ end
32
+
33
+ def visit_IndexDefinition(o, create = false)
34
+ index_type = o.type&.to_s&.upcase || o.unique && "UNIQUE"
35
+
36
+ sql = create ? ["CREATE"] : []
37
+ sql << index_type if index_type
38
+ sql << "INDEX"
39
+ sql << quote_column_name(o.name)
40
+ sql << "USING #{o.using}" if o.using
41
+ sql << "ON #{quote_table_name(o.table)}" if create
42
+ sql << "(#{quoted_columns(o)})"
43
+
44
+ add_sql_comment!(sql.join(" "), o.comment)
45
+ end
46
+
47
+ def add_table_options!(create_sql, o)
48
+ create_sql << " DEFAULT CHARSET=#{o.charset}" if o.charset
49
+ create_sql << " COLLATE=#{o.collation}" if o.collation
50
+ add_sql_comment!(super, o.comment)
26
51
  end
27
52
 
28
53
  def add_column_options!(sql, options)
29
54
  # By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
30
55
  # and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
31
56
  # column to contain NULL, explicitly declare it with the NULL attribute.
32
- # See https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
57
+ # See https://dev.mysql.com/doc/refman/en/timestamp-initialization.html
33
58
  if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
34
59
  sql << " NULL" unless options[:null] == false || options_include_default?(options)
35
60
  end
@@ -63,8 +88,8 @@ module ActiveRecord
63
88
  end
64
89
 
65
90
  def index_in_create(table_name, column_name, options)
66
- index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
67
- add_sql_comment!((+"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})"), comment)
91
+ index, _ = @conn.add_index_options(table_name, column_name, **options)
92
+ accept(index)
68
93
  end
69
94
  end
70
95
  end
@@ -60,6 +60,14 @@ module ActiveRecord
60
60
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
61
61
  include ColumnMethods
62
62
 
63
+ attr_reader :charset, :collation
64
+
65
+ def initialize(conn, name, charset: nil, collation: nil, **)
66
+ super
67
+ @charset = charset
68
+ @collation = collation
69
+ end
70
+
63
71
  def new_column_definition(name, type, **options) # :nodoc:
64
72
  case type
65
73
  when :virtual
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  end
50
50
 
51
51
  def schema_limit(column)
52
- super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
52
+ super unless /\A(?:tiny|medium|long)?(?:text|blob)\b/.match?(column.sql_type)
53
53
  end
54
54
 
55
55
  def schema_precision(column)
@@ -79,7 +79,10 @@ module ActiveRecord
79
79
  " WHERE table_schema = #{scope[:schema]}" \
80
80
  " AND table_name = #{scope[:name]}" \
81
81
  " AND column_name = #{column_name}"
82
- @connection.query_value(sql, "SCHEMA").inspect
82
+ # Calling .inspect leads into issues with the query result
83
+ # which already returns escaped quotes.
84
+ # We remove the escape sequence from the result in order to deal with double escaping issues.
85
+ @connection.query_value(sql, "SCHEMA").gsub("\\'", "'").inspect
83
86
  end
84
87
  end
85
88
  end
@@ -51,26 +51,26 @@ module ActiveRecord
51
51
  end
52
52
 
53
53
  indexes.map do |index|
54
- options = index.last
54
+ options = index.pop
55
55
 
56
56
  if expressions = options.delete(:expressions)
57
57
  orders = options.delete(:orders)
58
58
  lengths = options.delete(:lengths)
59
59
 
60
- columns = index[-2].map { |name|
60
+ columns = index[-1].map { |name|
61
61
  [ name.to_sym, expressions[name] || +quote_column_name(name) ]
62
62
  }.to_h
63
63
 
64
- index[-2] = add_options_for_index_columns(
64
+ index[-1] = add_options_for_index_columns(
65
65
  columns, order: orders, length: lengths
66
66
  ).values.join(", ")
67
67
  end
68
68
 
69
- IndexDefinition.new(*index)
69
+ IndexDefinition.new(*index, **options)
70
70
  end
71
71
  end
72
72
 
73
- def remove_column(table_name, column_name, type = nil, options = {})
73
+ def remove_column(table_name, column_name, type = nil, **options)
74
74
  if foreign_key_exists?(table_name, column: column_name)
75
75
  remove_foreign_key(table_name, column: column_name)
76
76
  end
@@ -122,7 +122,7 @@ module ActiveRecord
122
122
  end
123
123
 
124
124
  def table_alias_length
125
- 256 # https://dev.mysql.com/doc/refman/8.0/en/identifiers.html
125
+ 256 # https://dev.mysql.com/doc/refman/en/identifiers.html
126
126
  end
127
127
 
128
128
  private
@@ -154,8 +154,8 @@ module ActiveRecord
154
154
  MySQL::SchemaCreation.new(self)
155
155
  end
156
156
 
157
- def create_table_definition(*args)
158
- MySQL::TableDefinition.new(self, *args)
157
+ def create_table_definition(name, **options)
158
+ MySQL::TableDefinition.new(self, name, **options)
159
159
  end
160
160
 
161
161
  def new_column_from_field(table_name, field)
@@ -196,17 +196,21 @@ module ActiveRecord
196
196
  end
197
197
 
198
198
  def add_options_for_index_columns(quoted_columns, **options)
199
- quoted_columns = add_index_length(quoted_columns, options)
199
+ quoted_columns = add_index_length(quoted_columns, **options)
200
200
  super
201
201
  end
202
202
 
203
203
  def data_source_sql(name = nil, type: nil)
204
204
  scope = quoted_scope(name, type: type)
205
205
 
206
- sql = +"SELECT table_name FROM information_schema.tables"
207
- sql << " WHERE table_schema = #{scope[:schema]}"
208
- sql << " AND table_name = #{scope[:name]}" if scope[:name]
209
- sql << " AND table_type = #{scope[:type]}" if scope[:type]
206
+ sql = +"SELECT table_name FROM (SELECT * FROM information_schema.tables "
207
+ sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
208
+ if scope[:type] || scope[:name]
209
+ conditions = []
210
+ conditions << "_subquery.table_type = #{scope[:type]}" if scope[:type]
211
+ conditions << "_subquery.table_name = #{scope[:name]}" if scope[:name]
212
+ sql << " WHERE #{conditions.join(" AND ")}"
213
+ end
210
214
  sql
211
215
  end
212
216