activerecord 5.2.8 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (364) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1393 -587
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +10 -9
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +122 -47
  10. data/lib/active_record/associations/association_scope.rb +24 -24
  11. data/lib/active_record/associations/belongs_to_association.rb +67 -49
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
  13. data/lib/active_record/associations/builder/association.rb +52 -23
  14. data/lib/active_record/associations/builder/belongs_to.rb +44 -61
  15. data/lib/active_record/associations/builder/collection_association.rb +17 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +10 -3
  18. data/lib/active_record/associations/builder/has_one.rb +35 -3
  19. data/lib/active_record/associations/builder/singular_association.rb +5 -3
  20. data/lib/active_record/associations/collection_association.rb +59 -50
  21. data/lib/active_record/associations/collection_proxy.rb +32 -23
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +27 -14
  25. data/lib/active_record/associations/has_many_through_association.rb +26 -19
  26. data/lib/active_record/associations/has_one_association.rb +52 -37
  27. data/lib/active_record/associations/has_one_through_association.rb +6 -6
  28. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +97 -62
  31. data/lib/active_record/associations/preloader/association.rb +220 -60
  32. data/lib/active_record/associations/preloader/batch.rb +48 -0
  33. data/lib/active_record/associations/preloader/branch.rb +147 -0
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -40
  35. data/lib/active_record/associations/preloader.rb +44 -105
  36. data/lib/active_record/associations/singular_association.rb +9 -17
  37. data/lib/active_record/associations/through_association.rb +4 -4
  38. data/lib/active_record/associations.rb +207 -66
  39. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  40. data/lib/active_record/attribute_assignment.rb +17 -19
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +141 -47
  43. data/lib/active_record/attribute_methods/primary_key.rb +22 -27
  44. data/lib/active_record/attribute_methods/query.rb +6 -10
  45. data/lib/active_record/attribute_methods/read.rb +15 -55
  46. data/lib/active_record/attribute_methods/serialization.rb +77 -18
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
  48. data/lib/active_record/attribute_methods/write.rb +18 -37
  49. data/lib/active_record/attribute_methods.rb +90 -153
  50. data/lib/active_record/attributes.rb +38 -12
  51. data/lib/active_record/autosave_association.rb +50 -50
  52. data/lib/active_record/base.rb +23 -18
  53. data/lib/active_record/callbacks.rb +159 -44
  54. data/lib/active_record/coders/yaml_column.rb +12 -3
  55. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
  63. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
  71. data/lib/active_record/connection_adapters/column.rb +33 -11
  72. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  73. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  74. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  75. data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
  76. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  77. data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
  78. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  80. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
  81. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
  82. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  83. data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
  84. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  85. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
  95. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
  116. data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
  122. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
  123. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  124. data/lib/active_record/connection_adapters.rb +53 -0
  125. data/lib/active_record/connection_handling.rb +292 -38
  126. data/lib/active_record/core.rb +385 -158
  127. data/lib/active_record/counter_cache.rb +8 -30
  128. data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
  129. data/lib/active_record/database_configurations/database_config.rb +83 -0
  130. data/lib/active_record/database_configurations/hash_config.rb +154 -0
  131. data/lib/active_record/database_configurations/url_config.rb +53 -0
  132. data/lib/active_record/database_configurations.rb +256 -0
  133. data/lib/active_record/delegated_type.rb +250 -0
  134. data/lib/active_record/destroy_association_async_job.rb +36 -0
  135. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  136. data/lib/active_record/dynamic_matchers.rb +4 -5
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +44 -0
  140. data/lib/active_record/encryption/configurable.rb +61 -0
  141. data/lib/active_record/encryption/context.rb +35 -0
  142. data/lib/active_record/encryption/contexts.rb +72 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +155 -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 +160 -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 +42 -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_serializer.rb +90 -0
  159. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  160. data/lib/active_record/encryption/properties.rb +76 -0
  161. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  162. data/lib/active_record/encryption/scheme.rb +99 -0
  163. data/lib/active_record/encryption.rb +55 -0
  164. data/lib/active_record/enum.rb +130 -51
  165. data/lib/active_record/errors.rb +129 -23
  166. data/lib/active_record/explain.rb +10 -6
  167. data/lib/active_record/explain_registry.rb +11 -6
  168. data/lib/active_record/explain_subscriber.rb +1 -1
  169. data/lib/active_record/fixture_set/file.rb +22 -15
  170. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  171. data/lib/active_record/fixture_set/render_context.rb +17 -0
  172. data/lib/active_record/fixture_set/table_row.rb +187 -0
  173. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  174. data/lib/active_record/fixtures.rb +206 -490
  175. data/lib/active_record/future_result.rb +139 -0
  176. data/lib/active_record/gem_version.rb +3 -3
  177. data/lib/active_record/inheritance.rb +104 -37
  178. data/lib/active_record/insert_all.rb +278 -0
  179. data/lib/active_record/integration.rb +69 -18
  180. data/lib/active_record/internal_metadata.rb +24 -9
  181. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  182. data/lib/active_record/locking/optimistic.rb +41 -26
  183. data/lib/active_record/locking/pessimistic.rb +18 -8
  184. data/lib/active_record/log_subscriber.rb +46 -35
  185. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  186. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  187. data/lib/active_record/middleware/database_selector.rb +82 -0
  188. data/lib/active_record/middleware/shard_selector.rb +60 -0
  189. data/lib/active_record/migration/command_recorder.rb +96 -44
  190. data/lib/active_record/migration/compatibility.rb +246 -64
  191. data/lib/active_record/migration/join_table.rb +1 -2
  192. data/lib/active_record/migration.rb +266 -187
  193. data/lib/active_record/model_schema.rb +165 -52
  194. data/lib/active_record/nested_attributes.rb +17 -19
  195. data/lib/active_record/no_touching.rb +11 -4
  196. data/lib/active_record/null_relation.rb +2 -7
  197. data/lib/active_record/persistence.rb +467 -92
  198. data/lib/active_record/query_cache.rb +21 -4
  199. data/lib/active_record/query_logs.rb +138 -0
  200. data/lib/active_record/querying.rb +51 -24
  201. data/lib/active_record/railtie.rb +224 -57
  202. data/lib/active_record/railties/console_sandbox.rb +2 -4
  203. data/lib/active_record/railties/controller_runtime.rb +31 -36
  204. data/lib/active_record/railties/databases.rake +369 -101
  205. data/lib/active_record/readonly_attributes.rb +15 -0
  206. data/lib/active_record/reflection.rb +170 -137
  207. data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
  208. data/lib/active_record/relation/batches.rb +46 -37
  209. data/lib/active_record/relation/calculations.rb +168 -96
  210. data/lib/active_record/relation/delegation.rb +37 -52
  211. data/lib/active_record/relation/finder_methods.rb +79 -58
  212. data/lib/active_record/relation/from_clause.rb +5 -1
  213. data/lib/active_record/relation/merger.rb +50 -51
  214. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  215. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  216. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  217. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  218. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  219. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  220. data/lib/active_record/relation/predicate_builder.rb +58 -46
  221. data/lib/active_record/relation/query_attribute.rb +9 -10
  222. data/lib/active_record/relation/query_methods.rb +685 -208
  223. data/lib/active_record/relation/record_fetch_warning.rb +9 -11
  224. data/lib/active_record/relation/spawn_methods.rb +10 -10
  225. data/lib/active_record/relation/where_clause.rb +108 -64
  226. data/lib/active_record/relation.rb +515 -151
  227. data/lib/active_record/result.rb +78 -42
  228. data/lib/active_record/runtime_registry.rb +9 -13
  229. data/lib/active_record/sanitization.rb +29 -44
  230. data/lib/active_record/schema.rb +37 -31
  231. data/lib/active_record/schema_dumper.rb +74 -23
  232. data/lib/active_record/schema_migration.rb +7 -9
  233. data/lib/active_record/scoping/default.rb +62 -17
  234. data/lib/active_record/scoping/named.rb +17 -32
  235. data/lib/active_record/scoping.rb +70 -41
  236. data/lib/active_record/secure_token.rb +16 -8
  237. data/lib/active_record/serialization.rb +6 -4
  238. data/lib/active_record/signed_id.rb +116 -0
  239. data/lib/active_record/statement_cache.rb +49 -6
  240. data/lib/active_record/store.rb +88 -9
  241. data/lib/active_record/suppressor.rb +13 -17
  242. data/lib/active_record/table_metadata.rb +42 -43
  243. data/lib/active_record/tasks/database_tasks.rb +352 -94
  244. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  245. data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
  246. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  247. data/lib/active_record/test_databases.rb +24 -0
  248. data/lib/active_record/test_fixtures.rb +287 -0
  249. data/lib/active_record/timestamp.rb +44 -34
  250. data/lib/active_record/touch_later.rb +23 -22
  251. data/lib/active_record/transactions.rb +67 -128
  252. data/lib/active_record/translation.rb +3 -3
  253. data/lib/active_record/type/adapter_specific_registry.rb +34 -19
  254. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  255. data/lib/active_record/type/internal/timezone.rb +2 -2
  256. data/lib/active_record/type/serialized.rb +7 -4
  257. data/lib/active_record/type/time.rb +10 -0
  258. data/lib/active_record/type/type_map.rb +17 -21
  259. data/lib/active_record/type/unsigned_integer.rb +0 -1
  260. data/lib/active_record/type.rb +9 -5
  261. data/lib/active_record/type_caster/connection.rb +15 -15
  262. data/lib/active_record/type_caster/map.rb +8 -8
  263. data/lib/active_record/validations/associated.rb +2 -3
  264. data/lib/active_record/validations/numericality.rb +35 -0
  265. data/lib/active_record/validations/uniqueness.rb +39 -31
  266. data/lib/active_record/validations.rb +4 -3
  267. data/lib/active_record.rb +209 -32
  268. data/lib/arel/alias_predication.rb +9 -0
  269. data/lib/arel/attributes/attribute.rb +33 -0
  270. data/lib/arel/collectors/bind.rb +29 -0
  271. data/lib/arel/collectors/composite.rb +39 -0
  272. data/lib/arel/collectors/plain_string.rb +20 -0
  273. data/lib/arel/collectors/sql_string.rb +27 -0
  274. data/lib/arel/collectors/substitute_binds.rb +35 -0
  275. data/lib/arel/crud.rb +48 -0
  276. data/lib/arel/delete_manager.rb +32 -0
  277. data/lib/arel/errors.rb +9 -0
  278. data/lib/arel/expressions.rb +29 -0
  279. data/lib/arel/factory_methods.rb +49 -0
  280. data/lib/arel/filter_predications.rb +9 -0
  281. data/lib/arel/insert_manager.rb +48 -0
  282. data/lib/arel/math.rb +45 -0
  283. data/lib/arel/nodes/and.rb +32 -0
  284. data/lib/arel/nodes/ascending.rb +23 -0
  285. data/lib/arel/nodes/binary.rb +126 -0
  286. data/lib/arel/nodes/bind_param.rb +44 -0
  287. data/lib/arel/nodes/case.rb +55 -0
  288. data/lib/arel/nodes/casted.rb +62 -0
  289. data/lib/arel/nodes/comment.rb +29 -0
  290. data/lib/arel/nodes/count.rb +12 -0
  291. data/lib/arel/nodes/delete_statement.rb +44 -0
  292. data/lib/arel/nodes/descending.rb +23 -0
  293. data/lib/arel/nodes/equality.rb +15 -0
  294. data/lib/arel/nodes/extract.rb +24 -0
  295. data/lib/arel/nodes/false.rb +16 -0
  296. data/lib/arel/nodes/filter.rb +10 -0
  297. data/lib/arel/nodes/full_outer_join.rb +8 -0
  298. data/lib/arel/nodes/function.rb +45 -0
  299. data/lib/arel/nodes/grouping.rb +11 -0
  300. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  301. data/lib/arel/nodes/in.rb +15 -0
  302. data/lib/arel/nodes/infix_operation.rb +92 -0
  303. data/lib/arel/nodes/inner_join.rb +8 -0
  304. data/lib/arel/nodes/insert_statement.rb +37 -0
  305. data/lib/arel/nodes/join_source.rb +20 -0
  306. data/lib/arel/nodes/matches.rb +18 -0
  307. data/lib/arel/nodes/named_function.rb +23 -0
  308. data/lib/arel/nodes/node.rb +51 -0
  309. data/lib/arel/nodes/node_expression.rb +13 -0
  310. data/lib/arel/nodes/ordering.rb +27 -0
  311. data/lib/arel/nodes/outer_join.rb +8 -0
  312. data/lib/arel/nodes/over.rb +15 -0
  313. data/lib/arel/nodes/regexp.rb +16 -0
  314. data/lib/arel/nodes/right_outer_join.rb +8 -0
  315. data/lib/arel/nodes/select_core.rb +67 -0
  316. data/lib/arel/nodes/select_statement.rb +41 -0
  317. data/lib/arel/nodes/sql_literal.rb +19 -0
  318. data/lib/arel/nodes/string_join.rb +11 -0
  319. data/lib/arel/nodes/table_alias.rb +31 -0
  320. data/lib/arel/nodes/terminal.rb +16 -0
  321. data/lib/arel/nodes/true.rb +16 -0
  322. data/lib/arel/nodes/unary.rb +44 -0
  323. data/lib/arel/nodes/unary_operation.rb +20 -0
  324. data/lib/arel/nodes/unqualified_column.rb +22 -0
  325. data/lib/arel/nodes/update_statement.rb +46 -0
  326. data/lib/arel/nodes/values_list.rb +9 -0
  327. data/lib/arel/nodes/window.rb +126 -0
  328. data/lib/arel/nodes/with.rb +11 -0
  329. data/lib/arel/nodes.rb +71 -0
  330. data/lib/arel/order_predications.rb +13 -0
  331. data/lib/arel/predications.rb +258 -0
  332. data/lib/arel/select_manager.rb +276 -0
  333. data/lib/arel/table.rb +117 -0
  334. data/lib/arel/tree_manager.rb +60 -0
  335. data/lib/arel/update_manager.rb +48 -0
  336. data/lib/arel/visitors/dot.rb +298 -0
  337. data/lib/arel/visitors/mysql.rb +99 -0
  338. data/lib/arel/visitors/postgresql.rb +110 -0
  339. data/lib/arel/visitors/sqlite.rb +38 -0
  340. data/lib/arel/visitors/to_sql.rb +955 -0
  341. data/lib/arel/visitors/visitor.rb +45 -0
  342. data/lib/arel/visitors.rb +13 -0
  343. data/lib/arel/window_predications.rb +9 -0
  344. data/lib/arel.rb +55 -0
  345. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  346. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  347. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  348. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  349. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  350. data/lib/rails/generators/active_record/migration.rb +19 -2
  351. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  352. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  353. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  354. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  355. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  356. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  357. metadata +162 -32
  358. data/lib/active_record/attribute_decorators.rb +0 -90
  359. data/lib/active_record/collection_cache_key.rb +0 -53
  360. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  361. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  362. data/lib/active_record/define_callbacks.rb +0 -22
  363. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  364. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -5,7 +5,9 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  # An abstract definition of a column in a table.
