activerecord 7.0.8 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1401 -1513
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +295 -199
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +347 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +337 -176
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +134 -146
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +108 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +60 -40
  142. data/lib/active_record/nested_attributes.rb +23 -3
  143. data/lib/active_record/normalization.rb +159 -0
  144. data/lib/active_record/persistence.rb +184 -34
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +108 -46
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +162 -44
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +77 -16
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  164. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  165. data/lib/active_record/relation/predicate_builder.rb +26 -14
  166. data/lib/active_record/relation/query_attribute.rb +2 -1
  167. data/lib/active_record/relation/query_methods.rb +351 -62
  168. data/lib/active_record/relation/spawn_methods.rb +18 -1
  169. data/lib/active_record/relation.rb +76 -35
  170. data/lib/active_record/result.rb +19 -5
  171. data/lib/active_record/runtime_registry.rb +10 -1
  172. data/lib/active_record/sanitization.rb +51 -11
  173. data/lib/active_record/schema.rb +2 -3
  174. data/lib/active_record/schema_dumper.rb +46 -7
  175. data/lib/active_record/schema_migration.rb +68 -33
  176. data/lib/active_record/scoping/default.rb +15 -5
  177. data/lib/active_record/scoping/named.rb +2 -2
  178. data/lib/active_record/scoping.rb +2 -1
  179. data/lib/active_record/secure_password.rb +60 -0
  180. data/lib/active_record/secure_token.rb +21 -3
  181. data/lib/active_record/signed_id.rb +7 -5
  182. data/lib/active_record/store.rb +8 -8
  183. data/lib/active_record/suppressor.rb +3 -1
  184. data/lib/active_record/table_metadata.rb +10 -1
  185. data/lib/active_record/tasks/database_tasks.rb +127 -105
  186. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  187. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  188. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  189. data/lib/active_record/test_fixtures.rb +113 -96
  190. data/lib/active_record/timestamp.rb +26 -14
  191. data/lib/active_record/token_for.rb +113 -0
  192. data/lib/active_record/touch_later.rb +11 -6
  193. data/lib/active_record/transactions.rb +36 -10
  194. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  195. data/lib/active_record/type/internal/timezone.rb +7 -2
  196. data/lib/active_record/type/time.rb +4 -0
  197. data/lib/active_record/validations/absence.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +5 -4
  199. data/lib/active_record/validations/presence.rb +5 -28
  200. data/lib/active_record/validations/uniqueness.rb +47 -2
  201. data/lib/active_record/validations.rb +8 -4
  202. data/lib/active_record/version.rb +1 -1
  203. data/lib/active_record.rb +121 -16
  204. data/lib/arel/errors.rb +10 -0
  205. data/lib/arel/factory_methods.rb +4 -0
  206. data/lib/arel/nodes/binary.rb +6 -1
  207. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  208. data/lib/arel/nodes/cte.rb +36 -0
  209. data/lib/arel/nodes/fragments.rb +35 -0
  210. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  211. data/lib/arel/nodes/leading_join.rb +8 -0
  212. data/lib/arel/nodes/node.rb +111 -2
  213. data/lib/arel/nodes/sql_literal.rb +6 -0
  214. data/lib/arel/nodes/table_alias.rb +4 -0
  215. data/lib/arel/nodes.rb +4 -0
  216. data/lib/arel/predications.rb +2 -0
  217. data/lib/arel/table.rb +9 -5
  218. data/lib/arel/visitors/mysql.rb +8 -1
  219. data/lib/arel/visitors/to_sql.rb +81 -17
  220. data/lib/arel/visitors/visitor.rb +2 -2
  221. data/lib/arel.rb +16 -2
  222. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  223. data/lib/rails/generators/active_record/migration.rb +3 -1
  224. data/lib/rails/generators/active_record/model/USAGE +113 -0
  225. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  226. metadata +46 -11
  227. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  228. data/lib/active_record/null_relation.rb +0 -63
@@ -4,138 +4,57 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
6
  module DatabaseStatements
