activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  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 +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,58 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record/connection_adapters/determine_if_preparable_visitor"
4
- require "active_record/connection_adapters/schema_cache"
3
+ require "set"
5
4
  require "active_record/connection_adapters/sql_type_metadata"
6
5
  require "active_record/connection_adapters/abstract/schema_dumper"
7
6
  require "active_record/connection_adapters/abstract/schema_creation"
7
+ require "active_support/concurrency/null_lock"
8
8
  require "active_support/concurrency/load_interlock_aware_monitor"
9
- require "active_support/deprecation"
10
9
  require "arel/collectors/bind"
11
10
  require "arel/collectors/composite"
12
11
  require "arel/collectors/sql_string"
13
12
  require "arel/collectors/substitute_binds"
14
- require "concurrent/atomic/thread_local_var"
15
13
 
16
14
  module ActiveRecord
17
15
  module ConnectionAdapters # :nodoc:
18
- extend ActiveSupport::Autoload
19
-
20
- autoload :Column
21
- autoload :ConnectionSpecification
22
-
23
- autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
24
- autoload :IndexDefinition
25
- autoload :ColumnDefinition
26
- autoload :ChangeColumnDefinition
27
- autoload :ForeignKeyDefinition
28
- autoload :TableDefinition
29
- autoload :Table
30
- autoload :AlterTable
31
- autoload :ReferenceDefinition
32
- end
33
-
34
- autoload_at "active_record/connection_adapters/abstract/connection_pool" do
35
- autoload :ConnectionHandler
36
- end
37
-
38
- autoload_under "abstract" do
39
- autoload :SchemaStatements
40
- autoload :DatabaseStatements
41
- autoload :DatabaseLimits
42
- autoload :Quoting
43
- autoload :ConnectionPool
44
- autoload :QueryCache
45
- autoload :Savepoints
46
- end
47
-
48
- autoload_at "active_record/connection_adapters/abstract/transaction" do
49
- autoload :TransactionManager
50
- autoload :NullTransaction
51
- autoload :RealTransaction
52
- autoload :SavepointTransaction
53
- autoload :TransactionState
54
- end
55
-
16
+ # = Active Record Abstract Adapter
17
+ #
56
18
  # Active Record supports multiple database systems. AbstractAdapter and
57
19
  # related classes form the abstraction layer which makes this possible.
58
20
  # An AbstractAdapter represents a connection to a database, and provides an
@@ -61,7 +23,7 @@ module ActiveRecord
61
23
  # and +:limit+ options, etc.
62
24
  #
63
25
  # All the concrete database adapters follow the interface laid down in this class.
64
- # {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling#connection] returns an AbstractAdapter object, which
26
+ # {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
65
27
  # you can use.
66
28
  #
67
29
  # Most of the methods in the adapter are useful during migrations. Most
@@ -77,11 +39,19 @@ module ActiveRecord
77
39
  include Savepoints
78
40
 
79
41
  SIMPLE_INT = /\A\d+\z/
42
+ COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
80
43
 
81
- attr_accessor :pool
44
+ attr_reader :pool
82
45
  attr_reader :visitor, :owner, :logger, :lock
46
+ attr_accessor :pinned # :nodoc:
83
47
  alias :in_use? :owner
84
48
 
49
+ def pool=(value)
50
+ return if value.eql?(@pool)
51
+ @schema_cache = nil
52
+ @pool = value
53
+ end
54
+
85
55
  set_callback :checkin, :after, :enable_lazy_transactions!
86
56
 
87
57
  def self.type_cast_config_to_integer(config)
@@ -102,82 +72,189 @@ module ActiveRecord
102
72
  end
103
73
  end
104
74
 
75
+ def self.validate_default_timezone(config)
76
+ case config
77
+ when nil
78
+ when "utc", "local"
79
+ config.to_sym
80
+ else
81
+ raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
82
+ end
83
+ end
84
+
85
+ DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
86
+ private_constant :DEFAULT_READ_QUERY
87
+
105
88
  def self.build_read_query_regexp(*parts) # :nodoc:
106
- parts = parts.map { |part| /\A[\(\s]*#{part}/i }
107
- Regexp.union(*parts)
89
+ parts += DEFAULT_READ_QUERY
90
+ parts = parts.map { |part| /#{part}/i }
91
+ /\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
108
92
  end
109
93
 
110
- def self.quoted_column_names # :nodoc:
111
- @quoted_column_names ||= {}
94
+ def self.find_cmd_and_exec(commands, *args) # :doc:
95
+ commands = Array(commands)
96
+
97
+ dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
98
+ unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
99
+ commands = commands.map { |cmd| "#{cmd}#{ext}" }
100
+ end
101
+
102
+ full_path_command = nil
103
+ found = commands.detect do |cmd|
104
+ dirs_on_path.detect do |path|
105
+ full_path_command = File.join(path, cmd)
106
+ begin
107
+ stat = File.stat(full_path_command)
108
+ rescue SystemCallError
109
+ else
110
+ stat.file? && stat.executable?
111
+ end
112
+ end
113
+ end
114
+
115
+ if found
116
+ exec full_path_command, *args
117
+ else
118
+ abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
119
+ end
112
120
  end
113
121
 
114
- def self.quoted_table_names # :nodoc:
115
- @quoted_table_names ||= {}
122
+ # Opens a database console session.
123
+ def self.dbconsole(config, options = {})
124
+ raise NotImplementedError
116
125
  end
117
126
 
118
- def initialize(connection, logger = nil, config = {}) # :nodoc:
127
+ def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
119
128
  super()
120
129
 
121
- @connection = connection
122
- @owner = nil
123
- @instrumenter = ActiveSupport::Notifications.instrumenter
124
- @logger = logger
125
- @config = config
126
- @pool = ActiveRecord::ConnectionAdapters::NullPool.new
127
- @idle_since = Concurrent.monotonic_time
128
- @visitor = arel_visitor
129
- @statements = build_statement_pool
130
- @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
130
+ @raw_connection = nil
131
+ @unconfigured_connection = nil
132
+
133
+ if config_or_deprecated_connection.is_a?(Hash)
134
+ @config = config_or_deprecated_connection.symbolize_keys
135
+ @logger = ActiveRecord::Base.logger
131
136
 
132
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
133
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
134
- @visitor.extend(DetermineIfPreparableVisitor)
137
+ if deprecated_logger || deprecated_connection_options || deprecated_config
138
+ raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
139
+ end
135
140
  else
136
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
141
+ # Soft-deprecated for now; we'll probably warn in future.
142
+
143
+ @unconfigured_connection = config_or_deprecated_connection
144
+ @logger = deprecated_logger || ActiveRecord::Base.logger
145
+ if deprecated_config
146
+ @config = (deprecated_config || {}).symbolize_keys
147
+ @connection_parameters = deprecated_connection_options
148
+ else
149
+ @config = (deprecated_connection_options || {}).symbolize_keys
150
+ @connection_parameters = nil
151
+ end
137
152
  end
138
153
 
154
+ @owner = nil
155
+ @pinned = false
156
+ @instrumenter = ActiveSupport::Notifications.instrumenter
157
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
158
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
159
+ @visitor = arel_visitor
160
+ @statements = build_statement_pool
161
+ self.lock_thread = nil
162
+
163
+ @prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
164
+ @config.fetch(:prepared_statements) { default_prepared_statements }
165
+ )
166
+
139
167
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
140
- config.fetch(:advisory_locks, true)
168
+ @config.fetch(:advisory_locks, true)
141
169
  )
170
+
171
+ @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
172
+
173
+ @raw_connection_dirty = false
174
+ @last_activity = nil
175
+ @verified = false
176
+ end
177
+
178
+ def inspect # :nodoc:
179
+ name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
180
+ shard_field = " shard=#{shard.inspect}" unless shard == :default
181
+
182
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
183
+ end
184
+
185
+ def lock_thread=(lock_thread) # :nodoc:
186
+ @lock =
187
+ case lock_thread
188
+ when Thread
189
+ ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
190
+ when Fiber
191
+ ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
192
+ else
193
+ ActiveSupport::Concurrency::NullLock
194
+ end
195
+ end
196
+
197
+ EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
198
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
199
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
200
+ def with_instrumenter(instrumenter, &block) # :nodoc:
201
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
202
+ previous_instrumenter = @instrumenter
203
+ @instrumenter = instrumenter
204
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
205
+ ensure
206
+ @instrumenter = previous_instrumenter
207
+ end
208
+ end
209
+
210
+ def check_if_write_query(sql) # :nodoc:
211
+ if preventing_writes? && write_query?(sql)
212
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
213
+ end
142
214
  end
143
215
 
144
216
  def replica?
145
217
  @config[:replica] || false
146
218
  end
147
219
 
148
- # Determines whether writes are currently being prevents.
149
- #
150
- # Returns true if the connection is a replica, or if +prevent_writes+
151
- # is set to true.
152
- def preventing_writes?
153
- replica? || ActiveRecord::Base.connection_handler.prevent_writes
220
+ def connection_retries
221
+ (@config[:connection_retries] || 1).to_i
154
222
  end
155
223
 
156
- def migrations_paths # :nodoc:
157
- @config[:migrations_paths] || Migrator.migrations_paths
224
+ def verify_timeout
225
+ (@config[:verify_timeout] || 2).to_i
158
226
  end
159
227
 
160
- def migration_context # :nodoc:
161
- MigrationContext.new(migrations_paths, schema_migration)
228
+ def retry_deadline
229
+ if @config[:retry_deadline]
230
+ @config[:retry_deadline].to_f
231
+ else
232
+ nil
233
+ end
162
234
  end
163
235
 
164
- def schema_migration # :nodoc:
165
- @schema_migration ||= begin
166
- conn = self
167
- spec_name = conn.pool.spec.name
168
- name = "#{spec_name}::SchemaMigration"
236
+ def default_timezone
237
+ @default_timezone || ActiveRecord.default_timezone
238
+ end
169
239
 
170
- Class.new(ActiveRecord::SchemaMigration) do
171
- define_singleton_method(:name) { name }
172
- define_singleton_method(:to_s) { name }
240
+ # Determines whether writes are currently being prevented.
241
+ #
242
+ # Returns true if the connection is a replica or returns
243
+ # the value of +current_preventing_writes+.
244
+ def preventing_writes?
245
+ return true if replica?
246
+ return false if connection_class.nil?
247
+
248
+ connection_class.current_preventing_writes
249
+ end
173
250
 
174
- self.connection_specification_name = spec_name
175
- end
176
- end
251
+ def prepared_statements?
252
+ @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
177
253
  end
254
+ alias :prepared_statements :prepared_statements?
178
255
 
179
- def prepared_statements
180
- @prepared_statement_status.value
256
+ def prepared_statements_disabled_cache # :nodoc:
257
+ ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
181
258
  end
182
259
 
183
260
  class Version
@@ -207,37 +284,48 @@ module ActiveRecord
207
284
  def lease
208
285
  if in_use?
209
286
  msg = +"Cannot lease connection, "
210
- if @owner == Thread.current
287
+ if @owner == ActiveSupport::IsolatedExecutionState.context
211
288
  msg << "it is already leased by the current thread."
212
289
  else
213
290
  msg << "it is already in use by a different thread: #{@owner}. " \
214
- "Current thread: #{Thread.current}."
291
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
215
292
  end
216
293
  raise ActiveRecordError, msg
217
294
  end
218
295
 
219
- @owner = Thread.current
296
+ @owner = ActiveSupport::IsolatedExecutionState.context
220
297
  end
221
298
 
222
- def schema_cache
223
- @pool.get_schema_cache(self)
299
+ def connection_class # :nodoc:
300
+ @pool.connection_class
224
301
  end
225
302
 
226
- def schema_cache=(cache)
227
- cache.connection = self
228
- @pool.set_schema_cache(cache)
303
+ # The role (e.g. +:writing+) for the current connection. In a
304
+ # non-multi role application, +:writing+ is returned.
305
+ def role
306
+ @pool.role
307
+ end
308
+
309
+ # The shard (e.g. +:default+) for the current connection. In
310
+ # a non-sharded application, +:default+ is returned.
311
+ def shard
312
+ @pool.shard
313
+ end
314
+
315
+ def schema_cache
316
+ @pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
229
317
  end
230
318
 
231
319
  # this method must only be called while holding connection pool's mutex
232
320
  def expire
233
321
  if in_use?
234
- if @owner != Thread.current
322
+ if @owner != ActiveSupport::IsolatedExecutionState.context
235
323
  raise ActiveRecordError, "Cannot expire connection, " \
236
324
  "it is owned by a different thread: #{@owner}. " \
237
- "Current thread: #{Thread.current}."
325
+ "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
238
326
  end
239
327
 
240
- @idle_since = Concurrent.monotonic_time
328
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
241
329
  @owner = nil
242
330
  else
243
331
  raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
@@ -247,10 +335,10 @@ module ActiveRecord
247
335
  # this method must only be called while holding connection pool's mutex (and a desire for segfaults)
248
336
  def steal! # :nodoc:
249
337
  if in_use?
250
- if @owner != Thread.current
338
+ if @owner != ActiveSupport::IsolatedExecutionState.context
251
339
  pool.send :remove_connection_from_thread_cache, self, @owner
252
340
 
253
- @owner = Thread.current
341
+ @owner = ActiveSupport::IsolatedExecutionState.context
254
342
  end
255
343
  else
256
344
  raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
@@ -260,11 +348,21 @@ module ActiveRecord
260
348
  # Seconds since this connection was returned to the pool
261
349
  def seconds_idle # :nodoc:
262
350
  return 0 if in_use?
263
- Concurrent.monotonic_time - @idle_since
351
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
352
+ end
353
+
354
+ # Seconds since this connection last communicated with the server
355
+ def seconds_since_last_activity # :nodoc:
356
+ if @raw_connection && @last_activity
357
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
358
+ end
264
359
  end
265
360
 
266
361
  def unprepared_statement
267
- @prepared_statement_status.bind(false) { yield }
362
+ cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
363
+ yield
364
+ ensure
365
+ cache&.delete(object_id)
268
366
  end
269
367
 
270
368
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -275,7 +373,14 @@ module ActiveRecord
275
373
 
276
374
  # Does the database for this adapter exist?
277
375
  def self.database_exists?(config)
278
- raise NotImplementedError
376
+ new(config).database_exists?
377
+ end
378
+
379
+ def database_exists?
380
+ connect!
381
+ true
382
+ rescue ActiveRecord::NoDatabaseError
383
+ false
279
384
  end
280
385
 
281
386
  # Does this adapter support DDL rollbacks in transactions? That is, would
@@ -293,6 +398,16 @@ module ActiveRecord
293
398
  false
294
399
  end
295
400
 
401
+ # Do TransactionRollbackErrors on savepoints affect the parent
402
+ # transaction?
403
+ def savepoint_errors_invalidate_transactions?
404
+ false
405
+ end
406
+
407
+ def supports_restart_db_transaction?
408
+ false
409
+ end
410
+
296
411
  # Does this adapter support application-enforced advisory locking?
297
412
  def supports_advisory_locks?
298
413
  false
@@ -305,6 +420,10 @@ module ActiveRecord
305
420
  false
306
421
  end
307
422
 
423
+ def supports_partitioned_indexes?
424
+ false
425
+ end
426
+
308
427
  # Does this adapter support index sort order?
309
428
  def supports_index_sort_order?
310
429
  false
@@ -315,6 +434,11 @@ module ActiveRecord
315
434
  false
316
435
  end
317
436
 
437
+ # Does this adapter support including non-key columns?
438
+ def supports_index_include?
439
+ false
440
+ end
441
+
318
442
  # Does this adapter support expression indices?
319
443
  def supports_expression_index?
320
444
  false
@@ -351,12 +475,25 @@ module ActiveRecord
351
475
  false
352
476
  end
353
477
 
354
- # Does this adapter support creating foreign key constraints
355
- # in the same statement as creating the table?
356
- def supports_foreign_keys_in_create?
357
- supports_foreign_keys?
478
+ # Does this adapter support creating deferrable constraints?
479
+ def supports_deferrable_constraints?
480
+ false
481
+ end
482
+
483
+ # Does this adapter support creating check constraints?
484
+ def supports_check_constraints?
485
+ false
486
+ end
487
+
488
+ # Does this adapter support creating exclusion constraints?
489
+ def supports_exclusion_constraints?
490
+ false
491
+ end
492
+
493
+ # Does this adapter support creating unique constraints?
494
+ def supports_unique_constraints?
495
+ false
358
496
  end
359
- deprecate :supports_foreign_keys_in_create?
360
497
 
361
498
  # Does this adapter support views?
362
499
  def supports_views?
@@ -373,7 +510,7 @@ module ActiveRecord
373
510
  false
374
511
  end
375
512
 
376
- # Does this adapter support json data type?
513
+ # Does this adapter support JSON data type?
377
514
  def supports_json?
378
515
  false
379
516
  end
@@ -388,12 +525,6 @@ module ActiveRecord
388
525
  false
389
526
  end
390
527
 
391
- # Does this adapter support multi-value insert?
392
- def supports_multi_insert?
393
- true
394
- end
395
- deprecate :supports_multi_insert?
396
-
397
528
  # Does this adapter support virtual columns?
398
529
  def supports_virtual_columns?
399
530
  false
@@ -409,6 +540,10 @@ module ActiveRecord
409
540
  false
410
541
  end
411
542
 
543
+ def supports_common_table_expressions?
544
+ false
545
+ end
546
+
412
547
  def supports_lazy_transactions?
413
548
  false
414
549
  end
@@ -429,12 +564,49 @@ module ActiveRecord
429
564
  false
430
565
  end
431
566
 
567
+ def supports_concurrent_connections?
568
+ true
569
+ end
570
+
571
+ def supports_nulls_not_distinct?
572
+ false
573
+ end
574
+
575
+ def return_value_after_insert?(column) # :nodoc:
576
+ column.auto_populated?
577
+ end
578
+
579
+ def async_enabled? # :nodoc:
580
+ supports_concurrent_connections? &&
581
+ !ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
582
+ end
583
+
432
584
  # This is meant to be implemented by the adapters that support extensions
433
- def disable_extension(name)
585
+ def disable_extension(name, **)
434
586
  end
435
587
 
436
588
  # This is meant to be implemented by the adapters that support extensions
437
- def enable_extension(name)
589
+ def enable_extension(name, **)
590
+ end
591
+
592
+ # This is meant to be implemented by the adapters that support custom enum types
593
+ def create_enum(*) # :nodoc:
594
+ end
595
+
596
+ # This is meant to be implemented by the adapters that support custom enum types
597
+ def drop_enum(*) # :nodoc:
598
+ end
599
+
600
+ # This is meant to be implemented by the adapters that support custom enum types
601
+ def rename_enum(*) # :nodoc:
602
+ end
603
+
604
+ # This is meant to be implemented by the adapters that support custom enum types
605
+ def add_enum_value(*) # :nodoc:
606
+ end
607
+
608
+ # This is meant to be implemented by the adapters that support custom enum types
609
+ def rename_enum_value(*) # :nodoc:
438
610
  end
439
611
 
440
612
  def advisory_locks_enabled? # :nodoc:
@@ -472,27 +644,74 @@ module ActiveRecord
472
644
  yield
473
645
  end
474
646
 
647
+ # Override to check all foreign key constraints in a database.
648
+ # The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
649
+ # constraints are not met.
650
+ def check_all_foreign_keys_valid!
651
+ end
652
+
475
653
  # CONNECTION MANAGEMENT ====================================
476
654
 
655
+ # Checks whether the connection to the database was established. This doesn't
656
+ # include checking whether the database is actually capable of responding, i.e.
657
+ # whether the connection is stale.
658
+ def connected?
659
+ !@raw_connection.nil?
660
+ end
661
+
477
662
  # Checks whether the connection to the database is still active. This includes
478
663
  # checking whether the database is actually capable of responding, i.e. whether
479
664
  # the connection isn't stale.
480
665
  def active?
481
666
  end
482
667
 
483
- # Disconnects from the database if already connected, and establishes a
484
- # new connection with the database. Implementors should call super if they
485
- # override the default implementation.
486
- def reconnect!
487
- clear_cache!
488
- reset_transaction
668
+ # Disconnects from the database if already connected, and establishes a new
669
+ # connection with the database. Implementors should define private #reconnect
670
+ # instead.
671
+ def reconnect!(restore_transactions: false)
672
+ retries_available = connection_retries
673
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
674
+
675
+ @lock.synchronize do
676
+ reconnect
677
+
678
+ enable_lazy_transactions!
679
+ @raw_connection_dirty = false
680
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
681
+ @verified = true
682
+
683
+ reset_transaction(restore: restore_transactions) do
684
+ clear_cache!(new_connection: true)
685
+ attempt_configure_connection
686
+ end
687
+ rescue => original_exception
688
+ translated_exception = translate_exception_class(original_exception, nil, nil)
689
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
690
+
691
+ if !retry_deadline_exceeded && retries_available > 0
692
+ retries_available -= 1
693
+
694
+ if retryable_connection_error?(translated_exception)
695
+ backoff(connection_retries - retries_available)
696
+ retry
697
+ end
698
+ end
699
+
700
+ @last_activity = nil
701
+ @verified = false
702
+
703
+ raise translated_exception
704
+ end
489
705
  end
490
706
 
491
707
  # Disconnects from the database if already connected. Otherwise, this
492
708
  # method does nothing.
493
709
  def disconnect!
494
- clear_cache!
495
- reset_transaction
710
+ @lock.synchronize do
711
+ clear_cache!(new_connection: true)
712
+ reset_transaction
713
+ @raw_connection_dirty = false
714
+ end
496
715
  end
497
716
 
498
717
  # Immediately forget this connection ever existed. Unlike disconnect!,
@@ -503,27 +722,39 @@ module ActiveRecord
503
722
  # rid of a connection that belonged to its parent.
504
723
  def discard!
505
724
  # This should be overridden by concrete adapters.
506
- #
507
- # Prevent @connection's finalizer from touching the socket, or
508
- # otherwise communicating with its server, when it is collected.
509
- if schema_cache.connection == self
510
- schema_cache.connection = nil
511
- end
512
725
  end
513
726
 
514
727
  # Reset the state of this connection, directing the DBMS to clear
515
728
  # transactions and other connection-related server-side state. Usually a
516
729
  # database-dependent operation.
517
730
  #
518
- # The default implementation does nothing; the implementation should be
519
- # overridden by concrete adapters.
731
+ # If a database driver or protocol does not support such a feature,
732
+ # implementors may alias this to #reconnect!. Otherwise, implementors
733
+ # should call super immediately after resetting the connection (and while
734
+ # still holding @lock).
520
735
  def reset!
521
- # this should be overridden by concrete adapters
736
+ clear_cache!(new_connection: true)
737
+ reset_transaction
738
+ attempt_configure_connection
739
+ end
740
+
741
+ # Removes the connection from the pool and disconnect it.
742
+ def throw_away!
743
+ pool.remove self
744
+ disconnect!
522
745
  end
523
746
 
524
747
  # Clear any caching the database adapter may be doing.
525
- def clear_cache!
526
- @lock.synchronize { @statements.clear } if @statements
748
+ def clear_cache!(new_connection: false)
749
+ if @statements
750
+ @lock.synchronize do
751
+ if new_connection
752
+ @statements.reset
753
+ else
754
+ @statements.clear
755
+ end
756
+ end
757
+ end
527
758
  end
528
759
 
529
760
  # Returns true if its required to reload the connection between requests for development mode.
@@ -535,7 +766,32 @@ module ActiveRecord
535
766
  # This is done under the hood by calling #active?. If the connection
536
767
  # is no longer active, then this method will reconnect to the database.
537
768
  def verify!
538
- reconnect! unless active?
769
+ unless active?
770
+ @lock.synchronize do
771
+ if @unconfigured_connection
772
+ @raw_connection = @unconfigured_connection
773
+ @unconfigured_connection = nil
774
+ attempt_configure_connection
775
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
776
+ @verified = true
777
+ return
778
+ end
779
+
780
+ reconnect!(restore_transactions: true)
781
+ end
782
+ end
783
+
784
+ @verified = true
785
+ end
786
+
787
+ def connect!
788
+ verify!
789
+ self
790
+ end
791
+
792
+ def clean! # :nodoc:
793
+ @raw_connection_dirty = false
794
+ @verified = nil
539
795
  end
540
796
 
541
797
  # Provides access to the underlying database driver for this adapter. For
@@ -544,12 +800,19 @@ module ActiveRecord
544
800
  #
545
801
  # This is useful for when you need to call a proprietary method such as
546
802
  # PostgreSQL's lo_* methods.
803
+ #
804
+ # Active Record cannot track if the database is getting modified using
805
+ # this client. If that is the case, generally you'll want to invalidate
806
+ # the query cache using +ActiveRecord::Base.clear_query_cache+.
547
807
  def raw_connection
548
- disable_lazy_transactions!
549
- @connection
808
+ with_raw_connection do |conn|
809
+ disable_lazy_transactions!
810
+ @raw_connection_dirty = true
811
+ conn
812
+ end
550
813
  end
551
814
 
552
- def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
815
+ def default_uniqueness_comparison(attribute, value) # :nodoc:
553
816
  attribute.eq(value)
554
817
  end
555
818
 
@@ -577,10 +840,6 @@ module ActiveRecord
577
840
  pool.checkin self
578
841
  end
579
842
 
580
- def column_name_for_operation(operation, node) # :nodoc:
581
- visitor.compile(node)
582
- end
583
-
584
843
  def default_index_type?(index) # :nodoc:
585
844
  index.using.nil?
586
845
  end
@@ -602,85 +861,270 @@ module ActiveRecord
602
861
  end
603
862
 
604
863
  def database_version # :nodoc:
605
- schema_cache.database_version
864
+ pool.server_version(self)
606
865
  end
607
866
 
608
867
  def check_version # :nodoc:
609
868
  end
610
869
 
611
- private
870
+ # Returns the version identifier of the schema currently available in
871
+ # the database. This is generally equal to the number of the highest-
872
+ # numbered migration that has been executed, or 0 if no schema
873
+ # information is present / the database is empty.
874
+ def schema_version
875
+ pool.migration_context.current_version
876
+ end
612
877
 
613
- def type_map
614
- @type_map ||= Type::TypeMap.new.tap do |mapping|
615
- initialize_type_map(mapping)
878
+ class << self
879
+ def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
880
+ mapping.register_type(key) do |*args|
881
+ precision = extract_precision(args.last)
882
+ klass.new(precision: precision, **kwargs)
616
883
  end
617
884
  end
618
885
 
619
- def initialize_type_map(m = type_map)
620
- register_class_with_limit m, %r(boolean)i, Type::Boolean
621
- register_class_with_limit m, %r(char)i, Type::String
622
- register_class_with_limit m, %r(binary)i, Type::Binary
623
- register_class_with_limit m, %r(text)i, Type::Text
624
- register_class_with_precision m, %r(date)i, Type::Date
625
- register_class_with_precision m, %r(time)i, Type::Time
626
- register_class_with_precision m, %r(datetime)i, Type::DateTime
627
- register_class_with_limit m, %r(float)i, Type::Float
628
- register_class_with_limit m, %r(int)i, Type::Integer
629
-
630
- m.alias_type %r(blob)i, "binary"
631
- m.alias_type %r(clob)i, "text"
632
- m.alias_type %r(timestamp)i, "datetime"
633
- m.alias_type %r(numeric)i, "decimal"
634
- m.alias_type %r(number)i, "decimal"
635
- m.alias_type %r(double)i, "float"
636
-
637
- m.register_type %r(^json)i, Type::Json.new
638
-
639
- m.register_type(%r(decimal)i) do |sql_type|
640
- scale = extract_scale(sql_type)
641
- precision = extract_precision(sql_type)
642
-
643
- if scale == 0
644
- # FIXME: Remove this class as well
645
- Type::DecimalWithoutScale.new(precision: precision)
886
+ def extended_type_map(default_timezone:) # :nodoc:
887
+ Type::TypeMap.new(self::TYPE_MAP).tap do |m|
888
+ register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
889
+ register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
890
+ m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
891
+ end
892
+ end
893
+
894
+ private
895
+ def initialize_type_map(m)
896
+ register_class_with_limit m, %r(boolean)i, Type::Boolean
897
+ register_class_with_limit m, %r(char)i, Type::String
898
+ register_class_with_limit m, %r(binary)i, Type::Binary
899
+ register_class_with_limit m, %r(text)i, Type::Text
900
+ register_class_with_precision m, %r(date)i, Type::Date
901
+ register_class_with_precision m, %r(time)i, Type::Time
902
+ register_class_with_precision m, %r(datetime)i, Type::DateTime
903
+ register_class_with_limit m, %r(float)i, Type::Float
904
+ register_class_with_limit m, %r(int)i, Type::Integer
905
+
906
+ m.alias_type %r(blob)i, "binary"
907
+ m.alias_type %r(clob)i, "text"
908
+ m.alias_type %r(timestamp)i, "datetime"
909
+ m.alias_type %r(numeric)i, "decimal"
910
+ m.alias_type %r(number)i, "decimal"
911
+ m.alias_type %r(double)i, "float"
912
+
913
+ m.register_type %r(^json)i, Type::Json.new
914
+
915
+ m.register_type(%r(decimal)i) do |sql_type|
916
+ scale = extract_scale(sql_type)
917
+ precision = extract_precision(sql_type)
918
+
919
+ if scale == 0
920
+ # FIXME: Remove this class as well
921
+ Type::DecimalWithoutScale.new(precision: precision)
922
+ else
923
+ Type::Decimal.new(precision: precision, scale: scale)
924
+ end
925
+ end
926
+ end
927
+
928
+ def register_class_with_limit(mapping, key, klass)
929
+ mapping.register_type(key) do |*args|
930
+ limit = extract_limit(args.last)
931
+ klass.new(limit: limit)
932
+ end
933
+ end
934
+
935
+ def extract_scale(sql_type)
936
+ case sql_type
937
+ when /\((\d+)\)/ then 0
938
+ when /\((\d+)(,(\d+))\)/ then $3.to_i
939
+ end
940
+ end
941
+
942
+ def extract_precision(sql_type)
943
+ $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
944
+ end
945
+
946
+ def extract_limit(sql_type)
947
+ $1.to_i if sql_type =~ /\((.*)\)/
948
+ end
949
+ end
950
+
951
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
952
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
953
+
954
+ private
955
+ def reconnect_can_restore_state?
956
+ transaction_manager.restorable? && !@raw_connection_dirty
957
+ end
958
+
959
+ # Lock the monitor, ensure we're properly connected and
960
+ # transactions are materialized, and then yield the underlying
961
+ # raw connection object.
962
+ #
963
+ # If +allow_retry+ is true, a connection-related exception will
964
+ # cause an automatic reconnect and re-run of the block, up to
965
+ # the connection's configured +connection_retries+ setting
966
+ # and the configured +retry_deadline+ limit. (Note that when
967
+ # +allow_retry+ is true, it's possible to return without having marked
968
+ # the connection as verified. If the block is guaranteed to exercise the
969
+ # connection, consider calling `verified!` to avoid needless
970
+ # verification queries in subsequent calls.)
971
+ #
972
+ # If +materialize_transactions+ is false, the block will be run without
973
+ # ensuring virtual transactions have been materialized in the DB
974
+ # server's state. The active transaction will also remain clean
975
+ # (if it is not already dirty), meaning it's able to be restored
976
+ # by reconnecting and opening an equivalent-depth set of new
977
+ # transactions. This should only be used by transaction control
978
+ # methods, and internal transaction-agnostic queries.
979
+ #
980
+ ###
981
+ #
982
+ # It's not the primary use case, so not something to optimize
983
+ # for, but note that this method does need to be re-entrant:
984
+ # +materialize_transactions+ will re-enter if it has work to do,
985
+ # and the yield block can also do so under some circumstances.
986
+ #
987
+ # In the latter case, we really ought to guarantee the inner
988
+ # call will not reconnect (which would interfere with the
989
+ # still-yielded connection in the outer block), but we currently
990
+ # provide no special enforcement there.
991
+ #
992
+ def with_raw_connection(allow_retry: false, materialize_transactions: true)
993
+ @lock.synchronize do
994
+ connect! if @raw_connection.nil? && reconnect_can_restore_state?
995
+
996
+ self.materialize_transactions if materialize_transactions
997
+
998
+ retries_available = allow_retry ? connection_retries : 0
999
+ deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
1000
+ reconnectable = reconnect_can_restore_state?
1001
+
1002
+ if @verified
1003
+ # Cool, we're confident the connection's ready to use. (Note this might have
1004
+ # become true during the above #materialize_transactions.)
1005
+ elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
1006
+ # We haven't actually verified the connection since we acquired it, but it
1007
+ # has been used very recently. We're going to assume it's still okay.
1008
+ elsif reconnectable
1009
+ if allow_retry
1010
+ # Not sure about the connection yet, but if anything goes wrong we can
1011
+ # just reconnect and re-run our query
1012
+ else
1013
+ # We can reconnect if needed, but we don't trust the upcoming query to be
1014
+ # safely re-runnable: let's verify the connection to be sure
1015
+ verify!
1016
+ end
646
1017
  else
647
- Type::Decimal.new(precision: precision, scale: scale)
1018
+ # We don't know whether the connection is okay, but it also doesn't matter:
1019
+ # we wouldn't be able to reconnect anyway. We're just going to run our query
1020
+ # and hope for the best.
1021
+ end
1022
+
1023
+ begin
1024
+ yield @raw_connection
1025
+ rescue => original_exception
1026
+ translated_exception = translate_exception_class(original_exception, nil, nil)
1027
+ invalidate_transaction(translated_exception)
1028
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
1029
+
1030
+ if !retry_deadline_exceeded && retries_available > 0
1031
+ retries_available -= 1
1032
+
1033
+ if retryable_query_error?(translated_exception)
1034
+ backoff(connection_retries - retries_available)
1035
+ retry
1036
+ elsif reconnectable && retryable_connection_error?(translated_exception)
1037
+ reconnect!(restore_transactions: true)
1038
+ # Only allowed to reconnect once, because reconnect! has its own retry
1039
+ # loop
1040
+ reconnectable = false
1041
+ retry
1042
+ end
1043
+ end
1044
+
1045
+ unless retryable_query_error?(translated_exception)
1046
+ # Barring a known-retryable error inside the query (regardless of
1047
+ # whether we were in a _position_ to retry it), we should infer that
1048
+ # there's likely a real problem with the connection.
1049
+ @last_activity = nil
1050
+ @verified = false
1051
+ end
1052
+
1053
+ raise translated_exception
1054
+ ensure
1055
+ dirty_current_transaction if materialize_transactions
648
1056
  end
649
1057
  end
650
1058
  end
651
1059
 
652
- def reload_type_map
653
- type_map.clear
654
- initialize_type_map
1060
+ # Mark the connection as verified. Call this inside a
1061
+ # `with_raw_connection` block only when the block is guaranteed to
1062
+ # exercise the raw connection.
1063
+ def verified!
1064
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1065
+ @verified = true
655
1066
  end
656
1067
 
657
- def register_class_with_limit(mapping, key, klass)
658
- mapping.register_type(key) do |*args|
659
- limit = extract_limit(args.last)
660
- klass.new(limit: limit)
661
- end
1068
+ def retryable_connection_error?(exception)
1069
+ exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
662
1070
  end
663
1071
 
664
- def register_class_with_precision(mapping, key, klass)
665
- mapping.register_type(key) do |*args|
666
- precision = extract_precision(args.last)
667
- klass.new(precision: precision)
668
- end
1072
+ def invalidate_transaction(exception)
1073
+ return unless exception.is_a?(TransactionRollbackError)
1074
+ return unless savepoint_errors_invalidate_transactions?
1075
+
1076
+ current_transaction.invalidate!
669
1077
  end
670
1078
 
671
- def extract_scale(sql_type)
672
- case sql_type
673
- when /\((\d+)\)/ then 0
674
- when /\((\d+)(,(\d+))\)/ then $3.to_i
675
- end
1079
+ def retryable_query_error?(exception)
1080
+ # We definitely can't retry if we were inside an invalidated transaction.
1081
+ return false if current_transaction.invalidated?
1082
+
1083
+ exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
676
1084
  end
677
1085
 
678
- def extract_precision(sql_type)
679
- $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
1086
+ def backoff(counter)
1087
+ sleep 0.1 * counter
680
1088
  end
681
1089
 
682
- def extract_limit(sql_type)
683
- $1.to_i if sql_type =~ /\((.*)\)/
1090
+ def reconnect
1091
+ raise NotImplementedError
1092
+ end
1093
+
1094
+ # Returns a raw connection for internal use with methods that are known
1095
+ # to both be thread-safe and not rely upon actual server communication.
1096
+ # This is useful for e.g. string escaping methods.
1097
+ def any_raw_connection
1098
+ @raw_connection || valid_raw_connection
1099
+ end
1100
+
1101
+ # Similar to any_raw_connection, but ensures it is validated and
1102
+ # connected. Any method called on this result still needs to be
1103
+ # independently thread-safe, so it probably shouldn't talk to the
1104
+ # server... but some drivers fail if they know the connection has gone
1105
+ # away.
1106
+ def valid_raw_connection
1107
+ (@verified && @raw_connection) ||
1108
+ # `allow_retry: false`, to force verification: the block won't
1109
+ # raise, so a retry wouldn't help us get the valid connection we
1110
+ # need.
1111
+ with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
1112
+ end
1113
+
1114
+ def extended_type_map_key
1115
+ if @default_timezone
1116
+ { default_timezone: @default_timezone }
1117
+ end
1118
+ end
1119
+
1120
+ def type_map
1121
+ if key = extended_type_map_key
1122
+ self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
1123
+ self.class.extended_type_map(**key)
1124
+ end
1125
+ else
1126
+ self.class::TYPE_MAP
1127
+ end
684
1128
  end
685
1129
 
686
1130
  def translate_exception_class(e, sql, binds)
@@ -693,7 +1137,7 @@ module ActiveRecord
693
1137
  exception
694
1138
  end
695
1139
 
696
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
1140
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
697
1141
  @instrumenter.instrument(
698
1142
  "sql.active_record",
699
1143
  sql: sql,
@@ -701,23 +1145,30 @@ module ActiveRecord
701
1145
  binds: binds,
702
1146
  type_casted_binds: type_casted_binds,
703
1147
  statement_name: statement_name,
704
- connection_id: object_id,
705
- connection: self) do
706
- @lock.synchronize do
707
- yield
708
- end
709
- rescue => e
710
- raise translate_exception_class(e, sql, binds)
1148
+ async: async,
1149
+ connection: self,
1150
+ transaction: current_transaction.user_transaction.presence,
1151
+ row_count: 0,
1152
+ &block
1153
+ )
1154
+ rescue ActiveRecord::StatementInvalid => ex
1155
+ raise ex.set_query(sql, binds)
1156
+ end
1157
+
1158
+ def transform_query(sql)
1159
+ ActiveRecord.query_transformers.each do |transformer|
1160
+ sql = transformer.call(sql, self)
711
1161
  end
