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
@@ -3,7 +3,10 @@
3
3
  require "cases/helper"
4
4
  require "models/author"
5
5
  require "models/book"
6
+ require "models/category"
6
7
  require "models/cart"
8
+ require "models/developer"
9
+ require "models/ship"
7
10
  require "models/speedometer"
8
11
  require "models/subscription"
9
12
  require "models/subscriber"
@@ -17,6 +20,7 @@ class InsertAllTest < ActiveRecord::TestCase
17
20
 
18
21
  def setup
19
22
  Arel::Table.engine = nil # should not rely on the global Arel::Table.engine
23
+ @original_db_warnings_action = :ignore
20
24
  end
21
25
 
22
26
  def teardown
@@ -43,6 +47,17 @@ class InsertAllTest < ActiveRecord::TestCase
43
47
  end
44
48
  end
45
49
 
50
+ def test_insert_with_type_casting_and_serialize_is_consistent
51
+ skip unless supports_insert_returning?
52
+
53
+ book_name = ["Array"]
54
+ created_book_id = Book.create!(name: book_name).id
55
+ inserted_book_id = Book.insert!({ name: book_name }, returning: :id).first["id"]
56
+ created_book = Book.find_by!(id: created_book_id)
57
+ inserted_book = Book.find_by!(id: inserted_book_id)
58
+ assert_equal created_book.name, inserted_book.name
59
+ end
60
+
46
61
  def test_insert_all
47
62
  assert_difference "Book.count", +10 do
