activerecord 7.0.8.7 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1944
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +54 -12
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -63,6 +63,15 @@ module ActiveRecord
63
63
  coder["comment"] = @comment
64
64
  end
65
65
 
66
+ # whether the column is auto-populated by the database using a sequence
67
+ def auto_incremented_by_db?
68
+ false
69
+ end
70
+
71
+ def auto_populated?
72
+ auto_incremented_by_db? || default_function
73
+ end
74
+
66
75
  def ==(other)
67
76
  other.is_a?(Column) &&
68
77
  name == other.name &&
@@ -17,6 +17,7 @@ module ActiveRecord
17
17
  def auto_increment?
18
18
  extra == "auto_increment"
19
19
  end
20
+ alias_method :auto_incremented_by_db?, :auto_increment?
20
21
 
21
22
  def virtual?
22
23
  /\b(?:VIRTUAL|STORED|PERSISTENT)\b/.match?(extra)
@@ -4,135 +4,62 @@ 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)", retryable: true).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)
46
-
47
- raw_execute(sql, name, async: async)
48
- end
36
+ def build_explain_clause(options = [])
37
+ return "EXPLAIN" if options.empty?
49
38
 
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
39
+ explain_clause = "EXPLAIN #{options.join(" ").upcase}"
69
40
 
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")
58
+ def returning_column_values(result)
59
+ if supports_insert_returning?
60
+ result.rows.first
120
61
  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)
62
+ super
136
63
  end
137
64
  end
138
65
 
@@ -162,46 +89,6 @@ module ActiveRecord
162
89
  def max_allowed_packet
163
90
  @max_allowed_packet ||= show_variable("max_allowed_packet")
164
91
  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
92
  end
206
93
  end
207
94
  end
@@ -6,34 +6,69 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
+ extend ActiveSupport::Concern
10
+
9
11
  QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
12
  QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
13
 
12
- def quote_bound_value(value)
14
+ module ClassMethods # :nodoc:
15
+ def column_name_matcher
16
+ /
17
+ \A
18
+ (
19
+ (?:
20
+ # `table_name`.`column_name` | function(one or no argument)
21
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
22
+ )
23
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
24
+ )
25
+ (?:\s*,\s*\g<1>)*
26
+ \z
27
+ /ix
28
+ end
29
+
30
+ def column_name_with_order_matcher
31
+ /
32
+ \A
33
+ (
34
+ (?:
35
+ # `table_name`.`column_name` | function(one or no argument)
36
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
37
+ )
38
+ (?:\s+COLLATE\s+(?:\w+|"\w+"))?
39
+ (?:\s+ASC|\s+DESC)?
40
+ )
41
+ (?:\s*,\s*\g<1>)*
42
+ \z
43
+ /ix
44
+ end
45
+
46
+ def quote_column_name(name)
47
+ QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub('`', '``')}`".freeze
48
+ end
49
+
50
+ def quote_table_name(name)
51
+ QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub('`', '``').gsub(".", "`.`")}`".freeze
52
+ end
53
+ end
54
+
55
+ def cast_bound_value(value)
13
56
  case value
14
57
  when Rational
15
- quote(value.to_f.to_s)
16
- when Numeric, ActiveSupport::Duration
17
- quote(value.to_s)
58
+ value.to_f.to_s
59
+ when Numeric
60
+ value.to_s
18
61
  when BigDecimal
19
- quote(value.to_s("F"))
62
+ value.to_s("F")
20
63
  when true
21
- "'1'"
64
+ "1"
22
65
  when false
23
- "'0'"
66
+ "0"
24
67
  else
25
- quote(value)
68
+ value
26
69
  end
27
70
  end
28
71
 
29
- def quote_column_name(name)
30
- QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
31
- end
32
-
33
- def quote_table_name(name)
34
- QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
35
- end
36
-
37
72
  def unquoted_true
38
73
  1
39
74
  end
@@ -63,14 +98,14 @@ module ActiveRecord
63
98
  end
64
99
 
65
100
  # 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.
101
+ # of Strings since MySQL adapters are able to handle those classes more efficiently.
67
102
  def type_cast(value) # :nodoc:
68
103
  case value
69
104
  when ActiveSupport::TimeWithZone
70
105
  # We need to check explicitly for ActiveSupport::TimeWithZone because
71
106
  # we need to transform it to Time objects but we don't want to
72
107
  # transform Time objects to themselves.
73
- if ActiveRecord.default_timezone == :utc
108
+ if default_timezone == :utc
74
109
  value.getutc
75
110
  else
76
111
  value.getlocal
@@ -81,42 +116,6 @@ module ActiveRecord
81
116
  super
82
117
  end
83
118
  end
