activerecord 5.0.7.2 → 6.1.1

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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
3
5
  def self.gem_version
@@ -5,10 +7,10 @@ module ActiveRecord
5
7
  end
6
8
 
7
9
  module VERSION
8
- MAJOR = 5
9
- MINOR = 0
10
- TINY = 7
11
- PRE = "2"
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 1
13
+ PRE = nil
12
14
 
13
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
16
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
2
4
 
3
5
  module ActiveRecord
4
6
  # == Single table inheritance
@@ -19,7 +21,7 @@ module ActiveRecord
19
21
  # Be aware that because the type column is an attribute on the record every new
20
22
  # subclass will instantly be marked as dirty and the type column will be included
21
23
  # in the list of changed attributes on the record. This is different from non
22
- # STI classes:
24
+ # Single Table Inheritance(STI) classes:
23
25
  #
24
26
  # Company.new.changed? # => false
25
27
  # Firm.new.changed? # => true
@@ -30,37 +32,42 @@ module ActiveRecord
30
32
  # for differentiating between them or reloading the right type with find.
31
33
  #
32
34
  # Note, all the attributes for all the cases are kept in the same table. Read more:
33
- # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
35
+ # https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
34
36
  #
35
37
  module Inheritance
36
38
  extend ActiveSupport::Concern
37
39
 
38
40
  included do
41
+ class_attribute :store_full_class_name, instance_writer: false, default: true
42
+
39
43
  # Determines whether to store the full constant name including namespace when using STI.
40
- class_attribute :store_full_sti_class, instance_writer: false
41
- self.store_full_sti_class = true
44
+ # This is true, by default.
45
+ class_attribute :store_full_sti_class, instance_writer: false, default: true
42
46
  end
43
47
 
44
48
  module ClassMethods
45
49
  # Determines if one of the attributes passed in is the inheritance column,
46
50
  # and if the inheritance column is attr accessible, it initializes an
47
51
  # instance of the given subclass instead of the base class.
48
- def new(*args, &block)
52
+ def new(attributes = nil, &block)
49
53
  if abstract_class? || self == Base
50
54
  raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
51
55
  end
52
56
 
53
- attrs = args.first
54
- if has_attribute?(inheritance_column)
55
- subclass = subclass_from_attributes(attrs)
57
+ if _has_attribute?(inheritance_column)
58
+ subclass = subclass_from_attributes(attributes)
59
+
60
+ if subclass.nil? && scope_attributes = current_scope&.scope_for_create
61
+ subclass = subclass_from_attributes(scope_attributes)
62
+ end
56
63
 
57
- if subclass.nil? && base_class == self
64
+ if subclass.nil? && base_class?
58
65
  subclass = subclass_from_attributes(column_defaults)
59
66
  end
60
67
  end
61
68
 
62
69
  if subclass && subclass != self
63
- subclass.new(*args, &block)
70
+ subclass.new(attributes, &block)
64
71
  else
65
72
  super
66
73
  end
@@ -103,21 +110,53 @@ module ActiveRecord
103
110
  end
104
111
  end
105
112
 
106
- # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
107
- # If you are using inheritance with ActiveRecord and don't want child classes
108
- # to utilize the implied STI table name of the parent class, this will need to be true.
109
- # For example, given the following:
113
+ # Returns whether the class is a base class.
114
+ # See #base_class for more information.
115
+ def base_class?
116
+ base_class == self
117
+ end
118
+
119
+ # Set this to +true+ if this is an abstract class (see
120
+ # <tt>abstract_class?</tt>).
121
+ # If you are using inheritance with Active Record and don't want a class
122
+ # to be considered as part of the STI hierarchy, you must set this to
123
+ # true.
124
+ # +ApplicationRecord+, for example, is generated as an abstract class.
110
125
  #
111
- # class SuperClass < ActiveRecord::Base
126
+ # Consider the following default behaviour:
127
+ #
128
+ # Shape = Class.new(ActiveRecord::Base)
129
+ # Polygon = Class.new(Shape)
130
+ # Square = Class.new(Polygon)
131
+ #
132
+ # Shape.table_name # => "shapes"
133
+ # Polygon.table_name # => "shapes"
134
+ # Square.table_name # => "shapes"
135
+ # Shape.create! # => #<Shape id: 1, type: nil>
136
+ # Polygon.create! # => #<Polygon id: 2, type: "Polygon">
137
+ # Square.create! # => #<Square id: 3, type: "Square">
138
+ #
139
+ # However, when using <tt>abstract_class</tt>, +Shape+ is omitted from
140
+ # the hierarchy:
141
+ #
142
+ # class Shape < ActiveRecord::Base
112
143
  # self.abstract_class = true
113
144
  # end
114
- # class Child < SuperClass
115
- # self.table_name = 'the_table_i_really_want'
116
- # end
117
- #
145
+ # Polygon = Class.new(Shape)
146
+ # Square = Class.new(Polygon)
118
147
  #