7
- # Returns an ActiveRecord::Result instance.
8
- def select_all(*, **) # :nodoc:
9
- result = if ExplainRegistry.collect? && prepared_statements
10
- unprepared_statement { super }
11
- else
12
- super
13
- end
14
- @connection.abandon_results!
15
- result
16
- end
17
-
18
- def query(sql, name = nil) # :nodoc:
19
- execute(sql, name).to_a
20
- end
21
-
22
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
7
+ READ_QUERY = AbstractAdapter.build_read_query_regexp(
23
8
  :desc, :describe, :set, :show, :use, :kill
24
9
  ) # :nodoc:
25
10
  private_constant :READ_QUERY
26
11
 
12
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
13
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
14
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
15
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
16
+
27
17
  def write_query?(sql) # :nodoc:
28
18
  !READ_QUERY.match?(sql)
29
19
  rescue ArgumentError # Invalid encoding
30
20
  !READ_QUERY.match?(sql.b)
31
21
  end
32
22
 
33
- def explain(arel, binds = [])
34
- sql = "EXPLAIN #{to_sql(arel, binds)}"
23
+ def high_precision_current_timestamp
24
+ HIGH_PRECISION_CURRENT_TIMESTAMP
25
+ end
26
+
27
+ def explain(arel, binds = [], options = [])
28
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
35
29
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
36
- result = exec_query(sql, "EXPLAIN", binds)
30
+ result = internal_exec_query(sql, "EXPLAIN", binds)
37
31
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
38
32
 
39
33
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
40
34
  end
41
35
 
42
- # Executes the SQL statement in the context of this connection.
43
- def execute(sql, name = nil, async: false)
44
- sql = transform_query(sql)
45
- check_if_write_query(sql)
36
+ def build_explain_clause(options = [])
37
+ return "EXPLAIN" if options.empty?
46
38
 
47
- raw_execute(sql, name, async: async)
48
- end
39
+ explain_clause = "EXPLAIN #{options.join(" ").upcase}"
49
40
 
50
- def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
51
- if without_prepared_statement?(binds)
52
- execute_and_free(sql, name, async: async) do |result|
53
- if result
54
- build_result(columns: result.fields, rows: result.to_a)
55
- else
56
- build_result(columns: [], rows: [])
57
- end
58
- end
59
- else
60
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
61
- if result
62
- build_result(columns: result.fields, rows: result.to_a)
63
- else
64
- build_result(columns: [], rows: [])
65
- end
66
- end
67
- end
68
- end
69
-
70
- def exec_delete(sql, name = nil, binds = []) # :nodoc:
71
- if without_prepared_statement?(binds)
72
- @lock.synchronize do
73
- execute_and_free(sql, name) { @connection.affected_rows }
74
- end
41
+ if analyze_without_explain? && explain_clause.include?("ANALYZE")
42
+ explain_clause.sub("EXPLAIN ", "")
75
43
  else
76
- exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
44
+ explain_clause
77
45
  end
78
46
  end
79
- alias :exec_update :exec_delete
80
-
81
- # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
82
- # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
83
- HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
84
- private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
85
-
86
- def high_precision_current_timestamp
87
- HIGH_PRECISION_CURRENT_TIMESTAMP
88
- end
89
47
 
90
48
  private
91
- def raw_execute(sql, name, async: false)
92
- # make sure we carry over any changes to ActiveRecord.default_timezone that have been
93
- # made since we established the connection
94
- @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
95
-
96
- super
97
- end
98
-
99
- def execute_batch(statements, name = nil)
100
- statements = statements.map { |sql| transform_query(sql) }
101
- combine_multi_statements(statements).each do |statement|
102
- raw_execute(statement, name)
103
- @connection.abandon_results!
104
- end
49
+ # https://mariadb.com/kb/en/analyze-statement/
50
+ def analyze_without_explain?
51
+ mariadb? && database_version >= "10.1.0"
105
52
  end
106
53
 
107
54
  def default_insert_value(column)
108
55
  super unless column.auto_increment?
109
56
  end
110
57
 
111
- def last_inserted_id(result)
112
- @connection.last_id
113
- end
114
-
115
- def multi_statements_enabled?
116
- flags = @config[:flags]
117
-
118
- if flags.is_a?(Array)
119
- flags.include?("MULTI_STATEMENTS")
120
- else
121
- flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
122
- end
123
- end
124
-
125
- def with_multi_statements
126
- multi_statements_was = multi_statements_enabled?
127
-
128
- unless multi_statements_was
129
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
130
- end
131
-
132
- yield
133
- ensure
134
- unless multi_statements_was
135
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
136
- end
137
- end
138
-
139
58
  def combine_multi_statements(total_sql)
