activerecord 7.0.4.3 → 7.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1657 -1274
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  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 +20 -4
  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 +16 -10
  15. data/lib/active_record/associations/collection_proxy.rb +20 -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 +31 -7
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +327 -222
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +52 -34
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +0 -4
  35. data/lib/active_record/attribute_methods/write.rb +3 -3
  36. data/lib/active_record/attribute_methods.rb +108 -26
  37. data/lib/active_record/attributes.rb +3 -3
  38. data/lib/active_record/autosave_association.rb +55 -9
  39. data/lib/active_record/base.rb +7 -2
  40. data/lib/active_record/callbacks.rb +16 -32
  41. data/lib/active_record/coders/column_serializer.rb +61 -0
  42. data/lib/active_record/coders/json.rb +1 -1
  43. data/lib/active_record/coders/yaml_column.rb +70 -42
  44. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  46. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  52. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +290 -124
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -102
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -112
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +362 -60
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +211 -83
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +72 -95
  97. data/lib/active_record/core.rb +175 -153
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/database_config.rb +9 -3
  100. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  101. data/lib/active_record/database_configurations/url_config.rb +17 -11
  102. data/lib/active_record/database_configurations.rb +86 -33
  103. data/lib/active_record/delegated_type.rb +9 -4
  104. data/lib/active_record/deprecator.rb +7 -0
  105. data/lib/active_record/destroy_association_async_job.rb +2 -0
  106. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  107. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  109. data/lib/active_record/encryption/config.rb +25 -1
  110. data/lib/active_record/encryption/configurable.rb +12 -19
  111. data/lib/active_record/encryption/context.rb +10 -3
  112. data/lib/active_record/encryption/contexts.rb +5 -1
  113. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  114. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  115. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  116. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  117. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  118. data/lib/active_record/encryption/key_generator.rb +12 -1
  119. data/lib/active_record/encryption/message_serializer.rb +2 -0
  120. data/lib/active_record/encryption/properties.rb +3 -3
  121. data/lib/active_record/encryption/scheme.rb +19 -22
  122. data/lib/active_record/encryption.rb +1 -0
  123. data/lib/active_record/enum.rb +112 -28
  124. data/lib/active_record/errors.rb +112 -18
  125. data/lib/active_record/explain.rb +23 -3
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  128. data/lib/active_record/fixture_set/render_context.rb +2 -0
  129. data/lib/active_record/fixture_set/table_row.rb +29 -8
  130. data/lib/active_record/fixtures.rb +135 -71
  131. data/lib/active_record/future_result.rb +31 -5
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +30 -16
  134. data/lib/active_record/insert_all.rb +57 -10
  135. data/lib/active_record/integration.rb +8 -8
  136. data/lib/active_record/internal_metadata.rb +120 -30
  137. data/lib/active_record/locking/optimistic.rb +32 -18
  138. data/lib/active_record/locking/pessimistic.rb +5 -2
  139. data/lib/active_record/log_subscriber.rb +29 -12
  140. data/lib/active_record/marshalling.rb +56 -0
  141. data/lib/active_record/message_pack.rb +124 -0
  142. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  143. data/lib/active_record/middleware/database_selector.rb +9 -11
  144. data/lib/active_record/middleware/shard_selector.rb +3 -1
  145. data/lib/active_record/migration/command_recorder.rb +105 -7
  146. data/lib/active_record/migration/compatibility.rb +157 -58
  147. data/lib/active_record/migration/default_strategy.rb +23 -0
  148. data/lib/active_record/migration/execution_strategy.rb +19 -0
  149. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  150. data/lib/active_record/migration.rb +271 -114
  151. data/lib/active_record/model_schema.rb +64 -44
  152. data/lib/active_record/nested_attributes.rb +24 -6
  153. data/lib/active_record/normalization.rb +167 -0
  154. data/lib/active_record/persistence.rb +195 -42
  155. data/lib/active_record/promise.rb +84 -0
  156. data/lib/active_record/query_cache.rb +3 -21
  157. data/lib/active_record/query_logs.rb +77 -52
  158. data/lib/active_record/query_logs_formatter.rb +41 -0
  159. data/lib/active_record/querying.rb +15 -2
  160. data/lib/active_record/railtie.rb +109 -47
  161. data/lib/active_record/railties/controller_runtime.rb +14 -9
  162. data/lib/active_record/railties/databases.rake +142 -148
  163. data/lib/active_record/railties/job_runtime.rb +23 -0
  164. data/lib/active_record/readonly_attributes.rb +32 -5
  165. data/lib/active_record/reflection.rb +182 -44
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  167. data/lib/active_record/relation/batches.rb +190 -61
  168. data/lib/active_record/relation/calculations.rb +232 -81
  169. data/lib/active_record/relation/delegation.rb +23 -9
  170. data/lib/active_record/relation/finder_methods.rb +77 -16
  171. data/lib/active_record/relation/merger.rb +2 -0
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  173. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  175. data/lib/active_record/relation/predicate_builder.rb +26 -14
  176. data/lib/active_record/relation/query_attribute.rb +25 -1
  177. data/lib/active_record/relation/query_methods.rb +387 -71
  178. data/lib/active_record/relation/spawn_methods.rb +18 -1
  179. data/lib/active_record/relation.rb +91 -35
  180. data/lib/active_record/result.rb +25 -9
  181. data/lib/active_record/runtime_registry.rb +24 -1
  182. data/lib/active_record/sanitization.rb +51 -11
  183. data/lib/active_record/schema.rb +2 -3
  184. data/lib/active_record/schema_dumper.rb +50 -7
  185. data/lib/active_record/schema_migration.rb +68 -33
  186. data/lib/active_record/scoping/default.rb +15 -5
  187. data/lib/active_record/scoping/named.rb +2 -2
  188. data/lib/active_record/scoping.rb +2 -1
  189. data/lib/active_record/secure_password.rb +60 -0
  190. data/lib/active_record/secure_token.rb +21 -3
  191. data/lib/active_record/signed_id.rb +7 -5
  192. data/lib/active_record/store.rb +9 -9
  193. data/lib/active_record/suppressor.rb +3 -1
  194. data/lib/active_record/table_metadata.rb +16 -3
  195. data/lib/active_record/tasks/database_tasks.rb +127 -105
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  199. data/lib/active_record/test_fixtures.rb +113 -96
  200. data/lib/active_record/timestamp.rb +27 -15
  201. data/lib/active_record/token_for.rb +113 -0
  202. data/lib/active_record/touch_later.rb +11 -6
  203. data/lib/active_record/transactions.rb +39 -13
  204. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  205. data/lib/active_record/type/internal/timezone.rb +7 -2
  206. data/lib/active_record/type/serialized.rb +8 -4
  207. data/lib/active_record/type/time.rb +4 -0
  208. data/lib/active_record/validations/absence.rb +1 -1
  209. data/lib/active_record/validations/numericality.rb +5 -4
  210. data/lib/active_record/validations/presence.rb +5 -28
  211. data/lib/active_record/validations/uniqueness.rb +47 -2
  212. data/lib/active_record/validations.rb +8 -4
  213. data/lib/active_record/version.rb +1 -1
  214. data/lib/active_record.rb +121 -16
  215. data/lib/arel/errors.rb +10 -0
  216. data/lib/arel/factory_methods.rb +4 -0
  217. data/lib/arel/filter_predications.rb +1 -1
  218. data/lib/arel/nodes/and.rb +4 -0
  219. data/lib/arel/nodes/binary.rb +6 -1
  220. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  221. data/lib/arel/nodes/cte.rb +36 -0
  222. data/lib/arel/nodes/filter.rb +1 -1
  223. data/lib/arel/nodes/fragments.rb +35 -0
  224. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  225. data/lib/arel/nodes/leading_join.rb +8 -0
  226. data/lib/arel/nodes/node.rb +111 -2
  227. data/lib/arel/nodes/sql_literal.rb +6 -0
  228. data/lib/arel/nodes/table_alias.rb +4 -0
  229. data/lib/arel/nodes.rb +4 -0
  230. data/lib/arel/predications.rb +2 -0
  231. data/lib/arel/table.rb +9 -5
  232. data/lib/arel/visitors/mysql.rb +8 -1
  233. data/lib/arel/visitors/to_sql.rb +81 -17
  234. data/lib/arel/visitors/visitor.rb +2 -2
  235. data/lib/arel.rb +16 -2
  236. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  237. data/lib/rails/generators/active_record/migration.rb +3 -1
  238. data/lib/rails/generators/active_record/model/USAGE +113 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  240. metadata +48 -12
  241. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  242. 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(
23
- :desc, :describe, :set, :show, :use
7
+ READ_QUERY = AbstractAdapter.build_read_query_regexp(
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
@@ -6,29 +6,35 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
- def quote_bound_value(value)
9
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
+
12
+ def cast_bound_value(value)
10
13
  case value
11
14
  when Rational
12
- quote(value.to_f.to_s)
13
- when Numeric, ActiveSupport::Duration
14
- quote(value.to_s)
15
+ value.to_f.to_s
16
+ when Numeric
17
+ value.to_s
15
18
  when BigDecimal
16
- quote(value.to_s("F"))
19
+ value.to_s("F")
17
20
  when true
18
- "'1'"
21
+ "1"
19
22
  when false
20
- "'0'"
23
+ "0"
24
+ when ActiveSupport::Duration
25
+ warn_quote_duration_deprecated
26
+ value.to_s
21
27
  else
22
- quote(value)
28
+ value
23
29
  end
24
30
  end
25
31
 
26
32
  def quote_column_name(name)
27
- self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
33
+ QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
28
34
  end
29
35
 
30
36
  def quote_table_name(name)
31
- self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
37
+ QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
32
38
  end
33
39
 
34
40
  def unquoted_true
@@ -51,15 +57,23 @@ module ActiveRecord
51
57
  "x'#{value.hex}'"
52
58
  end
53
59
 
60
+ def unquote_identifier(identifier)
61
+ if identifier && identifier.start_with?("`")
62
+ identifier[1..-2]
63
+ else
64
+ identifier
65
+ end
66
+ end
67
+
54
68
  # Override +type_cast+ we pass to mysql2 Date and Time objects instead
55
- # 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.
56
70
  def type_cast(value) # :nodoc:
57
71
  case value
58
72
  when ActiveSupport::TimeWithZone
59
73
  # We need to check explicitly for ActiveSupport::TimeWithZone because
60
74
  # we need to transform it to Time objects but we don't want to
61
75
  # transform Time objects to themselves.
62
- if ActiveRecord.default_timezone == :utc
76
+ if default_timezone == :utc
63
77
  value.getutc
64
78
  else
65
79
  value.getlocal
@@ -84,7 +98,7 @@ module ActiveRecord
84
98
  (
85
99
  (?:
86
100
  # `table_name`.`column_name` | function(one or no argument)
87
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
101
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
88
102
  )
89
103
  (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
90
104
  )
@@ -97,8 +111,9 @@ module ActiveRecord
97
111
  (
98
112
  (?:
99
113
  # `table_name`.`column_name` | function(one or no argument)
100
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
114
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
101
115
  )
116
+ (?:\s+COLLATE\s+(?:\w+|"\w+"))?
102
117
  (?:\s+ASC|\s+DESC)?
103
118
  )
104
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,16 +86,24 @@ 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
91
96
 
92
97
  def integer_like_primary_key_type(type, options)
93
- options[:auto_increment] = true
98
+ unless options[:auto_increment] == false
99
+ options[:auto_increment] = true
100
+ end
101
+
94
102
  type
95
103
  end
96
104
  end
97
105
 
106
+ # = Active Record MySQL Adapter \Table
98
107
  class Table < ActiveRecord::ConnectionAdapters::Table
99
108
  include ColumnMethods
100
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,151 @@
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
+ verified!
102
+ handle_warnings(sql)
103
+ result
104
+ end
105
+ end
106
+ end
107
+
108
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
109
+ sql = transform_query(sql)
110
+ check_if_write_query(sql)
111
+
112
+ mark_transaction_written_if_write(sql)
113
+
114
+ type_casted_binds = type_casted_binds(binds)
115
+
116
+ log(sql, name, binds, type_casted_binds, async: async) do
117
+ with_raw_connection do |conn|
118
+ sync_timezone_changes(conn)
119
+
120
+ if cache_stmt
121
+ stmt = @statements[sql] ||= conn.prepare(sql)
122
+ else
123
+ stmt = conn.prepare(sql)
124
+ end
125
+
126
+ begin
127
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
128
+ stmt.execute(*type_casted_binds)
129
+ end
130
+ verified!
131
+ result
132
+ rescue ::Mysql2::Error => e
133
+ if cache_stmt
134
+ @statements.delete(sql)
135
+ else
136
+ stmt.close
137
+ end
138
+ raise e
139
+ end
140
+
141
+ ret = yield stmt, result
142
+ result.free if result
143
+ stmt.close unless cache_stmt
144
+ ret
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end