activerecord 6.1.7 → 7.2.2

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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -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 &&
@@ -87,6 +96,10 @@ module ActiveRecord
87
96
  comment.hash
88
97
  end
89
98
 
99
+ def virtual?
100
+ false
101
+ end
102
+
90
103
  private
91
104
  def deduplicated
92
105
  @name = -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,122 +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(
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)", 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)}"
35
- start = Concurrent.monotonic_time
36
- result = exec_query(sql, "EXPLAIN", binds)
37
- elapsed = Concurrent.monotonic_time - start
38
-
39
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
23
+ def high_precision_current_timestamp
24
+ HIGH_PRECISION_CURRENT_TIMESTAMP
40
25
  end
41
26
 
42
- # Executes the SQL statement in the context of this connection.
43
- def execute(sql, name = nil)
44
- if preventing_writes? && write_query?(sql)
45
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
46
- end
47
-
48
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
49
- # made since we established the connection
50
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
27
+ def explain(arel, binds = [], options = [])
28
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
29
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
30
+ result = internal_exec_query(sql, "EXPLAIN", binds)
31
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
51
32
 
52
- super
33
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
53
34
  end
54
35
 
55
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
56
- if without_prepared_statement?(binds)
57
- execute_and_free(sql, name) do |result|
58
- if result
59
- build_result(columns: result.fields, rows: result.to_a)
60
- else
61
- build_result(columns: [], rows: [])
62
- end
63
- end
64
- else
65
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
66
- if result
67
- build_result(columns: result.fields, rows: result.to_a)
68
- else
69
- build_result(columns: [], rows: [])
70
- end
71
- end
72
- end
73
- end
36
+ def build_explain_clause(options = [])
37
+ return "EXPLAIN" if options.empty?
74
38
 
75
- def exec_delete(sql, name = nil, binds = [])
76
- if without_prepared_statement?(binds)
77
- @lock.synchronize do
78
- execute_and_free(sql, name) { @connection.affected_rows }
79
- end
39
+ explain_clause = "EXPLAIN #{options.join(" ").upcase}"
40
+
41
+ if analyze_without_explain? && explain_clause.include?("ANALYZE")
42
+ explain_clause.sub("EXPLAIN ", "")
80
43
  else
81
- exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
44
+ explain_clause
82
45
  end
83
46
  end
84
- alias :exec_update :exec_delete
85
47
 
86
48
  private
87
- def execute_batch(statements, name = nil)
88
- combine_multi_statements(statements).each do |statement|
89
- execute(statement, name)
90
- end
91
- @connection.abandon_results!
49
+ # https://mariadb.com/kb/en/analyze-statement/
50
+ def analyze_without_explain?
51
+ mariadb? && database_version >= "10.1.0"
92
52
  end
93
53
 
94
54
  def default_insert_value(column)
95
55
  super unless column.auto_increment?
96
56
  end
97
57
 
98
- def last_inserted_id(result)
99
- @connection.last_id
100
- end
101
-
102
- def multi_statements_enabled?
103
- flags = @config[:flags]
104
-
105
- if flags.is_a?(Array)
106
- flags.include?("MULTI_STATEMENTS")
58
+ def returning_column_values(result)
59
+ if supports_insert_returning?
60
+ result.rows.first
107
61
  else
108
- flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
109
- end
110
- end
111
-
112
- def with_multi_statements
113
- multi_statements_was = multi_statements_enabled?
114
-
115
- unless multi_statements_was
116
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
117
- end
118
-
119
- yield
120
- ensure
121
- unless multi_statements_was
122
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
62
+ super
123
63
  end
124
64
  end
125
65
 
@@ -149,47 +89,6 @@ module ActiveRecord
149
89
  def max_allowed_packet
150
90
  @max_allowed_packet ||= show_variable("max_allowed_packet")
151
91
  end
152
-
153
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
154
- if preventing_writes? && write_query?(sql)
155
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
156
- end
157
-
158
- materialize_transactions
159
- mark_transaction_written_if_write(sql)
160
-
161
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
162
- # made since we established the connection
163
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
164
-
165
- type_casted_binds = type_casted_binds(binds)
166
-
167
- log(sql, name, binds, type_casted_binds) do
168
- if cache_stmt
169
- stmt = @statements[sql] ||= @connection.prepare(sql)
170
- else
171
- stmt = @connection.prepare(sql)
172
- end
173
-
174
- begin
175
- result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
176
- stmt.execute(*type_casted_binds)
177
- end
178
- rescue Mysql2::Error => e
179
- if cache_stmt
180
- @statements.delete(sql)
181
- else
182
- stmt.close
183
- end
184
- raise e
185
- end
186
-
187
- ret = yield stmt, result
188
- result.free if result
189
- stmt.close unless cache_stmt
190
- ret
191
- end
192
- end
193
92
  end
194
93
  end
195
94
  end
@@ -6,12 +6,67 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
- def quote_column_name(name)
10
- self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
9
+ extend ActiveSupport::Concern
10
+
11
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
12
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
13
+
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
11
53
  end
12
54
 
13
- def quote_table_name(name)
14
- self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
55
+ def cast_bound_value(value)
56
+ case value
57
+ when Rational
58
+ value.to_f.to_s
59
+ when Numeric
60
+ value.to_s
61
+ when BigDecimal
62
+ value.to_s("F")
63
+ when true
64
+ "1"
65
+ when false
66
+ "0"
67
+ else
68
+ value
69
+ end
15
70
  end
16
71
 
17
72
  def unquoted_true