48
63
  Book.insert_all! [
@@ -61,9 +76,11 @@ class InsertAllTest < ActiveRecord::TestCase
61
76
  end
62
77
 
63
78
  def test_insert_all_should_handle_empty_arrays
64
- assert_raise ArgumentError do
65
- Book.insert_all! []
66
- end
79
+ skip unless supports_insert_on_duplicate_update?
80
+
81
+ assert_empty Book.insert_all([])
82
+ assert_empty Book.insert_all!([])
83
+ assert_empty Book.upsert_all([])
67
84
  end
68
85
 
69
86
  def test_insert_all_raises_on_duplicate_records
@@ -109,6 +126,13 @@ class InsertAllTest < ActiveRecord::TestCase
109
126
  assert_equal %w[ Rework ], result.pluck("name")
110
127
  end
111
128
 
129
+ def test_insert_all_returns_requested_sql_fields
130
+ skip unless supports_insert_returning?
131
+
132
+ result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(name) as name")
133
+ assert_equal %w[ REWORK ], result.pluck("name")
134
+ end
135
+
112
136
  def test_insert_all_can_skip_duplicate_records
113
137
  skip unless supports_insert_on_duplicate_skip?
114
138
 
@@ -117,6 +141,24 @@ class InsertAllTest < ActiveRecord::TestCase
117
141
  end
118
142
  end
119
143
 
144
+ if current_adapter?(:Mysql2Adapter, :TrilogyAdapter)
145
+ def test_insert_all_generates_correct_sql
146
+ skip unless supports_insert_on_duplicate_skip?
147
+
148
+ assert_queries_match(/ON DUPLICATE KEY UPDATE/) do
149
+ Book.insert_all [{ id: 1, name: "Agile Web Development with Rails" }]
150
+ end
151
+ end
152
+
153
+ def test_insert_all_succeeds_when_passed_no_attributes
154
+ skip unless supports_insert_on_duplicate_skip?
155
+
156
+ assert_nothing_raised do
157
+ Book.insert_all [{}]
158
+ end
159
+ end
160
+ end
161
+
120
162
  def test_insert_all_with_skip_duplicates_and_autonumber_id_not_given
121
163
  skip unless supports_insert_on_duplicate_skip?
122
164
 
@@ -146,10 +188,10 @@ class InsertAllTest < ActiveRecord::TestCase
146
188
  def test_skip_duplicates_strategy_does_not_secretly_upsert
147
189
  skip unless supports_insert_on_duplicate_skip?
148
190
 
149
- book = Book.create!(author_id: 8, name: "Refactoring", format: "EXPECTED")
191
+ book = Book.create!(format: "EXPECTED", author_id: 8, name: "Refactoring")
150
192
 
151
193
  assert_no_difference "Book.count" do
152
- Book.insert({ author_id: 8, name: "Refactoring", format: "UNEXPECTED" })
194
+ Book.insert_all([{ format: "UNEXPECTED", author_id: 8, name: "Refactoring" }])
153
195
  end
154
196
 
155
197
  assert_equal "EXPECTED", book.reload.format
@@ -209,6 +251,18 @@ class InsertAllTest < ActiveRecord::TestCase
209
251
  end
210
252
  end
211
253
 
254
+ def test_insert_all_and_upsert_all_finds_index_with_inverted_unique_by_columns
255
+ skip unless supports_insert_conflict_target?
256
+
257
+ columns = [:author_id, :name]
258
+ assert ActiveRecord::Base.lease_connection.index_exists?(:books, columns)
259
+
260
+ assert_difference "Book.count", +2 do
261
+ Book.insert_all [{ name: "Remote", author_id: 1 }], unique_by: columns.reverse
262
+ Book.upsert_all [{ name: "Rework", author_id: 1 }], unique_by: columns.reverse
263
+ end
264
+ end
265
+
212
266
  def test_insert_all_and_upsert_all_works_with_composite_primary_keys_when_unique_by_is_provided
213
267
  skip unless supports_insert_conflict_target?
214
268
 
@@ -217,10 +271,27 @@ class InsertAllTest < ActiveRecord::TestCase
217
271
 
218
272
  Cart.upsert_all [{ id: 3, shop_id: 2, title: "My other cart" }], unique_by: [:shop_id, :id]
219
273
  end
274
+ end
220
275
 
221
- error = assert_raises ArgumentError do
276
+ def test_insert_all_bang_does_not_require_unique_index
277
+ skip unless supports_insert_conflict_target?
278
+
279
+ assert_difference "Cart.count", +1 do
222
280
  Cart.insert_all! [{ id: 2, shop_id: 1, title: "My cart" }]
223
281
  end
282
+ end
283
+
284
+ def test_insert_all_and_upsert_all_raises_when_no_unique_index_found_for_composite_primary_key
285
+ skip unless supports_insert_conflict_target?
286
+
287
+ error = assert_raises ArgumentError do
288
+ Cart.insert_all [{ id: 2, shop_id: 1, title: "My cart" }]
289
+ end
290
+ assert_match "No unique index found for id", error.message
291
+
292
+ error = assert_raises ArgumentError do
293
+ Cart.upsert_all [{ id: 2, shop_id: 1, title: "My cart" }]
294
+ end
224
295
  assert_match "No unique index found for id", error.message
225
296
  end
226
297
 
@@ -245,6 +316,21 @@ class InsertAllTest < ActiveRecord::TestCase
245
316
  end
246
317
  end
247
318
 
319
+ def test_insert_logs_message_including_model_name_from_anonymous_class
320
+ skip unless supports_insert_conflict_target?
321
+
322
+ anonymous_book_klass = Class.new(Book) do
323
+ def self.name
324
+ "AnonymousBook"
325
+ end
326
+ end
327
+
328
+ capture_log_output do |output|
329
+ anonymous_book_klass.insert({ name: "Rework", author_id: 1 })
330
+ assert_match "AnonymousBook Insert", output.string
331
+ end
332
+ end
333
+
248
334
  def test_insert_all_logs_message_including_model_name
249
335
  skip unless supports_insert_conflict_target?
250
336
 
@@ -254,6 +340,45 @@ class InsertAllTest < ActiveRecord::TestCase
254
340
  end
255
341
  end
256
342
 
343
+ def test_insert_all_and_upsert_all_with_aliased_attributes
344
+ skip unless supports_insert_on_duplicate_update?
345
+
346
+ if supports_insert_returning?
347
+ assert_difference "Book.count" do
348
+ result = Book.insert_all [{ title: "Remote", author_id: 1 }], returning: :title
349
+ assert_includes result.columns, "title"
350
+ end
351
+ end
352
+
353
+ Book.upsert_all [{ id: 101, title: "Perelandra", author_id: 7, isbn: "1974522598" }]
354
+ Book.upsert_all [{ id: 101, title: "Perelandra 2", author_id: 6, isbn: "111111" }], update_only: %i[ title isbn ]
355
+
356
+ book = Book.find(101)
357
+ assert_equal "Perelandra 2", book.title, "Should have updated the title"
358
+ assert_equal "111111", book.isbn, "Should have updated the isbn"
359
+ assert_equal 7, book.author_id, "Should not have updated the author_id"
360
+ end
361
+
362
+ def test_insert_all_and_upsert_all_with_sti
363
+ skip unless supports_insert_on_duplicate_update?
364
+
365
+ assert_difference -> { Category.count }, 2 do
366
+ SpecialCategory.insert_all [{ name: "First" }, { name: "Second", type: nil }]
367
+ end
368
+
369
+ first, second = Category.last(2)
370
+ assert_equal "SpecialCategory", first.type
371
+ assert_nil second.type
372
+
373
+ SpecialCategory.upsert_all [{ id: 103, name: "First" }, { id: 104, name: "Second", type: nil }]
374
+
375
+ category3 = Category.find(103)
376
+ assert_equal "SpecialCategory", category3.type
377
+
378
+ category4 = Category.find(104)
379
+ assert_nil category4.type
380
+ end
381
+
257
382
  def test_upsert_logs_message_including_model_name
258
383
  skip unless supports_insert_on_duplicate_update?
259
384
 
@@ -263,6 +388,24 @@ class InsertAllTest < ActiveRecord::TestCase
263
388
  end
264
389
  end
265
390
 
391
+ unless in_memory_db?
392
+ def test_upsert_and_db_warnings
393
+ skip unless supports_insert_on_duplicate_update?
394
+
395
+ begin
396
+ with_db_warnings_action(:raise) do
397
+ assert_nothing_raised do
398
+ Book.upsert({ id: 1001, name: "Remote", author_id: 1 })
399
+ end
400
+ end
401
+ ensure
402
+ # We need to explicitly remove the record, because `with_db_warnings_action`
403
+ # prevents the wrapping transaction to be rolled back.
404
+ Book.delete(1001)
405
+ end
406
+ end
407
+ end
408
+
266
409
  def test_upsert_all_logs_message_including_model_name
267
410
  skip unless supports_insert_on_duplicate_update?
268
411
 
@@ -327,6 +470,35 @@ class InsertAllTest < ActiveRecord::TestCase
327
470
  assert_equal "1974522598", book.isbn, "Should have updated the isbn"
328
471
  end
329
472
 
473
+ def test_upsert_all_passing_both_on_duplicate_and_update_only_will_raise_an_error
474
+ assert_raises ArgumentError do
475
+ Book.upsert_all [{ id: 101, name: "Perelandra", author_id: 7, isbn: "1974522598" }], on_duplicate: "NAME=values(name)", update_only: :name
476
+ end
477
+ end
478
+
479
+ def test_upsert_all_only_updates_the_column_provided_via_update_only
480
+ skip unless supports_insert_on_duplicate_update?
481
+
482
+ Book.upsert_all [{ id: 101, name: "Perelandra", author_id: 7, isbn: "1974522598" }]
483
+ Book.upsert_all [{ id: 101, name: "Perelandra 2", author_id: 7, isbn: "111111" }], update_only: :name
484
+
485
+ book = Book.find(101)
486
+ assert_equal "Perelandra 2", book.name, "Should have updated the name"
487
+ assert_equal "1974522598", book.isbn, "Should not have updated the isbn"
488
+ end
489
+
490
+ def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
491
+ skip unless supports_insert_on_duplicate_update?
492
+
493
+ Book.upsert_all [{ id: 101, name: "Perelandra", author_id: 7, isbn: "1974522598" }]
494
+ Book.upsert_all [{ id: 101, name: "Perelandra 2", author_id: 6, isbn: "111111" }], update_only: %i[ name isbn ]
495
+
496
+ book = Book.find(101)
497
+ assert_equal "Perelandra 2", book.name, "Should have updated the name"
498
+ assert_equal "111111", book.isbn, "Should have updated the isbn"
499
+ assert_equal 7, book.author_id, "Should not have updated the author_id"
500
+ end
501
+
330
502
  def test_upsert_all_does_not_perform_an_upsert_if_a_partial_index_doesnt_apply
331
503
  skip unless supports_insert_on_duplicate_update? && supports_insert_conflict_target? && supports_partial_index?
332
504
 
@@ -353,8 +525,22 @@ class InsertAllTest < ActiveRecord::TestCase
353
525
  Book.insert_all [{ id: 101, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 1), updated_at: 5.years.ago, updated_on: 5.years.ago }]
