ibm_db 5.6.1 → 5.7.0

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 (592) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Makefile +13 -9
  3. data/ext/extconf.rb +176 -110
  4. data/ext/ibm_db.c +60 -0
  5. data/ext/ibm_db.o +0 -0
  6. data/ext/ibm_db.so +0 -0
  7. data/ext/mkmf.log +6 -6
  8. data/ext/ruby_ibm_db_cli.c +14 -10
  9. data/ext/ruby_ibm_db_cli.h +4 -0
  10. data/ext/ruby_ibm_db_cli.o +0 -0
  11. data/lib/IBM_DB.rb +2 -2
  12. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1254 -277
  13. data/test/active_record/connection_adapters/fake_legacy_adapter.rb +17 -0
  14. data/test/active_record/connection_adapters/fake_misleading_legacy_adapter.rb +8 -0
  15. data/test/activejob/destroy_association_async_job_test.rb +62 -0
  16. data/test/activejob/destroy_association_async_test.rb +147 -11
  17. data/test/activejob/job_runtime_test.rb +41 -0
  18. data/test/activejob/unloadable_base_job.rb +3 -0
  19. data/test/cases/active_record_schema_test.rb +196 -0
  20. data/test/cases/active_record_test.rb +21 -0
  21. data/test/cases/adapter_prevent_writes_test.rb +26 -202
  22. data/test/cases/adapter_test.rb +592 -82
  23. data/test/cases/adapters/abstract_mysql_adapter/active_schema_test.rb +210 -0
  24. data/test/cases/adapters/abstract_mysql_adapter/adapter_prevent_writes_test.rb +111 -0
  25. data/test/cases/adapters/abstract_mysql_adapter/auto_increment_test.rb +48 -0
  26. data/test/cases/adapters/abstract_mysql_adapter/bind_parameter_test.rb +91 -0
  27. data/test/cases/adapters/abstract_mysql_adapter/case_sensitivity_test.rb +65 -0
  28. data/test/cases/adapters/abstract_mysql_adapter/charset_collation_test.rb +85 -0
  29. data/test/cases/adapters/abstract_mysql_adapter/connection_test.rb +267 -0
  30. data/test/cases/adapters/abstract_mysql_adapter/count_deleted_rows_with_lock_test.rb +29 -0
  31. data/test/cases/adapters/abstract_mysql_adapter/json_test.rb +24 -0
  32. data/test/cases/adapters/abstract_mysql_adapter/mysql_boolean_test.rb +102 -0
  33. data/test/cases/adapters/abstract_mysql_adapter/mysql_enum_test.rb +47 -0
  34. data/test/cases/adapters/abstract_mysql_adapter/mysql_explain_test.rb +71 -0
  35. data/test/cases/adapters/abstract_mysql_adapter/nested_deadlock_test.rb +191 -0
  36. data/test/cases/adapters/abstract_mysql_adapter/optimizer_hints_test.rb +69 -0
  37. data/test/cases/adapters/abstract_mysql_adapter/quoting_test.rb +49 -0
  38. data/test/cases/adapters/abstract_mysql_adapter/schema_migrations_test.rb +68 -0
  39. data/test/cases/adapters/abstract_mysql_adapter/schema_test.rb +130 -0
  40. data/test/cases/adapters/abstract_mysql_adapter/set_test.rb +32 -0
  41. data/test/cases/adapters/abstract_mysql_adapter/sp_test.rb +39 -0
  42. data/test/cases/adapters/abstract_mysql_adapter/sql_types_test.rb +16 -0
  43. data/test/cases/adapters/abstract_mysql_adapter/table_options_test.rb +125 -0
  44. data/test/cases/adapters/abstract_mysql_adapter/transaction_test.rb +204 -0
  45. data/test/cases/adapters/abstract_mysql_adapter/unsigned_type_test.rb +77 -0
  46. data/test/cases/adapters/abstract_mysql_adapter/virtual_column_test.rb +69 -0
  47. data/test/cases/adapters/abstract_mysql_adapter/warnings_test.rb +109 -0
  48. data/test/cases/adapters/mysql2/check_constraint_quoting_test.rb +35 -0
  49. data/test/cases/adapters/mysql2/dbconsole_test.rb +87 -0
  50. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +136 -10
  51. data/test/cases/adapters/mysql2/mysql2_rake_test.rb +433 -0
  52. data/test/cases/adapters/postgresql/active_schema_test.rb +8 -4
  53. data/test/cases/adapters/postgresql/array_test.rb +6 -6
  54. data/test/cases/adapters/postgresql/bind_parameter_test.rb +52 -0
  55. data/test/cases/adapters/postgresql/bit_string_test.rb +2 -2
  56. data/test/cases/adapters/postgresql/bytea_test.rb +3 -3
  57. data/test/cases/adapters/postgresql/case_insensitive_test.rb +1 -1
  58. data/test/cases/adapters/postgresql/change_schema_test.rb +32 -1
  59. data/test/cases/adapters/postgresql/cidr_test.rb +17 -0
  60. data/test/cases/adapters/postgresql/citext_test.rb +7 -1
  61. data/test/cases/adapters/postgresql/collation_test.rb +1 -1
  62. data/test/cases/adapters/postgresql/composite_test.rb +1 -1
  63. data/test/cases/adapters/postgresql/connection_test.rb +43 -36
  64. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +3 -2
  65. data/test/cases/adapters/postgresql/datatype_test.rb +9 -4
  66. data/test/cases/adapters/postgresql/date_test.rb +2 -2
  67. data/test/cases/adapters/postgresql/dbconsole_test.rb +109 -0
  68. data/test/cases/adapters/postgresql/deferred_constraints_test.rb +55 -0
  69. data/test/cases/adapters/postgresql/domain_test.rb +1 -1
  70. data/test/cases/adapters/postgresql/enum_test.rb +208 -8
  71. data/test/cases/adapters/postgresql/explain_test.rb +25 -6
  72. data/test/cases/adapters/postgresql/extension_migration_test.rb +51 -9
  73. data/test/cases/adapters/postgresql/foreign_table_test.rb +3 -3
  74. data/test/cases/adapters/postgresql/full_text_test.rb +1 -1
  75. data/test/cases/adapters/postgresql/geometric_test.rb +13 -6
  76. data/test/cases/adapters/postgresql/hstore_test.rb +22 -32
  77. data/test/cases/adapters/postgresql/infinity_test.rb +13 -2
  78. data/test/cases/adapters/postgresql/integer_test.rb +1 -1
  79. data/test/cases/adapters/postgresql/interval_test.rb +9 -17
  80. data/test/cases/adapters/postgresql/invertible_migration_test.rb +139 -0
  81. data/test/cases/adapters/postgresql/json_test.rb +5 -0
  82. data/test/cases/adapters/postgresql/ltree_test.rb +1 -1
  83. data/test/cases/adapters/postgresql/money_test.rb +17 -9
  84. data/test/cases/adapters/postgresql/network_test.rb +13 -1
  85. data/test/cases/adapters/postgresql/numbers_test.rb +18 -2
  86. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +9 -9
  87. data/test/cases/adapters/postgresql/partitions_test.rb +5 -2
  88. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +7 -110
  89. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +403 -20
  90. data/test/cases/adapters/postgresql/postgresql_rake_test.rb +637 -0
  91. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +1 -1
  92. data/test/cases/adapters/postgresql/quoting_test.rb +60 -1
  93. data/test/cases/adapters/postgresql/range_test.rb +185 -21
  94. data/test/cases/adapters/postgresql/referential_integrity_test.rb +29 -5
  95. data/test/cases/adapters/postgresql/rename_table_test.rb +35 -8
  96. data/test/cases/adapters/postgresql/schema_authorization_test.rb +2 -2
  97. data/test/cases/adapters/postgresql/schema_test.rb +270 -20
  98. data/test/cases/adapters/postgresql/serial_test.rb +5 -5
  99. data/test/cases/adapters/postgresql/statement_pool_test.rb +6 -2
  100. data/test/cases/adapters/postgresql/timestamp_test.rb +122 -5
  101. data/test/cases/adapters/postgresql/transaction_nested_test.rb +130 -6
  102. data/test/cases/adapters/postgresql/transaction_test.rb +37 -11
  103. data/test/cases/adapters/postgresql/type_lookup_test.rb +1 -1
  104. data/test/cases/adapters/postgresql/uuid_test.rb +77 -9
  105. data/test/cases/adapters/postgresql/virtual_column_test.rb +114 -0
  106. data/test/cases/adapters/postgresql/xml_test.rb +1 -1
  107. data/test/cases/adapters/sqlite3/bind_parameter_test.rb +52 -0
  108. data/test/cases/adapters/sqlite3/collation_test.rb +1 -1
  109. data/test/cases/adapters/sqlite3/copy_table_test.rb +28 -7
  110. data/test/cases/adapters/sqlite3/dbconsole_test.rb +81 -0
  111. data/test/cases/adapters/sqlite3/explain_test.rb +3 -3
  112. data/test/cases/adapters/sqlite3/quoting_test.rb +34 -3
  113. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +9 -102
  114. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +727 -72
  115. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +6 -3
  116. data/test/cases/adapters/sqlite3/sqlite_rake_test.rb +264 -0
  117. data/test/cases/adapters/sqlite3/statement_pool_test.rb +1 -1
  118. data/test/cases/adapters/sqlite3/transaction_test.rb +6 -3
  119. data/test/cases/adapters/sqlite3/virtual_column_test.rb +138 -0
  120. data/test/cases/adapters/sqlite3/virtual_table_test.rb +51 -0
  121. data/test/cases/adapters/trilogy/dbconsole_test.rb +76 -0
  122. data/test/cases/adapters/trilogy/trilogy_adapter_test.rb +473 -0
  123. data/test/cases/adapters/trilogy/trilogy_rake_test.rb +433 -0
  124. data/test/cases/aggregations_test.rb +16 -0
  125. data/test/cases/annotate_test.rb +14 -12
  126. data/test/cases/arel/attributes/attribute_test.rb +86 -47
  127. data/test/cases/arel/collectors/bind_test.rb +1 -1
  128. data/test/cases/arel/collectors/composite_test.rb +11 -1
  129. data/test/cases/arel/collectors/sql_string_test.rb +1 -1
  130. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +1 -1
  131. data/test/cases/arel/delete_manager_test.rb +0 -6
  132. data/test/cases/arel/factory_methods_test.rb +45 -0
  133. data/test/cases/arel/insert_manager_test.rb +1 -7
  134. data/test/cases/arel/nodes/as_test.rb +12 -0
  135. data/test/cases/arel/nodes/ascending_test.rb +1 -1
  136. data/test/cases/arel/nodes/bin_test.rb +1 -1
  137. data/test/cases/arel/nodes/bound_sql_literal_test.rb +31 -0
  138. data/test/cases/arel/nodes/cte_test.rb +46 -0
  139. data/test/cases/arel/nodes/descending_test.rb +1 -1
  140. data/test/cases/arel/nodes/equality_test.rb +3 -3
  141. data/test/cases/arel/nodes/filter_test.rb +37 -0
  142. data/test/cases/arel/nodes/fragments_test.rb +38 -0
  143. data/test/cases/arel/nodes/homogeneous_in_test.rb +52 -0
  144. data/test/cases/arel/nodes/infix_operation_test.rb +1 -1
  145. data/test/cases/arel/nodes/node_test.rb +1 -1
  146. data/test/cases/arel/nodes/or_test.rb +2 -2
  147. data/test/cases/arel/nodes/sql_literal_test.rb +18 -1
  148. data/test/cases/arel/nodes/table_alias_test.rb +12 -0
  149. data/test/cases/arel/nodes/unary_operation_test.rb +1 -1
  150. data/test/cases/arel/nodes/update_statement_test.rb +4 -0
  151. data/test/cases/arel/nodes_test.rb +3 -2
  152. data/test/cases/arel/select_manager_test.rb +12 -12
  153. data/test/cases/arel/support/fake_record.rb +22 -17
  154. data/test/cases/arel/table_test.rb +15 -25
  155. data/test/cases/arel/update_manager_test.rb +51 -6
  156. data/test/cases/arel/visitors/dispatch_contamination_test.rb +1 -1
  157. data/test/cases/arel/visitors/dot_test.rb +177 -2
  158. data/test/cases/arel/visitors/mysql_test.rb +49 -1
  159. data/test/cases/arel/visitors/postgres_test.rb +1 -31
  160. data/test/cases/arel/visitors/sqlite_test.rb +1 -1
  161. data/test/cases/arel/visitors/to_sql_test.rb +330 -20
  162. data/test/cases/assertions/query_assertions_test.rb +136 -0
  163. data/test/cases/associations/belongs_to_associations_test.rb +502 -41
  164. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +1 -1
  165. data/test/cases/associations/cascaded_eager_loading_test.rb +26 -15
  166. data/test/cases/associations/eager_load_nested_include_test.rb +1 -1
  167. data/test/cases/associations/eager_singularization_test.rb +1 -1
  168. data/test/cases/associations/eager_test.rb +255 -93
  169. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +25 -53
  170. data/test/cases/associations/has_many_associations_test.rb +282 -72
  171. data/test/cases/associations/has_many_through_associations_test.rb +172 -45
  172. data/test/cases/associations/has_many_through_disable_joins_associations_test.rb +197 -0
  173. data/test/cases/associations/has_one_associations_test.rb +160 -29
  174. data/test/cases/associations/has_one_through_associations_test.rb +64 -15
  175. data/test/cases/associations/has_one_through_disable_joins_associations_test.rb +82 -0
  176. data/test/cases/associations/inner_join_association_test.rb +36 -6
  177. data/test/cases/associations/inverse_associations_test.rb +177 -40
  178. data/test/cases/associations/join_model_test.rb +17 -12
  179. data/test/cases/associations/left_outer_join_association_test.rb +14 -3
  180. data/test/cases/associations/nested_error_test.rb +116 -0
  181. data/test/cases/associations/nested_through_associations_test.rb +54 -19
  182. data/test/cases/associations/required_test.rb +1 -1
  183. data/test/cases/associations_test.rb +1278 -21
  184. data/test/cases/asynchronous_queries_test.rb +310 -0
  185. data/test/cases/attribute_methods/read_test.rb +6 -0
  186. data/test/cases/attribute_methods/time_zone_converter_test.rb +21 -0
  187. data/test/cases/attribute_methods_test.rb +565 -46
  188. data/test/cases/attributes_test.rb +42 -1
  189. data/test/cases/autosave_association_test.rb +645 -92
  190. data/test/cases/base_prevent_writes_test.rb +28 -159
  191. data/test/cases/base_test.rb +434 -209
  192. data/test/cases/batches_test.rb +441 -57
  193. data/test/cases/binary_test.rb +31 -0
  194. data/test/cases/bind_parameter_test.rb +107 -16
  195. data/test/cases/cache_key_test.rb +7 -7
  196. data/test/cases/calculations_test.rb +541 -110
  197. data/test/cases/callbacks_test.rb +0 -1
  198. data/test/cases/clone_test.rb +5 -4
  199. data/test/cases/coders/yaml_column_test.rb +97 -2
  200. data/test/cases/collection_cache_key_test.rb +24 -16
  201. data/test/cases/column_alias_test.rb +2 -8
  202. data/test/cases/column_definition_test.rb +12 -3
  203. data/test/cases/comment_test.rb +23 -26
  204. data/test/cases/connection_adapters/adapter_leasing_test.rb +9 -9
  205. data/test/cases/connection_adapters/connection_handler_test.rb +145 -99
  206. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +79 -55
  207. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +17 -20
  208. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +146 -55
  209. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +136 -23
  210. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +96 -27
  211. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +5 -5
  212. data/test/cases/connection_adapters/registration_test.rb +98 -0
  213. data/test/cases/connection_adapters/schema_cache_test.rb +288 -135
  214. data/test/cases/connection_adapters/standalone_connection_test.rb +38 -0
  215. data/test/cases/connection_adapters/statement_pool_test.rb +29 -0
  216. data/test/cases/connection_adapters/type_lookup_test.rb +12 -24
  217. data/test/cases/connection_handling_test.rb +210 -0
  218. data/test/cases/connection_management_test.rb +44 -7
  219. data/test/cases/connection_pool_test.rb +466 -99
  220. data/test/cases/core_test.rb +167 -9
  221. data/test/cases/counter_cache_test.rb +129 -2
  222. data/test/cases/custom_locking_test.rb +3 -3
  223. data/test/cases/database_configurations/hash_config_test.rb +149 -13
  224. data/test/cases/database_configurations/resolver_test.rb +14 -10
  225. data/test/cases/database_configurations/url_config_test.rb +53 -0
  226. data/test/cases/database_configurations_test.rb +86 -43
  227. data/test/cases/database_selector_test.rb +30 -8
  228. data/test/cases/database_statements_test.rb +10 -14
  229. data/test/cases/date_time_precision_test.rb +141 -11
  230. data/test/cases/date_time_test.rb +19 -1
  231. data/test/cases/defaults_test.rb +107 -41
  232. data/test/cases/delegated_type_test.rb +69 -9
  233. data/test/cases/dirty_test.rb +82 -25
  234. data/test/cases/disconnected_test.rb +10 -9
  235. data/test/cases/dup_test.rb +26 -6
  236. data/test/cases/encryption/cipher/aes256_gcm_test.rb +50 -0
  237. data/test/cases/encryption/cipher_test.rb +65 -0
  238. data/test/cases/encryption/concurrency_test.rb +27 -0
  239. data/test/cases/encryption/config_test.rb +21 -0
  240. data/test/cases/encryption/configurable_test.rb +93 -0
  241. data/test/cases/encryption/contexts_test.rb +105 -0
  242. data/test/cases/encryption/derived_secret_key_provider_test.rb +31 -0
  243. data/test/cases/encryption/deterministic_key_provider_test.rb +11 -0
  244. data/test/cases/encryption/encryptable_record_api_test.rb +175 -0
  245. data/test/cases/encryption/encryptable_record_message_pack_serialized_test.rb +42 -0
  246. data/test/cases/encryption/encryptable_record_test.rb +470 -0
  247. data/test/cases/encryption/encrypted_fixtures_test.rb +22 -0
  248. data/test/cases/encryption/encrypting_only_encryptor_test.rb +20 -0
  249. data/test/cases/encryption/encryption_schemes_test.rb +240 -0
  250. data/test/cases/encryption/encryptor_test.rb +113 -0
  251. data/test/cases/encryption/envelope_encryption_key_provider_test.rb +49 -0
  252. data/test/cases/encryption/extended_deterministic_queries_test.rb +81 -0
  253. data/test/cases/encryption/helper.rb +152 -0
  254. data/test/cases/encryption/key_generator_test.rb +55 -0
  255. data/test/cases/encryption/key_provider_test.rb +57 -0
  256. data/test/cases/encryption/key_test.rb +17 -0
  257. data/test/cases/encryption/message_pack_message_serializer_test.rb +70 -0
  258. data/test/cases/encryption/message_serializer_test.rb +82 -0
  259. data/test/cases/encryption/message_test.rb +43 -0
  260. data/test/cases/encryption/null_encryptor_test.rb +21 -0
  261. data/test/cases/encryption/properties_test.rb +69 -0
  262. data/test/cases/encryption/read_only_null_encryptor_test.rb +19 -0
  263. data/test/cases/encryption/scheme_test.rb +58 -0
  264. data/test/cases/encryption/unencrypted_attributes_test.rb +27 -0
  265. data/test/cases/encryption/uniqueness_validations_test.rb +65 -0
  266. data/test/cases/enum_test.rb +374 -72
  267. data/test/cases/errors_test.rb +7 -4
  268. data/test/cases/excluding_test.rb +116 -0
  269. data/test/cases/explain_subscriber_test.rb +6 -1
  270. data/test/cases/explain_test.rb +119 -10
  271. data/test/cases/filter_attributes_test.rb +7 -14
  272. data/test/cases/finder_respond_to_test.rb +3 -3
  273. data/test/cases/finder_test.rb +357 -72
  274. data/test/cases/fixture_set/file_test.rb +9 -0
  275. data/test/cases/fixtures_test.rb +426 -190
  276. data/test/cases/helper.rb +66 -192
  277. data/test/cases/hot_compatibility_test.rb +10 -10
  278. data/test/cases/inheritance_test.rb +51 -61
  279. data/test/cases/inherited_test.rb +40 -0
  280. data/test/cases/insert_all_test.rb +419 -9
  281. data/test/cases/instrumentation_test.rb +202 -30
  282. data/test/cases/integration_test.rb +25 -7
  283. data/test/cases/invalid_connection_test.rb +5 -5
  284. data/test/cases/invertible_migration_test.rb +108 -24
  285. data/test/cases/json_attribute_test.rb +1 -0
  286. data/test/cases/json_serialization_test.rb +5 -5
  287. data/test/cases/json_shared_test_cases.rb +5 -9
  288. data/test/cases/locking_test.rb +67 -31
  289. data/test/cases/log_subscriber_test.rb +17 -15
  290. data/test/cases/marshal_serialization_test.rb +78 -5
  291. data/test/cases/message_pack_test.rb +91 -0
  292. data/test/cases/migration/change_schema_test.rb +77 -46
  293. data/test/cases/migration/change_table_test.rb +129 -114
  294. data/test/cases/migration/check_constraint_test.rb +192 -10
  295. data/test/cases/migration/column_attributes_test.rb +4 -9
  296. data/test/cases/migration/column_positioning_test.rb +2 -2
  297. data/test/cases/migration/columns_test.rb +118 -4
  298. data/test/cases/migration/command_recorder_test.rb +181 -14
  299. data/test/cases/migration/compatibility_test.rb +883 -196
  300. data/test/cases/migration/create_join_table_test.rb +8 -1
  301. data/test/cases/migration/exclusion_constraint_test.rb +192 -0
  302. data/test/cases/migration/foreign_key_test.rb +356 -14
  303. data/test/cases/migration/helper.rb +4 -1
  304. data/test/cases/migration/index_test.rb +67 -2
  305. data/test/cases/migration/invalid_options_test.rb +129 -0
  306. data/test/cases/migration/logger_test.rb +7 -4
  307. data/test/cases/migration/pending_migrations_test.rb +103 -60
  308. data/test/cases/migration/references_foreign_key_test.rb +73 -4
  309. data/test/cases/migration/references_index_test.rb +25 -27
  310. data/test/cases/migration/references_statements_test.rb +27 -3
  311. data/test/cases/migration/rename_table_test.rb +39 -54
  312. data/test/cases/migration/schema_definitions_test.rb +109 -0
  313. data/test/cases/migration/unique_constraint_test.rb +248 -0
  314. data/test/cases/migration_test.rb +677 -220
  315. data/test/cases/migrator_test.rb +102 -75
  316. data/test/cases/multi_db_migrator_test.rb +55 -34
  317. data/test/cases/multiparameter_attributes_test.rb +26 -29
  318. data/test/cases/multiple_db_test.rb +42 -19
  319. data/test/cases/nested_attributes_test.rb +124 -6
  320. data/test/cases/nested_attributes_with_callbacks_test.rb +2 -2
  321. data/test/cases/normalized_attribute_test.rb +117 -0
  322. data/test/cases/null_relation_test.rb +35 -2
  323. data/test/cases/persistence/reload_association_cache_test.rb +20 -0
  324. data/test/cases/persistence_test.rb +629 -22
  325. data/test/cases/pooled_connections_test.rb +57 -56
  326. data/test/cases/prepared_statement_status_test.rb +8 -3
  327. data/test/cases/primary_class_test.rb +134 -0
  328. data/test/cases/primary_keys_test.rb +171 -55
  329. data/test/cases/query_cache_test.rb +558 -263
  330. data/test/cases/query_logs_test.rb +293 -0
  331. data/test/cases/quoting_test.rb +39 -72
  332. data/test/cases/readonly_test.rb +50 -12
  333. data/test/cases/reaper_test.rb +22 -6
  334. data/test/cases/reflection_test.rb +207 -9
  335. data/test/cases/relation/and_test.rb +43 -0
  336. data/test/cases/relation/delegation_test.rb +32 -5
  337. data/test/cases/relation/delete_all_test.rb +27 -6
  338. data/test/cases/relation/field_ordered_values_test.rb +128 -0
  339. data/test/cases/relation/load_async_test.rb +650 -0
  340. data/test/cases/relation/merging_test.rb +80 -109
  341. data/test/cases/relation/mutation_test.rb +12 -7
  342. data/test/cases/relation/or_test.rb +11 -8
  343. data/test/cases/relation/order_test.rb +90 -0
  344. data/test/cases/relation/predicate_builder_test.rb +21 -6
  345. data/test/cases/relation/select_test.rb +162 -3
  346. data/test/cases/relation/structural_compatibility_test.rb +38 -0
  347. data/test/cases/relation/update_all_test.rb +81 -27
  348. data/test/cases/relation/where_chain_test.rb +190 -3
  349. data/test/cases/relation/where_clause_test.rb +43 -1
  350. data/test/cases/relation/where_test.rb +95 -7
  351. data/test/cases/relation/with_test.rb +165 -0
  352. data/test/cases/relation_test.rb +42 -37
  353. data/test/cases/relations_test.rb +436 -138
  354. data/test/cases/reload_models_test.rb +18 -14
  355. data/test/cases/reserved_word_test.rb +2 -1
  356. data/test/cases/result_test.rb +14 -22
  357. data/test/cases/sanitize_test.rb +89 -27
  358. data/test/cases/schema_dumper_test.rb +535 -62
  359. data/test/cases/scoping/default_scoping_test.rb +219 -35
  360. data/test/cases/scoping/named_scoping_test.rb +25 -46
  361. data/test/cases/scoping/relation_scoping_test.rb +112 -4
  362. data/test/cases/secure_password_test.rb +108 -0
  363. data/test/cases/secure_token_test.rb +72 -0
  364. data/test/cases/serialization_test.rb +1 -1
  365. data/test/cases/serialized_attribute_test.rb +305 -60
  366. data/test/cases/shard_keys_test.rb +125 -0
  367. data/test/cases/shard_selector_test.rb +45 -0
  368. data/test/cases/signed_id_test.rb +57 -5
  369. data/test/cases/statement_cache_test.rb +31 -30
  370. data/test/cases/statement_invalid_test.rb +8 -4
  371. data/test/cases/store_test.rb +73 -9
  372. data/test/cases/strict_loading_test.rb +327 -13
  373. data/test/cases/table_metadata_test.rb +16 -0
  374. data/test/cases/tasks/database_tasks_test.rb +517 -244
  375. data/test/cases/test_case.rb +254 -60
  376. data/test/cases/test_case_test.rb +69 -0
  377. data/test/cases/test_databases_test.rb +7 -3
  378. data/test/cases/test_fixtures_test.rb +19 -40
  379. data/test/cases/time_precision_test.rb +17 -5
  380. data/test/cases/timestamp_test.rb +124 -16
  381. data/test/cases/token_for_test.rb +165 -0
  382. data/test/cases/touch_later_test.rb +43 -1
  383. data/test/cases/transaction_callbacks_test.rb +325 -8
  384. data/test/cases/transaction_instrumentation_test.rb +453 -0
  385. data/test/cases/transaction_isolation_test.rb +12 -8
  386. data/test/cases/transactions_test.rb +629 -112
  387. data/test/cases/type/date_time_test.rb +7 -1
  388. data/test/cases/type/time_test.rb +9 -0
  389. data/test/cases/type/type_map_test.rb +108 -73
  390. data/test/cases/type/unsigned_integer_test.rb +12 -0
  391. data/test/cases/type_caster/connection_test.rb +19 -0
  392. data/test/cases/type_test.rb +1 -1
  393. data/test/cases/unconnected_test.rb +7 -7
  394. data/test/cases/unsafe_raw_sql_test.rb +39 -8
  395. data/test/cases/validations/absence_validation_test.rb +1 -1
  396. data/test/cases/validations/association_validation_test.rb +25 -3
  397. data/test/cases/validations/numericality_validation_test.rb +3 -9
  398. data/test/cases/validations/presence_validation_test.rb +1 -1
  399. data/test/cases/validations/uniqueness_validation_test.rb +280 -40
  400. data/test/cases/validations_test.rb +2 -2
  401. data/test/cases/view_test.rb +33 -22
  402. data/test/cases/yaml_serialization_test.rb +23 -17
  403. data/test/config.example.yml +58 -56
  404. data/test/config.rb +1 -0
  405. data/test/fixtures/1_need_quoting.yml +2 -0
  406. data/test/fixtures/accounts.yml +5 -0
  407. data/test/fixtures/aircrafts.yml +7 -0
  408. data/test/fixtures/book_identifiers.yml +11 -0
  409. data/test/fixtures/books.yml +5 -4
  410. data/test/fixtures/cake_designers.yml +5 -0
  411. data/test/fixtures/cars.yml +6 -0
  412. data/test/fixtures/chefs.yml +5 -0
  413. data/test/fixtures/clothing_items.yml +23 -0
  414. data/test/fixtures/clubs.yml +1 -1
  415. data/test/fixtures/comments.yml +2 -1
  416. data/test/fixtures/cpk_authors.yml +8 -0
  417. data/test/fixtures/cpk_books.yml +21 -0
  418. data/test/fixtures/cpk_order_agreements.yml +13 -0
  419. data/test/fixtures/cpk_order_tags.yml +15 -0
  420. data/test/fixtures/cpk_orders.yml +11 -0
  421. data/test/fixtures/cpk_posts.yml +12 -0
  422. data/test/fixtures/cpk_reviews.yml +13 -0
  423. data/test/fixtures/cpk_tags.yml +11 -0
  424. data/test/fixtures/drink_designers.yml +5 -0
  425. data/test/fixtures/encrypted_book_that_ignores_cases.yml +4 -0
  426. data/test/fixtures/encrypted_books.yml +4 -0
  427. data/test/fixtures/fk_object_to_point_to.yml +2 -0
  428. data/test/fixtures/memberships.yml +15 -15
  429. data/test/fixtures/paragraphs.yml +5 -0
  430. data/test/fixtures/parrots.yml +4 -0
  431. data/test/fixtures/people.yml +2 -0
  432. data/test/fixtures/pirates.yml +2 -2
  433. data/test/fixtures/primary_key_error/primary_key_error.yml +6 -0
  434. data/test/fixtures/references.yml +3 -3
  435. data/test/fixtures/sharded_blog_posts.yml +14 -0
  436. data/test/fixtures/sharded_blog_posts_tags.yml +27 -0
  437. data/test/fixtures/sharded_blogs.yml +8 -0
  438. data/test/fixtures/sharded_comments.yml +22 -0
  439. data/test/fixtures/sharded_tags.yml +34 -0
  440. data/test/fixtures/sponsors.yml +2 -2
  441. data/test/fixtures/topics.yml +1 -0
  442. data/test/fixtures/treasures.yml +4 -0
  443. data/test/fixtures/virtual_columns.yml +5 -0
  444. data/test/fixtures/warehouse_things.yml +1 -1
  445. data/test/migrations/old_and_new_versions/20210716122844_add_people_description.rb +5 -0
  446. data/test/migrations/old_and_new_versions/20210716123013_add_people_number_of_legs.rb +5 -0
  447. data/test/migrations/old_and_new_versions/230_add_people_hobby.rb +5 -0
  448. data/test/migrations/old_and_new_versions/231_add_people_last_name.rb +5 -0
  449. data/test/migrations/scope/1_unscoped.rb +7 -0
  450. data/test/migrations/scope/2_mysql_only.mysql.rb +7 -0
  451. data/test/models/admin/user.rb +2 -0
  452. data/test/models/admin/user_json.rb +48 -0
  453. data/test/models/attachment.rb +7 -0
  454. data/test/models/author.rb +70 -3
  455. data/test/models/author_encrypted.rb +16 -0
  456. data/test/models/bird.rb +1 -1
  457. data/test/models/book.rb +18 -11
  458. data/test/models/book_destroy_async.rb +1 -1
  459. data/test/models/book_encrypted.rb +106 -0
  460. data/test/models/book_identifier.rb +5 -0
  461. data/test/models/branch.rb +11 -0
  462. data/test/models/bulb.rb +1 -1
  463. data/test/models/car.rb +8 -1
  464. data/test/models/cat.rb +1 -1
  465. data/test/models/category.rb +3 -1
  466. data/test/models/chat_message.rb +8 -0
  467. data/test/models/chef.rb +52 -0
  468. data/test/models/clothing_item.rb +12 -0
  469. data/test/models/club.rb +4 -1
  470. data/test/models/comment.rb +7 -4
  471. data/test/models/comment_overlapping_counter_cache.rb +15 -0
  472. data/test/models/company.rb +16 -2
  473. data/test/models/computer.rb +1 -0
  474. data/test/models/contact.rb +3 -3
  475. data/test/models/contract.rb +5 -1
  476. data/test/models/cpk/author.rb +9 -0
  477. data/test/models/cpk/book.rb +55 -0
  478. data/test/models/cpk/book_destroy_async.rb +9 -0
  479. data/test/models/cpk/car.rb +9 -0
  480. data/test/models/cpk/car_review.rb +9 -0
  481. data/test/models/cpk/chapter.rb +16 -0
  482. data/test/models/cpk/chapter_destroy_async.rb +10 -0
  483. data/test/models/cpk/comment.rb +9 -0
  484. data/test/models/cpk/order.rb +55 -0
  485. data/test/models/cpk/order_agreement.rb +10 -0
  486. data/test/models/cpk/order_tag.rb +10 -0
  487. data/test/models/cpk/post.rb +10 -0
  488. data/test/models/cpk/posts_tag.rb +10 -0
  489. data/test/models/cpk/review.rb +9 -0
  490. data/test/models/cpk/tag.rb +10 -0
  491. data/test/models/cpk.rb +15 -0
  492. data/test/models/customer.rb +3 -1
  493. data/test/models/destroy_async_parent.rb +1 -1
  494. data/test/models/developer.rb +44 -3
  495. data/test/models/discount.rb +4 -0
  496. data/test/models/drink_designer.rb +1 -0
  497. data/test/models/editor.rb +8 -0
  498. data/test/models/editorship.rb +6 -0
  499. data/test/models/entry.rb +5 -0
  500. data/test/models/essay.rb +6 -1
  501. data/test/models/essay_destroy_async.rb +1 -0
  502. data/test/models/eye.rb +87 -21
  503. data/test/models/face.rb +1 -1
  504. data/test/models/friendship.rb +3 -0
  505. data/test/models/hardback.rb +7 -0
  506. data/test/models/hotel.rb +2 -0
  507. data/test/models/human.rb +2 -2
  508. data/test/models/invoice.rb +2 -1
  509. data/test/models/invokes_an_undefined_method.rb +5 -0
  510. data/test/models/line_item.rb +6 -0
  511. data/test/models/member.rb +9 -4
  512. data/test/models/membership.rb +2 -2
  513. data/test/models/message.rb +2 -1
  514. data/test/models/need_quoting.rb +5 -0
  515. data/test/models/paragraph.rb +5 -0
  516. data/test/models/parrot.rb +6 -3
  517. data/test/models/person.rb +7 -4
  518. data/test/models/pirate.rb +4 -1
  519. data/test/models/pk_autopopulated_by_a_trigger_record.rb +5 -0
  520. data/test/models/post.rb +65 -2
  521. data/test/models/post_encrypted.rb +15 -0
  522. data/test/models/post_with_prefetched_pk.rb +15 -0
  523. data/test/models/project.rb +1 -0
  524. data/test/models/publication.rb +16 -0
  525. data/test/models/raises_argument_error.rb +5 -0
  526. data/test/models/raises_no_method_error.rb +5 -0
  527. data/test/models/recipient.rb +5 -0
  528. data/test/models/reference.rb +1 -1
  529. data/test/models/reply.rb +1 -1
  530. data/test/models/room.rb +3 -0
  531. data/test/models/sharded/blog.rb +12 -0
  532. data/test/models/sharded/blog_post.rb +17 -0
  533. data/test/models/sharded/blog_post_destroy_async.rb +14 -0
  534. data/test/models/sharded/blog_post_tag.rb +11 -0
  535. data/test/models/sharded/blog_post_with_revision.rb +11 -0
  536. data/test/models/sharded/comment.rb +12 -0
  537. data/test/models/sharded/comment_destroy_async.rb +12 -0
  538. data/test/models/sharded/tag.rb +11 -0
  539. data/test/models/sharded.rb +8 -0
  540. data/test/models/ship.rb +1 -1
  541. data/test/models/shipping_line.rb +11 -0
  542. data/test/models/subscriber.rb +1 -0
  543. data/test/models/subscription.rb +3 -1
  544. data/test/models/too_long_table_name.rb +5 -0
  545. data/test/models/topic.rb +3 -1
  546. data/test/models/traffic_light.rb +2 -2
  547. data/test/models/traffic_light_encrypted.rb +21 -0
  548. data/test/models/translation.rb +9 -0
  549. data/test/models/treasure.rb +1 -1
  550. data/test/models/tyre.rb +1 -1
  551. data/test/models/user.rb +16 -0
  552. data/test/models/user_with_invalid_relation.rb +28 -0
  553. data/test/models/uuid_comment.rb +5 -0
  554. data/test/models/uuid_entry.rb +5 -0
  555. data/test/models/uuid_message.rb +5 -0
  556. data/test/models/zine.rb +2 -1
  557. data/test/schema/mysql2_specific_schema.rb +26 -12
  558. data/test/schema/postgresql_specific_schema.rb +121 -13
  559. data/test/schema/schema.rb +361 -103
  560. data/test/schema/sqlite_specific_schema.rb +17 -6
  561. data/test/schema/trilogy_specific_schema.rb +96 -0
  562. data/test/support/adapter_helper.rb +104 -0
  563. data/test/support/async_helper.rb +15 -0
  564. data/test/support/config.rb +1 -1
  565. data/test/support/connection.rb +14 -2
  566. data/test/support/ddl_helper.rb +1 -1
  567. data/test/support/fake_adapter.rb +42 -0
  568. data/test/support/load_schema_helper.rb +22 -0
  569. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic.dump +0 -0
  570. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic_associations.dump +0 -0
  571. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic.dump +0 -0
  572. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic_associations.dump +0 -0
  573. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic.dump +0 -0
  574. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic_associations.dump +0 -0
  575. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic.dump +0 -0
  576. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic_associations.dump +0 -0
  577. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic.dump +0 -0
  578. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic_associations.dump +0 -0
  579. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic.dump +0 -0
  580. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic_associations.dump +0 -0
  581. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic.dump +0 -0
  582. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic_associations.dump +0 -0
  583. data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic.dump +0 -0
  584. data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic_associations.dump +0 -0
  585. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic.dump +0 -0
  586. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic_associations.dump +0 -0
  587. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic.dump +0 -0
  588. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic_associations.dump +0 -0
  589. data/test/support/schema_dumping_helper.rb +15 -9
  590. data/test/support/tools.rb +37 -0
  591. data/test/support/yaml_compatibility_fixtures/rails_4_1_no_symbol.yml +22 -0
  592. metadata +247 -15