119
- # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
148
+ # Shape.table_name # => nil
149
+ # Polygon.table_name # => "polygons"
150
+ # Square.table_name # => "polygons"
151
+ # Shape.create! # => NotImplementedError: Shape is an abstract class and cannot be instantiated.
152
+ # Polygon.create! # => #<Polygon id: 1, type: nil>
153
+ # Square.create! # => #<Square id: 2, type: "Square">
120
154
  #
155
+ # Note that in the above example, to disallow the creation of a plain
156
+ # +Polygon+, you should use <tt>validates :type, presence: true</tt>,
157
+ # instead of setting it as an abstract class. This way, +Polygon+ will
158
+ # stay in the hierarchy, and Active Record will continue to correctly
159
+ # derive the table name.
121
160
  attr_accessor :abstract_class
122
161
 
123
162
  # Returns whether this class is an abstract class or not.
@@ -125,91 +164,126 @@ module ActiveRecord
125
164
  defined?(@abstract_class) && @abstract_class == true
126
165
  end
127
166
 
167
+ # Returns the value to be stored in the inheritance column for STI.
128
168
  def sti_name
129
- store_full_sti_class ? name : name.demodulize
169
+ store_full_sti_class && store_full_class_name ? name : name.demodulize
130
170
  end
131
171
 
132
- protected
133
-
134
- # Returns the class type of the record using the current module as a prefix. So descendants of
135
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
136
- def compute_type(type_name)
137
- if type_name.match(/^::/)
138
- # If the type is prefixed with a scope operator then we assume that
139
- # the type_name is an absolute reference.
172
+ # Returns the class for the provided +type_name+.
173
+ #
174
+ # It is used to find the class correspondent to the value stored in the inheritance column.
175
+ def sti_class_for(type_name)
176
+ if store_full_sti_class && store_full_class_name
140
177
  ActiveSupport::Dependencies.constantize(type_name)
141
178
  else
142
- # Build a list of candidates to search for
143
- candidates = []
144
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
145
- candidates << type_name
146
-
147
- candidates.each do |candidate|
148
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
149
- return constant if candidate == constant.to_s
150
- end
151
-
152
- raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
179
+ compute_type(type_name)
153
180
  end
181
+ rescue NameError
182
+ raise SubclassNotFound,
183
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
184
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
185
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
186
+ "or overwrite #{name}.inheritance_column to use another column for that information."
154
187
  end
155
188
 
156
- private
189
+ # Returns the value to be stored in the polymorphic type column for Polymorphic Associations.
190
+ def polymorphic_name
191
+ store_full_class_name ? base_class.name : base_class.name.demodulize
192
+ end
157
193
 
158
- # Called by +instantiate+ to decide which class to use for a new
159
- # record instance. For single-table inheritance, we check the record
160
- # for a +type+ column and return the corresponding class.
161
- def discriminate_class_for_record(record)
162
- if using_single_table_inheritance?(record)
163
- find_sti_class(record[inheritance_column])
194
+ # Returns the class for the provided +name+.
195
+ #
196
+ # It is used to find the class correspondent to the value stored in the polymorphic type column.
197
+ def polymorphic_class_for(name)
198
+ if store_full_class_name
199
+ ActiveSupport::Dependencies.constantize(name)
164
200
  else
165
- super
201
+ compute_type(name)
166
202
  end
167
203
  end
168
204
 
169
- def using_single_table_inheritance?(record)
170
- record[inheritance_column].present? && has_attribute?(inheritance_column)
205
+ def inherited(subclass)
206
+ subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
207
+ super
171
208
  end
172
209
 
173
- def find_sti_class(type_name)
174
- type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
175
- subclass = begin
176
- if store_full_sti_class
210
+ protected
211
+ # Returns the class type of the record using the current module as a prefix. So descendants of
212
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
213
+ def compute_type(type_name)
214
+ if type_name.start_with?("::")
215
+ # If the type is prefixed with a scope operator then we assume that
216
+ # the type_name is an absolute reference.
177
217
  ActiveSupport::Dependencies.constantize(type_name)
178
218
  else
179
- compute_type(type_name)
219
+ type_candidate = @_type_candidates_cache[type_name]
220
+ if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
221
+ return type_constant
222
+ end
223
+
224
+ # Build a list of candidates to search for
225
+ candidates = []
226
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
227
+ candidates << type_name
228
+
229
+ candidates.each do |candidate|
230
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
231
+ if candidate == constant.to_s
232
+ @_type_candidates_cache[type_name] = candidate
233
+ return constant
234
+ end
235
+ end
236
+
237
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
180
238
  end
181
- rescue NameError
182
- raise SubclassNotFound,
183
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
184
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
185
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
186
- "or overwrite #{name}.inheritance_column to use another column for that information."
187
239
  end