354
526
  Book.upsert_all [{ id: 101, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 8) }]
355
527
 
356
- assert_equal Time.now.year, Book.find(101).updated_at.year
357
- assert_equal Time.now.year, Book.find(101).updated_on.year
528
+ assert_equal Time.now.utc.year, Book.find(101).updated_at.year
529
+ assert_equal Time.now.utc.year, Book.find(101).updated_on.year
530
+ end
531
+
532
+ def test_upsert_all_respects_updated_at_precision_when_touched_implicitly
533
+ skip unless supports_insert_on_duplicate_update?
534
+
535
+ Book.insert_all [{ id: 101, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 1), updated_at: 5.years.ago, updated_on: 5.years.ago }]
536
+
537
+ # A single upsert can occur exactly at the seconds boundary (when usec is naturally zero), so try multiple times.
538
+ has_subsecond_precision = (1..100).any? do |i|
539
+ Book.upsert_all [{ id: 101, name: "Out of the Silent Planet (Edition #{i})" }]
540
+ Book.find(101).updated_at.usec > 0
541
+ end
542
+
543
+ assert has_subsecond_precision, "updated_at should have sub-second precision"
358
544
  end
359
545
 
360
546
  def test_upsert_all_uses_given_updated_at_over_implicit_updated_at
@@ -377,6 +563,160 @@ class InsertAllTest < ActiveRecord::TestCase
377
563
  assert_equal updated_on, Book.find(101).updated_on
