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
@@ -0,0 +1,650 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+ require "models/post"
5
+ require "models/category"
6
+ require "models/comment"
7
+ require "models/other_dog"
8
+ require "concurrent/atomic/count_down_latch"
9
+
10
+ module ActiveRecord
11
+ class LoadAsyncTest < ActiveRecord::TestCase
12
+ include WaitForAsyncTestHelper
13
+
14
+ fixtures :posts, :comments, :categories, :categories_posts
15
+
16
+ setup do
17
+ end
18
+
19
+ def test_scheduled?
20
+ deferred_posts = Post.where(author_id: 1).load_async
21
+ if in_memory_db?
22
+ assert_not_predicate deferred_posts, :scheduled?
23
+ else
24
+ assert_predicate deferred_posts, :scheduled?
25
+ end
26
+ assert_predicate deferred_posts, :loaded?
27
+ deferred_posts.to_a
28
+ assert_not_predicate deferred_posts, :scheduled?
29
+ end
30
+
31
+ def test_null_scheduled?
32
+ deferred_null_posts = Post.none.load_async
33
+ if in_memory_db?
34
+ assert_not_predicate deferred_null_posts, :scheduled?
35
+ else
36
+ assert_predicate deferred_null_posts, :scheduled?
37
+ end
38
+ assert_predicate deferred_null_posts, :loaded?
39
+ deferred_null_posts.to_a
40
+ assert_not_predicate deferred_null_posts, :scheduled?
41
+ end
42
+
43
+ def test_reset
44
+ deferred_posts = Post.where(author_id: 1).load_async
45
+ if in_memory_db?
46
+ assert_not_predicate deferred_posts, :scheduled?
47
+ else
48
+ assert_predicate deferred_posts, :scheduled?
49
+ end
50
+ deferred_posts.reset
51
+ assert_not_predicate deferred_posts, :scheduled?
52
+ end
53
+
54
+ unless in_memory_db?
55
+ def test_load_async_has_many_association
56
+ post = Post.first
57
+
58
+ defered_comments = post.comments.load_async
59
+ assert_predicate defered_comments, :scheduled?
60
+
61
+ events = []
62
+ callback = -> (event) do
63
+ events << event unless event.payload[:name] == "SCHEMA"
64
+ end
65
+
66
+ wait_for_async_query
67
+ ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
68
+ defered_comments.to_a
69
+ end
70
+
71
+ assert_equal [["Comment Load", true]], events.map { |e| [e.payload[:name], e.payload[:async]] }
72
+ assert_not_predicate post.comments, :loaded?
73
+ end
74
+
75
+ def test_load_async_has_many_through_association
76
+ post = Post.first
77
+
78
+ defered_categories = post.scategories.load_async
79
+ assert_predicate defered_categories, :scheduled?
80
+
81
+ events = []
82
+ callback = -> (event) do
83
+ events << event unless event.payload[:name] == "SCHEMA"
84
+ end
85
+
86
+ wait_for_async_query
87
+ ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
88
+ defered_categories.to_a
89
+ end
90
+
91
+ assert_equal [["Category Load", true]], events.map { |e| [e.payload[:name], e.payload[:async]] }
92
+ assert_not_predicate post.scategories, :loaded?
93
+ end
94
+ end
95
+
96
+ def test_notification_forwarding
97
+ expected_records = Post.where(author_id: 1).to_a
98
+
99
+ status = {}
100
+
101
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
102
+ if event.payload[:name] == "Post Load"
103
+ status[:executed] = true
104
+ status[:async] = event.payload[:async]
105
+ status[:thread_id] = Thread.current.object_id
106
+ status[:lock_wait] = event.payload[:lock_wait]
107
+ end
108
+ end
109
+
110
+ deferred_posts = Post.where(author_id: 1).load_async
111
+ wait_for_async_query
112
+
113
+ assert_equal expected_records, deferred_posts.to_a
114
+ assert_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
115
+ assert_equal Thread.current.object_id, status[:thread_id]
116
+ if Post.lease_connection.supports_concurrent_connections?
117
+ assert_instance_of Float, status[:lock_wait]
118
+ else
119
+ assert_nil status[:lock_wait]
120
+ end
121
+ ensure
122
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
123
+ end
124
+
125
+ def test_simple_query
126
+ expected_records = Post.where(author_id: 1).to_a
127
+
128
+ status = {}
129
+
130
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
131
+ if event.payload[:name] == "Post Load"
132
+ status[:executed] = true
133
+ status[:async] = event.payload[:async]
134
+ end
135
+ end
136
+
137
+ deferred_posts = Post.where(author_id: 1).load_async
138
+ wait_for_async_query
139
+
140
+ assert_equal expected_records, deferred_posts.to_a
141
+ assert_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
142
+ ensure
143
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
144
+ end
145
+
146
+ def test_load_async_from_transaction
147
+ posts = nil
148
+ Post.transaction do
149
+ Post.where(author_id: 1).update_all(title: "In Transaction")
150
+ posts = Post.where(author_id: 1).load_async
151
+ if in_memory_db?
152
+ assert_not_predicate posts, :scheduled?
153
+ else
154
+ assert_predicate posts, :scheduled?
155
+ end
156
+ assert_predicate posts, :loaded?
157
+ raise ActiveRecord::Rollback
158
+ end
159
+
160
+ assert_not_nil posts
161
+ assert_equal ["In Transaction"], posts.map(&:title).uniq
162
+ end
163
+
164
+ def test_load_async_instrumentation_is_thread_safe
165
+ skip unless ActiveRecord::Base.connection.async_enabled?
166
+ skip "IBM_DB async executor uses pinned connection; with_instrumenter contaminates shared connection while blocked" if current_adapter?(:IBM_DBAdapter)
167
+
168
+ begin
169
+ latch1 = Concurrent::CountDownLatch.new
170
+ latch2 = Concurrent::CountDownLatch.new
171
+
172
+ old_log = ActiveRecord::Base.connection.method(:log)
173
+ ActiveRecord::Base.connection.singleton_class.undef_method(:log)
174
+
175
+ ActiveRecord::Base.connection.singleton_class.define_method(:log) do |*args, **kwargs, &block|
176
+ unless kwargs[:async]
177
+ return old_log.call(*args, **kwargs, &block)
178
+ end
179
+
180
+ latch1.count_down
181
+ latch2.wait
182
+ old_log.call(*args, **kwargs, &block)
183
+ end
184
+
185
+ Post.async_count
186
+ latch1.wait
187
+
188
+ notification_called = false
189
+ ActiveSupport::Notifications.subscribed(->(*) { notification_called = true }, "sql.active_record") do
190
+ Post.count
191
+ end
192
+
193
+ assert(notification_called)
194
+ ensure
195
+ latch2.count_down
196
+ ActiveRecord::Base.connection.singleton_class.undef_method(:log)
197
+ ActiveRecord::Base.connection.singleton_class.define_method(:log, old_log)
198
+ end
199
+ end
200
+
201
+ def test_execute_or_skip_does_not_contaminate_caller_thread_instrumenter
202
+ skip unless ActiveRecord::Base.connection.async_enabled?
203
+
204
+ begin
205
+ # Intercept schedule_query to run execute_or_skip on the current thread,
206
+ # simulating what happens when the async executor's caller_runs fallback
207
+ # policy triggers due to a saturated thread pool.
208
+ pool = Post.connection_pool
209
+ old_schedule_query = pool.method(:schedule_query)
210
+ pool.singleton_class.undef_method(:schedule_query)
211
+ pool.singleton_class.define_method(:schedule_query) do |future_result|
212
+ future_result.execute_or_skip
213
+ end
214
+
215
+ # This async query runs execute_or_skip on the current thread
216
+ Post.async_count
217
+
218
+ # After the async query completes, synchronous queries must still
219
+ # publish sql.active_record notifications
220
+ notification_called = false
221
+ ActiveSupport::Notifications.subscribed(->(*) { notification_called = true }, "sql.active_record") do
222
+ Post.count
223
+ end
224
+
225
+ assert notification_called,
226
+ "sql.active_record notification was not published after execute_or_skip ran on the caller thread"
227
+ ensure
228
+ pool.singleton_class.undef_method(:schedule_query)
229
+ pool.singleton_class.define_method(:schedule_query, old_schedule_query)
230
+ ActiveSupport::IsolatedExecutionState.delete(:active_record_instrumenter)
231
+ end
232
+ end
233
+
234
+ def test_eager_loading_query
235
+ expected_records = Post.where(author_id: 1).eager_load(:comments).to_a
236
+
237
+ status = {}
238
+
239
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
240
+ if event.payload[:name] == "SQL"
241
+ status[:executed] = true
242
+ status[:async] = event.payload[:async]
243
+ end
244
+ end
245
+
246
+ deferred_posts = Post.where(author_id: 1).eager_load(:comments).load_async
247
+ wait_for_async_query
248
+
249
+ if in_memory_db?
250
+ assert_not_predicate deferred_posts, :scheduled?
251
+ else
252
+ assert_predicate deferred_posts, :scheduled?
253
+ end
254
+
255
+ assert_equal expected_records, deferred_posts.to_a
256
+ assert_queries_count(0) do
257
+ deferred_posts.each(&:comments)
258
+ end
259
+ assert_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
260
+ ensure
261
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
262
+ end
263
+
264
+ def test_contradiction
265
+ assert_queries_count(0) do
266
+ assert_equal [], Post.where(id: []).load_async.to_a
267
+ end
268
+
269
+ Post.where(id: []).load_async.reset
270
+ end
271
+
272
+ def test_pluck
273
+ titles = Post.where(author_id: 1).pluck(:title)
274
+ assert_equal titles, Post.where(author_id: 1).load_async.pluck(:title)
275
+ end
276
+
277
+ def test_count
278
+ count = Post.where(author_id: 1).count
279
+ assert_equal count, Post.where(author_id: 1).load_async.count
280
+ end
281
+
282
+ def test_size
283
+ expected_size = Post.where(author_id: 1).size
284
+
285
+ deferred_posts = Post.where(author_id: 1).load_async
286
+
287
+ assert_equal expected_size, deferred_posts.size
288
+ assert_predicate deferred_posts, :loaded?
289
+ end
290
+
291
+ def test_empty?
292
+ deferred_posts = Post.where(author_id: 1).load_async
293
+
294
+ assert_equal false, deferred_posts.empty?
295
+ assert_predicate deferred_posts, :loaded?
296
+ end
297
+
298
+ def test_load_async_pluck_with_query_cache
299
+ titles = Post.where(author_id: 1).pluck(:title)
300
+ Post.cache do
301
+ assert_equal titles, Post.where(author_id: 1).load_async.pluck(:title)
302
+ end
303
+ end
304
+
305
+ def test_load_async_count_with_query_cache
306
+ count = Post.where(author_id: 1).count
307
+ Post.cache do
308
+ assert_equal count, Post.where(author_id: 1).load_async.count
309
+ end
310
+ end
311
+ end
312
+
313
+ class LoadAsyncNullExecutorTest < ActiveRecord::TestCase
314
+ unless in_memory_db?
315
+ fixtures :posts, :comments
316
+
317
+ def setup
318
+ @old_config = ActiveRecord.async_query_executor
319
+ ActiveRecord.async_query_executor = nil
320
+ end
321
+
322
+ def teardown
323
+ ActiveRecord.async_query_executor = @old_config
324
+ end
325
+
326
+ def test_scheduled?
327
+ deferred_posts = Post.where(author_id: 1).load_async
328
+ assert_not_predicate deferred_posts, :scheduled?
329
+ assert_predicate deferred_posts, :loaded?
330
+ deferred_posts.to_a
331
+ assert_not_predicate deferred_posts, :scheduled?
332
+ end
333
+
334
+ def test_reset
335
+ deferred_posts = Post.where(author_id: 1).load_async
336
+ assert_not_predicate deferred_posts, :scheduled?
337
+ deferred_posts.reset
338
+ assert_not_predicate deferred_posts, :scheduled?
339
+ end
340
+
341
+ def test_simple_query
342
+ expected_records = Post.where(author_id: 1).to_a
343
+
344
+ status = {}
345
+
346
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
347
+ if event.payload[:name] == "Post Load"
348
+ status[:executed] = true
349
+ status[:async] = event.payload[:async]
350
+ end
351
+ end
352
+
353
+ deferred_posts = Post.where(author_id: 1).load_async
354
+
355
+ assert_equal expected_records, deferred_posts.to_a
356
+ assert_not_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
357
+ ensure
358
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
359
+ end
360
+
361
+ def test_load_async_from_transaction
362
+ posts = nil
363
+ Post.transaction do
364
+ Post.where(author_id: 1).update_all(title: "In Transaction")
365
+ posts = Post.where(author_id: 1).load_async
366
+ assert_not_predicate posts, :scheduled?
367
+ assert_predicate posts, :loaded?
368
+ raise ActiveRecord::Rollback
369
+ end
370
+
371
+ assert_not_nil posts
372
+ assert_equal ["In Transaction"], posts.map(&:title).uniq
373
+ end
374
+
375
+ def test_eager_loading_query
376
+ expected_records = Post.where(author_id: 1).eager_load(:comments).to_a
377
+
378
+ status = {}
379
+
380
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
381
+ if event.payload[:name] == "SQL"
382
+ status[:executed] = true
383
+ status[:async] = event.payload[:async]
384
+ end
385
+ end
386
+
387
+ deferred_posts = Post.where(author_id: 1).eager_load(:comments).load_async
388
+
389
+ assert_not_predicate deferred_posts, :scheduled?
390
+
391
+ assert_equal expected_records, deferred_posts.to_a
392
+ assert_queries_count(0) do
393
+ deferred_posts.each(&:comments)
394
+ end
395
+
396
+ assert_predicate Post.lease_connection, :supports_concurrent_connections?
397
+ assert_not status[:async], "Expected status[:async] to be false with NullExecutor"
398
+ ensure
399
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
400
+ end
401
+
402
+ def test_contradiction
403
+ assert_queries_count(0) do
404
+ assert_equal [], Post.where(id: []).load_async.to_a
405
+ end
406
+
407
+ Post.where(id: []).load_async.reset
408
+ end
409
+
410
+ def test_pluck
411
+ titles = Post.where(author_id: 1).pluck(:title)
412
+ assert_equal titles, Post.where(author_id: 1).load_async.pluck(:title)
413
+ end
414
+
415
+ def test_size
416
+ expected_size = Post.where(author_id: 1).size
417
+
418
+ deferred_posts = Post.where(author_id: 1).load_async
419
+
420
+ assert_equal expected_size, deferred_posts.size
421
+ assert_predicate deferred_posts, :loaded?
422
+ end
423
+
424
+ def test_empty?
425
+ deferred_posts = Post.where(author_id: 1).load_async
426
+
427
+ assert_equal false, deferred_posts.empty?
428
+ assert_predicate deferred_posts, :loaded?
429
+ end
430
+ end
431
+ end
432
+
433
+ class LoadAsyncMultiThreadPoolExecutorTest < ActiveRecord::TestCase
434
+ unless in_memory_db?
435
+ include WaitForAsyncTestHelper
436
+
437
+ fixtures :posts, :comments
438
+
439
+ def setup
440
+ @old_config = ActiveRecord.async_query_executor
441
+ ActiveRecord.async_query_executor = :multi_thread_pool
442
+
443
+ config_hash1 = ActiveRecord::Base.connection_pool.db_config.configuration_hash.merge(min_threads: 0, max_threads: 10)
444
+ config_hash2 = ARUnit2Model.connection_pool.db_config.configuration_hash.merge(min_threads: 0, max_threads: 10)
445
+
446
+ ActiveRecord::Base.establish_connection(config_hash1)
447
+ ARUnit2Model.establish_connection(config_hash2)
448
+ end
449
+
450
+ def teardown
451
+ ActiveRecord.async_query_executor = @old_config
452
+ clean_up_connection_handler
453
+ ActiveRecord::Base.establish_connection(:arunit)
454
+ ARUnit2Model.establish_connection(:arunit2)
455
+ end
456
+
457
+ def test_async_query_executor_and_configuration
458
+ assert_equal :multi_thread_pool, ActiveRecord.async_query_executor
459
+
460
+ assert_equal 0, ActiveRecord::Base.connection_pool.db_config.configuration_hash[:min_threads]
461
+ assert_equal 0, ARUnit2Model.connection_pool.db_config.configuration_hash[:min_threads]
462
+
463
+ assert_equal 10, ActiveRecord::Base.connection_pool.db_config.configuration_hash[:max_threads]
464
+ assert_equal 10, ARUnit2Model.connection_pool.db_config.configuration_hash[:max_threads]
465
+ end
466
+
467
+ def test_scheduled?
468
+ deferred_posts = Post.where(author_id: 1).load_async
469
+ assert_predicate deferred_posts, :scheduled?
470
+ assert_predicate deferred_posts, :loaded?
471
+ deferred_posts.to_a
472
+ assert_not_predicate deferred_posts, :scheduled?
473
+ end
474
+
475
+ def test_reset
476
+ deferred_posts = Post.where(author_id: 1).load_async
477
+ assert_predicate deferred_posts, :scheduled?
478
+ deferred_posts.reset
479
+ assert_not_predicate deferred_posts, :scheduled?
480
+ end
481
+
482
+ def test_simple_query
483
+ expected_records = Post.where(author_id: 1).to_a
484
+
485
+ status = {}
486
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
487
+ if event.payload[:name] == "Post Load"
488
+ status[:executed] = true
489
+ status[:async] = event.payload[:async]
490
+ end
491
+ end
492
+
493
+ deferred_posts = Post.where(author_id: 1).load_async
494
+ wait_for_async_query
495
+
496
+ assert_equal expected_records, deferred_posts.to_a
497
+ assert_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
498
+ ensure
499
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
500
+ end
501
+
502
+ def test_load_async_from_transaction
503
+ posts = nil
504
+ Post.transaction do
505
+ Post.where(author_id: 1).update_all(title: "In Transaction")
506
+ posts = Post.where(author_id: 1).load_async
507
+ assert_predicate posts, :scheduled?
508
+ assert_predicate posts, :loaded?
509
+ raise ActiveRecord::Rollback
510
+ end
511
+
512
+ assert_not_nil posts
513
+ assert_equal ["In Transaction"], posts.map(&:title).uniq
514
+ end
515
+
516
+ def test_eager_loading_query
517
+ expected_records = Post.where(author_id: 1).eager_load(:comments).to_a
518
+
519
+ status = {}
520
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
521
+ if event.payload[:name] == "SQL"
522
+ status[:executed] = true
523
+ status[:async] = event.payload[:async]
524
+ end
525
+ end
526
+
527
+ deferred_posts = Post.where(author_id: 1).eager_load(:comments).load_async
528
+ wait_for_async_query
529
+
530
+ assert_predicate deferred_posts, :scheduled?
531
+
532
+ assert_equal expected_records, deferred_posts.to_a
533
+ assert_queries_count(0) do
534
+ deferred_posts.each(&:comments)
535
+ end
536
+ assert_equal Post.lease_connection.supports_concurrent_connections?, status[:async]
537
+ ensure
538
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
539
+ end
540
+
541
+ def test_contradiction
542
+ assert_queries_count(0) do
543
+ assert_equal [], Post.where(id: []).load_async.to_a
544
+ end
545
+
546
+ Post.where(id: []).load_async.reset
547
+ end
548
+
549
+ def test_pluck
550
+ titles = Post.where(author_id: 1).pluck(:title)
551
+ assert_equal titles, Post.where(author_id: 1).load_async.pluck(:title)
552
+ end
553
+
554
+ def test_size
555
+ expected_size = Post.where(author_id: 1).size
556
+
557
+ deferred_posts = Post.where(author_id: 1).load_async
558
+
559
+ assert_equal expected_size, deferred_posts.size
560
+ assert_predicate deferred_posts, :loaded?
561
+ end
562
+
563
+ def test_empty?
564
+ deferred_posts = Post.where(author_id: 1).load_async
565
+
566
+ assert_equal false, deferred_posts.empty?
567
+ assert_predicate deferred_posts, :loaded?
568
+ end
569
+ end
570
+ end
571
+
572
+ class LoadAsyncMixedThreadPoolExecutorTest < ActiveRecord::TestCase
573
+ unless in_memory_db?
574
+ include WaitForAsyncTestHelper
575
+
576
+ fixtures :posts, :comments, :other_dogs
577
+
578
+ def setup
579
+ @previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
580
+ @old_config = ActiveRecord.async_query_executor
581
+ ActiveRecord.async_query_executor = :multi_thread_pool
582
+ config_hash1 = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").configuration_hash
583
+ config_hash2 = ActiveRecord::Base.configurations.configs_for(env_name: "arunit2", name: "primary").configuration_hash
584
+ config = {
585
+ "default_env" => {
586
+ "animals" => config_hash2.merge({ min_threads: 0, max_threads: 0 }),
587
+ "primary" => config_hash1.merge({ min_threads: 0, max_threads: 10 })
588
+ }
589
+ }
590
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
591
+
592
+ ActiveRecord::Base.establish_connection(:primary)
593
+ ARUnit2Model.establish_connection(:animals)
594
+ end
595
+
596
+ def teardown
597
+ ENV["RAILS_ENV"] = @previous_env
598
+ ActiveRecord::Base.configurations = @prev_configs
599
+ ActiveRecord.async_query_executor = @old_config
600
+ clean_up_connection_handler
601
+ end
602
+
603
+ def test_scheduled?
604
+ deferred_posts = Post.where(author_id: 1).load_async
605
+ assert_predicate deferred_posts, :scheduled?
606
+ assert_predicate deferred_posts, :loaded?
607
+ deferred_posts.to_a
608
+ assert_not_predicate deferred_posts, :scheduled?
609
+
610
+ deferred_dogs = OtherDog.where(id: 1).load_async
611
+ assert_not_predicate deferred_dogs, :scheduled?
612
+ assert_predicate deferred_dogs, :loaded?
613
+ end
614
+
615
+ def test_simple_query
616
+ expected_records = Post.where(author_id: 1).to_a
617
+ expected_dogs = OtherDog.where(id: 1).to_a
618
+
619
+ status = {}
620
+ dog_status = {}
621
+
622
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
623
+ if event.payload[:name] == "Post Load"
624
+ status[:executed] = true
625
+ status[:async] = event.payload[:async]
626
+ end
627
+
628
+ if event.payload[:name] == "OtherDog Load"
629
+ dog_status[:executed] = true
630
+ dog_status[:async] = event.payload[:async]
631
+ end
632
+ end
633
+
634
+ deferred_posts = Post.where(author_id: 1).load_async
635
+ deferred_dogs = OtherDog.where(id: 1).load_async
636
+
637
+ wait_for_async_query
638
+ wait_for_async_query
639
+
640
+ assert_equal expected_records, deferred_posts.to_a
641
+ assert_equal expected_dogs, deferred_dogs.to_a
642
+
643
+ assert_equal Post.lease_connection.async_enabled?, status[:async]
644
+ assert_equal OtherDog.lease_connection.async_enabled?, dog_status[:async]
645
+ ensure
646
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
647
+ end
648
+ end
649
+ end
650
+ end