@@ -34,62 +89,33 @@ module ActiveRecord
34
89
  "x'#{value.hex}'"
35
90
  end
36
91
 
37
- def column_name_matcher
38
- COLUMN_NAME
39
- end
40
-
41
- def column_name_with_order_matcher
42
- COLUMN_NAME_WITH_ORDER
92
+ def unquote_identifier(identifier)
93
+ if identifier && identifier.start_with?("`")
94
+ identifier[1..-2]
95
+ else
96
+ identifier
97
+ end
43
98
  end
44
99
 
45
- COLUMN_NAME = /
46
- \A
47
- (
48
- (?:
49
- # `table_name`.`column_name` | function(one or no argument)
50
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
51
- )
52
- (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
53
- )
54
- (?:\s*,\s*\g<1>)*
55
- \z
56
- /ix
57
-
58
- COLUMN_NAME_WITH_ORDER = /
59
- \A
60
- (
61
- (?:
62
- # `table_name`.`column_name` | function(one or no argument)
63
- ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
64
- )
65
- (?:\s+ASC|\s+DESC)?
66
- )
67
- (?:\s*,\s*\g<1>)*
68
- \z
69
- /ix
70
-
71
- private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
72
-
73
- private
74
- # Override +_type_cast+ we pass to mysql2 Date and Time objects instead
75
- # of Strings since mysql2 is able to handle those classes more efficiently.
76
- def _type_cast(value)
77
- case value
78
- when ActiveSupport::TimeWithZone
79
- # We need to check explicitly for ActiveSupport::TimeWithZone because
80
- # we need to transform it to Time objects but we don't want to
81
- # transform Time objects to themselves.
82
- if ActiveRecord::Base.default_timezone == :utc
83
- value.getutc
84
- else
85
- value.getlocal
86
- end
87
- when Date, Time
88
- value
100
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
101
+ # of Strings since MySQL adapters are able to handle those classes more efficiently.
102
+ def type_cast(value) # :nodoc:
103
+ case value
104
+ when ActiveSupport::TimeWithZone
105
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
106
+ # we need to transform it to Time objects but we don't want to
107
+ # transform Time objects to themselves.
108
+ if default_timezone == :utc
109
+ value.getutc
89
110
  else
90
- super
111
+ value.getlocal
91
112
  end
113
+ when Date, Time
114
+ value
115
+ else
116
+ super
92
117
  end
118
+ end
93
119
  end
94
120
  end
95
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,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
@@ -53,14 +53,20 @@ module ActiveRecord
53
53
  end
54
54
 
55
55
  def schema_precision(column)
56
- super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
56
+ if /\Atime(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
57
+ nil
58
+ elsif column.type == :datetime
59
+ column.precision == 0 ? "nil" : super
60
+ else
61
+ super
62
+ end
57
63
  end
58
64
 
59
65
  def schema_collation(column)
60
66
  if column.collation
61
67
  @table_collation_cache ||= {}
62
68
  @table_collation_cache[table_name] ||=
63
- @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"]
64
70
  column.collation.inspect if column.collation != @table_collation_cache[table_name]
65
71
  end
66
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,26 +160,46 @@ 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)
158
168
  MySQL::TableDefinition.new(self, name, **options)
159
169
  end
160
170
 
161
- def new_column_from_field(table_name, field)
171
+ def default_type(table_name, field_name)
172
+ match = create_table_info(table_name)&.match(/`#{field_name}` (.+) DEFAULT ('|\d+|[A-z]+)/)
173
+ default_pre = match[2] if match
174
+
175
+ if default_pre == "'"
176
+ :string
177
+ elsif default_pre&.match?(/^\d+$/)
178
+ :integer
179
+ elsif default_pre&.match?(/^[A-z]+$/)
180
+ :function
181
+ end
182
+ end
183
+
184
+ def new_column_from_field(table_name, field, _definitions)
185
+ field_name = field.fetch(:Field)
162
186
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
163
187
  default, default_function = field[:Default], nil
164
188
 
165
189
  if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
190
+ default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
166
191
  default, default_function = nil, default
167
192
  elsif type_metadata.extra == "DEFAULT_GENERATED"
168
193
  default = +"(#{default})" unless default.start_with?("(")
194
+ default = default.gsub("\\'", "'")
169
195
  default, default_function = nil, default
170
- elsif type_metadata.type == :text && default
196
+ elsif type_metadata.type == :text && default&.start_with?("'")
171
197
  # strip and unescape quotes
172
198
  default = default[1...-1].gsub("\\'", "'")
199
+ elsif default&.match?(/\A\d/)
200
+ # Its a number so we can skip the query to check if it is a function
201
+ elsif default && default_type(table_name, field_name) == :function
202
+ default, default_function = nil, default
173
203
  end
174
204
 
175
205
  MySQL::Column.new(
@@ -206,14 +236,15 @@ module ActiveRecord
206
236
  def data_source_sql(name = nil, type: nil)
207
237
  scope = quoted_scope(name, type: type)
208
238
 
209
- sql = +"SELECT table_name FROM (SELECT table_name, table_type FROM information_schema.tables "
210
- sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
211
- if scope[:type] || scope[:name]
212
- conditions = []
213
- conditions << "_subquery.table_type = #{scope[:type]}" if scope[:type]
214
- conditions << "_subquery.table_name = #{scope[:name]}" if scope[:name]
215
- 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]})"
216
245
  end
246
+
247
+ sql << " AND table_type = #{scope[:type]}" if scope[:type]
217
248
  sql
218
249
  end
219
250