188
- unless subclass == self || descendants.include?(subclass)
189
- raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
240
+
241
+ private
242
+ # Called by +instantiate+ to decide which class to use for a new
243
+ # record instance. For single-table inheritance, we check the record
244
+ # for a +type+ column and return the corresponding class.
245
+ def discriminate_class_for_record(record)
246
+ if using_single_table_inheritance?(record)
247
+ find_sti_class(record[inheritance_column])
248
+ else
249
+ super
250
+ end
190
251
  end
191
- subclass
192
- end
193
252
 
194
- def type_condition(table = arel_table)
195
- sti_column = arel_attribute(inheritance_column, table)
196
- sti_names = ([self] + descendants).map(&:sti_name)
253
+ def using_single_table_inheritance?(record)
254
+ record[inheritance_column].present? && _has_attribute?(inheritance_column)
255
+ end
197
256
 
198
- sti_column.in(sti_names)
199
- end
257
+ def find_sti_class(type_name)
258
+ type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
259
+ subclass = sti_class_for(type_name)
260
+
261
+ unless subclass == self || descendants.include?(subclass)
262
+ raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
263
+ end
264
+
265
+ subclass
266
+ end
200
267
 
201
- # Detect the subclass from the inheritance column of attrs. If the inheritance column value
202
- # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
203
- def subclass_from_attributes(attrs)
204
- attrs = attrs.to_h if attrs.respond_to?(:permitted?)
205
- if attrs.is_a?(Hash)
206
- subclass_name = attrs.with_indifferent_access[inheritance_column]
268
+ def type_condition(table = arel_table)
269
+ sti_column = table[inheritance_column]
270
+ sti_names = ([self] + descendants).map(&:sti_name)
207
271
 
208
- if subclass_name.present?
209
- find_sti_class(subclass_name)
272
+ predicate_builder.build(sti_column, sti_names)
273
+ end
274
+
275
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
276
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
277
+ def subclass_from_attributes(attrs)
278
+ attrs = attrs.to_h if attrs.respond_to?(:permitted?)
279
+ if attrs.is_a?(Hash)
280
+ subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym]
281
+
282
+ if subclass_name.present?
283
+ find_sti_class(subclass_name)
284
+ end
210
285
  end
211
286
  end
212
- end
213
287
  end
214
288
 
215
289
  def initialize_dup(other)
@@ -218,22 +292,21 @@ module ActiveRecord
218
292
  end
219
293
 
220
294
  private
295
+ def initialize_internals_callback
296
+ super
297
+ ensure_proper_type
298
+ end
221
299
 
222
- def initialize_internals_callback
223
- super
224
- ensure_proper_type
225
- end
226
-
227
- # Sets the attribute used for single table inheritance to this class name if this is not the
228
- # ActiveRecord::Base descendant.
229
- # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
230
- # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
231
- # No such attribute would be set for objects of the Message class in that example.
232
- def ensure_proper_type
233
- klass = self.class
234
- if klass.finder_needs_type_condition?
235
- write_attribute(klass.inheritance_column, klass.sti_name)
300
+ # Sets the attribute used for single table inheritance to this class name if this is not the
301
+ # ActiveRecord::Base descendant.
302
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
303
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
304
+ # No such attribute would be set for objects of the Message class in that example.
305
+ def ensure_proper_type
306
+ klass = self.class
307
+ if klass.finder_needs_type_condition?
308
+ _write_attribute(klass.inheritance_column, klass.sti_name)
309
+ end
236
310
  end
237
- end
238
311
  end
239
312
  end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