140
59
  total_sql.each_with_object([]) do |sql, total_sql_chunks|
141
60
  previous_packet = total_sql_chunks.last
@@ -162,46 +81,6 @@ module ActiveRecord
162
81
  def max_allowed_packet
163
82
  @max_allowed_packet ||= show_variable("max_allowed_packet")
164
83
  end
165
-
166
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
167
- sql = transform_query(sql)
168
- check_if_write_query(sql)
169
-
170
- materialize_transactions
171
- mark_transaction_written_if_write(sql)
172
-
173
- # make sure we carry over any changes to ActiveRecord.default_timezone that have been
174
- # made since we established the connection
175
- @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
176
-
177
- type_casted_binds = type_casted_binds(binds)
178
-
179
- log(sql, name, binds, type_casted_binds, async: async) do
180
- if cache_stmt
181
- stmt = @statements[sql] ||= @connection.prepare(sql)
182
- else
183
- stmt = @connection.prepare(sql)
184
- end
185
-
186
- begin
187
- result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
188
- stmt.execute(*type_casted_binds)
189
- end
190
- rescue Mysql2::Error => e
191
- if cache_stmt
192
- @statements.delete(sql)
193
- else
194
- stmt.close
195
- end
196
- raise e
197
- end
198
-
199
- ret = yield stmt, result
200
- result.free if result
201
- stmt.close unless cache_stmt
202
- ret
203
- end
204
- end
205
84
  end
206
85
  end
207
86
  end
@@ -9,20 +9,23 @@ module ActiveRecord
9
9
  QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
10
  QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
11
 
12
- def quote_bound_value(value)
12
+ def cast_bound_value(value)
13
13
  case value
14
14
  when Rational
15
- quote(value.to_f.to_s)
16
- when Numeric, ActiveSupport::Duration
17
- quote(value.to_s)
15
+ value.to_f.to_s
16
+ when Numeric
17
+ value.to_s
18
18
  when BigDecimal
19
- quote(value.to_s("F"))
19
+ value.to_s("F")
20
20
  when true
21
- "'1'"
21
+ "1"
22
22
  when false
23
- "'0'"
23
+ "0"
24
+ when ActiveSupport::Duration
25
+ warn_quote_duration_deprecated
26
+ value.to_s
24
27
  else
25
- quote(value)
28
+ value
26
29
  end
27
30
  end
28
31
 
@@ -63,14 +66,14 @@ module ActiveRecord
63
66
  end
64
67
 
65
68
  # Override +type_cast+ we pass to mysql2 Date and Time objects instead
66
- # of Strings since mysql2 is able to handle those classes more efficiently.
69
+ # of Strings since MySQL adapters are able to handle those classes more efficiently.
67
70
  def type_cast(value) # :nodoc:
68
71
  case value
69
72
  when ActiveSupport::TimeWithZone
70
73
  # We need to check explicitly for ActiveSupport::TimeWithZone because
71
74
  # we need to transform it to Time objects but we don't want to
72
75
  # transform Time objects to themselves.
73
- if ActiveRecord.default_timezone == :utc
76
+ if default_timezone == :utc
74
77
  value.getutc
75
78
  else
76
79
  value.getlocal