7
7
  class Column
8
- attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation, :comment
8
+ include Deduplicable
9
+
10
+ attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
9
11
 
10
12
  delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
11
13
 
@@ -15,9 +17,8 @@ module ActiveRecord
15
17
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
16
18
  # +sql_type_metadata+ is various information about the type of the column
17
19
  # +null+ determines if this column allows +NULL+ values.
18
- def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
20
+ def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
19
21
  @name = name.freeze
20
- @table_name = table_name
21
22
  @sql_type_metadata = sql_type_metadata
22
23
  @null = null
23
24
  @default = default
@@ -44,7 +45,6 @@ module ActiveRecord
44
45
 
45
46
  def init_with(coder)
46
47
  @name = coder["name"]
47
- @table_name = coder["table_name"]
48
48
  @sql_type_metadata = coder["sql_type_metadata"]
49
49
  @null = coder["null"]
50
50
  @default = coder["default"]
@@ -55,7 +55,6 @@ module ActiveRecord
55
55
 
56
56
  def encode_with(coder)
57
57
  coder["name"] = @name
58
- coder["table_name"] = @table_name
59
58
  coder["sql_type_metadata"] = @sql_type_metadata
60
59
  coder["null"] = @null
61
60
  coder["default"] = @default
@@ -66,23 +65,46 @@ module ActiveRecord
66
65
 