378
564
  end
379
565
 
566
+ def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
567
+ skip unless supports_insert_on_duplicate_update?
568
+
569
+ with_record_timestamps(Ship, true) do
570
+ Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }]
571
+
572
+ ship = Ship.find(101)
573
+ assert_equal Time.new.utc.year, ship.created_at.year
574
+ assert_equal Time.new.utc.year, ship.created_on.year
575
+ assert_equal Time.new.utc.year, ship.updated_at.year
576
+ assert_equal Time.new.utc.year, ship.updated_on.year
577
+ end
578
+ end
579
+
580
+ def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
581
+ skip unless supports_insert_on_duplicate_update?
582
+
583
+ with_record_timestamps(Ship, true) do
584
+ Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }], record_timestamps: false
585
+
586
+ ship = Ship.find(101)
587
+ assert_nil ship.created_at
588
+ assert_nil ship.created_on
589
+ assert_nil ship.updated_at
590
+ assert_nil ship.updated_on
591
+ end
592
+ end
593
+
594
+ def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
595
+ skip unless supports_insert_on_duplicate_update?
596
+
597
+ with_record_timestamps(Ship, false) do
598
+ Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }]
599
+
600
+ ship = Ship.find(101)
601
+ assert_nil ship.created_at
602
+ assert_nil ship.created_on
603
+ assert_nil ship.updated_at
604
+ assert_nil ship.updated_on
605
+ end
606
+ end
607
+
608
+ def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
609
+ skip unless supports_insert_on_duplicate_update?
610
+
611
+ with_record_timestamps(Ship, false) do
612
+ Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }], record_timestamps: true
613
+
614
+ ship = Ship.find(101)
615
+ assert_equal Time.now.utc.year, ship.created_at.year
616
+ assert_equal Time.now.utc.year, ship.created_on.year
617
+ assert_equal Time.now.utc.year, ship.updated_at.year
618
+ assert_equal Time.now.utc.year, ship.updated_on.year
619
+ end
620
+ end
621
+
622
+ def test_upsert_all_respects_created_at_precision_when_touched_implicitly
623
+ skip unless supports_insert_on_duplicate_update?
624
+
625
+ # A single upsert can occur exactly at the seconds boundary (when usec is naturally zero), so try multiple times.
626
+ has_subsecond_precision = (1..100).any? do |i|
627
+ Book.upsert_all [{ id: 101 + i, name: "Out of the Silent Planet", published_on: Date.new(1938, 4, 1) }]
628
+ Book.find(101 + i).created_at.usec > 0
629
+ end
630
+
631
+ assert has_subsecond_precision, "created_at should have sub-second precision"
632
+ end
633
+
634
+ def test_upsert_all_implicitly_sets_timestamps_on_update_when_model_record_timestamps_is_true
635
+ skip unless supports_insert_on_duplicate_update?
636
+
637
+ with_record_timestamps(Ship, true) do
638
+ travel_to(Date.new(2016, 4, 17)) { Ship.create! id: 101, name: "RSS Boaty McBoatface" }
639
+
640
+ Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }]
641
+
642
+ ship = Ship.find(101)
643
+ assert_equal 2016, ship.created_at.year
644
+ assert_equal 2016, ship.created_on.year
645
+ assert_equal Time.now.utc.year, ship.updated_at.year
646
+ assert_equal Time.now.utc.year, ship.updated_on.year
647
+ end
648
+ end
649
+
650
+ def test_upsert_all_does_not_implicitly_set_timestamps_on_update_when_model_record_timestamps_is_true_but_overridden
651
+ skip unless supports_insert_on_duplicate_update?
652
+
653
+ with_record_timestamps(Ship, true) do
654
+ travel_to(Date.new(2016, 4, 17)) { Ship.create! id: 101, name: "RSS Boaty McBoatface" }
655
+
656
+ Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }], record_timestamps: false
657
+
658
+ ship = Ship.find(101)
659
+ assert_equal 2016, ship.created_at.year
660
+ assert_equal 2016, ship.created_on.year
661
+ assert_equal 2016, ship.updated_at.year
662
+ assert_equal 2016, ship.updated_on.year
663
+ end
664
+ end
665
+
666
+ def test_upsert_all_does_not_implicitly_set_timestamps_on_update_when_model_record_timestamps_is_false
667
+ skip unless supports_insert_on_duplicate_update?
668
+
669
+ with_record_timestamps(Ship, false) do
670
+ Ship.create! id: 101, name: "RSS Boaty McBoatface"
671
+
672
+ Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }]
673
+
674
+ ship = Ship.find(101)
675
+ assert_nil ship.created_at
676
+ assert_nil ship.created_on
677
+ assert_nil ship.updated_at
678
+ assert_nil ship.updated_on
679
+ end
680
+ end
681
+
682
+ def test_upsert_all_implicitly_sets_timestamps_on_update_when_model_record_timestamps_is_false_but_overridden
683
+ skip unless supports_insert_on_duplicate_update?
684
+
685
+ with_record_timestamps(Ship, false) do
686
+ Ship.create! id: 101, name: "RSS Boaty McBoatface"
687
+
688
+ Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }], record_timestamps: true
689
+
690
+ ship = Ship.find(101)
691
+ assert_nil ship.created_at
692
+ assert_nil ship.created_on
693
+ assert_equal Time.now.utc.year, ship.updated_at.year
694
+ assert_equal Time.now.utc.year, ship.updated_on.year
695
+ end
696
+ end
697
+
698
+ def test_upsert_all_implicitly_sets_timestamps_even_when_columns_are_aliased
699
+ skip unless supports_insert_on_duplicate_update?
700
+
701
+ Developer.upsert_all [{ id: 101, name: "Alice" }]
702
+ alice = Developer.find(101)
703
+
704
+ assert_not_nil alice.created_at
705
+ assert_not_nil alice.created_on
706
+ assert_not_nil alice.updated_at
707
+ assert_not_nil alice.updated_on
708
+
709
+ alice.update!(created_at: nil, created_on: nil, updated_at: nil, updated_on: nil)
710
+
711
+ Developer.upsert_all [{ id: alice.id, name: alice.name, salary: alice.salary * 2 }]
712
+ alice.reload
713
+
714
+ assert_nil alice.created_at
715
+ assert_nil alice.created_on
716
+ assert_not_nil alice.updated_at
717
+ assert_not_nil alice.updated_on
718
+ end
719
+
380
720
  def test_insert_all_raises_on_unknown_attribute