5
+ module ActiveRecord
6
+ class InsertAll # :nodoc:
7
+ attr_reader :model, :connection, :inserts, :keys
8
+ attr_reader :on_duplicate, :returning, :unique_by
9
+
10
+ def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
11
+ raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
12
+
13
+ @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
14
+ @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
15
+
16
+ if model.scope_attributes?
17
+ @scope_attributes = model.scope_attributes
18
+ @keys |= @scope_attributes.keys
19
+ end
20
+ @keys = @keys.to_set
21
+
22
+ @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
23
+ @returning = false if @returning == []
24
+
25
+ @unique_by = find_unique_index_for(unique_by)
26
+ @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
27
+
28
+ ensure_valid_options_for_connection!
29
+ end
30
+
31
+ def execute
32
+ message = +"#{model} "
33
+ message << "Bulk " if inserts.many?
34
+ message << (on_duplicate == :update ? "Upsert" : "Insert")
35
+ connection.exec_insert_all to_sql, message
36
+ end
37
+
38
+ def updatable_columns
39
+ keys - readonly_columns - unique_by_columns
40
+ end
41
+
42
+ def primary_keys
43
+ Array(connection.schema_cache.primary_keys(model.table_name))
44
+ end
45
+
46
+
47
+ def skip_duplicates?
48
+ on_duplicate == :skip
49
+ end
50
+
51
+ def update_duplicates?
52
+ on_duplicate == :update
53
+ end
54
+
55
+ def map_key_with_value
56
+ inserts.map do |attributes|
57
+ attributes = attributes.stringify_keys
58
+ attributes.merge!(scope_attributes) if scope_attributes
59
+
60
+ verify_attributes(attributes)
61
+
62
+ keys.map do |key|
63
+ yield key, attributes[key]
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+ attr_reader :scope_attributes
70
+
71
+ def find_unique_index_for(unique_by)
72
+ return unique_by if !connection.supports_insert_conflict_target?
73
+
74
+ name_or_columns = unique_by || model.primary_key
75
+ match = Array(name_or_columns).map(&:to_s)
76
+
77
+ if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
78
+ index
79
+ elsif match == primary_keys
80
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
81
+ else
82
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
83
+ end
84
+ end
85
+
86
+ def unique_indexes
87
+ connection.schema_cache.indexes(model.table_name).select(&:unique)
88
+ end
89
+
90
+
91
+ def ensure_valid_options_for_connection!
92
+ if returning && !connection.supports_insert_returning?
93
+ raise ArgumentError, "#{connection.class} does not support :returning"
94
+ end
95
+
96
+ if skip_duplicates? && !connection.supports_insert_on_duplicate_skip?
97
+ raise ArgumentError, "#{connection.class} does not support skipping duplicates"
98
+ end
99
+
100
+ if update_duplicates? && !connection.supports_insert_on_duplicate_update?
101
+ raise ArgumentError, "#{connection.class} does not support upsert"
102
+ end
103
+
104
+ if unique_by && !connection.supports_insert_conflict_target?
105
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
106
+ end
107
+ end
108
+
109
+
110
+ def to_sql
111
+ connection.build_insert_sql(ActiveRecord::InsertAll::Builder.new(self))
112
+ end
113
+
114
+
115
+ def readonly_columns
116
+ primary_keys + model.readonly_attributes.to_a
117
+ end
118
+
119
+ def unique_by_columns
120
+ Array(unique_by&.columns)
121
+ end
122
+
123
+
124
+ def verify_attributes(attributes)
125
+ if keys != attributes.keys.to_set
126
+ raise ArgumentError, "All objects being inserted must have the same keys"
127
+ end
128
+ end
129
+
130
+ class Builder # :nodoc:
131
+ attr_reader :model
132
+
133
+ delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
134
+
135
+ def initialize(insert_all)
136
+ @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
137
+ end
138
+
139
+ def into
140
+ "INTO #{model.quoted_table_name} (#{columns_list})"
141
+ end
142
+
143
+ def values_list
144
+ types = extract_types_from_columns_on(model.table_name, keys: keys)
145
+
146
+ values_list = insert_all.map_key_with_value do |key, value|
147
+ connection.with_yaml_fallback(types[key].serialize(value))
148
+ end
149
+
150
+ connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
151
+ end
152
+
153
+ def returning
154
+ format_columns(insert_all.returning) if insert_all.returning
155
+ end
156
+
157
+ def conflict_target
158
+ if index = insert_all.unique_by
159
+ sql = +"(#{format_columns(index.columns)})"
160
+ sql << " WHERE #{index.where}" if index.where
161
+ sql
162
+ elsif update_duplicates?
163
+ "(#{format_columns(insert_all.primary_keys)})"
164
+ end
165
+ end
166
+
167
+ def updatable_columns
168
+ quote_columns(insert_all.updatable_columns)
169
+ end
170
+
171
+ def touch_model_timestamps_unless(&block)
172
+ model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
173
+ if touch_timestamp_attribute?(column_name)
174
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
175
+ end
176
+ end.compact.join
177
+ end
178
+
179
+ private
180
+ attr_reader :connection, :insert_all
181
+
182
+ def touch_timestamp_attribute?(column_name)
183
+ update_duplicates? && !insert_all.updatable_columns.include?(column_name)
184
+ end
185
+
186
+ def columns_list
187
+ format_columns(insert_all.keys)
188
+ end
189
+
190
+ def extract_types_from_columns_on(table_name, keys:)
191
+ columns = connection.schema_cache.columns_hash(table_name)
192
+
193
+ unknown_column = (keys - columns.keys).first
194
+ raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
195
+
196
+ keys.index_with { |key| model.type_for_attribute(key) }
197
+ end
198
+
199
+ def format_columns(columns)
200
+ columns.respond_to?(:map) ? quote_columns(columns).join(",") : columns
201
+ end
202
+
203
+ def quote_columns(columns)
204
+ columns.map(&connection.method(:quote_column_name))
205
+ end
206
+ end
207
+ end
208
+ end