67
66
  def ==(other)
68
67
  other.is_a?(Column) &&
69
- attributes_for_hash == other.attributes_for_hash
68
+ name == other.name &&
69
+ default == other.default &&
70
+ sql_type_metadata == other.sql_type_metadata &&
71
+ null == other.null &&
72
+ default_function == other.default_function &&
73
+ collation == other.collation &&
74
+ comment == other.comment
70
75
  end
71
76
  alias :eql? :==
72
77
 
73
78
  def hash
74
- attributes_for_hash.hash
79
+ Column.hash ^
80
+ name.hash ^
81
+ name.encoding.hash ^
82
+ default.hash ^
83
+ sql_type_metadata.hash ^
84
+ null.hash ^
85
+ default_function.hash ^
86
+ collation.hash ^
87
+ comment.hash
75
88
  end
76
89
 
77
- protected
90
+ def virtual?
91
+ false
92
+ end
78
93
 
79
- def attributes_for_hash
80
- [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
94
+ private
95
+ def deduplicated
96
+ @name = -name
97
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
98
+ @default = -default if default
99
+ @default_function = -default_function if default_function
100
+ @collation = -collation if collation
101
+ @comment = -comment if comment
102
+ super
81
103
  end
82
104
  end
83
105
 
84
106
  class NullColumn < Column
85
- def initialize(name)
107
+ def initialize(name, **)
86
108
  super(name, nil)
87
109
  end
88
110
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Deduplicable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def registry
10
+ @registry ||= {}
11
+ end
12
+
13
+ def new(*, **)
14
+ super.deduplicate
15
+ end
16
+ end
17
+
18
+ def deduplicate
19
+ self.class.registry[self] ||= deduplicated
20
+ end
21
+ alias :-@ :deduplicate
22
+
23
+ private
24
+ def deduplicated
25
+ freeze
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class LegacyPoolManager # :nodoc:
6
+ def initialize
7
+ @name_to_pool_config = {}
8
+ end
9
+
10
+ def shard_names
11
+ @name_to_pool_config.keys
12
+ end
13
+
14
+ def pool_configs(_ = nil)
15
+ @name_to_pool_config.values
16
+ end
17
+
18
+ def remove_pool_config(_, shard)
19
+ @name_to_pool_config.delete(shard)
20
+ end
21
+
22
+ def get_pool_config(_, shard)
23
+ @name_to_pool_config[shard]
24
+ end
25
+
26
+ def set_pool_config(role, shard, pool_config)
27
+ if pool_config
28
+ @name_to_pool_config[shard] = pool_config
29
+ else
30
+ raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  end
12
12
 
13
13
  def case_sensitive?
14
- collation && !/_ci\z/.match?(collation)
14
+ collation && !collation.end_with?("_ci")
15
15
  end
16
16
 
17
17
  def auto_increment?
@@ -5,13 +5,13 @@ module ActiveRecord
5
5
  module MySQL
6
6
  module DatabaseStatements
7
7
  # Returns an ActiveRecord::Result instance.
8
- def select_all(*) # :nodoc:
8
+ def select_all(*, **) # :nodoc:
9
9
  result = if ExplainRegistry.collect? && prepared_statements
10
10
  unprepared_statement { super }
11
11
  else
12
12
  super
13
13
  end
14
- discard_remaining_results
14
+ @connection.abandon_results!
15
15
  result
16
16
  end
17
17
 
@@ -19,98 +19,166 @@ module ActiveRecord
19
19
  execute(sql, name).to_a
20
20
  end
21
21
 
22
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
23
+ :desc, :describe, :set, :show, :use
24
+ ) # :nodoc:
25
+ private_constant :READ_QUERY
26
+
27
+ def write_query?(sql) # :nodoc:
28
+ !READ_QUERY.match?(sql)
29
+ rescue ArgumentError # Invalid encoding
30
+ !READ_QUERY.match?(sql.b)
31
+ end
32
+
33
+ def explain(arel, binds = [])
34
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
35
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
36
+ result = exec_query(sql, "EXPLAIN", binds)
37
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
38
+
39
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
40
+ end
41
+
22
42
  # Executes the SQL statement in the context of this connection.