84
-
85
- def column_name_matcher
86
- COLUMN_NAME
87
- end
88
-
89
- def column_name_with_order_matcher
90
- COLUMN_NAME_WITH_ORDER
91
- end
92
-
93
- COLUMN_NAME = /
94
- \A
95
- (
96
- (?:
97
- # `table_name`.`column_name` | function(one or no argument)
98
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
99
- )
100
- (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
101
- )
102
- (?:\s*,\s*\g<1>)*
103
- \z
104
- /ix
105
-
106
- COLUMN_NAME_WITH_ORDER = /
107
- \A
108
- (
109
- (?:
110
- # `table_name`.`column_name` | function(one or no argument)
111
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
112
- )
113
- (?:\s+ASC|\s+DESC)?
114
- )
115
- (?:\s*,\s*\g<1>)*
116
- \z
117
- /ix
118
-
119
- private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
120
119
  end
121
120
  end
122
121
  end
@@ -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
@@ -68,6 +68,12 @@ module ActiveRecord
68
68
 
69
69
  IndexDefinition.new(*index, **options)
70
70
  end
71
+ rescue StatementInvalid => e
72
+ if e.message.match?(/Table '.+' doesn't exist/)
73
+ []
74
+ else
75
+ raise
76
+ end
71
77
  end
72
78
 
73
79
  def remove_column(table_name, column_name, type = nil, **options)
@@ -125,6 +131,10 @@ module ActiveRecord
125
131
  256 # https://dev.mysql.com/doc/refman/en/identifiers.html
126
132
  end
127
133
 
134
+ def schema_creation # :nodoc:
135
+ MySQL::SchemaCreation.new(self)
136
+ end
137
+
128
138
  private
129
139
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
130
140
 
@@ -150,8 +160,8 @@ module ActiveRecord
150
160
  @default_row_format
151
161
  end
152
162
 
153
- def schema_creation
154
- MySQL::SchemaCreation.new(self)
163
+ def valid_primary_key_options
164
+ super + [:unsigned, :auto_increment]
155
165
  end
156
166
 
157
167
  def create_table_definition(name, **options)
@@ -171,7 +181,7 @@ module ActiveRecord
171
181
  end
172
182
  end
173
183
 
174
- def new_column_from_field(table_name, field)
184
+ def new_column_from_field(table_name, field, _definitions)
175
185
  field_name = field.fetch(:Field)
176
186
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
177
187
  default, default_function = field[:Default], nil
@@ -181,6 +191,7 @@ module ActiveRecord
181
191
  default, default_function = nil, default
182
192
  elsif type_metadata.extra == "DEFAULT_GENERATED"
183
193
  default = +"(#{default})" unless default.start_with?("(")
194
+ default = default.gsub("\\'", "'")
184
195
  default, default_function = nil, default
185
196
  elsif type_metadata.type == :text && default&.start_with?("'")
186
197
  # strip and unescape quotes
@@ -225,14 +236,15 @@ module ActiveRecord
225
236
  def data_source_sql(name = nil, type: nil)
226
237
  scope = quoted_scope(name, type: type)
227
238
 
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 ")}"
239
+ sql = +"SELECT table_name FROM information_schema.tables"
240
+ sql << " WHERE table_schema = #{scope[:schema]}"
241
+
242
+ if scope[:name]
243
+ sql << " AND table_name = #{scope[:name]}"
244
+ sql << " AND table_name IN (SELECT table_name FROM information_schema.tables WHERE table_schema = #{scope[:schema]})"
235
245
  end
246
+
247
+ sql << " AND table_type = #{scope[:type]}" if scope[:type]
236
248
  sql
237
249
  end
238
250
 
@@ -0,0 +1,152 @@
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
+ if ExplainRegistry.collect? && prepared_statements
10
+ unprepared_statement { super }
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
17
+ if without_prepared_statement?(binds)
18
+ execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
19
+ if result
20
+ build_result(columns: result.fields, rows: result.to_a)
21
+ else
22
+ build_result(columns: [], rows: [])
23
+ end
24
+ end
25
+ else
26
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
27
+ if result
28
+ build_result(columns: result.fields, rows: result.to_a)
29
+ else
30
+ build_result(columns: [], rows: [])
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
37
+ if without_prepared_statement?(binds)
38
+ with_raw_connection do |conn|
39
+ @affected_rows_before_warnings = nil
40
+ execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
41
+ end
42
+ else
43
+ exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
44
+ end
45
+ end
46
+ alias :exec_update :exec_delete
47
+
48
+ private
49
+ def sync_timezone_changes(raw_connection)
50
+ raw_connection.query_options[:database_timezone] = default_timezone
51
+ end
52
+
53
+ def execute_batch(statements, name = nil)
54
+ statements = statements.map { |sql| transform_query(sql) }
55
+ combine_multi_statements(statements).each do |statement|
56
+ with_raw_connection do |conn|
57
+ raw_execute(statement, name)
58
+ end
59
+ end
60
+ end
61
+
62
+ def last_inserted_id(result)
63
+ if supports_insert_returning?
64
+ super
65
+ else
66
+ @raw_connection&.last_id
67
+ end
68
+ end
69
+
70
+ def multi_statements_enabled?
71
+ flags = @config[:flags]
72
+
73
+ if flags.is_a?(Array)
74
+ flags.include?("MULTI_STATEMENTS")
75
+ else
76
+ flags.anybits?(::Mysql2::Client::MULTI_STATEMENTS)
77
+ end
78
+ end
79
+
80
+ def with_multi_statements
81
+ if multi_statements_enabled?
82
+ return yield
83
+ end
84
+
85
+ with_raw_connection do |conn|
86
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
87
+
88
+ yield
89
+ ensure
90
+ conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
91
+ end
92
+ end
93
+
94
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
95
+ log(sql, name, async: async) do |notification_payload|
96
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
97
+ sync_timezone_changes(conn)
98
+ result = conn.query(sql)
99
+ conn.abandon_results!
100
+ verified!
101
+ handle_warnings(sql)
102
+ notification_payload[:row_count] = result&.size || 0
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 |notification_payload|
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
+ notification_payload[:row_count] = result&.size || 0
143
+ result.free if result
144
+ stmt.close unless cache_stmt
145
+ ret
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end