@@ -5,15 +5,28 @@ require "concurrent/atomic/count_down_latch"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
- class ConnectionPoolTest < ActiveRecord::TestCase
8
+ module ConnectionPoolTests
9
+ def self.included(test)
10
+ super
11
+ test.use_transactional_tests = false
12
+ end
13
+
9
14
  attr_reader :pool
10
15
 
11
16
  def setup
12
- super
17
+ @previous_isolation_level = ActiveSupport::IsolatedExecutionState.isolation_level
13
18
 
14
19
  # Keep a duplicate pool so we do not bother others
15
- @db_config = ActiveRecord::Base.connection_pool.db_config
16
- @pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, @db_config)
20
+ config = ActiveRecord::Base.connection_pool.db_config
21
+ @db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(
22
+ config.env_name,
23
+ config.name,
24
+ config.configuration_hash.merge(
25
+ checkout_timeout: 0.2, # Reduce checkout_timeout to speedup tests
26
+ )
27
+ )
28
+
29
+ @pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, @db_config, :writing, :default)
17
30
  @pool = ConnectionPool.new(@pool_config)
18
31
 
19
32
  if in_memory_db?
@@ -27,28 +40,26 @@ module ActiveRecord
27
40
  end
28
41
  end
29
42
 
30
- teardown do
43
+ def teardown
44
+ super
31
45
  @pool.disconnect!