@@ -95,7 +98,7 @@ module ActiveRecord
95
98
  (
96
99
  (?:
97
100
  # `table_name`.`column_name` | function(one or no argument)
98
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
101
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
99
102
  )
100
103
  (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
101
104
  )
@@ -108,8 +111,9 @@ module ActiveRecord
108
111
  (
109
112
  (?:
110
113
  # `table_name`.`column_name` | function(one or no argument)
111
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
114
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
112
115
  )
116
+ (?:\s+COLLATE\s+(?:\w+|"\w+"))?
113
117
  (?:\s+ASC|\s+DESC)?
114
118
  )
115
119
  (?:\s*,\s*\g<1>)*
@@ -24,6 +24,15 @@ module ActiveRecord
24
24
  add_column_position!(change_column_sql, column_options(o.column))
25
25
  end
26
26
 
27
+ def visit_ChangeColumnDefaultDefinition(o)
28
+ sql = +"ALTER COLUMN #{quote_column_name(o.column.name)} "
29
+ if o.default.nil? && !o.column.null
30
+ sql << "DROP DEFAULT"
31
+ else
32
+ sql << "SET DEFAULT #{quote_default_expression(o.default, o.column)}"
33
+ end
34
+ end
35
+
27
36
  def visit_CreateIndexDefinition(o)
28
37
  sql = visit_IndexDefinition(o.index, true)
29
38
  sql << " #{o.algorithm}" if o.algorithm
@@ -57,6 +57,7 @@ module ActiveRecord
57
57
  end
58
58
  end
59
59
 
60
+ # = Active Record MySQL Adapter \Table Definition
60
61
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
61
62
  include ColumnMethods
62
63
 
@@ -85,6 +86,10 @@ module ActiveRecord
85
86
  end
86
87
 
87
88
  private
89
+ def valid_column_definition_options
90
+ super + [:auto_increment, :charset, :as, :size, :unsigned, :first, :after, :type, :stored]
91
+ end
92
+
88
93
  def aliased_types(name, fallback)
89
94
  fallback
90
95
  end
@@ -98,6 +103,7 @@ module ActiveRecord
98
103
  end
99
104
  end
100
105
 
106
+ # = Active Record MySQL Adapter \Table
101
107
  class Table < ActiveRecord::ConnectionAdapters::Table
102
108
  include ColumnMethods
103
109
  end
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  if column.collation
67
67
  @table_collation_cache ||= {}
68
68
  @table_collation_cache[table_name] ||=
69
- @connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
69
+ @connection.internal_exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
70
70
  column.collation.inspect if column.collation != @table_collation_cache[table_name]
71
71
  end
72
72
  end
@@ -36,7 +36,7 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  if row[:Expression]
39
- expression = row[:Expression]
39
+ expression = row[:Expression].gsub("\\'", "'")
40
40
  expression = +"(#{expression})" unless expression.start_with?("(")
41
41
  indexes.last[-2] << expression
42
42
  indexes.last[-1][:expressions] ||= {}
@@ -57,9 +57,9 @@ module ActiveRecord
57
57
  orders = options.delete(:orders)
58
58
  lengths = options.delete(:lengths)
59
59
 
60
- columns = index[-1].map { |name|
60
+ columns = index[-1].to_h { |name|
61
61
  [ name.to_sym, expressions[name] || +quote_column_name(name) ]
62
- }.to_h
62
+ }
63
63
 
64
64
  index[-1] = add_options_for_index_columns(
65
65
  columns, order: orders, length: lengths
@@ -125,6 +125,10 @@ module ActiveRecord
125
125
  256 # https://dev.mysql.com/doc/refman/en/identifiers.html
126
126
  end
127
127
 
128
+ def schema_creation # :nodoc:
129
+ MySQL::SchemaCreation.new(self)
130
+ end
131
+
128
132
  private
129
133
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
130
134
 
@@ -150,8 +154,8 @@ module ActiveRecord
150
154
  @default_row_format
151
155
  end
152
156
 
153
- def schema_creation
154
- MySQL::SchemaCreation.new(self)
157
+ def valid_primary_key_options
158
+ super + [:unsigned]
155
159
  end
156
160
 
157
161
  def create_table_definition(name, **options)
@@ -171,7 +175,7 @@ module ActiveRecord
171
175
  end
172
176
  end
173
177
 
174
- def new_column_from_field(table_name, field)
178
+ def new_column_from_field(table_name, field, _definitions)
175
179
  field_name = field.fetch(:Field)
176
180
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
177
181
  default, default_function = field[:Default], nil
@@ -225,14 +229,15 @@ module ActiveRecord
225
229
  def data_source_sql(name = nil, type: nil)
226
230
  scope = quoted_scope(name, type: type)
227
231
 
228
- sql = +"SELECT table_name FROM (SELECT table_name, table_type FROM information_schema.tables "
229
- sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
230
- if scope[:type] || scope[:name]
231
- conditions = []
232
- conditions << "_subquery.table_type = #{scope[:type]}" if scope[:type]
233
- conditions << "_subquery.table_name = #{scope[:name]}" if scope[:name]
234
- sql << " WHERE #{conditions.join(" AND ")}"
232
+ sql = +"SELECT table_name FROM information_schema.tables"
233
+ sql << " WHERE table_schema = #{scope[:schema]}"
234
+
235
+ if scope[:name]
236
+ sql << " AND table_name = #{scope[:name]}"
237
+ sql << " AND table_name IN (SELECT table_name FROM information_schema.tables WHERE table_schema = #{scope[:schema]})"
235
238
  end
239
+
240
+ sql << " AND table_type = #{scope[:type]}" if scope[:type]
236
241
  sql
237
242
  end
238
243
 
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Mysql2
6
+ module DatabaseStatements
7
+ # Returns an ActiveRecord::Result instance.
8
+ def select_all(*, **) # :nodoc:
9
+ result = nil
10
+ with_raw_connection do |conn|
11
+ result = if ExplainRegistry.collect? && prepared_statements
12
+ unprepared_statement { super }
13
+ else
14
+ super
15
+ end
16
+ conn.abandon_results!
17
+ end
18
+ result
19
+ end
20
+
21
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
22
+ if without_prepared_statement?(binds)
23
+ execute_and_free(sql, name, async: async) do |result|
24
+ if result
25
+ build_result(columns: result.fields, rows: result.to_a)
26
+ else
27
+ build_result(columns: [], rows: [])
28
+ end
29
+ end
30
+ else
31
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
32
+ if result
33
+ build_result(columns: result.fields, rows: result.to_a)
34
+ else
35
+ build_result(columns: [], rows: [])
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
42
+ if without_prepared_statement?(binds)
43
+ with_raw_connection do |conn|
44
+ @affected_rows_before_warnings = nil
45
+ execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
46
+ end
47
+ else
48
+ exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
49
+ end
50
+ end
51
+ alias :exec_update :exec_delete
52
+
53
+ private
54
+ def sync_timezone_changes(raw_connection)
55
+ raw_connection.query_options[:database_timezone] = default_timezone
56
+ end
57
+
58
+ def execute_batch(statements, name = nil)
59
+ statements = statements.map { |sql| transform_query(sql) }
60
+ combine_multi_statements(statements).each do |statement|
61
+ with_raw_connection do |conn|
62
+ raw_execute(statement, name)
63
+ conn.abandon_results!
64
+ end
65
+ end
66
+ end
67
+
68
+ def last_inserted_id(result)
69
+ @raw_connection&.last_id
70
+ end
71
+
72
+ def multi_statements_enabled?
73
+ flags = @config[:flags]
74
+
75
+ if flags.is_a?(Array)
76
+ flags.include?("MULTI_STATEMENTS")
77
+ else
78
+ flags.anybits?(::Mysql2::Client::MULTI_STATEMENTS)
79
+ end
80
+ end
81
+
82
+ def with_multi_statements
83
+ if multi_statements_enabled?
84
+ return yield
85
+ end
86
+
87
+ with_raw_connection do |conn|
88
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
89
+
90
+ yield
91
+ ensure
92
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
93
+ end
94
+ end
95
+
96
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
97
+ log(sql, name, async: async) do
98
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
99
+ sync_timezone_changes(conn)
100
+ result = conn.query(sql)
101
+ handle_warnings(sql)
102
+ result
103
+ end
104
+ end
105
+ end
106
+
107
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
108
+ sql = transform_query(sql)
109
+ check_if_write_query(sql)
110
+
111
+ mark_transaction_written_if_write(sql)
112
+
113
+ type_casted_binds = type_casted_binds(binds)
114
+
115
+ log(sql, name, binds, type_casted_binds, async: async) do
116
+ with_raw_connection do |conn|
117
+ sync_timezone_changes(conn)
118
+
119
+ if cache_stmt
120
+ stmt = @statements[sql] ||= conn.prepare(sql)
121
+ else
122
+ stmt = conn.prepare(sql)
123
+ end
124
+
125
+ begin
126
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
127
+ stmt.execute(*type_casted_binds)
128
+ end
129
+ rescue ::Mysql2::Error => e
130
+ if cache_stmt
131
+ @statements.delete(sql)
132
+ else
133
+ stmt.close
134
+ end
135
+ raise e
136
+ end
137
+
138
+ ret = yield stmt, result
139
+ result.free if result
140
+ stmt.close unless cache_stmt
141
+ ret
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end