activerecord 7.0.8.7 → 7.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  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 +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  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 +12 -9
  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 +193 -97
  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 +109 -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 +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -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 +17 -12
  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 +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -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 +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  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 +42 -36
  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 +162 -77
  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 +128 -138
  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 +2 -2
  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 +89 -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 +4 -4
  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 +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  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 +107 -45
  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 +169 -45
  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 +85 -15
  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/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. 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
@@ -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