23
- def execute(sql, name = nil)
24
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
25
- # made since we established the connection
26
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
43
+ def execute(sql, name = nil, async: false)
44
+ sql = transform_query(sql)
45
+ check_if_write_query(sql)
27
46
 
28
- super
47
+ raw_execute(sql, name, async: async)
29
48
  end
30
49
 
31
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
50
+ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
32
51
  if without_prepared_statement?(binds)
33
- execute_and_free(sql, name) do |result|
34
- ActiveRecord::Result.new(result.fields, result.to_a) if result
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
35
58
  end
36
59
  else
37
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
38
- ActiveRecord::Result.new(result.fields, result.to_a) if result
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
39
66
  end
40
67
  end
41
68
  end
42
69
 
43
- def exec_delete(sql, name = nil, binds = [])
70
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
44
71
  if without_prepared_statement?(binds)
45
- execute_and_free(sql, name) { @connection.affected_rows }
72
+ @lock.synchronize do
73
+ execute_and_free(sql, name) { @connection.affected_rows }
74
+ end
46
75
  else
47
76
  exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
48
77
  end
49
78
  end
50
79
  alias :exec_update :exec_delete
51
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
+
52
90
  private
53
- def default_insert_value(column)
54
- Arel.sql("DEFAULT") unless column.auto_increment?
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
55
97
  end