381
721
  assert_raise ActiveRecord::UnknownAttributeError do
382
722
  Book.insert_all! [{ unknown_attribute: "Test" }]
@@ -462,17 +802,79 @@ class InsertAllTest < ActiveRecord::TestCase
462
802
  end
463
803
 
464
804
  def test_upsert_all_has_many_through
805
+ skip unless supports_insert_on_duplicate_update?
806
+
465
807
  book = Book.first
466
808
  assert_raise(ArgumentError) { book.subscribers.upsert_all([ { nick: "Jimmy" } ]) }
467
809
  end
468
810
 
811
+ def test_upsert_all_updates_using_provided_sql
812
+ skip unless supports_insert_on_duplicate_update?
813
+
814
+ operator = current_adapter?(:SQLite3Adapter) ? "MAX" : "GREATEST"
815
+
816
+ Book.upsert_all(
817
+ [{ id: 1, status: 1 }, { id: 2, status: 1 }],
818
+ on_duplicate: Arel.sql("status = #{operator}(books.status, 1)")
819
+ )
820
+ assert_equal "published", Book.find(1).status
821
+ assert_equal "written", Book.find(2).status
822
+ end
823
+
824
+ if current_adapter?(:Mysql2Adapter, :TrilogyAdapter)
825
+ def test_upsert_all_updates_using_values_function_on_duplicate_raw_sql
826
+ skip unless supports_insert_on_duplicate_update?
827
+
828
+ b1 = Book.create!(name: "Name")
829
+ b2 = Book.create!(name: nil)
830
+
831
+ Book.upsert_all(
832
+ [{ id: b1.id, name: "No Name" }, { id: b2.id, name: "No Name" }],
833
+ on_duplicate: Arel.sql("name = IFNULL(name, values(name))")
834
+ )
835
+
836
+ b1.reload
837
+ b2.reload
838
+
839
+ assert_equal "Name", b1.name
840
+ assert_equal "No Name", b2.name
841
+ end
842
+ end
843
+
844
+ def test_upsert_all_updates_using_provided_sql_and_unique_by
845
+ skip unless supports_insert_on_duplicate_update? && supports_insert_conflict_target?
846
+
847
+ book = books(:rfr)
848
+ assert_equal "proposed", book.status
849
+
850
+ Book.upsert_all(
851
+ [{ name: book.name, author_id: book.author_id }],
852
+ unique_by: [:name, :author_id],
853
+ on_duplicate: Arel.sql("status = 2")
854
+ )
855
+ assert_equal "published", book.reload.status
856
+ end
857
+
469
858
  def test_upsert_all_with_unique_by_fails_cleanly_for_adapters_not_supporting_insert_conflict_target