1162
+ sql
712
1163
  end
713
1164
 
714
1165
  def translate_exception(exception, message:, sql:, binds:)
715
1166
  # override in derived class
716
1167
  case exception
717
- when RuntimeError
1168
+ when RuntimeError, ActiveRecord::ActiveRecordError
718
1169
  exception
719
1170
  else
720
- ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
1171
+ ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
721
1172
  end
722
1173
  end
723
1174
 
@@ -756,6 +1207,42 @@ module ActiveRecord
756
1207
 
757
1208
  def build_statement_pool
758
1209
  end
1210
+
1211
+ # Builds the result object.
1212
+ #
1213
+ # This is an internal hook to make possible connection adapters to build
1214
+ # custom result objects with connection-specific data.
1215
+ def build_result(columns:, rows:, column_types: nil)
1216
+ ActiveRecord::Result.new(columns, rows, column_types)
1217
+ end
1218
+
1219
+ # Perform any necessary initialization upon the newly-established
1220
+ # @raw_connection -- this is the place to modify the adapter's
1221
+ # connection settings, run queries to configure any application-global
1222
+ # "session" variables, etc.
1223
+ #
1224
+ # Implementations may assume this method will only be called while
1225
+ # holding @lock (or from #initialize).
1226
+ def configure_connection
1227
+ check_version
1228
+ end
1229
+
1230
+ def attempt_configure_connection
1231
+ configure_connection
1232
+ rescue Exception # Need to handle things such as Timeout::ExitException
1233
+ disconnect!
1234
+ raise
1235
+ end
1236
+
1237
+ def default_prepared_statements
1238
+ true
1239
+ end
1240
+
1241
+ def warning_ignored?(warning)
1242
+ ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
1243
+ warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
1244
+ end
1245
+ end
759
1246
  end
760
1247
  end
761
1248
  end