56
98
 
57
- def last_inserted_id(result)
58
- @connection.last_id
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
59
105
  end
60
106
 
61
- def discard_remaining_results
62
- @connection.next_result while @connection.more_results?
107
+ def default_insert_value(column)
108
+ super unless column.auto_increment?
63
109
  end
64
110
 
65
- def supports_set_server_option?
66
- @connection.respond_to?(:set_server_option)
111
+ def last_inserted_id(result)
112
+ @connection.last_id
67
113
  end
68
114
 
69
- def multi_statements_enabled?(flags)
115
+ def multi_statements_enabled?
116
+ flags = @config[:flags]
117
+
70
118
  if flags.is_a?(Array)
71
119
  flags.include?("MULTI_STATEMENTS")
72
120
  else
73
- (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
121
+ flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
74
122
  end
75
123
  end
76
124
 
77
125
  def with_multi_statements
78
- previous_flags = @config[:flags]
126
+ multi_statements_was = multi_statements_enabled?
79
127
 
80
- unless multi_statements_enabled?(previous_flags)
81
- if supports_set_server_option?
82
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
83
- else
84
- @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
85
- reconnect!
86
- end
128
+ unless multi_statements_was
129
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
87
130
  end
88
131
 
89
132
  yield
90
133
  ensure
91
- unless multi_statements_enabled?(previous_flags)
92
- if supports_set_server_option?
93
- @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
134
+ unless multi_statements_was
135
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
136
+ end
137
+ end
138
+
139
+ def combine_multi_statements(total_sql)
140
+ total_sql.each_with_object([]) do |sql, total_sql_chunks|
141
+ previous_packet = total_sql_chunks.last
142
+ if max_allowed_packet_reached?(sql, previous_packet)
143
+ total_sql_chunks << +sql
94
144
  else
95
- @config[:flags] = previous_flags
96
- reconnect!
145
+ previous_packet << ";\n"
146
+ previous_packet << sql
97
147
  end
98
148
  end
99
149
  end
100
150
 
101
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
102
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
151
+ def max_allowed_packet_reached?(current_packet, previous_packet)
152
+ if current_packet.bytesize > max_allowed_packet
153
+ raise ActiveRecordError,
154
+ "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
155
+ elsif previous_packet.nil?
156
+ true
157
+ else
158
+ (current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
159
+ end
160
+ end
161
+
162
+ def max_allowed_packet
163
+ @max_allowed_packet ||= show_variable("max_allowed_packet")
164
+ 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
103
174
  # made since we established the connection
104
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
175
+ @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
105
176
 
106
177
  type_casted_binds = type_casted_binds(binds)
107
178
 
108
- log(sql, name, binds, type_casted_binds) do
179
+ log(sql, name, binds, type_casted_binds, async: async) do
109
180
  if cache_stmt
110
- cache = @statements[sql] ||= {
111
- stmt: @connection.prepare(sql)
112
- }
113
- stmt = cache[:stmt]
181
+ stmt = @statements[sql] ||= @connection.prepare(sql)
114
182
  else
115
183
  stmt = @connection.prepare(sql)
116
184
  end
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  private
40
-
41
40
  def compute_column_widths(result)
42
41
  [].tap do |widths|
43
42
  result.columns.each_with_index do |column, i|
@@ -57,7 +56,7 @@ module ActiveRecord
57
56
  items.each_with_index do |item, i|
58
57
  item = "NULL" if item.nil?
59
58
  justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
60
- cells << item.to_s.send(justifier, widths[i])
59
+ cells << item.to_s.public_send(justifier, widths[i])
61
60
  end
62
61
  "| " + cells.join(" | ") + " |"
63
62
  end
@@ -1,15 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/time_with_zone"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module MySQL
6
8
  module Quoting # :nodoc:
9
+ def quote_bound_value(value)
10
+ case value
11
+ when Numeric
12
+ quote(value.to_s)
13
+ when BigDecimal
14
+ quote(value.to_s("F"))
15
+ when true
16
+ "'1'"
17
+ when false
18
+ "'0'"
19
+ else
20
+ quote(value)
21
+ end
22
+ end
23
+
7
24
  def quote_column_name(name)
8
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
25
+ self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
9
26
  end
10
27
 
11
28
  def quote_table_name(name)
12
- @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
29
+ self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
13
30
  end
14
31
 
15
32
  def unquoted_true
@@ -32,12 +49,61 @@ module ActiveRecord
32
49
  "x'#{value.hex}'"
33
50
  end
34
51
 
35
- def _type_cast(value)
52
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
53
+ # of Strings since mysql2 is able to handle those classes more efficiently.
54
+ def type_cast(value) # :nodoc:
36
55
  case value
37
- when Date, Time then value
38
- else super
56
+ when ActiveSupport::TimeWithZone
57
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
58
+ # we need to transform it to Time objects but we don't want to
59
+ # transform Time objects to themselves.
60
+ if ActiveRecord.default_timezone == :utc
61
+ value.getutc
62
+ else
63
+ value.getlocal
64
+ end
65
+ when Date, Time
66
+ value
67
+ else
68
+ super
39
69
  end
40
70
  end
71
+
72
+ def column_name_matcher
73
+ COLUMN_NAME
74
+ end
75
+
76
+ def column_name_with_order_matcher
77
+ COLUMN_NAME_WITH_ORDER
78
+ end
79
+
80
+ COLUMN_NAME = /
81
+ \A
82
+ (
83
+ (?:
84
+ # `table_name`.`column_name` | function(one or no argument)
85
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
86
+ )
87
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
88
+ )
89
+ (?:\s*,\s*\g<1>)*
90
+ \z
91
+ /ix
92
+
93
+ COLUMN_NAME_WITH_ORDER = /
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+ASC|\s+DESC)?
101
+ )
102
+ (?:\s*,\s*\g<1>)*
103
+ \z
104
+ /ix
105
+
106
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
41
107
  end