32
- end
33
-
34
- def active_connections(pool)
35
- pool.connections.find_all(&:in_use?)
46
+ ActiveSupport::IsolatedExecutionState.isolation_level = @previous_isolation_level
36
47
  end
37
48
 
38
49
  def test_checkout_after_close
39
- connection = pool.connection
50
+ connection = pool.lease_connection
40
51
  assert_predicate connection, :in_use?
41
52
 
42
53
  connection.close
43
54
  assert_not_predicate connection, :in_use?
44
55
 
45
- assert_predicate pool.connection, :in_use?
56
+ assert_predicate pool.lease_connection, :in_use?
46
57
  end
47
58
 
48
59
  def test_released_connection_moves_between_threads
49
60
  thread_conn = nil
50
61
 
51
- Thread.new {
62
+ new_thread {
52
63
  pool.with_connection do |conn|
53
64
  thread_conn = conn
54
65
  end
@@ -56,7 +67,7 @@ module ActiveRecord
56
67
 
57
68
  assert thread_conn
58
69
 
59
- Thread.new {
70
+ new_thread {
60
71
  pool.with_connection do |conn|
61
72
  assert_equal thread_conn, conn
62
73
  end
@@ -66,14 +77,24 @@ module ActiveRecord
66
77
  def test_with_connection
67
78
  assert_equal 0, active_connections(pool).size
68
79
 
69
- main_thread = pool.connection
80
+ main_thread = pool.lease_connection
70
81
  assert_equal 1, active_connections(pool).size
71
82
 
72
- Thread.new {
83
+ new_thread {
84
+ pool.with_connection do |conn|
85
+ assert conn
86
+ assert_equal 2, active_connections(pool).size
87
+ end
88
+ assert_equal 1, active_connections(pool).size
89
+
73
90
  pool.with_connection do |conn|
74
91
  assert conn
75
92
  assert_equal 2, active_connections(pool).size
93
+ pool.lease_connection
76
94
  end
95
+
96
+ assert_equal 2, active_connections(pool).size
97
+ pool.release_connection
77
98
  assert_equal 1, active_connections(pool).size
78
99
  }.join
79
100
 
@@ -81,9 +102,21 @@ module ActiveRecord
81
102
  assert_equal 0, active_connections(pool).size
82
103
  end
83
104
 
105
+ def test_new_connection_no_query
106
+ skip("Can't test with in-memory dbs") if in_memory_db?
107
+ assert_equal 0, pool.connections.size
108
+ pool.with_connection { |_conn| } # warm the schema cache
109
+ pool.flush(0)
110
+ assert_equal 0, pool.connections.size
111
+
112
+ assert_no_queries do
113
+ pool.with_connection { |_conn| }
114
+ end
115
+ end
116
+
84
117
  def test_active_connection_in_use
85
118
  assert_not_predicate pool, :active_connection?
86
- main_thread = pool.connection
119
+ main_thread = pool.lease_connection
87
120
 
88
121
  assert_predicate pool, :active_connection?
89
122
 
@@ -96,17 +129,19 @@ module ActiveRecord
96
129
  @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
97
130
  @pool.size.times { assert @pool.checkout }
98
131
 
99
- assert_raises(ConnectionTimeoutError) do
132
+ error = assert_raises(ConnectionTimeoutError) do
100
133
  @pool.checkout
101
134
  end
135
+ assert_equal @pool, error.connection_pool
102
136
  end
103
137
 
104
138
  def test_full_pool_blocks
139
+ skip_fiber_testing
105
140
  cs = @pool.size.times.map { @pool.checkout }
106
- t = Thread.new { @pool.checkout }
141
+ t = new_thread { @pool.checkout }
107
142
 
108
143
  # make sure our thread is in the timeout section
109
- Thread.pass until @pool.num_waiting_in_queue == 1
144
+ pass_to(t) until @pool.num_waiting_in_queue == 1
110
145
 
111
146
  connection = cs.first
112
147
  connection.close
@@ -114,6 +149,7 @@ module ActiveRecord
114
149
  end
115
150
 
116
151
  def test_full_pool_blocking_shares_load_interlock
152
+ skip_fiber_testing
117
153
  @pool.instance_variable_set(:@size, 1)
118
154
 
119
155
  load_interlock_latch = Concurrent::CountDownLatch.new
@@ -122,7 +158,7 @@ module ActiveRecord
122
158
  able_to_get_connection = false
123
159
  able_to_load = false
124
160
 
125
- thread_with_load_interlock = Thread.new do
161
+ thread_with_load_interlock = new_thread do
126
162
  ActiveSupport::Dependencies.interlock.running do
127
163
  load_interlock_latch.count_down
128
164
  connection_latch.wait
@@ -133,7 +169,7 @@ module ActiveRecord
133
169
  end
134
170
  end
135
171
 
136
- thread_with_last_connection = Thread.new do
172
+ thread_with_last_connection = new_thread do
137
173
  @pool.with_connection do
138
174
  connection_latch.count_down
139
175
  load_interlock_latch.wait
@@ -152,11 +188,12 @@ module ActiveRecord
152
188
  end
153
189
 
154
190
  def test_removing_releases_latch
191
+ skip_fiber_testing
155
192
  cs = @pool.size.times.map { @pool.checkout }
156
- t = Thread.new { @pool.checkout }
193
+ t = new_thread { @pool.checkout }
157
194
 
158
195
  # make sure our thread is in the timeout section
159
- Thread.pass until @pool.num_waiting_in_queue == 1
196
+ pass_to(t) until @pool.num_waiting_in_queue == 1
160
197
 
161
198
  connection = cs.first
162
199
  @pool.remove connection
@@ -179,13 +216,13 @@ module ActiveRecord
179
216
  def test_reap_inactive
180
217
  ready = Concurrent::CountDownLatch.new
181
218
  @pool.checkout
182
- child = Thread.new do
219
+ child = new_thread do
183
220
  @pool.checkout
184
221
  @pool.checkout
185
222
  ready.count_down
186
- Thread.stop
223
+ stop_thread
187
224
  end
188
- ready.wait
225
+ pass_to(child) until ready.wait(0)
189
226
 
190
227
  assert_equal 3, active_connections(@pool).size
191
228
 
@@ -198,20 +235,44 @@ module ActiveRecord
198
235
  @pool.connections.each { |conn| conn.close if conn.in_use? }
199
236
  end
200
237
 
238
+ def test_inactive_are_returned_from_dead_thread
239
+ ready = Concurrent::CountDownLatch.new
240
+ @pool.instance_variable_set(:@size, 1)
241
+
242
+ child = new_thread do
243
+ @pool.checkout
244
+ ready.count_down
245
+ stop_thread
246
+ end
247
+
248
+ pass_to(child) until ready.wait(0)
249
+
250
+ assert_equal 1, active_connections(@pool).size
251
+
252
+ child.terminate
253
+ child.join
254
+
255
+ @pool.checkout
256
+
257
+ assert_equal 1, active_connections(@pool).size
258
+ ensure
259
+ @pool.connections.each { |conn| conn.close if conn.in_use? }
260
+ end
261
+
201
262
  def test_idle_timeout_configuration
202
263
  @pool.disconnect!
203
264
 
204
265
  config = @db_config.configuration_hash.merge(idle_timeout: "0.02")
205
266
  db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(@db_config.env_name, @db_config.name, config)
206
267
 
207
- pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config)
268
+ pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config, :writing, :default)
208
269
  @pool = ConnectionPool.new(pool_config)
209
270
  idle_conn = @pool.checkout
210
271
  @pool.checkin(idle_conn)
211
272
 
212
273
  idle_conn.instance_variable_set(
213
274
  :@idle_since,
214
- Concurrent.monotonic_time - 0.01
275
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - 0.01
215
276
  )
216
277
 
217
278
  @pool.flush
@@ -219,7 +280,7 @@ module ActiveRecord
219
280
 
220
281
  idle_conn.instance_variable_set(
221
282
  :@idle_since,
222
- Concurrent.monotonic_time - 0.02
283
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - 0.03
223
284
  )
224
285
 
225
286
  @pool.flush
@@ -231,14 +292,14 @@ module ActiveRecord
231
292
 
232
293
  config = @db_config.configuration_hash.merge(idle_timeout: -5)
233
294
  db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(@db_config.env_name, @db_config.name, config)
234
- pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config)
295
+ pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config, :writing, :default)
235
296
  @pool = ConnectionPool.new(pool_config)
236
297
  idle_conn = @pool.checkout
237
298
  @pool.checkin(idle_conn)
238
299
 
239
300
  idle_conn.instance_variable_set(
240
301
  :@idle_since,
241
- Concurrent.monotonic_time - 1
302
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - 1
242
303
  )
243
304
 
244
305
  @pool.flush
@@ -257,7 +318,7 @@ module ActiveRecord
257
318
 
258
319
  idle_conn.instance_variable_set(
259
320
  :@idle_since,
260
- Concurrent.monotonic_time - 1000
321
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - 1000
261
322
  )
262
323
 
263
324
  @pool.flush(30)
@@ -273,7 +334,7 @@ module ActiveRecord
273
334
  idle_conn = @pool.checkout
274
335
  recent_conn = @pool.checkout
275
336
  active_conn = @pool.checkout
276
- _dead_conn = Thread.new { @pool.checkout }.join
337
+ _dead_conn = new_thread { @pool.checkout }.join
277
338
 
278
339
  @pool.checkin idle_conn
279
340
  @pool.checkin recent_conn
@@ -306,29 +367,29 @@ module ActiveRecord
306
367
  end
307
368
 
308
369
  def test_remove_connection_for_thread
309
- conn = @pool.connection
370
+ conn = @pool.lease_connection
310
371
  @pool.remove conn
311
- assert_not_equal(conn, @pool.connection)
372
+ assert_not_equal(conn, @pool.lease_connection)
312
373
  ensure
313
374
  conn.close if conn
314
375
  end
315
376
 
316
377
  def test_active_connection?
317
378
  assert_not_predicate @pool, :active_connection?
318
- assert @pool.connection
379
+ assert @pool.lease_connection
319
380
  assert_predicate @pool, :active_connection?
320
381
  @pool.release_connection
321
382
  assert_not_predicate @pool, :active_connection?
322
383
  end
323
384
 
324
- def test_checkout_behaviour
385
+ def test_checkout_behavior
325
386
  pool = ConnectionPool.new(@pool_config)
326
- main_connection = pool.connection
387
+ main_connection = pool.lease_connection
327
388
  assert_not_nil main_connection
328
389
  threads = []
329
390
  4.times do |i|
330
- threads << Thread.new(i) do
331
- thread_connection = pool.connection
391
+ threads << new_thread(i) do
392
+ thread_connection = pool.lease_connection
332
393
  assert_not_nil thread_connection
333
394
  thread_connection.close
334
395
  end
@@ -336,9 +397,9 @@ module ActiveRecord
336
397
 
337
398
  threads.each(&:join)
338
399
 
339
- Thread.new do
340
- assert pool.connection
341
- pool.connection.close
400
+ new_thread do
401
+ assert pool.lease_connection
402
+ pool.lease_connection.close
342
403
  end.join
343
404
  end
344
405
 
@@ -364,6 +425,8 @@ module ActiveRecord
364
425
  # available connections slowly, ensuring the wakeup order is
365
426
  # correct in this case.
366
427
  def test_checkout_fairness
428
+ skip_fiber_testing
429
+
367
430
  @pool.instance_variable_set(:@size, 10)
368
431
  expected = (1..@pool.size).to_a.freeze
369
432
  # check out all connections so our threads start out waiting
@@ -371,23 +434,30 @@ module ActiveRecord
371
434
  mutex = Mutex.new
372
435
  order = []
373
436
  errors = []
437
+ dispose_held_connections = Concurrent::Event.new
374
438
 
375
439
  threads = expected.map do |i|
376
- t = Thread.new {
440
+ t = new_thread {
377
441
  begin
378
442
  @pool.checkout # never checked back in
379
443
  mutex.synchronize { order << i }
444
+
445
+ # if the thread terminates, its connection may be
446
+ # reclaimed by the pool, so we need to hold on to it
447
+ # until we're done trickling in connections
448
+ dispose_held_connections.wait
380
449
  rescue => e
381
450
  mutex.synchronize { errors << e }
382
451
  end
383
452
  }
384
- Thread.pass until @pool.num_waiting_in_queue == i
453
+ pass_to(t) until @pool.num_waiting_in_queue == i
385
454
  t
386
455
  end
387
456
 
388
457
  # this should wake up the waiting threads one by one in order
389
- conns.each { |conn| @pool.checkin(conn); sleep 0.1 }
458
+ conns.each { |conn| @pool.checkin(conn); sleep 0.01 }
390
459
 
460
+ dispose_held_connections.set
391
461
  threads.each(&:join)
392
462
 
393
463
  raise errors.first if errors.any?
@@ -402,23 +472,28 @@ module ActiveRecord
402
472
  # group1 threads, and the fact that only group1 and no group2
403
473
  # threads acquired a connection is enforced.
404
474
  def test_checkout_fairness_by_group
475
+ skip_fiber_testing
476
+
405
477
  @pool.instance_variable_set(:@size, 10)
406
478
  # take all the connections
407
479
  conns = (1..10).map { @pool.checkout }
408
480
  mutex = Mutex.new
409
481
  successes = [] # threads that successfully got a connection
410
482
  errors = []
483
+ dispose_held_connections = Concurrent::Event.new
411
484
 
412
485
  make_thread = proc do |i|
413
- t = Thread.new {
486
+ t = new_thread {
414
487
  begin
415
488
  @pool.checkout # never checked back in
416
489
  mutex.synchronize { successes << i }
490
+
491
+ dispose_held_connections.wait
417
492
  rescue => e
418
493
  mutex.synchronize { errors << e }
419
494
  end
420
495
  }
421
- Thread.pass until @pool.num_waiting_in_queue == i
496
+ pass_to(t) until @pool.num_waiting_in_queue == i
422
497
  t
423
498
  end
424
499
 
@@ -444,6 +519,7 @@ module ActiveRecord
444
519
  winners = mutex.synchronize { successes.dup }
445
520
  checkin.call(group2.size) # should wake up everyone remaining
446
521
 
522
+ dispose_held_connections.set
447
523
  group1.each(&:join)
448
524
  group2.each(&:join)
449
525
 
@@ -457,10 +533,10 @@ module ActiveRecord
457
533
  def test_automatic_reconnect_restores_after_disconnect
458
534
  pool = ConnectionPool.new(@pool_config)
459
535
  assert pool.automatic_reconnect
460
- assert pool.connection
536
+ assert pool.lease_connection
461
537
 
462
538
  pool.disconnect!
463
- assert pool.connection
539
+ assert pool.lease_connection
464
540
  end
465
541
 
466
542
  def test_automatic_reconnect_can_be_disabled
@@ -469,7 +545,7 @@ module ActiveRecord
469
545
  pool.automatic_reconnect = false
470
546
 
471
547
  assert_raises(ConnectionNotEstablished) do
472
- pool.connection
548
+ pool.lease_connection
473
549
  end
474
550
 
475
551
  assert_raises(ConnectionNotEstablished) do
@@ -478,7 +554,7 @@ module ActiveRecord
478
554
  end
479
555
 
480
556
  def test_pool_sets_connection_visitor
481
- assert @pool.connection.visitor.is_a?(Arel::Visitors::ToSql)
557
+ assert @pool.lease_connection.visitor.is_a?(Arel::Visitors::ToSql)
482
558
  end
483
559
 
484
560
  # make sure exceptions are thrown when establish_connection
@@ -491,21 +567,20 @@ module ActiveRecord
491
567
  end
492
568
  end
493
569
 
494
- class ConnectionTestModel < ActiveRecord::Base
495
- self.abstract_class = true
496
- end
497
-
498
570
  def test_connection_notification_is_called
499
571
  payloads = []
500
572
  subscription = ActiveSupport::Notifications.subscribe("!connection.active_record") do |name, started, finished, unique_id, payload|
501
573
  payloads << payload
502
574
  end
503
- ConnectionTestModel.establish_connection :arunit
504
575
 
505
- assert_equal [:config, :shard, :spec_name], payloads[0].keys.sort
506
- assert_equal "ActiveRecord::ConnectionAdapters::ConnectionPoolTest::ConnectionTestModel", payloads[0][:spec_name]
576
+ @connection_test_model_class.establish_connection :arunit
577
+
578
+ assert_equal [:config, :connection_name, :role, :shard], payloads[0].keys.sort
579
+ assert_equal @connection_test_model_class.name, payloads[0][:connection_name]
507
580
  assert_equal ActiveRecord::Base.default_shard, payloads[0][:shard]
581
+ assert_equal :writing, payloads[0][:role]
508
582
  ensure
583
+ @connection_test_model_class.remove_connection
509
584
  ActiveSupport::Notifications.unsubscribe(subscription) if subscription
510
585
  end
511
586
 
@@ -514,30 +589,47 @@ module ActiveRecord
514
589
  subscription = ActiveSupport::Notifications.subscribe("!connection.active_record") do |name, started, finished, unique_id, payload|
515
590
  payloads << payload
516
591
  end
517
- ConnectionTestModel.connects_to shards: { shard_two: { writing: :arunit } }
592
+ @connection_test_model_class.connects_to shards: { default: { writing: :arunit } }
518
593
 
519
- assert_equal [:config, :shard, :spec_name], payloads[0].keys.sort
520
- assert_equal "ActiveRecord::ConnectionAdapters::ConnectionPoolTest::ConnectionTestModel", payloads[0][:spec_name]
521
- assert_equal :shard_two, payloads[0][:shard]
594
+ assert_equal [:config, :connection_name, :role, :shard], payloads[0].keys.sort
595
+ assert_equal @connection_test_model_class.name, payloads[0][:connection_name]
596
+ assert_equal :default, payloads[0][:shard]
597
+ assert_equal :writing, payloads[0][:role]
522
598
  ensure
599
+ @connection_test_model_class.remove_connection
523
600
  ActiveSupport::Notifications.unsubscribe(subscription) if subscription
524
601
  end
525
602
 
603
+ def test_sets_pool_schema_reflection
604
+ pool.schema_cache.add(:posts)
605
+ assert pool.schema_cache.cached?(:posts)
606
+
607
+ pool.schema_reflection = SchemaReflection.new("does-not-exist")
608
+ assert_not pool.schema_cache.cached?(:posts)
609
+
610
+ pool.schema_cache.add(:posts)
611
+ assert pool.schema_cache.cached?(:posts)
612
+ end
613
+
526
614
  def test_pool_sets_connection_schema_cache
615
+ pool.schema_cache.add(:posts)
527
616
  connection = pool.checkout
528
- schema_cache = SchemaCache.new connection
529
- schema_cache.add(:posts)
530
- pool.schema_cache = schema_cache
531
617
 
532
618
  pool.with_connection do |conn|
533
- assert_equal pool.schema_cache.size, conn.schema_cache.size
534
- assert_same pool.schema_cache.columns(:posts), conn.schema_cache.columns(:posts)
619
+ # We've retrieved a second, distinct, connection from the pool
620
+ assert_not_same connection, conn
621
+
622
+ # But the new connection can already see the schema cache
623
+ # entry we added above
624
+ assert_equal connection.schema_cache.size, conn.schema_cache.size
625
+ assert_same connection.schema_cache.columns(:posts), conn.schema_cache.columns(:posts)
535
626
  end
536
627
 
537
628
  pool.checkin connection
538
629
  end
539
630
 
540
631
  def test_concurrent_connection_establishment
632
+ skip_fiber_testing
541
633
  assert_operator @pool.connections.size, :<=, 1
542
634
 
543
635
  all_threads_in_new_connection = Concurrent::CountDownLatch.new(@pool.size - @pool.connections.size)
@@ -553,7 +645,7 @@ module ActiveRecord
553
645
 
554
646
  connecting_threads = []
555
647
  @pool.size.times do
556
- connecting_threads << Thread.new { @pool.checkout }
648
+ connecting_threads << new_thread { @pool.checkout }
557
649
  end
558
650
 
559
651
  begin
@@ -578,9 +670,10 @@ module ActiveRecord
578
670
  @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
579
671
  [:disconnect, :clear_reloadable_connections].each do |group_action_method|
580
672
  @pool.with_connection do |connection|
581
- assert_raises(ExclusiveConnectionTimeoutError) do
582
- Thread.new { @pool.public_send(group_action_method) }.join
673
+ error = assert_raises(ExclusiveConnectionTimeoutError) do
674
+ new_thread { @pool.public_send(group_action_method) }.join
583
675
  end
676
+ assert_equal @pool, error.connection_pool
584
677
  end
585
678
  end
586
679
  ensure
@@ -588,10 +681,11 @@ module ActiveRecord
588
681
  end
589
682
 
590
683
  def test_disconnect_and_clear_reloadable_connections_attempt_to_wait_for_threads_to_return_their_conns
684
+ skip_fiber_testing
591
685
  [:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
592
686
  thread = timed_join_result = nil
593
687
  @pool.with_connection do |connection|
594
- thread = Thread.new { @pool.send(group_action_method) }
688
+ thread = new_thread { @pool.send(group_action_method) }
595
689
 
596
690
  # give the other `thread` some time to get stuck in `group_action_method`
597
691
  timed_join_result = thread.join(0.3)
@@ -609,43 +703,53 @@ module ActiveRecord
609
703
 
610
704
  def test_bang_versions_of_disconnect_and_clear_reloadable_connections_if_unable_to_acquire_all_connections_proceed_anyway
611
705
  @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
612
- [:disconnect!, :clear_reloadable_connections!].each do |group_action_method|
613
- @pool.with_connection do |connection|
614
- Thread.new { @pool.send(group_action_method) }.join
615
- # assert connection has been forcefully taken away from us
616
- assert_not_predicate @pool, :active_connection?
617
706
 
618
- # make a new connection for with_connection to clean up
619
- @pool.connection
620
- end
707
+ @pool.with_connection do |connection|
708
+ new_thread { @pool.disconnect! }.join
709
+ # assert connection has been forcefully taken away from us
710
+ assert_not_predicate @pool, :active_connection?
711
+
712
+ # make a new connection for with_connection to clean up
713
+ @pool.lease_connection
714
+ end
715
+ @pool.release_connection
716
+
717
+ @pool.with_connection do |connection|
718
+ new_thread { @pool.clear_reloadable_connections! }.join
719
+ # assert connection has been forcefully taken away from us
720
+ assert_not_predicate @pool, :active_connection?
721
+
722
+ # make a new connection for with_connection to clean up
723
+ @pool.lease_connection
621
724
  end
622
725
  end
623
726
 
624
727
  def test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads
728
+ skip_fiber_testing
625
729
  with_single_connection_pool do |pool|
626
730
  [:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
627
- conn = pool.connection # drain the only available connection
731
+ conn = pool.lease_connection # drain the only available connection
628
732
  second_thread_done = Concurrent::Event.new
629
733
 
630
734
  begin
631
735
  # create a first_thread and let it get into the FIFO queue first
632
- first_thread = Thread.new do
736
+ first_thread = new_thread do
633
737
  pool.with_connection { second_thread_done.wait }
634
738
  end
635
739
 
636
740
  # wait for first_thread to get in queue
637
- Thread.pass until pool.num_waiting_in_queue == 1
741
+ pass_to(first_thread) until pool.num_waiting_in_queue == 1
638
742
 
639
743
  # create a different, later thread, that will attempt to do a "group action",
640
744
  # but because of the group action semantics it should be able to preempt the
641
745
  # first_thread when a connection is made available
642
- second_thread = Thread.new do
746
+ second_thread = new_thread do
643
747
  pool.send(group_action_method)
644
748
  second_thread_done.set
645
749
  end
646
750
 
647
751
  # wait for second_thread to get in queue
648
- Thread.pass until pool.num_waiting_in_queue == 2
752
+ pass_to(second_thread) until pool.num_waiting_in_queue == 2
649
753
 
650
754
  # return the only available connection
651
755
  pool.checkin(conn)
@@ -662,8 +766,8 @@ module ActiveRecord
662
766
  if failed
663
767
  second_thread_done.set
664
768
 
665
- first_thread.join(2)
666
- second_thread.join(2)
769
+ first_thread&.join(2)
770
+ second_thread&.join(2)
667
771
  end
668
772
 
669
773
  first_thread.join(10) || raise("first_thread got stuck")
@@ -674,18 +778,19 @@ module ActiveRecord
674
778
  end
675
779
 
676
780
  def test_clear_reloadable_connections_creates_new_connections_for_waiting_threads_if_necessary
781
+ skip_fiber_testing
677
782
  with_single_connection_pool do |pool|
678
- conn = pool.connection # drain the only available connection
783
+ conn = pool.lease_connection # drain the only available connection
679
784
  def conn.requires_reloading? # make sure it gets removed from the pool by clear_reloadable_connections
680
785
  true
681
786
  end
682
787
 
683
- stuck_thread = Thread.new do
788
+ stuck_thread = new_thread do
684
789
  pool.with_connection { }
685
790
  end
686
791
 
687
792
  # wait for stuck_thread to get in queue
688
- Thread.pass until pool.num_waiting_in_queue == 1
793
+ pass_to(stuck_thread) until pool.num_waiting_in_queue == 1
689
794
 
690
795
  pool.clear_reloadable_connections
691
796
 
@@ -698,22 +803,27 @@ module ActiveRecord
698
803
  end
699
804
 
700
805
  def test_connection_pool_stat
806
+ Thread.report_on_exception, original_report_on_exception = false, Thread.report_on_exception
701
807
  with_single_connection_pool do |pool|
702
808
  pool.with_connection do |connection|
703
809
  stats = pool.stat
704
- assert_equal({ size: 1, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
810
+ assert_equal({ size: 1, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 0.2 }, stats)
705
811
  end
706
812
 
707
813
  stats = pool.stat
708
- assert_equal({ size: 1, connections: 1, busy: 0, dead: 0, idle: 1, waiting: 0, checkout_timeout: 5 }, stats)
814
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 0, idle: 1, waiting: 0, checkout_timeout: 0.2 }, stats)
709
815
 
710
- Thread.new do
711
- pool.checkout
712
- Thread.current.kill
713
- end.join
816
+ assert_raise(ThreadError) do
817
+ new_thread do
818
+ pool.checkout
819
+ raise ThreadError
820
+ end.join
821
+ end
714
822
 
715
823
  stats = pool.stat
716
- assert_equal({ size: 1, connections: 1, busy: 0, dead: 1, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
824
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 1, idle: 0, waiting: 0, checkout_timeout: 0.2 }, stats)
825
+ ensure
826
+ Thread.report_on_exception = original_report_on_exception
717
827
  end
718
828
  end
719
829
 
@@ -729,7 +839,7 @@ module ActiveRecord
729
839
  # the pool is thread-safe.
730
840
  connections.each_index do |idx|
731
841
  if connections[idx] == conn2
732
- Thread.new do
842
+ new_thread do
733
843
  @pool.remove(conn2)
734
844
  end.join
735
845
  end
@@ -739,16 +849,273 @@ module ActiveRecord
739
849
  assert_not_nil found_conn
740
850
  end
741
851
 
852
+ def test_role_and_shard_is_returned
853
+ assert_equal :writing, @pool_config.role
854
+ assert_equal :writing, @pool.role
855
+ assert_equal :writing, @pool.lease_connection.role
856
+
857
+ assert_equal :default, @pool_config.shard
858
+ assert_equal :default, @pool.shard
859
+ assert_equal :default, @pool.lease_connection.shard
860
+
861
+ db_config = ActiveRecord::Base.connection_pool.db_config
862
+ pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config, :reading, :shard_one)
863
+ pool = ConnectionPool.new(pool_config)
864
+
865
+ assert_equal :reading, pool_config.role
866
+ assert_equal :reading, pool.role
867
+ assert_equal :reading, pool.lease_connection.role
868
+
869
+ assert_equal :shard_one, pool_config.shard
870
+ assert_equal :shard_one, pool.shard
871
+ assert_equal :shard_one, pool.lease_connection.shard
872
+ end
873
+
874
+ def test_pin_connection_always_returns_the_same_connection
875
+ assert_not_predicate @pool, :active_connection?
876
+ @pool.pin_connection!(true)
877
+ pinned_connection = @pool.checkout
878
+
879
+ assert_not_predicate @pool, :active_connection?
880
+ assert_same pinned_connection, @pool.lease_connection
881
+ assert_predicate @pool, :active_connection?
882
+
883
+ assert_same pinned_connection, @pool.checkout
884
+
885
+ @pool.release_connection
886
+ assert_not_predicate @pool, :active_connection?
887
+ assert_same pinned_connection, @pool.checkout
888
+ end
889
+
890
+ def test_pin_connection_connected?
891
+ skip("Can't test with in-memory dbs") if in_memory_db?
892
+
893
+ assert_not_predicate @pool, :connected?
894
+ @pool.pin_connection!(true)
895
+ assert_predicate @pool, :connected?
896
+
897
+ pin_connection = @pool.checkout
898
+
899
+ @pool.disconnect
900
+ assert_not_predicate @pool, :connected?
901
+ assert_same pin_connection, @pool.checkout
902
+ assert_predicate @pool, :connected?
903
+ end
904
+
905
+ def test_pin_connection_synchronize_the_connection
906
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
907
+ @pool.pin_connection!(true)
908
+ assert_not_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
909
+ @pool.unpin_connection!
910
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
911
+
912
+ @pool.pin_connection!(false)
913
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
914
+ end
915
+
916
+ def test_pin_connection_opens_a_transaction
917
+ assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
918
+ @pool.pin_connection!(true)
919
+ assert_instance_of RealTransaction, @pool.lease_connection.current_transaction
920
+ @pool.unpin_connection!
921
+ assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
922
+ end
923
+
924
+ def test_unpin_connection_returns_whether_transaction_has_been_rolledback
925
+ @pool.pin_connection!(true)
926
+ assert_equal true, @pool.unpin_connection!
927
+
928
+ @pool.pin_connection!(true)
929
+ @pool.lease_connection.commit_transaction
930
+ assert_equal false, @pool.unpin_connection!
931
+
932
+ @pool.pin_connection!(true)
933
+ @pool.lease_connection.rollback_transaction
934
+ assert_equal false, @pool.unpin_connection!
935
+ end
936
+
937
+ def test_pin_connection_nesting
938
+ assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
939
+ @pool.pin_connection!(true)
940
+ assert_instance_of RealTransaction, @pool.lease_connection.current_transaction
941
+ @pool.pin_connection!(true)
942
+ assert_instance_of SavepointTransaction, @pool.lease_connection.current_transaction
943
+ @pool.unpin_connection!
944
+ assert_instance_of RealTransaction, @pool.lease_connection.current_transaction
945
+ @pool.unpin_connection!
946
+ assert_instance_of NullTransaction, @pool.lease_connection.current_transaction
947
+
948
+ assert_raises(RuntimeError, match: /There isn't a pinned connection/) do
949
+ @pool.unpin_connection!
950
+ end
951
+ end
952
+
953
+ def test_pin_connection_nesting_lock
954
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
955
+
956
+ @pool.pin_connection!(true)
957
+ actual_lock = @pool.lease_connection.lock
958
+ assert_not_equal ActiveSupport::Concurrency::NullLock, actual_lock
959
+
960
+ @pool.pin_connection!(false)
961
+ assert_same actual_lock, @pool.lease_connection.lock
962
+
963
+ @pool.unpin_connection!
964
+ assert_same actual_lock, @pool.lease_connection.lock
965
+
966
+ @pool.unpin_connection!
967
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
968
+ end
969
+
970
+ def test_pin_connection_nesting_lock_inverse
971
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
972
+
973
+ @pool.pin_connection!(false)
974
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
975
+
976
+ @pool.pin_connection!(true)
977
+ actual_lock = @pool.lease_connection.lock
978
+ assert_not_equal ActiveSupport::Concurrency::NullLock, actual_lock
979
+
980
+ @pool.unpin_connection!
981
+ assert_same actual_lock, @pool.lease_connection.lock # The lock persist until full unpin
982
+
983
+ @pool.unpin_connection!
984
+ assert_equal ActiveSupport::Concurrency::NullLock, @pool.lease_connection.lock
985
+ end
986
+
987
+ def test_inspect_does_not_show_secrets
988
+ assert_match(/#<ActiveRecord::ConnectionAdapters::ConnectionPool env_name="\w+" role=:writing>/, @pool.inspect)
989
+
990
+ db_config = ActiveRecord::Base.connection_pool.db_config
991
+ pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config, :reading, :shard_one)
992
+ pool = ConnectionPool.new(pool_config)
993
+
994
+ assert_match(/#<ActiveRecord::ConnectionAdapters::ConnectionPool env_name="\w+" role=:reading shard=:shard_one>/, pool.inspect)
995
+ end
996
+
997
+ def test_checkout_callback_error_does_not_affect_permanent_lease_and_active_connection_state
998
+ checkout_error = StandardError.new("error during checkout")
999
+ proc_to_raise = -> { raise checkout_error }
1000
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.set_callback(:checkout, :after, proc_to_raise)
1001
+
1002
+ assert_predicate @pool, :permanent_lease?
1003
+ assert_not_predicate @pool, :active_connection?
1004
+
1005
+ error = assert_raises StandardError do
1006
+ @pool.lease_connection
1007
+ end
1008
+ assert_same checkout_error, error
1009
+
1010
+ assert_predicate @pool, :permanent_lease?
1011
+ assert_not_predicate @pool, :active_connection?
1012
+ ensure
1013
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.skip_callback(:checkout, :after, proc_to_raise)
1014
+ end
1015
+
742
1016
  private
1017
+ def active_connections(pool)
1018
+ pool.connections.find_all(&:in_use?)
1019
+ end
1020
+
743
1021
  def with_single_connection_pool
744
1022
  config = @db_config.configuration_hash.merge(pool: 1)
745
1023
  db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", config)
746
- pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config)
1024
+ pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new(ActiveRecord::Base, db_config, :writing, :default)
747
1025
 
748
1026
  yield(pool = ConnectionPool.new(pool_config))
749
1027
  ensure
750
1028
  pool.disconnect! if pool
751
1029
  end
752
1030
  end
1031
+
1032
+ class ConnectionPoolThreadTest < ActiveRecord::TestCase
1033
+ include ConnectionPoolTests
1034
+
1035
+ class ThreadConnectionTestModel < ActiveRecord::Base
1036
+ self.abstract_class = true
1037
+ end
1038
+
1039
+ def setup
1040
+ super
1041
+ ActiveSupport::IsolatedExecutionState.isolation_level = :thread
1042
+ @connection_test_model_class = ThreadConnectionTestModel
1043
+ end
1044
+
1045
+ def test_lock_thread_allow_fiber_reentrency
1046
+ connection = @pool.checkout
1047
+ connection.lock_thread = ActiveSupport::IsolatedExecutionState.context
1048
+ connection.transaction do
1049
+ enumerator = Enumerator.new do |yielder|
1050
+ connection.transaction do
1051
+ yielder.yield 1
1052
+ end
1053
+ end
1054
+ assert_equal 1, enumerator.next
1055
+ end
1056
+ end
1057
+
1058
+ private
1059
+ def new_thread(...)
1060
+ Thread.new(...)
1061
+ end
1062
+
1063
+ def stop_thread
1064
+ Thread.stop
1065
+ end
1066
+
1067
+ def pass_to(_thread)
1068
+ Thread.pass
1069
+ end
1070
+
1071
+ def skip_fiber_testing; end
1072
+ end
1073
+
1074
+ class ConnectionPoolFiberTest < ActiveRecord::TestCase
1075
+ include ConnectionPoolTests
1076
+
1077
+ class FiberConnectionTestModel < ActiveRecord::Base
1078
+ self.abstract_class = true
1079
+ end
1080
+
1081
+ class ThreadlikeFiber < Fiber
1082
+ def join(timeout = nil)
1083
+ now = Time.now
1084
+ resume while alive? && (!timeout || Time.now - now < timeout)
1085
+ end
1086
+
1087
+ def terminate
1088
+ nil
1089
+ end
1090
+
1091
+ unless method_defined?(:kill) # RUBY_VERSION <= "3.3"
1092
+ def kill
1093
+ end
1094
+ end
1095
+ end
1096
+
1097
+ def setup
1098
+ super
1099
+ ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
1100
+ @connection_test_model_class = FiberConnectionTestModel
1101
+ end
1102
+
1103
+ private
1104
+ def new_thread(*args, &block)
1105
+ ThreadlikeFiber.new(*args, &block)
1106
+ end
1107
+
1108
+ def stop_thread
1109
+ Fiber.yield
1110
+ end
1111
+
1112
+ def pass_to(fiber)
1113
+ fiber.resume
1114
+ end
1115
+
1116
+ def skip_fiber_testing
1117
+ skip "Can't test isolation_level=fiber without a Ruby 3.1+ Fiber Scheduler"
1118
+ end
1119
+ end
753
1120
  end
754
1121
  end