470
859
  skip if supports_insert_conflict_target?
471
860
 
472
861
  error = assert_raises ArgumentError do
473
862
  Book.upsert_all [{ name: "Rework", author_id: 1 }], unique_by: :isbn
474
863
  end
475
- assert_match "#{ActiveRecord::Base.connection.class} does not support :unique_by", error.message
864
+ assert_match "#{ActiveRecord::Base.lease_connection.class} does not support :unique_by", error.message
865
+ end
866
+
867
+ if current_adapter?(:Mysql2Adapter, :TrilogyAdapter)
868
+ def test_insert_all_when_table_name_contains_database
869
+ database_name = Book.connection_db_config.database
870
+ Book.table_name = "#{database_name}.books"
871
+
872
+ assert_nothing_raised do
873
+ Book.insert_all! [{ name: "Rework", author_id: 1 }]
874
+ end
875
+ ensure
876
+ Book.table_name = "books"
877
+ end
476
878
  end
477
879
 
478
880
  private
@@ -486,4 +888,12 @@ class InsertAllTest < ActiveRecord::TestCase
486
888
  ActiveRecord::Base.logger = old_logger
487
889
  end
488
890
  end
891
+
892
+ def with_record_timestamps(model, value)
893
+ original = model.record_timestamps
894
+ model.record_timestamps = value
895
+ yield
896
+ ensure
897
+ model.record_timestamps = original
898
+ end
489
899
  end