42
108
  end
43
109
  end
@@ -3,34 +3,58 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
- class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
7
- delegate :add_sql_comment!, :mariadb?, to: :@conn
8
- private :add_sql_comment!, :mariadb?
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
+ delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
9
8
 
10
9
  private
11
-
12
10
  def visit_DropForeignKey(name)
13
11
  "DROP FOREIGN KEY #{name}"
14
12
  end
15
13
 
14
+ def visit_DropCheckConstraint(name)
15
+ "DROP #{mariadb? ? 'CONSTRAINT' : 'CHECK'} #{name}"
16
+ end
17
+
16
18
  def visit_AddColumnDefinition(o)
17
19
  add_column_position!(super, column_options(o.column))
18
20
  end
19
21
 
20
22
  def visit_ChangeColumnDefinition(o)
21
- change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}".dup
23
+ change_column_sql = +"CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
22
24
  add_column_position!(change_column_sql, column_options(o.column))
23
25
  end
24
26
 
25
- def add_table_options!(create_sql, options)
26
- add_sql_comment!(super, options[:comment])
27
+ def visit_CreateIndexDefinition(o)
28
+ sql = visit_IndexDefinition(o.index, true)
29
+ sql << " #{o.algorithm}" if o.algorithm
30
+ sql
31
+ end
32
+
33
+ def visit_IndexDefinition(o, create = false)
34
+ index_type = o.type&.to_s&.upcase || o.unique && "UNIQUE"
35
+
36
+ sql = create ? ["CREATE"] : []
37
+ sql << index_type if index_type
38
+ sql << "INDEX"
39
+ sql << quote_column_name(o.name)
40
+ sql << "USING #{o.using}" if o.using
41
+ sql << "ON #{quote_table_name(o.table)}" if create
42
+ sql << "(#{quoted_columns(o)})"
43
+
44
+ add_sql_comment!(sql.join(" "), o.comment)
45
+ end
46
+
47
+ def add_table_options!(create_sql, o)
48
+ create_sql << " DEFAULT CHARSET=#{o.charset}" if o.charset
49
+ create_sql << " COLLATE=#{o.collation}" if o.collation
50
+ add_sql_comment!(super, o.comment)
27
51
  end
28
52
 
29
53
  def add_column_options!(sql, options)
30
54
  # By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
31
55
  # and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
32
56
  # column to contain NULL, explicitly declare it with the NULL attribute.
33
- # See https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
57
+ # See https://dev.mysql.com/doc/refman/en/timestamp-initialization.html
34
58
  if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
35
59
  sql << " NULL" unless options[:null] == false || options_include_default?(options)
36
60
  end
@@ -64,8 +88,8 @@ module ActiveRecord
64
88
  end
65
89
 
66
90
  def index_in_create(table_name, column_name, options)
67
- index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
68
- add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})".dup, comment)
91
+ index, _ = @conn.add_index_options(table_name, column_name, **options)
92
+ accept(index)
69
93
  end
70
94
  end
71
95
  end