ibm_db 3.0.5 → 4.0.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 (580) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/LICENSE +1 -1
  4. data/MANIFEST +14 -14
  5. data/ParameterizedQueries README +6 -6
  6. data/README +208 -225
  7. data/ext/Makefile.nt32 +181 -181
  8. data/ext/Makefile.nt32.191 +212 -212
  9. data/ext/extconf.rb +291 -291
  10. data/ext/ibm_db.c +11887 -11887
  11. data/ext/ruby_ibm_db.h +241 -241
  12. data/ext/ruby_ibm_db_cli.c +866 -866
  13. data/ext/ruby_ibm_db_cli.h +500 -500
  14. data/init.rb +41 -41
  15. data/lib/IBM_DB.rb +27 -27
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3452 -3177
  17. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -2
  18. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  19. data/test/active_record/connection_adapters/fake_adapter.rb +49 -46
  20. data/test/assets/example.log +1 -1
  21. data/test/assets/test.txt +1 -1
  22. data/test/cases/adapter_test.rb +351 -276
  23. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -0
  24. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -0
  25. data/test/cases/adapters/mysql2/boolean_test.rb +100 -0
  26. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -0
  27. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -0
  28. data/test/cases/adapters/mysql2/connection_test.rb +210 -0
  29. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -0
  30. data/test/cases/adapters/mysql2/enum_test.rb +26 -0
  31. data/test/cases/adapters/mysql2/explain_test.rb +21 -0
  32. data/test/cases/adapters/mysql2/json_test.rb +195 -0
  33. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -0
  34. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
  35. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -0
  36. data/test/cases/adapters/mysql2/schema_test.rb +126 -0
  37. data/test/cases/adapters/mysql2/sp_test.rb +36 -0
  38. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -0
  39. data/test/cases/adapters/mysql2/table_options_test.rb +42 -0
  40. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -0
  41. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -0
  42. data/test/cases/adapters/postgresql/array_test.rb +339 -0
  43. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -0
  44. data/test/cases/adapters/postgresql/bytea_test.rb +134 -0
  45. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -0
  46. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -0
  47. data/test/cases/adapters/postgresql/cidr_test.rb +25 -0
  48. data/test/cases/adapters/postgresql/citext_test.rb +78 -0
  49. data/test/cases/adapters/postgresql/collation_test.rb +53 -0
  50. data/test/cases/adapters/postgresql/composite_test.rb +132 -0
  51. data/test/cases/adapters/postgresql/connection_test.rb +257 -0
  52. data/test/cases/adapters/postgresql/datatype_test.rb +92 -0
  53. data/test/cases/adapters/postgresql/domain_test.rb +47 -0
  54. data/test/cases/adapters/postgresql/enum_test.rb +91 -0
  55. data/test/cases/adapters/postgresql/explain_test.rb +20 -0
  56. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -0
  57. data/test/cases/adapters/postgresql/full_text_test.rb +44 -0
  58. data/test/cases/adapters/postgresql/geometric_test.rb +378 -0
  59. data/test/cases/adapters/postgresql/hstore_test.rb +382 -0
  60. data/test/cases/adapters/postgresql/infinity_test.rb +69 -0
  61. data/test/cases/adapters/postgresql/integer_test.rb +25 -0
  62. data/test/cases/adapters/postgresql/json_test.rb +237 -0
  63. data/test/cases/adapters/postgresql/ltree_test.rb +53 -0
  64. data/test/cases/adapters/postgresql/money_test.rb +96 -0
  65. data/test/cases/adapters/postgresql/network_test.rb +94 -0
  66. data/test/cases/adapters/postgresql/numbers_test.rb +49 -0
  67. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -0
  68. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
  69. data/test/cases/adapters/postgresql/quoting_test.rb +44 -0
  70. data/test/cases/adapters/postgresql/range_test.rb +343 -0
  71. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -0
  72. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -0
  73. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -0
  74. data/test/cases/adapters/postgresql/schema_test.rb +597 -0
  75. data/test/cases/adapters/postgresql/serial_test.rb +154 -0
  76. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -0
  77. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -0
  78. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -0
  79. data/test/cases/adapters/postgresql/utils_test.rb +62 -0
  80. data/test/cases/adapters/postgresql/uuid_test.rb +294 -0
  81. data/test/cases/adapters/postgresql/xml_test.rb +54 -0
  82. data/test/cases/adapters/sqlite3/collation_test.rb +53 -0
  83. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -0
  84. data/test/cases/adapters/sqlite3/explain_test.rb +21 -0
  85. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -0
  86. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -0
  87. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
  88. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -0
  89. data/test/cases/aggregations_test.rb +168 -158
  90. data/test/cases/ar_schema_test.rb +146 -161
  91. data/test/cases/associations/association_scope_test.rb +16 -21
  92. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1029
  93. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -0
  94. data/test/cases/associations/callbacks_test.rb +190 -192
  95. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  96. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  97. data/test/cases/associations/eager_load_nested_include_test.rb +126 -128
  98. data/test/cases/associations/eager_singularization_test.rb +148 -148
  99. data/test/cases/associations/eager_test.rb +1514 -1429
  100. data/test/cases/associations/extension_test.rb +87 -82
  101. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -972
  102. data/test/cases/associations/has_many_associations_test.rb +2501 -2182
  103. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1204
  104. data/test/cases/associations/has_one_associations_test.rb +707 -610
  105. data/test/cases/associations/has_one_through_associations_test.rb +383 -380
  106. data/test/cases/associations/inner_join_association_test.rb +139 -139
  107. data/test/cases/associations/inverse_associations_test.rb +733 -706
  108. data/test/cases/associations/join_model_test.rb +777 -754
  109. data/test/cases/associations/left_outer_join_association_test.rb +88 -0
  110. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  111. data/test/cases/associations/required_test.rb +102 -82
  112. data/test/cases/associations_test.rb +385 -380
  113. data/test/cases/attribute_decorators_test.rb +125 -125
  114. data/test/cases/attribute_methods/read_test.rb +60 -60
  115. data/test/cases/attribute_methods_test.rb +1009 -952
  116. data/test/cases/attribute_set_test.rb +270 -210
  117. data/test/cases/attribute_test.rb +246 -180
  118. data/test/cases/attributes_test.rb +253 -136
  119. data/test/cases/autosave_association_test.rb +1708 -1595
  120. data/test/cases/base_test.rb +1713 -1664
  121. data/test/cases/batches_test.rb +489 -212
  122. data/test/cases/binary_test.rb +44 -52
  123. data/test/cases/bind_parameter_test.rb +110 -100
  124. data/test/cases/cache_key_test.rb +25 -0
  125. data/test/cases/calculations_test.rb +798 -646
  126. data/test/cases/callbacks_test.rb +636 -543
  127. data/test/cases/clone_test.rb +40 -40
  128. data/test/cases/coders/json_test.rb +15 -0
  129. data/test/cases/coders/yaml_column_test.rb +63 -63
  130. data/test/cases/collection_cache_key_test.rb +115 -0
  131. data/test/cases/column_alias_test.rb +17 -17
  132. data/test/cases/column_definition_test.rb +92 -123
  133. data/test/cases/comment_test.rb +143 -0
  134. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -54
  135. data/test/cases/connection_adapters/connection_handler_test.rb +160 -53
  136. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  137. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -293
  138. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -65
  139. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  140. data/test/cases/connection_adapters/schema_cache_test.rb +61 -56
  141. data/test/cases/connection_adapters/type_lookup_test.rb +118 -110
  142. data/test/cases/connection_management_test.rb +112 -122
  143. data/test/cases/connection_pool_test.rb +521 -346
  144. data/test/cases/connection_specification/resolver_test.rb +131 -116
  145. data/test/cases/core_test.rb +112 -112
  146. data/test/cases/counter_cache_test.rb +214 -209
  147. data/test/cases/custom_locking_test.rb +17 -17
  148. data/test/cases/database_statements_test.rb +34 -19
  149. data/test/cases/{invalid_date_test.rb → date_test.rb} +44 -32
  150. data/test/cases/date_time_precision_test.rb +106 -0
  151. data/test/cases/date_time_test.rb +61 -61
  152. data/test/cases/defaults_test.rb +218 -223
  153. data/test/cases/dirty_test.rb +763 -785
  154. data/test/cases/disconnected_test.rb +30 -28
  155. data/test/cases/dup_test.rb +157 -157
  156. data/test/cases/enum_test.rb +444 -290
  157. data/test/cases/errors_test.rb +16 -0
  158. data/test/cases/explain_subscriber_test.rb +64 -64
  159. data/test/cases/explain_test.rb +87 -76
  160. data/test/cases/finder_respond_to_test.rb +60 -60
  161. data/test/cases/finder_test.rb +1294 -1169
  162. data/test/cases/fixture_set/file_test.rb +156 -138
  163. data/test/cases/fixtures_test.rb +988 -908
  164. data/test/cases/forbidden_attributes_protection_test.rb +165 -99
  165. data/test/cases/habtm_destroy_order_test.rb +61 -61
  166. data/test/cases/helper.rb +204 -210
  167. data/test/cases/hot_compatibility_test.rb +142 -54
  168. data/test/cases/i18n_test.rb +45 -45
  169. data/test/cases/inheritance_test.rb +606 -375
  170. data/test/cases/integration_test.rb +155 -139
  171. data/test/cases/invalid_connection_test.rb +24 -22
  172. data/test/cases/invertible_migration_test.rb +387 -295
  173. data/test/cases/json_serialization_test.rb +311 -302
  174. data/test/cases/locking_test.rb +493 -477
  175. data/test/cases/log_subscriber_test.rb +225 -136
  176. data/test/cases/migration/change_schema_test.rb +458 -512
  177. data/test/cases/migration/change_table_test.rb +256 -224
  178. data/test/cases/migration/column_attributes_test.rb +176 -192
  179. data/test/cases/migration/column_positioning_test.rb +56 -56
  180. data/test/cases/migration/columns_test.rb +310 -304
  181. data/test/cases/migration/command_recorder_test.rb +350 -305
  182. data/test/cases/migration/compatibility_test.rb +118 -0
  183. data/test/cases/migration/create_join_table_test.rb +157 -148
  184. data/test/cases/migration/foreign_key_test.rb +360 -328
  185. data/test/cases/migration/helper.rb +39 -39
  186. data/test/cases/migration/index_test.rb +218 -216
  187. data/test/cases/migration/logger_test.rb +36 -36
  188. data/test/cases/migration/pending_migrations_test.rb +52 -53
  189. data/test/cases/migration/references_foreign_key_test.rb +216 -169
  190. data/test/cases/migration/references_index_test.rb +101 -101
  191. data/test/cases/migration/references_statements_test.rb +136 -116
  192. data/test/cases/migration/rename_table_test.rb +93 -93
  193. data/test/cases/migration_test.rb +1157 -959
  194. data/test/cases/migrator_test.rb +470 -388
  195. data/test/cases/mixin_test.rb +68 -70
  196. data/test/cases/modules_test.rb +172 -173
  197. data/test/cases/multiparameter_attributes_test.rb +372 -350
  198. data/test/cases/multiple_db_test.rb +122 -115
  199. data/test/cases/nested_attributes_test.rb +1098 -1070
  200. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  201. data/test/cases/persistence_test.rb +1001 -909
  202. data/test/cases/pooled_connections_test.rb +81 -81
  203. data/test/cases/primary_keys_test.rb +376 -237
  204. data/test/cases/query_cache_test.rb +446 -326
  205. data/test/cases/quoting_test.rb +202 -156
  206. data/test/cases/readonly_test.rb +119 -118
  207. data/test/cases/reaper_test.rb +85 -85
  208. data/test/cases/reflection_test.rb +509 -463
  209. data/test/cases/relation/delegation_test.rb +63 -68
  210. data/test/cases/relation/merging_test.rb +157 -161
  211. data/test/cases/relation/mutation_test.rb +183 -165
  212. data/test/cases/relation/or_test.rb +92 -0
  213. data/test/cases/relation/predicate_builder_test.rb +16 -14
  214. data/test/cases/relation/record_fetch_warning_test.rb +40 -0
  215. data/test/cases/relation/where_chain_test.rb +105 -181
  216. data/test/cases/relation/where_clause_test.rb +182 -0
  217. data/test/cases/relation/where_test.rb +322 -300
  218. data/test/cases/relation_test.rb +328 -319
  219. data/test/cases/relations_test.rb +2026 -1815
  220. data/test/cases/reload_models_test.rb +22 -22
  221. data/test/cases/result_test.rb +90 -80
  222. data/test/cases/sanitize_test.rb +176 -83
  223. data/test/cases/schema_dumper_test.rb +457 -463
  224. data/test/cases/schema_loading_test.rb +52 -0
  225. data/test/cases/scoping/default_scoping_test.rb +528 -454
  226. data/test/cases/scoping/named_scoping_test.rb +561 -524
  227. data/test/cases/scoping/relation_scoping_test.rb +400 -357
  228. data/test/cases/secure_token_test.rb +32 -0
  229. data/test/cases/serialization_test.rb +104 -104
  230. data/test/cases/serialized_attribute_test.rb +364 -277
  231. data/test/cases/statement_cache_test.rb +136 -98
  232. data/test/cases/store_test.rb +195 -194
  233. data/test/cases/suppressor_test.rb +63 -0
  234. data/test/cases/tasks/database_tasks_test.rb +462 -398
  235. data/test/cases/tasks/mysql_rake_test.rb +345 -324
  236. data/test/cases/tasks/postgresql_rake_test.rb +304 -250
  237. data/test/cases/tasks/sqlite_rake_test.rb +220 -193
  238. data/test/cases/test_case.rb +131 -123
  239. data/test/cases/test_fixtures_test.rb +36 -0
  240. data/test/cases/time_precision_test.rb +102 -0
  241. data/test/cases/timestamp_test.rb +501 -467
  242. data/test/cases/touch_later_test.rb +121 -0
  243. data/test/cases/transaction_callbacks_test.rb +518 -452
  244. data/test/cases/transaction_isolation_test.rb +106 -106
  245. data/test/cases/transactions_test.rb +834 -817
  246. data/test/cases/type/adapter_specific_registry_test.rb +133 -0
  247. data/test/cases/type/date_time_test.rb +14 -0
  248. data/test/cases/type/integer_test.rb +27 -121
  249. data/test/cases/type/string_test.rb +22 -36
  250. data/test/cases/type/type_map_test.rb +177 -177
  251. data/test/cases/type_test.rb +39 -0
  252. data/test/cases/types_test.rb +24 -141
  253. data/test/cases/unconnected_test.rb +33 -33
  254. data/test/cases/validations/absence_validation_test.rb +73 -0
  255. data/test/cases/validations/association_validation_test.rb +97 -86
  256. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  257. data/test/cases/validations/i18n_validation_test.rb +86 -90
  258. data/test/cases/validations/length_validation_test.rb +79 -47
  259. data/test/cases/validations/presence_validation_test.rb +103 -68
  260. data/test/cases/validations/uniqueness_validation_test.rb +548 -457
  261. data/test/cases/validations_repair_helper.rb +19 -23
  262. data/test/cases/validations_test.rb +194 -165
  263. data/test/cases/view_test.rb +216 -119
  264. data/test/cases/yaml_serialization_test.rb +121 -126
  265. data/test/config.example.yml +97 -0
  266. data/test/config.rb +5 -5
  267. data/test/fixtures/accounts.yml +29 -29
  268. data/test/fixtures/admin/accounts.yml +2 -2
  269. data/test/fixtures/admin/users.yml +10 -10
  270. data/test/fixtures/author_addresses.original +11 -0
  271. data/test/fixtures/author_addresses.yml +17 -17
  272. data/test/fixtures/author_favorites.yml +3 -3
  273. data/test/fixtures/authors.original +17 -0
  274. data/test/fixtures/authors.yml +23 -23
  275. data/test/fixtures/bad_posts.yml +9 -0
  276. data/test/fixtures/binaries.yml +133 -133
  277. data/test/fixtures/books.yml +31 -11
  278. data/test/fixtures/bulbs.yml +5 -5
  279. data/test/fixtures/cars.yml +9 -9
  280. data/test/fixtures/categories.yml +19 -19
  281. data/test/fixtures/categories/special_categories.yml +9 -9
  282. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  283. data/test/fixtures/categories_ordered.yml +7 -7
  284. data/test/fixtures/categories_posts.yml +31 -31
  285. data/test/fixtures/categorizations.yml +23 -23
  286. data/test/fixtures/clubs.yml +8 -8
  287. data/test/fixtures/collections.yml +3 -3
  288. data/test/fixtures/colleges.yml +3 -3
  289. data/test/fixtures/comments.yml +65 -65
  290. data/test/fixtures/companies.yml +67 -67
  291. data/test/fixtures/computers.yml +10 -10
  292. data/test/fixtures/content.yml +3 -0
  293. data/test/fixtures/content_positions.yml +3 -0
  294. data/test/fixtures/courses.yml +8 -8
  295. data/test/fixtures/customers.yml +25 -25
  296. data/test/fixtures/dashboards.yml +6 -6
  297. data/test/fixtures/dead_parrots.yml +5 -0
  298. data/test/fixtures/developers.yml +22 -22
  299. data/test/fixtures/developers_projects.yml +16 -16
  300. data/test/fixtures/dog_lovers.yml +7 -7
  301. data/test/fixtures/dogs.yml +4 -4
  302. data/test/fixtures/doubloons.yml +3 -3
  303. data/test/fixtures/edges.yml +5 -5
  304. data/test/fixtures/entrants.yml +14 -14
  305. data/test/fixtures/essays.yml +6 -6
  306. data/test/fixtures/faces.yml +11 -11
  307. data/test/fixtures/fk_test_has_fk.yml +3 -3
  308. data/test/fixtures/fk_test_has_pk.yml +1 -1
  309. data/test/fixtures/friendships.yml +4 -4
  310. data/test/fixtures/funny_jokes.yml +10 -10
  311. data/test/fixtures/interests.yml +33 -33
  312. data/test/fixtures/items.yml +3 -3
  313. data/test/fixtures/jobs.yml +7 -7
  314. data/test/fixtures/legacy_things.yml +3 -3
  315. data/test/fixtures/live_parrots.yml +4 -0
  316. data/test/fixtures/mateys.yml +4 -4
  317. data/test/fixtures/member_details.yml +8 -8
  318. data/test/fixtures/member_types.yml +6 -6
  319. data/test/fixtures/members.yml +11 -11
  320. data/test/fixtures/memberships.yml +34 -34
  321. data/test/fixtures/men.yml +5 -5
  322. data/test/fixtures/minimalistics.yml +2 -2
  323. data/test/fixtures/minivans.yml +5 -5
  324. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  325. data/test/fixtures/mixins.yml +29 -29
  326. data/test/fixtures/movies.yml +7 -7
  327. data/test/fixtures/naked/yml/accounts.yml +1 -1
  328. data/test/fixtures/naked/yml/companies.yml +1 -1
  329. data/test/fixtures/naked/yml/courses.yml +1 -1
  330. data/test/fixtures/naked/yml/parrots.yml +2 -0
  331. data/test/fixtures/naked/yml/trees.yml +3 -0
  332. data/test/fixtures/nodes.yml +29 -0
  333. data/test/fixtures/organizations.yml +5 -5
  334. data/test/fixtures/other_comments.yml +6 -0
  335. data/test/fixtures/other_dogs.yml +2 -0
  336. data/test/fixtures/other_posts.yml +7 -0
  337. data/test/fixtures/other_topics.yml +42 -42
  338. data/test/fixtures/owners.yml +9 -9
  339. data/test/fixtures/parrots.yml +27 -27
  340. data/test/fixtures/parrots_pirates.yml +7 -7
  341. data/test/fixtures/people.yml +24 -24
  342. data/test/fixtures/peoples_treasures.yml +3 -3
  343. data/test/fixtures/pets.yml +19 -19
  344. data/test/fixtures/pirates.yml +15 -12
  345. data/test/fixtures/posts.yml +80 -80
  346. data/test/fixtures/price_estimates.yml +16 -7
  347. data/test/fixtures/products.yml +4 -4
  348. data/test/fixtures/projects.yml +7 -7
  349. data/test/fixtures/ratings.yml +14 -14
  350. data/test/fixtures/readers.yml +11 -11
  351. data/test/fixtures/references.yml +17 -17
  352. data/test/fixtures/reserved_words/distinct.yml +5 -5
  353. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  354. data/test/fixtures/reserved_words/group.yml +14 -14
  355. data/test/fixtures/reserved_words/select.yml +8 -8
  356. data/test/fixtures/reserved_words/values.yml +7 -7
  357. data/test/fixtures/ships.yml +6 -6
  358. data/test/fixtures/speedometers.yml +8 -8
  359. data/test/fixtures/sponsors.yml +12 -12
  360. data/test/fixtures/string_key_objects.yml +7 -7
  361. data/test/fixtures/subscribers.yml +10 -10
  362. data/test/fixtures/subscriptions.yml +12 -12
  363. data/test/fixtures/taggings.yml +78 -78
  364. data/test/fixtures/tags.yml +11 -11
  365. data/test/fixtures/tasks.yml +7 -7
  366. data/test/fixtures/teapots.yml +3 -3
  367. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  368. data/test/fixtures/to_be_linked/users.yml +10 -10
  369. data/test/fixtures/topics.yml +49 -49
  370. data/test/fixtures/toys.yml +14 -14
  371. data/test/fixtures/traffic_lights.yml +9 -9
  372. data/test/fixtures/treasures.yml +10 -10
  373. data/test/fixtures/trees.yml +3 -0
  374. data/test/fixtures/uuid_children.yml +3 -3
  375. data/test/fixtures/uuid_parents.yml +2 -2
  376. data/test/fixtures/variants.yml +4 -4
  377. data/test/fixtures/vegetables.yml +19 -19
  378. data/test/fixtures/vertices.yml +3 -3
  379. data/test/fixtures/warehouse_things.yml +2 -2
  380. data/test/fixtures/zines.yml +5 -5
  381. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  382. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  383. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  384. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  385. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  386. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  387. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  388. data/test/migrations/rename/1_we_need_things.rb +11 -11
  389. data/test/migrations/rename/2_rename_things.rb +9 -9
  390. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  391. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  392. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  393. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  394. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  395. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  396. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  397. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  398. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  399. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  400. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  401. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  402. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  403. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  404. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  405. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  406. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  407. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  408. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  409. data/test/models/admin.rb +5 -5
  410. data/test/models/admin/account.rb +3 -3
  411. data/test/models/admin/randomly_named_c1.rb +6 -2
  412. data/test/models/admin/user.rb +40 -40
  413. data/test/models/aircraft.rb +5 -4
  414. data/test/models/arunit2_model.rb +3 -3
  415. data/test/models/author.rb +209 -212
  416. data/test/models/auto_id.rb +4 -4
  417. data/test/models/autoloadable/extra_firm.rb +2 -2
  418. data/test/models/binary.rb +2 -2
  419. data/test/models/bird.rb +12 -12
  420. data/test/models/book.rb +23 -18
  421. data/test/models/boolean.rb +2 -2
  422. data/test/models/bulb.rb +52 -51
  423. data/test/models/cake_designer.rb +3 -3
  424. data/test/models/car.rb +29 -26
  425. data/test/models/carrier.rb +2 -2
  426. data/test/models/cat.rb +10 -0
  427. data/test/models/categorization.rb +19 -19
  428. data/test/models/category.rb +35 -35
  429. data/test/models/chef.rb +8 -7
  430. data/test/models/citation.rb +3 -3
  431. data/test/models/club.rb +25 -23
  432. data/test/models/college.rb +10 -10
  433. data/test/models/column.rb +3 -3
  434. data/test/models/column_name.rb +3 -3
  435. data/test/models/comment.rb +76 -64
  436. data/test/models/company.rb +230 -228
  437. data/test/models/company_in_module.rb +98 -98
  438. data/test/models/computer.rb +3 -3
  439. data/test/models/contact.rb +41 -41
  440. data/test/models/content.rb +40 -0
  441. data/test/models/contract.rb +20 -20
  442. data/test/models/country.rb +7 -7
  443. data/test/models/course.rb +6 -6
  444. data/test/models/customer.rb +83 -77
  445. data/test/models/customer_carrier.rb +14 -14
  446. data/test/models/dashboard.rb +3 -3
  447. data/test/models/default.rb +2 -2
  448. data/test/models/department.rb +4 -4
  449. data/test/models/developer.rb +274 -255
  450. data/test/models/dog.rb +5 -5
  451. data/test/models/dog_lover.rb +5 -5
  452. data/test/models/doubloon.rb +12 -12
  453. data/test/models/drink_designer.rb +3 -3
  454. data/test/models/edge.rb +5 -5
  455. data/test/models/electron.rb +5 -5
  456. data/test/models/engine.rb +4 -4
  457. data/test/models/entrant.rb +3 -3
  458. data/test/models/essay.rb +5 -5
  459. data/test/models/event.rb +3 -3
  460. data/test/models/eye.rb +37 -37
  461. data/test/models/face.rb +9 -9
  462. data/test/models/friendship.rb +6 -6
  463. data/test/models/guid.rb +2 -2
  464. data/test/models/guitar.rb +4 -0
  465. data/test/models/hotel.rb +11 -9
  466. data/test/models/image.rb +3 -3
  467. data/test/models/interest.rb +5 -5
  468. data/test/models/invoice.rb +4 -4
  469. data/test/models/item.rb +7 -7
  470. data/test/models/job.rb +7 -7
  471. data/test/models/joke.rb +7 -7
  472. data/test/models/keyboard.rb +3 -3
  473. data/test/models/legacy_thing.rb +3 -3
  474. data/test/models/lesson.rb +11 -11
  475. data/test/models/line_item.rb +3 -3
  476. data/test/models/liquid.rb +4 -4
  477. data/test/models/man.rb +11 -11
  478. data/test/models/matey.rb +4 -4
  479. data/test/models/member.rb +42 -41
  480. data/test/models/member_detail.rb +8 -7
  481. data/test/models/member_type.rb +3 -3
  482. data/test/models/membership.rb +35 -35
  483. data/test/models/mentor.rb +3 -0
  484. data/test/models/minimalistic.rb +2 -2
  485. data/test/models/minivan.rb +9 -9
  486. data/test/models/mixed_case_monkey.rb +3 -3
  487. data/test/models/mocktail_designer.rb +2 -0
  488. data/test/models/molecule.rb +6 -6
  489. data/test/models/movie.rb +5 -5
  490. data/test/models/node.rb +5 -0
  491. data/test/models/non_primary_key.rb +2 -0
  492. data/test/models/notification.rb +3 -0
  493. data/test/models/order.rb +4 -4
  494. data/test/models/organization.rb +14 -14
  495. data/test/models/other_dog.rb +5 -0
  496. data/test/models/owner.rb +37 -34
  497. data/test/models/parrot.rb +28 -29
  498. data/test/models/person.rb +142 -143
  499. data/test/models/personal_legacy_thing.rb +4 -4
  500. data/test/models/pet.rb +18 -15
  501. data/test/models/pet_treasure.rb +6 -0
  502. data/test/models/pirate.rb +92 -92
  503. data/test/models/possession.rb +3 -3
  504. data/test/models/post.rb +273 -264
  505. data/test/models/price_estimate.rb +4 -4
  506. data/test/models/professor.rb +5 -5
  507. data/test/models/project.rb +40 -31
  508. data/test/models/publisher.rb +2 -2
  509. data/test/models/publisher/article.rb +4 -4
  510. data/test/models/publisher/magazine.rb +3 -3
  511. data/test/models/randomly_named_c1.rb +1 -1
  512. data/test/models/rating.rb +4 -4
  513. data/test/models/reader.rb +23 -23
  514. data/test/models/recipe.rb +3 -0
  515. data/test/models/record.rb +2 -2
  516. data/test/models/reference.rb +22 -22
  517. data/test/models/reply.rb +61 -61
  518. data/test/models/ship.rb +39 -33
  519. data/test/models/ship_part.rb +8 -8
  520. data/test/models/shop.rb +17 -17
  521. data/test/models/shop_account.rb +6 -6
  522. data/test/models/speedometer.rb +6 -6
  523. data/test/models/sponsor.rb +7 -7
  524. data/test/models/string_key_object.rb +3 -3
  525. data/test/models/student.rb +4 -4
  526. data/test/models/subject.rb +16 -16
  527. data/test/models/subscriber.rb +8 -8
  528. data/test/models/subscription.rb +4 -4
  529. data/test/models/tag.rb +13 -7
  530. data/test/models/tagging.rb +13 -13
  531. data/test/models/task.rb +5 -5
  532. data/test/models/topic.rb +118 -124
  533. data/test/models/toy.rb +6 -6
  534. data/test/models/traffic_light.rb +4 -4
  535. data/test/models/treasure.rb +14 -14
  536. data/test/models/treaty.rb +7 -7
  537. data/test/models/tree.rb +3 -0
  538. data/test/models/tuning_peg.rb +4 -0
  539. data/test/models/tyre.rb +11 -11
  540. data/test/models/user.rb +14 -0
  541. data/test/models/uuid_child.rb +3 -3
  542. data/test/models/uuid_item.rb +6 -0
  543. data/test/models/uuid_parent.rb +3 -3
  544. data/test/models/vegetables.rb +24 -24
  545. data/test/models/vehicle.rb +6 -6
  546. data/test/models/vertex.rb +9 -9
  547. data/test/models/warehouse_thing.rb +5 -5
  548. data/test/models/wheel.rb +3 -3
  549. data/test/models/without_table.rb +3 -3
  550. data/test/models/zine.rb +3 -3
  551. data/test/schema/mysql2_specific_schema.rb +68 -58
  552. data/test/schema/oracle_specific_schema.rb +40 -43
  553. data/test/schema/postgresql_specific_schema.rb +114 -202
  554. data/test/schema/schema.rb +1057 -952
  555. data/test/schema/schema.rb.original +1057 -0
  556. data/test/schema/sqlite_specific_schema.rb +18 -22
  557. data/test/support/config.rb +43 -43
  558. data/test/support/connection.rb +23 -22
  559. data/test/support/connection_helper.rb +14 -14
  560. data/test/support/ddl_helper.rb +8 -8
  561. data/test/support/schema_dumping_helper.rb +20 -20
  562. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
  563. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
  564. metadata +129 -24
  565. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +0 -26
  566. data/test/cases/attribute_methods/serialization_test.rb +0 -29
  567. data/test/cases/migration/change_schema_test - Copy.rb +0 -448
  568. data/test/cases/migration/foreign_key_test - Changed.rb +0 -325
  569. data/test/cases/migration/table_and_index_test.rb +0 -24
  570. data/test/cases/relation/where_test2.rb +0 -36
  571. data/test/cases/type/decimal_test.rb +0 -56
  572. data/test/cases/type/unsigned_integer_test.rb +0 -18
  573. data/test/cases/xml_serialization_test.rb +0 -457
  574. data/test/connections/native_ibm_db/connection.rb +0 -44
  575. data/test/fixtures/naked/csv/accounts.csv +0 -1
  576. data/test/schema/i5/ibm_db_specific_schema.rb +0 -137
  577. data/test/schema/ids/ibm_db_specific_schema.rb +0 -140
  578. data/test/schema/luw/ibm_db_specific_schema.rb +0 -137
  579. data/test/schema/mysql_specific_schema.rb +0 -70
  580. data/test/schema/zOS/ibm_db_specific_schema.rb +0 -208
@@ -1,106 +1,106 @@
1
- require 'cases/helper'
2
-
3
- unless ActiveRecord::Base.connection.supports_transaction_isolation?
4
- class TransactionIsolationUnsupportedTest < ActiveRecord::TestCase
5
- self.use_transactional_fixtures = false
6
-
7
- class Tag < ActiveRecord::Base
8
- end
9
-
10
- test "setting the isolation level raises an error" do
11
- assert_raises(ActiveRecord::TransactionIsolationError) do
12
- Tag.transaction(isolation: :serializable) { }
13
- end
14
- end
15
- end
16
- end
17
-
18
- if ActiveRecord::Base.connection.supports_transaction_isolation?
19
- class TransactionIsolationTest < ActiveRecord::TestCase
20
- self.use_transactional_fixtures = false
21
-
22
- class Tag < ActiveRecord::Base
23
- self.table_name = 'tags'
24
- end
25
-
26
- class Tag2 < ActiveRecord::Base
27
- self.table_name = 'tags'
28
- end
29
-
30
- setup do
31
- Tag.establish_connection :arunit
32
- Tag2.establish_connection :arunit
33
- Tag.destroy_all
34
- end
35
-
36
- # It is impossible to properly test read uncommitted. The SQL standard only
37
- # specifies what must not happen at a certain level, not what must happen. At
38
- # the read uncommitted level, there is nothing that must not happen.
39
- if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:read_uncommitted)
40
- test "read uncommitted" do
41
- Tag.transaction(isolation: :read_uncommitted) do
42
- assert_equal 0, Tag.count
43
- Tag2.create
44
- assert_equal 1, Tag.count
45
- end
46
- end
47
- end
48
-
49
- # We are testing that a dirty read does not happen
50
- test "read committed" do
51
- Tag.transaction(isolation: :read_committed) do
52
- assert_equal 0, Tag.count
53
-
54
- Tag2.transaction do
55
- Tag2.create
56
- assert_equal 0, Tag.count
57
- end
58
- end
59
-
60
- assert_equal 1, Tag.count
61
- end
62
-
63
- # We are testing that a nonrepeatable read does not happen
64
- if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
65
- test "repeatable read" do
66
- tag = Tag.create(name: 'jon')
67
-
68
- Tag.transaction(isolation: :repeatable_read) do
69
- tag.reload
70
- Tag2.find(tag.id).update(name: 'emily')
71
-
72
- tag.reload
73
- assert_equal 'jon', tag.name
74
- end
75
-
76
- tag.reload
77
- assert_equal 'emily', tag.name
78
- end
79
- end
80
-
81
- # We are only testing that there are no errors because it's too hard to
82
- # test serializable. Databases behave differently to enforce the serializability
83
- # constraint.
84
- test "serializable" do
85
- Tag.transaction(isolation: :serializable) do
86
- Tag.create
87
- end
88
- end
89
-
90
- test "setting isolation when joining a transaction raises an error" do
91
- Tag.transaction do
92
- assert_raises(ActiveRecord::TransactionIsolationError) do
93
- Tag.transaction(isolation: :serializable) { }
94
- end
95
- end
96
- end
97
-
98
- test "setting isolation when starting a nested transaction raises error" do
99
- Tag.transaction do
100
- assert_raises(ActiveRecord::TransactionIsolationError) do
101
- Tag.transaction(requires_new: true, isolation: :serializable) { }
102
- end
103
- end
104
- end
105
- end
106
- end
1
+ require 'cases/helper'
2
+
3
+ unless ActiveRecord::Base.connection.supports_transaction_isolation?
4
+ class TransactionIsolationUnsupportedTest < ActiveRecord::TestCase
5
+ self.use_transactional_tests = false
6
+
7
+ class Tag < ActiveRecord::Base
8
+ end
9
+
10
+ test "setting the isolation level raises an error" do
11
+ assert_raises(ActiveRecord::TransactionIsolationError) do
12
+ Tag.transaction(isolation: :serializable) { }
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ if ActiveRecord::Base.connection.supports_transaction_isolation?
19
+ class TransactionIsolationTest < ActiveRecord::TestCase
20
+ self.use_transactional_tests = false
21
+
22
+ class Tag < ActiveRecord::Base
23
+ self.table_name = 'tags'
24
+ end
25
+
26
+ class Tag2 < ActiveRecord::Base
27
+ self.table_name = 'tags'
28
+ end
29
+
30
+ setup do
31
+ Tag.establish_connection :arunit
32
+ Tag2.establish_connection :arunit
33
+ Tag.destroy_all
34
+ end
35
+
36
+ # It is impossible to properly test read uncommitted. The SQL standard only
37
+ # specifies what must not happen at a certain level, not what must happen. At
38
+ # the read uncommitted level, there is nothing that must not happen.
39
+ if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:read_uncommitted)
40
+ test "read uncommitted" do
41
+ Tag.transaction(isolation: :read_uncommitted) do
42
+ assert_equal 0, Tag.count
43
+ Tag2.create
44
+ assert_equal 1, Tag.count
45
+ end
46
+ end
47
+ end
48
+
49
+ # We are testing that a dirty read does not happen
50
+ test "read committed" do
51
+ Tag.transaction(isolation: :read_committed) do
52
+ assert_equal 0, Tag.count
53
+
54
+ Tag2.transaction do
55
+ Tag2.create
56
+ assert_equal 0, Tag.count
57
+ end
58
+ end
59
+
60
+ assert_equal 1, Tag.count
61
+ end
62
+
63
+ # We are testing that a nonrepeatable read does not happen
64
+ if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
65
+ test "repeatable read" do
66
+ tag = Tag.create(name: 'jon')
67
+
68
+ Tag.transaction(isolation: :repeatable_read) do
69
+ tag.reload
70
+ Tag2.find(tag.id).update(name: 'emily')
71
+
72
+ tag.reload
73
+ assert_equal 'jon', tag.name
74
+ end
75
+
76
+ tag.reload
77
+ assert_equal 'emily', tag.name
78
+ end
79
+ end
80
+
81
+ # We are only testing that there are no errors because it's too hard to
82
+ # test serializable. Databases behave differently to enforce the serializability
83
+ # constraint.
84
+ test "serializable" do
85
+ Tag.transaction(isolation: :serializable) do
86
+ Tag.create
87
+ end
88
+ end
89
+
90
+ test "setting isolation when joining a transaction raises an error" do
91
+ Tag.transaction do
92
+ assert_raises(ActiveRecord::TransactionIsolationError) do
93
+ Tag.transaction(isolation: :serializable) { }
94
+ end
95
+ end
96
+ end
97
+
98
+ test "setting isolation when starting a nested transaction raises error" do
99
+ Tag.transaction do
100
+ assert_raises(ActiveRecord::TransactionIsolationError) do
101
+ Tag.transaction(requires_new: true, isolation: :serializable) { }
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,817 +1,834 @@
1
- require "cases/helper"
2
- require 'models/topic'
3
- require 'models/reply'
4
- require 'models/developer'
5
- require 'models/computer'
6
- require 'models/book'
7
- require 'models/author'
8
- require 'models/post'
9
- require 'models/movie'
10
-
11
- class TransactionTest < ActiveRecord::TestCase
12
- self.use_transactional_fixtures = false
13
- fixtures :topics, :developers, :authors, :posts, :author_addresses
14
-
15
- def setup
16
- @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
17
- end
18
-
19
- def test_persisted_in_a_model_with_custom_primary_key_after_failed_save
20
- movie = Movie.create
21
- assert !movie.persisted?
22
- end
23
-
24
- def test_raise_after_destroy
25
- assert_not @first.frozen?
26
-
27
- assert_raises(RuntimeError) {
28
- Topic.transaction do
29
- @first.destroy
30
- assert @first.frozen?
31
- raise
32
- end
33
- }
34
-
35
- assert @first.reload
36
- assert_not @first.frozen?
37
- end
38
-
39
- def test_successful
40
- Topic.transaction do
41
- @first.approved = true
42
- @second.approved = false
43
- @first.save
44
- @second.save
45
- end
46
-
47
- assert Topic.find(1).approved?, "First should have been approved"
48
- assert !Topic.find(2).approved?, "Second should have been unapproved"
49
- end
50
-
51
- def transaction_with_return
52
- Topic.transaction do
53
- @first.approved = true
54
- @second.approved = false
55
- @first.save
56
- @second.save
57
- return
58
- end
59
- end
60
-
61
- def test_successful_with_return
62
- committed = false
63
-
64
- Topic.connection.class_eval do
65
- alias :real_commit_db_transaction :commit_db_transaction
66
- define_method(:commit_db_transaction) do
67
- committed = true
68
- real_commit_db_transaction
69
- end
70
- end
71
-
72
- transaction_with_return
73
- assert committed
74
-
75
- assert Topic.find(1).approved?, "First should have been approved"
76
- assert !Topic.find(2).approved?, "Second should have been unapproved"
77
- ensure
78
- Topic.connection.class_eval do
79
- remove_method :commit_db_transaction
80
- alias :commit_db_transaction :real_commit_db_transaction rescue nil
81
- end
82
- end
83
-
84
- def test_number_of_transactions_in_commit
85
- num = nil
86
-
87
- Topic.connection.class_eval do
88
- alias :real_commit_db_transaction :commit_db_transaction
89
- define_method(:commit_db_transaction) do
90
- num = transaction_manager.open_transactions
91
- real_commit_db_transaction
92
- end
93
- end
94
-
95
- Topic.transaction do
96
- @first.approved = true
97
- @first.save!
98
- end
99
-
100
- assert_equal 0, num
101
- ensure
102
- Topic.connection.class_eval do
103
- remove_method :commit_db_transaction
104
- alias :commit_db_transaction :real_commit_db_transaction rescue nil
105
- end
106
- end
107
-
108
- def test_successful_with_instance_method
109
- @first.transaction do
110
- @first.approved = true
111
- @second.approved = false
112
- @first.save
113
- @second.save
114
- end
115
-
116
- assert Topic.find(1).approved?, "First should have been approved"
117
- assert !Topic.find(2).approved?, "Second should have been unapproved"
118
- end
119
-
120
- def test_failing_on_exception
121
- begin
122
- Topic.transaction do
123
- @first.approved = true
124
- @second.approved = false
125
- @first.save
126
- @second.save
127
- raise "Bad things!"
128
- end
129
- rescue
130
- # caught it
131
- end
132
-
133
- assert @first.approved?, "First should still be changed in the objects"
134
- assert !@second.approved?, "Second should still be changed in the objects"
135
-
136
- assert !Topic.find(1).approved?, "First shouldn't have been approved"
137
- assert Topic.find(2).approved?, "Second should still be approved"
138
- end
139
-
140
- def test_raising_exception_in_callback_rollbacks_in_save
141
- def @first.after_save_for_transaction
142
- raise 'Make the transaction rollback'
143
- end
144
-
145
- @first.approved = true
146
- e = assert_raises(RuntimeError) { @first.save }
147
- assert_equal "Make the transaction rollback", e.message
148
- assert !Topic.find(1).approved?
149
- end
150
-
151
- def test_rolling_back_in_a_callback_rollbacks_before_save
152
- def @first.before_save_for_transaction
153
- raise ActiveRecord::Rollback
154
- end
155
- assert !@first.approved
156
-
157
- Topic.transaction do
158
- @first.approved = true
159
- @first.save!
160
- end
161
- assert !Topic.find(@first.id).approved?, "Should not commit the approved flag"
162
- end
163
-
164
- def test_raising_exception_in_nested_transaction_restore_state_in_save
165
- topic = Topic.new
166
-
167
- def topic.after_save_for_transaction
168
- raise 'Make the transaction rollback'
169
- end
170
-
171
- assert_raises(RuntimeError) do
172
- Topic.transaction { topic.save }
173
- end
174
-
175
- assert topic.new_record?, "#{topic.inspect} should be new record"
176
- end
177
-
178
- def test_transaction_state_is_cleared_when_record_is_persisted
179
- author = Author.create! name: 'foo'
180
- author.name = nil
181
- assert_not author.save
182
- assert_not author.new_record?
183
- end
184
-
185
- def test_update_should_rollback_on_failure
186
- author = Author.find(1)
187
- posts_count = author.posts.size
188
- assert posts_count > 0
189
- status = author.update(name: nil, post_ids: [])
190
- assert !status
191
- assert_equal posts_count, author.posts(true).size
192
- end
193
-
194
- def test_update_should_rollback_on_failure!
195
- author = Author.find(1)
196
- posts_count = author.posts.size
197
- assert posts_count > 0
198
- assert_raise(ActiveRecord::RecordInvalid) do
199
- author.update!(name: nil, post_ids: [])
200
- end
201
- assert_equal posts_count, author.posts(true).size
202
- end
203
-
204
- def test_cancellation_from_before_destroy_rollbacks_in_destroy
205
- add_cancelling_before_destroy_with_db_side_effect_to_topic @first
206
- nbooks_before_destroy = Book.count
207
- status = @first.destroy
208
- assert !status
209
- @first.reload
210
- assert_equal nbooks_before_destroy, Book.count
211
- end
212
-
213
- %w(validation save).each do |filter|
214
- define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}") do
215
- send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
216
- nbooks_before_save = Book.count
217
- original_author_name = @first.author_name
218
- @first.author_name += '_this_should_not_end_up_in_the_db'
219
- status = @first.save
220
- assert !status
221
- assert_equal original_author_name, @first.reload.author_name
222
- assert_equal nbooks_before_save, Book.count
223
- end
224
-
225
- define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}!") do
226
- send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
227
- nbooks_before_save = Book.count
228
- original_author_name = @first.author_name
229
- @first.author_name += '_this_should_not_end_up_in_the_db'
230
-
231
- begin
232
- @first.save!
233
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved
234
- end
235
-
236
- assert_equal original_author_name, @first.reload.author_name
237
- assert_equal nbooks_before_save, Book.count
238
- end
239
- end
240
-
241
- def test_callback_rollback_in_create
242
- topic = Class.new(Topic) {
243
- def after_create_for_transaction
244
- raise 'Make the transaction rollback'
245
- end
246
- }
247
-
248
- new_topic = topic.new(:title => "A new topic",
249
- :author_name => "Ben",
250
- :author_email_address => "ben@example.com",
251
- :written_on => "2003-07-16t15:28:11.2233+01:00",
252
- :last_read => "2004-04-15",
253
- :bonus_time => "2005-01-30t15:28:00.00+01:00",
254
- :content => "Have a nice day",
255
- :approved => false)
256
-
257
- new_record_snapshot = !new_topic.persisted?
258
- id_present = new_topic.has_attribute?(Topic.primary_key)
259
- id_snapshot = new_topic.id
260
-
261
- # Make sure the second save gets the after_create callback called.
262
- 2.times do
263
- new_topic.approved = true
264
- e = assert_raises(RuntimeError) { new_topic.save }
265
- assert_equal "Make the transaction rollback", e.message
266
- assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value"
267
- assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
268
- assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
269
- end
270
- end
271
-
272
- def test_callback_rollback_in_create_with_record_invalid_exception
273
- topic = Class.new(Topic) {
274
- def after_create_for_transaction
275
- raise ActiveRecord::RecordInvalid.new(Author.new)
276
- end
277
- }
278
-
279
- new_topic = topic.create(:title => "A new topic")
280
- assert !new_topic.persisted?, "The topic should not be persisted"
281
- assert_nil new_topic.id, "The topic should not have an ID"
282
- end
283
-
284
- def test_nested_explicit_transactions
285
- Topic.transaction do
286
- Topic.transaction do
287
- @first.approved = true
288
- @second.approved = false
289
- @first.save
290
- @second.save
291
- end
292
- end
293
-
294
- assert Topic.find(1).approved?, "First should have been approved"
295
- assert !Topic.find(2).approved?, "Second should have been unapproved"
296
- end
297
-
298
- def test_manually_rolling_back_a_transaction
299
- Topic.transaction do
300
- @first.approved = true
301
- @second.approved = false
302
- @first.save
303
- @second.save
304
-
305
- raise ActiveRecord::Rollback
306
- end
307
-
308
- assert @first.approved?, "First should still be changed in the objects"
309
- assert !@second.approved?, "Second should still be changed in the objects"
310
-
311
- assert !Topic.find(1).approved?, "First shouldn't have been approved"
312
- assert Topic.find(2).approved?, "Second should still be approved"
313
- end
314
-
315
- def test_invalid_keys_for_transaction
316
- assert_raise ArgumentError do
317
- Topic.transaction :nested => true do
318
- end
319
- end
320
- end
321
-
322
- def test_force_savepoint_in_nested_transaction
323
- Topic.transaction do
324
- @first.approved = true
325
- @second.approved = false
326
- @first.save!
327
- @second.save!
328
-
329
- begin
330
- Topic.transaction :requires_new => true do
331
- @first.happy = false
332
- @first.save!
333
- raise
334
- end
335
- rescue
336
- end
337
- end
338
-
339
- assert @first.reload.approved?
340
- assert !@second.reload.approved?
341
- end if Topic.connection.supports_savepoints?
342
-
343
- def test_force_savepoint_on_instance
344
- @first.transaction do
345
- @first.approved = true
346
- @second.approved = false
347
- @first.save!
348
- @second.save!
349
-
350
- begin
351
- @second.transaction :requires_new => true do
352
- @first.happy = false
353
- @first.save!
354
- raise
355
- end
356
- rescue
357
- end
358
- end
359
-
360
- assert @first.reload.approved?
361
- assert !@second.reload.approved?
362
- end if Topic.connection.supports_savepoints?
363
-
364
- def test_no_savepoint_in_nested_transaction_without_force
365
- Topic.transaction do
366
- @first.approved = true
367
- @second.approved = false
368
- @first.save!
369
- @second.save!
370
-
371
- begin
372
- Topic.transaction do
373
- @first.approved = false
374
- @first.save!
375
- raise
376
- end
377
- rescue
378
- end
379
- end
380
-
381
- assert !@first.reload.approved?
382
- assert !@second.reload.approved?
383
- end if Topic.connection.supports_savepoints?
384
-
385
- def test_many_savepoints
386
- Topic.transaction do
387
- @first.content = "One"
388
- @first.save!
389
-
390
- begin
391
- Topic.transaction :requires_new => true do
392
- @first.content = "Two"
393
- @first.save!
394
-
395
- begin
396
- Topic.transaction :requires_new => true do
397
- @first.content = "Three"
398
- @first.save!
399
-
400
- begin
401
- Topic.transaction :requires_new => true do
402
- @first.content = "Four"
403
- @first.save!
404
- raise
405
- end
406
- rescue
407
- end
408
-
409
- @three = @first.reload.content
410
- raise
411
- end
412
- rescue
413
- end
414
-
415
- @two = @first.reload.content
416
- raise
417
- end
418
- rescue
419
- end
420
-
421
- @one = @first.reload.content
422
- end
423
-
424
- assert_equal "One", @one
425
- assert_equal "Two", @two
426
- assert_equal "Three", @three
427
- end if Topic.connection.supports_savepoints?
428
-
429
- def test_using_named_savepoints
430
- Topic.transaction do
431
- @first.approved = true
432
- @first.save!
433
- Topic.connection.create_savepoint("first")
434
-
435
- @first.approved = false
436
- @first.save!
437
- Topic.connection.rollback_to_savepoint("first")
438
- assert @first.reload.approved?
439
-
440
- @first.approved = false
441
- @first.save!
442
- Topic.connection.release_savepoint("first")
443
- assert_not @first.reload.approved?
444
- end
445
- end if Topic.connection.supports_savepoints?
446
-
447
- def test_releasing_named_savepoints
448
- Topic.transaction do
449
- Topic.connection.create_savepoint("another")
450
- Topic.connection.release_savepoint("another")
451
-
452
- # The savepoint is now gone and we can't remove it again.
453
- assert_raises(ActiveRecord::StatementInvalid) do
454
- Topic.connection.release_savepoint("another")
455
- end
456
- end
457
- end
458
-
459
- def test_savepoints_name
460
- Topic.transaction do
461
- assert_nil Topic.connection.current_savepoint_name
462
- assert_nil Topic.connection.current_transaction.savepoint_name
463
-
464
- Topic.transaction(requires_new: true) do
465
- assert_equal "active_record_1", Topic.connection.current_savepoint_name
466
- assert_equal "active_record_1", Topic.connection.current_transaction.savepoint_name
467
-
468
- Topic.transaction(requires_new: true) do
469
- assert_equal "active_record_2", Topic.connection.current_savepoint_name
470
- assert_equal "active_record_2", Topic.connection.current_transaction.savepoint_name
471
- end
472
-
473
- assert_equal "active_record_1", Topic.connection.current_savepoint_name
474
- assert_equal "active_record_1", Topic.connection.current_transaction.savepoint_name
475
- end
476
- end
477
- end
478
-
479
- def test_rollback_when_commit_raises
480
- Topic.connection.expects(:begin_db_transaction)
481
- Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
482
- Topic.connection.expects(:rollback_db_transaction)
483
-
484
- assert_raise RuntimeError do
485
- Topic.transaction do
486
- # do nothing
487
- end
488
- end
489
- end
490
-
491
- def test_rollback_when_saving_a_frozen_record
492
- topic = Topic.new(:title => 'test')
493
- topic.freeze
494
- e = assert_raise(RuntimeError) { topic.save }
495
- assert_match(/frozen/i, e.message) # Not good enough, but we can't do much
496
- # about it since there is no specific error
497
- # for frozen objects.
498
- assert !topic.persisted?, 'not persisted'
499
- assert_nil topic.id
500
- assert topic.frozen?, 'not frozen'
501
- end
502
-
503
- # The behavior of killed threads having a status of "aborting" was changed
504
- # in Ruby 2.0, so Thread#kill on 1.9 will prematurely commit the transaction
505
- # and there's nothing we can do about it.
506
- if !RUBY_VERSION.start_with?('1.9') && !in_memory_db?
507
- def test_rollback_when_thread_killed
508
- queue = Queue.new
509
- thread = Thread.new do
510
- Topic.transaction do
511
- @first.approved = true
512
- @second.approved = false
513
- @first.save
514
-
515
- queue.push nil
516
- sleep
517
-
518
- @second.save
519
- end
520
- end
521
-
522
- queue.pop
523
- thread.kill
524
- thread.join
525
-
526
- assert @first.approved?, "First should still be changed in the objects"
527
- assert !@second.approved?, "Second should still be changed in the objects"
528
-
529
- assert !Topic.find(1).approved?, "First shouldn't have been approved"
530
- assert Topic.find(2).approved?, "Second should still be approved"
531
- end
532
- end
533
-
534
- def test_restore_active_record_state_for_all_records_in_a_transaction
535
- topic_without_callbacks = Class.new(ActiveRecord::Base) do
536
- self.table_name = 'topics'
537
- end
538
-
539
- topic_1 = Topic.new(:title => 'test_1')
540
- topic_2 = Topic.new(:title => 'test_2')
541
- topic_3 = topic_without_callbacks.new(:title => 'test_3')
542
-
543
- Topic.transaction do
544
- assert topic_1.save
545
- assert topic_2.save
546
- assert topic_3.save
547
- @first.save
548
- @second.destroy
549
- assert topic_1.persisted?, 'persisted'
550
- assert_not_nil topic_1.id
551
- assert topic_2.persisted?, 'persisted'
552
- assert_not_nil topic_2.id
553
- assert topic_3.persisted?, 'persisted'
554
- assert_not_nil topic_3.id
555
- assert @first.persisted?, 'persisted'
556
- assert_not_nil @first.id
557
- assert @second.destroyed?, 'destroyed'
558
- raise ActiveRecord::Rollback
559
- end
560
-
561
- assert !topic_1.persisted?, 'not persisted'
562
- assert_nil topic_1.id
563
- assert !topic_2.persisted?, 'not persisted'
564
- assert_nil topic_2.id
565
- assert !topic_3.persisted?, 'not persisted'
566
- assert_nil topic_3.id
567
- assert @first.persisted?, 'persisted'
568
- assert_not_nil @first.id
569
- assert !@second.destroyed?, 'not destroyed'
570
- end
571
-
572
- def test_restore_frozen_state_after_double_destroy
573
- topic = Topic.create
574
- reply = topic.replies.create
575
-
576
- Topic.transaction do
577
- topic.destroy # calls #destroy on reply (since dependent: destroy)
578
- reply.destroy
579
-
580
- raise ActiveRecord::Rollback
581
- end
582
-
583
- assert_not reply.frozen?
584
- assert_not topic.frozen?
585
- end
586
-
587
- def test_rollback_of_frozen_records
588
- topic = Topic.create.freeze
589
- Topic.transaction do
590
- topic.destroy
591
- raise ActiveRecord::Rollback
592
- end
593
- assert topic.frozen?, 'frozen'
594
- end
595
-
596
- def test_rollback_for_freshly_persisted_records
597
- topic = Topic.create
598
- Topic.transaction do
599
- topic.destroy
600
- raise ActiveRecord::Rollback
601
- end
602
- assert topic.persisted?, 'persisted'
603
- end
604
-
605
- def test_sqlite_add_column_in_transaction
606
- return true unless current_adapter?(:SQLite3Adapter)
607
-
608
- # Test first if column creation/deletion works correctly when no
609
- # transaction is in place.
610
- #
611
- # We go back to the connection for the column queries because
612
- # Topic.columns is cached and won't report changes to the DB
613
-
614
- assert_nothing_raised do
615
- Topic.reset_column_information
616
- Topic.connection.add_column('topics', 'stuff', :string)
617
- assert Topic.column_names.include?('stuff')
618
-
619
- Topic.reset_column_information
620
- Topic.connection.remove_column('topics', 'stuff')
621
- assert !Topic.column_names.include?('stuff')
622
- end
623
-
624
- if Topic.connection.supports_ddl_transactions?
625
- assert_nothing_raised do
626
- Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
627
- end
628
- else
629
- Topic.transaction do
630
- assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
631
- raise ActiveRecord::Rollback
632
- end
633
- end
634
- ensure
635
- begin
636
- Topic.connection.remove_column('topics', 'stuff')
637
- rescue
638
- ensure
639
- Topic.reset_column_information
640
- end
641
- end
642
-
643
- def test_transactions_state_from_rollback
644
- connection = Topic.connection
645
- transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
646
-
647
- assert transaction.open?
648
- assert !transaction.state.rolledback?
649
- assert !transaction.state.committed?
650
-
651
- transaction.rollback
652
-
653
- assert transaction.state.rolledback?
654
- assert !transaction.state.committed?
655
- end
656
-
657
- def test_transactions_state_from_commit
658
- connection = Topic.connection
659
- transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
660
-
661
- assert transaction.open?
662
- assert !transaction.state.rolledback?
663
- assert !transaction.state.committed?
664
-
665
- transaction.commit
666
-
667
- assert !transaction.state.rolledback?
668
- assert transaction.state.committed?
669
- end
670
-
671
- def test_transaction_rollback_with_primarykeyless_tables
672
- connection = ActiveRecord::Base.connection
673
- connection.create_table(:transaction_without_primary_keys, force: true, id: false) do |t|
674
- t.integer :thing_id
675
- end
676
-
677
- klass = Class.new(ActiveRecord::Base) do
678
- self.table_name = 'transaction_without_primary_keys'
679
- after_commit { } # necessary to trigger the has_transactional_callbacks branch
680
- end
681
-
682
- assert_no_difference(-> { klass.count }) do
683
- ActiveRecord::Base.transaction do
684
- klass.create!
685
- raise ActiveRecord::Rollback
686
- end
687
- end
688
- ensure
689
- connection.drop_table("transaction_without_primary_keys") if connection.table_exists? "transaction_without_primary_keys"
690
- end
691
-
692
- private
693
-
694
- %w(validation save destroy).each do |filter|
695
- define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
696
- meta = class << topic; self; end
697
- meta.send("define_method", "before_#{filter}_for_transaction") do
698
- Book.create
699
- false
700
- end
701
- end
702
- end
703
- end
704
-
705
- class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
706
- self.use_transactional_fixtures = true
707
- fixtures :topics
708
-
709
- def test_automatic_savepoint_in_outer_transaction
710
- @first = Topic.find(1)
711
-
712
- begin
713
- Topic.transaction do
714
- @first.approved = true
715
- @first.save!
716
- raise
717
- end
718
- rescue
719
- assert !@first.reload.approved?
720
- end
721
- end
722
-
723
- def test_no_automatic_savepoint_for_inner_transaction
724
- @first = Topic.find(1)
725
-
726
- Topic.transaction do
727
- @first.approved = true
728
- @first.save!
729
-
730
- begin
731
- Topic.transaction do
732
- @first.approved = false
733
- @first.save!
734
- raise
735
- end
736
- rescue
737
- end
738
- end
739
-
740
- assert !@first.reload.approved?
741
- end
742
- end if Topic.connection.supports_savepoints?
743
-
744
- if current_adapter?(:PostgreSQLAdapter)
745
- class ConcurrentTransactionTest < TransactionTest
746
- # This will cause transactions to overlap and fail unless they are performed on
747
- # separate database connections.
748
- unless in_memory_db?
749
- def test_transaction_per_thread
750
- threads = 3.times.map do
751
- Thread.new do
752
- Topic.transaction do
753
- topic = Topic.find(1)
754
- topic.approved = !topic.approved?
755
- assert topic.save!
756
- topic.approved = !topic.approved?
757
- assert topic.save!
758
- end
759
- Topic.connection.close
760
- end
761
- end
762
-
763
- threads.each { |t| t.join }
764
- end
765
- end
766
-
767
- # Test for dirty reads among simultaneous transactions.
768
- def test_transaction_isolation__read_committed
769
- # Should be invariant.
770
- original_salary = Developer.find(1).salary
771
- temporary_salary = 200000
772
-
773
- assert_nothing_raised do
774
- threads = (1..3).map do
775
- Thread.new do
776
- Developer.transaction do
777
- # Expect original salary.
778
- dev = Developer.find(1)
779
- assert_equal original_salary, dev.salary
780
-
781
- dev.salary = temporary_salary
782
- dev.save!
783
-
784
- # Expect temporary salary.
785
- dev = Developer.find(1)
786
- assert_equal temporary_salary, dev.salary
787
-
788
- dev.salary = original_salary
789
- dev.save!
790
-
791
- # Expect original salary.
792
- dev = Developer.find(1)
793
- assert_equal original_salary, dev.salary
794
- end
795
- Developer.connection.close
796
- end
797
- end
798
-
799
- # Keep our eyes peeled.
800
- threads << Thread.new do
801
- 10.times do
802
- sleep 0.05
803
- Developer.transaction do
804
- # Always expect original salary.
805
- assert_equal original_salary, Developer.find(1).salary
806
- end
807
- end
808
- Developer.connection.close
809
- end
810
-
811
- threads.each { |t| t.join }
812
- end
813
-
814
- assert_equal original_salary, Developer.find(1).salary
815
- end
816
- end
817
- end
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/reply'
4
+ require 'models/developer'
5
+ require 'models/computer'
6
+ require 'models/book'
7
+ require 'models/author'
8
+ require 'models/post'
9
+ require 'models/movie'
10
+
11
+ class TransactionTest < ActiveRecord::TestCase
12
+ self.use_transactional_tests = false
13
+ fixtures :topics, :developers, :authors, :posts
14
+
15
+ def setup
16
+ @first, @second = Topic.find(1, 2).sort_by(&:id)
17
+ end
18
+
19
+ def test_persisted_in_a_model_with_custom_primary_key_after_failed_save
20
+ movie = Movie.create
21
+ assert !movie.persisted?
22
+ end
23
+
24
+ def test_raise_after_destroy
25
+ assert_not @first.frozen?
26
+
27
+ assert_raises(RuntimeError) {
28
+ Topic.transaction do
29
+ @first.destroy
30
+ assert @first.frozen?
31
+ raise
32
+ end
33
+ }
34
+
35
+ assert @first.reload
36
+ assert_not @first.frozen?
37
+ end
38
+
39
+ def test_successful
40
+ Topic.transaction do
41
+ @first.approved = true
42
+ @second.approved = false
43
+ @first.save
44
+ @second.save
45
+ end
46
+
47
+ assert Topic.find(1).approved?, "First should have been approved"
48
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
49
+ end
50
+
51
+ def transaction_with_return
52
+ Topic.transaction do
53
+ @first.approved = true
54
+ @second.approved = false
55
+ @first.save
56
+ @second.save
57
+ return
58
+ end
59
+ end
60
+
61
+ def test_add_to_null_transaction
62
+ topic = Topic.new
63
+ topic.add_to_transaction
64
+ end
65
+
66
+ def test_successful_with_return
67
+ committed = false
68
+
69
+ Topic.connection.class_eval do
70
+ alias :real_commit_db_transaction :commit_db_transaction
71
+ define_method(:commit_db_transaction) do
72
+ committed = true
73
+ real_commit_db_transaction
74
+ end
75
+ end
76
+
77
+ transaction_with_return
78
+ assert committed
79
+
80
+ assert Topic.find(1).approved?, "First should have been approved"
81
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
82
+ ensure
83
+ Topic.connection.class_eval do
84
+ remove_method :commit_db_transaction
85
+ alias :commit_db_transaction :real_commit_db_transaction rescue nil
86
+ end
87
+ end
88
+
89
+ def test_number_of_transactions_in_commit
90
+ num = nil
91
+
92
+ Topic.connection.class_eval do
93
+ alias :real_commit_db_transaction :commit_db_transaction
94
+ define_method(:commit_db_transaction) do
95
+ num = transaction_manager.open_transactions
96
+ real_commit_db_transaction
97
+ end
98
+ end
99
+
100
+ Topic.transaction do
101
+ @first.approved = true
102
+ @first.save!
103
+ end
104
+
105
+ assert_equal 0, num
106
+ ensure
107
+ Topic.connection.class_eval do
108
+ remove_method :commit_db_transaction
109
+ alias :commit_db_transaction :real_commit_db_transaction rescue nil
110
+ end
111
+ end
112
+
113
+ def test_successful_with_instance_method
114
+ @first.transaction do
115
+ @first.approved = true
116
+ @second.approved = false
117
+ @first.save
118
+ @second.save
119
+ end
120
+
121
+ assert Topic.find(1).approved?, "First should have been approved"
122
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
123
+ end
124
+
125
+ def test_failing_on_exception
126
+ begin
127
+ Topic.transaction do
128
+ @first.approved = true
129
+ @second.approved = false
130
+ @first.save
131
+ @second.save
132
+ raise "Bad things!"
133
+ end
134
+ rescue
135
+ # caught it
136
+ end
137
+
138
+ assert @first.approved?, "First should still be changed in the objects"
139
+ assert !@second.approved?, "Second should still be changed in the objects"
140
+
141
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
142
+ assert Topic.find(2).approved?, "Second should still be approved"
143
+ end
144
+
145
+ def test_raising_exception_in_callback_rollbacks_in_save
146
+ def @first.after_save_for_transaction
147
+ raise 'Make the transaction rollback'
148
+ end
149
+
150
+ @first.approved = true
151
+ e = assert_raises(RuntimeError) { @first.save }
152
+ assert_equal "Make the transaction rollback", e.message
153
+ assert !Topic.find(1).approved?
154
+ end
155
+
156
+ def test_rolling_back_in_a_callback_rollbacks_before_save
157
+ def @first.before_save_for_transaction
158
+ raise ActiveRecord::Rollback
159
+ end
160
+ assert !@first.approved
161
+
162
+ Topic.transaction do
163
+ @first.approved = true
164
+ @first.save!
165
+ end
166
+ assert !Topic.find(@first.id).approved?, "Should not commit the approved flag"
167
+ end
168
+
169
+ def test_raising_exception_in_nested_transaction_restore_state_in_save
170
+ topic = Topic.new
171
+
172
+ def topic.after_save_for_transaction
173
+ raise 'Make the transaction rollback'
174
+ end
175
+
176
+ assert_raises(RuntimeError) do
177
+ Topic.transaction { topic.save }
178
+ end
179
+
180
+ assert topic.new_record?, "#{topic.inspect} should be new record"
181
+ end
182
+
183
+ def test_transaction_state_is_cleared_when_record_is_persisted
184
+ author = Author.create! name: 'foo'
185
+ author.name = nil
186
+ assert_not author.save
187
+ assert_not author.new_record?
188
+ end
189
+
190
+ def test_update_should_rollback_on_failure
191
+ author = Author.find(1)
192
+ posts_count = author.posts.size
193
+ assert posts_count > 0
194
+ status = author.update(name: nil, post_ids: [])
195
+ assert !status
196
+ assert_equal posts_count, author.posts.reload.size
197
+ end
198
+
199
+ def test_update_should_rollback_on_failure!
200
+ author = Author.find(1)
201
+ posts_count = author.posts.size
202
+ assert posts_count > 0
203
+ assert_raise(ActiveRecord::RecordInvalid) do
204
+ author.update!(name: nil, post_ids: [])
205
+ end
206
+ assert_equal posts_count, author.posts.reload.size
207
+ end
208
+
209
+ def test_cancellation_from_returning_false_in_before_filter
210
+ def @first.before_save_for_transaction
211
+ false
212
+ end
213
+
214
+ assert_deprecated do
215
+ @first.save
216
+ end
217
+ end
218
+
219
+ def test_cancellation_from_before_destroy_rollbacks_in_destroy
220
+ add_cancelling_before_destroy_with_db_side_effect_to_topic @first
221
+ nbooks_before_destroy = Book.count
222
+ status = @first.destroy
223
+ assert !status
224
+ @first.reload
225
+ assert_equal nbooks_before_destroy, Book.count
226
+ end
227
+
228
+ %w(validation save).each do |filter|
229
+ define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}") do
230
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
231
+ nbooks_before_save = Book.count
232
+ original_author_name = @first.author_name
233
+ @first.author_name += '_this_should_not_end_up_in_the_db'
234
+ status = @first.save
235
+ assert !status
236
+ assert_equal original_author_name, @first.reload.author_name
237
+ assert_equal nbooks_before_save, Book.count
238
+ end
239
+
240
+ define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}!") do
241
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
242
+ nbooks_before_save = Book.count
243
+ original_author_name = @first.author_name
244
+ @first.author_name += '_this_should_not_end_up_in_the_db'
245
+
246
+ begin
247
+ @first.save!
248
+ rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved
249
+ end
250
+
251
+ assert_equal original_author_name, @first.reload.author_name
252
+ assert_equal nbooks_before_save, Book.count
253
+ end
254
+ end
255
+
256
+ def test_callback_rollback_in_create
257
+ topic = Class.new(Topic) {
258
+ def after_create_for_transaction
259
+ raise 'Make the transaction rollback'
260
+ end
261
+ }
262
+
263
+ new_topic = topic.new(:title => "A new topic",
264
+ :author_name => "Ben",
265
+ :author_email_address => "ben@example.com",
266
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
267
+ :last_read => "2004-04-15",
268
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
269
+ :content => "Have a nice day",
270
+ :approved => false)
271
+
272
+ new_record_snapshot = !new_topic.persisted?
273
+ id_present = new_topic.has_attribute?(Topic.primary_key)
274
+ id_snapshot = new_topic.id
275
+
276
+ # Make sure the second save gets the after_create callback called.
277
+ 2.times do
278
+ new_topic.approved = true
279
+ e = assert_raises(RuntimeError) { new_topic.save }
280
+ assert_equal "Make the transaction rollback", e.message
281
+ assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value"
282
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
283
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
284
+ end
285
+ end
286
+
287
+ def test_callback_rollback_in_create_with_record_invalid_exception
288
+ topic = Class.new(Topic) {
289
+ def after_create_for_transaction
290
+ raise ActiveRecord::RecordInvalid.new(Author.new)
291
+ end
292
+ }
293
+
294
+ new_topic = topic.create(:title => "A new topic")
295
+ assert !new_topic.persisted?, "The topic should not be persisted"
296
+ assert_nil new_topic.id, "The topic should not have an ID"
297
+ end
298
+
299
+ def test_nested_explicit_transactions
300
+ Topic.transaction do
301
+ Topic.transaction do
302
+ @first.approved = true
303
+ @second.approved = false
304
+ @first.save
305
+ @second.save
306
+ end
307
+ end
308
+
309
+ assert Topic.find(1).approved?, "First should have been approved"
310
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
311
+ end
312
+
313
+ def test_manually_rolling_back_a_transaction
314
+ Topic.transaction do
315
+ @first.approved = true
316
+ @second.approved = false
317
+ @first.save
318
+ @second.save
319
+
320
+ raise ActiveRecord::Rollback
321
+ end
322
+
323
+ assert @first.approved?, "First should still be changed in the objects"
324
+ assert !@second.approved?, "Second should still be changed in the objects"
325
+
326
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
327
+ assert Topic.find(2).approved?, "Second should still be approved"
328
+ end
329
+
330
+ def test_invalid_keys_for_transaction
331
+ assert_raise ArgumentError do
332
+ Topic.transaction :nested => true do
333
+ end
334
+ end
335
+ end
336
+
337
+ def test_force_savepoint_in_nested_transaction
338
+ Topic.transaction do
339
+ @first.approved = true
340
+ @second.approved = false
341
+ @first.save!
342
+ @second.save!
343
+
344
+ begin
345
+ Topic.transaction :requires_new => true do
346
+ @first.happy = false
347
+ @first.save!
348
+ raise
349
+ end
350
+ rescue
351
+ end
352
+ end
353
+
354
+ assert @first.reload.approved?
355
+ assert !@second.reload.approved?
356
+ end if Topic.connection.supports_savepoints?
357
+
358
+ def test_force_savepoint_on_instance
359
+ @first.transaction do
360
+ @first.approved = true
361
+ @second.approved = false
362
+ @first.save!
363
+ @second.save!
364
+
365
+ begin
366
+ @second.transaction :requires_new => true do
367
+ @first.happy = false
368
+ @first.save!
369
+ raise
370
+ end
371
+ rescue
372
+ end
373
+ end
374
+
375
+ assert @first.reload.approved?
376
+ assert !@second.reload.approved?
377
+ end if Topic.connection.supports_savepoints?
378
+
379
+ def test_no_savepoint_in_nested_transaction_without_force
380
+ Topic.transaction do
381
+ @first.approved = true
382
+ @second.approved = false
383
+ @first.save!
384
+ @second.save!
385
+
386
+ begin
387
+ Topic.transaction do
388
+ @first.approved = false
389
+ @first.save!
390
+ raise
391
+ end
392
+ rescue
393
+ end
394
+ end
395
+
396
+ assert !@first.reload.approved?
397
+ assert !@second.reload.approved?
398
+ end if Topic.connection.supports_savepoints?
399
+
400
+ def test_many_savepoints
401
+ Topic.transaction do
402
+ @first.content = "One"
403
+ @first.save!
404
+
405
+ begin
406
+ Topic.transaction :requires_new => true do
407
+ @first.content = "Two"
408
+ @first.save!
409
+
410
+ begin
411
+ Topic.transaction :requires_new => true do
412
+ @first.content = "Three"
413
+ @first.save!
414
+
415
+ begin
416
+ Topic.transaction :requires_new => true do
417
+ @first.content = "Four"
418
+ @first.save!
419
+ raise
420
+ end
421
+ rescue
422
+ end
423
+
424
+ @three = @first.reload.content
425
+ raise
426
+ end
427
+ rescue
428
+ end
429
+
430
+ @two = @first.reload.content
431
+ raise
432
+ end
433
+ rescue
434
+ end
435
+
436
+ @one = @first.reload.content
437
+ end
438
+
439
+ assert_equal "One", @one
440
+ assert_equal "Two", @two
441
+ assert_equal "Three", @three
442
+ end if Topic.connection.supports_savepoints?
443
+
444
+ def test_using_named_savepoints
445
+ Topic.transaction do
446
+ @first.approved = true
447
+ @first.save!
448
+ Topic.connection.create_savepoint("first")
449
+
450
+ @first.approved = false
451
+ @first.save!
452
+ Topic.connection.rollback_to_savepoint("first")
453
+ assert @first.reload.approved?
454
+
455
+ @first.approved = false
456
+ @first.save!
457
+ Topic.connection.release_savepoint("first")
458
+ assert_not @first.reload.approved?
459
+ end
460
+ end if Topic.connection.supports_savepoints?
461
+
462
+ def test_releasing_named_savepoints
463
+ Topic.transaction do
464
+ Topic.connection.create_savepoint("another")
465
+ Topic.connection.release_savepoint("another")
466
+
467
+ # The savepoint is now gone and we can't remove it again.
468
+ assert_raises(ActiveRecord::StatementInvalid) do
469
+ Topic.connection.release_savepoint("another")
470
+ end
471
+ end
472
+ end
473
+
474
+ def test_savepoints_name
475
+ Topic.transaction do
476
+ assert_nil Topic.connection.current_savepoint_name
477
+ assert_nil Topic.connection.current_transaction.savepoint_name
478
+
479
+ Topic.transaction(requires_new: true) do
480
+ assert_equal "active_record_1", Topic.connection.current_savepoint_name
481
+ assert_equal "active_record_1", Topic.connection.current_transaction.savepoint_name
482
+
483
+ Topic.transaction(requires_new: true) do
484
+ assert_equal "active_record_2", Topic.connection.current_savepoint_name
485
+ assert_equal "active_record_2", Topic.connection.current_transaction.savepoint_name
486
+ end
487
+
488
+ assert_equal "active_record_1", Topic.connection.current_savepoint_name
489
+ assert_equal "active_record_1", Topic.connection.current_transaction.savepoint_name
490
+ end
491
+ end
492
+ end
493
+
494
+ def test_rollback_when_commit_raises
495
+ assert_called(Topic.connection, :begin_db_transaction) do
496
+ Topic.connection.stub(:commit_db_transaction, ->{ raise('OH NOES') }) do
497
+ assert_called(Topic.connection, :rollback_db_transaction) do
498
+
499
+ e = assert_raise RuntimeError do
500
+ Topic.transaction do
501
+ # do nothing
502
+ end
503
+ end
504
+ assert_equal 'OH NOES', e.message
505
+ end
506
+ end
507
+ end
508
+ end
509
+
510
+ def test_rollback_when_saving_a_frozen_record
511
+ topic = Topic.new(:title => 'test')
512
+ topic.freeze
513
+ e = assert_raise(frozen_error_class) { topic.save }
514
+ # Not good enough, but we can't do much
515
+ # about it since there is no specific error
516
+ # for frozen objects.
517
+ assert_match(/frozen/i, e.message)
518
+ assert !topic.persisted?, "not persisted"
519
+ assert_nil topic.id
520
+ assert topic.frozen?, 'not frozen'
521
+ end
522
+
523
+ def test_rollback_when_thread_killed
524
+ return if in_memory_db?
525
+
526
+ queue = Queue.new
527
+ thread = Thread.new do
528
+ Topic.transaction do
529
+ @first.approved = true
530
+ @second.approved = false
531
+ @first.save
532
+
533
+ queue.push nil
534
+ sleep
535
+
536
+ @second.save
537
+ end
538
+ end
539
+
540
+ queue.pop
541
+ thread.kill
542
+ thread.join
543
+
544
+ assert @first.approved?, "First should still be changed in the objects"
545
+ assert !@second.approved?, "Second should still be changed in the objects"
546
+
547
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
548
+ assert Topic.find(2).approved?, "Second should still be approved"
549
+ end
550
+
551
+ def test_restore_active_record_state_for_all_records_in_a_transaction
552
+ topic_without_callbacks = Class.new(ActiveRecord::Base) do
553
+ self.table_name = 'topics'
554
+ end
555
+
556
+ topic_1 = Topic.new(:title => 'test_1')
557
+ topic_2 = Topic.new(:title => 'test_2')
558
+ topic_3 = topic_without_callbacks.new(:title => 'test_3')
559
+
560
+ Topic.transaction do
561
+ assert topic_1.save
562
+ assert topic_2.save
563
+ assert topic_3.save
564
+ @first.save
565
+ @second.destroy
566
+ assert topic_1.persisted?, 'persisted'
567
+ assert_not_nil topic_1.id
568
+ assert topic_2.persisted?, 'persisted'
569
+ assert_not_nil topic_2.id
570
+ assert topic_3.persisted?, 'persisted'
571
+ assert_not_nil topic_3.id
572
+ assert @first.persisted?, 'persisted'
573
+ assert_not_nil @first.id
574
+ assert @second.destroyed?, 'destroyed'
575
+ raise ActiveRecord::Rollback
576
+ end
577
+
578
+ assert !topic_1.persisted?, 'not persisted'
579
+ assert_nil topic_1.id
580
+ assert !topic_2.persisted?, 'not persisted'
581
+ assert_nil topic_2.id
582
+ assert !topic_3.persisted?, 'not persisted'
583
+ assert_nil topic_3.id
584
+ assert @first.persisted?, 'persisted'
585
+ assert_not_nil @first.id
586
+ assert !@second.destroyed?, 'not destroyed'
587
+ end
588
+
589
+ def test_restore_frozen_state_after_double_destroy
590
+ topic = Topic.create
591
+ reply = topic.replies.create
592
+
593
+ Topic.transaction do
594
+ topic.destroy # calls #destroy on reply (since dependent: destroy)
595
+ reply.destroy
596
+
597
+ raise ActiveRecord::Rollback
598
+ end
599
+
600
+ assert_not reply.frozen?
601
+ assert_not topic.frozen?
602
+ end
603
+
604
+ def test_rollback_of_frozen_records
605
+ topic = Topic.create.freeze
606
+ Topic.transaction do
607
+ topic.destroy
608
+ raise ActiveRecord::Rollback
609
+ end
610
+ assert topic.frozen?, 'frozen'
611
+ end
612
+
613
+ def test_rollback_for_freshly_persisted_records
614
+ topic = Topic.create
615
+ Topic.transaction do
616
+ topic.destroy
617
+ raise ActiveRecord::Rollback
618
+ end
619
+ assert topic.persisted?, 'persisted'
620
+ end
621
+
622
+ def test_sqlite_add_column_in_transaction
623
+ return true unless current_adapter?(:SQLite3Adapter)
624
+
625
+ # Test first if column creation/deletion works correctly when no
626
+ # transaction is in place.
627
+ #
628
+ # We go back to the connection for the column queries because
629
+ # Topic.columns is cached and won't report changes to the DB
630
+
631
+ assert_nothing_raised do
632
+ Topic.reset_column_information
633
+ Topic.connection.add_column('topics', 'stuff', :string)
634
+ assert Topic.column_names.include?('stuff')
635
+
636
+ Topic.reset_column_information
637
+ Topic.connection.remove_column('topics', 'stuff')
638
+ assert !Topic.column_names.include?('stuff')
639
+ end
640
+
641
+ if Topic.connection.supports_ddl_transactions?
642
+ assert_nothing_raised do
643
+ Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
644
+ end
645
+ else
646
+ Topic.transaction do
647
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
648
+ raise ActiveRecord::Rollback
649
+ end
650
+ end
651
+ ensure
652
+ begin
653
+ Topic.connection.remove_column('topics', 'stuff')
654
+ rescue
655
+ ensure
656
+ Topic.reset_column_information
657
+ end
658
+ end
659
+
660
+ def test_transactions_state_from_rollback
661
+ connection = Topic.connection
662
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
663
+
664
+ assert transaction.open?
665
+ assert !transaction.state.rolledback?
666
+ assert !transaction.state.committed?
667
+
668
+ transaction.rollback
669
+
670
+ assert transaction.state.rolledback?
671
+ assert !transaction.state.committed?
672
+ end
673
+
674
+ def test_transactions_state_from_commit
675
+ connection = Topic.connection
676
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
677
+
678
+ assert transaction.open?
679
+ assert !transaction.state.rolledback?
680
+ assert !transaction.state.committed?
681
+
682
+ transaction.commit
683
+
684
+ assert !transaction.state.rolledback?
685
+ assert transaction.state.committed?
686
+ end
687
+
688
+ def test_transaction_rollback_with_primarykeyless_tables
689
+ connection = ActiveRecord::Base.connection
690
+ connection.create_table(:transaction_without_primary_keys, force: true, id: false) do |t|
691
+ t.integer :thing_id
692
+ end
693
+
694
+ klass = Class.new(ActiveRecord::Base) do
695
+ self.table_name = 'transaction_without_primary_keys'
696
+ after_commit { } # necessary to trigger the has_transactional_callbacks branch
697
+ end
698
+
699
+ assert_no_difference(-> { klass.count }) do
700
+ ActiveRecord::Base.transaction do
701
+ klass.create!
702
+ raise ActiveRecord::Rollback
703
+ end
704
+ end
705
+ ensure
706
+ connection.drop_table 'transaction_without_primary_keys', if_exists: true
707
+ end
708
+
709
+ private
710
+
711
+ %w(validation save destroy).each do |filter|
712
+ define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
713
+ meta = class << topic; self; end
714
+ meta.send("define_method", "before_#{filter}_for_transaction") do
715
+ Book.create
716
+ throw(:abort)
717
+ end
718
+ end
719
+ end
720
+ end
721
+
722
+ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
723
+ self.use_transactional_tests = true
724
+ fixtures :topics
725
+
726
+ def test_automatic_savepoint_in_outer_transaction
727
+ @first = Topic.find(1)
728
+
729
+ begin
730
+ Topic.transaction do
731
+ @first.approved = true
732
+ @first.save!
733
+ raise
734
+ end
735
+ rescue
736
+ assert !@first.reload.approved?
737
+ end
738
+ end
739
+
740
+ def test_no_automatic_savepoint_for_inner_transaction
741
+ @first = Topic.find(1)
742
+
743
+ Topic.transaction do
744
+ @first.approved = true
745
+ @first.save!
746
+
747
+ begin
748
+ Topic.transaction do
749
+ @first.approved = false
750
+ @first.save!
751
+ raise
752
+ end
753
+ rescue
754
+ end
755
+ end
756
+
757
+ assert !@first.reload.approved?
758
+ end
759
+ end if Topic.connection.supports_savepoints?
760
+
761
+ if current_adapter?(:PostgreSQLAdapter)
762
+ class ConcurrentTransactionTest < TransactionTest
763
+ # This will cause transactions to overlap and fail unless they are performed on
764
+ # separate database connections.
765
+ unless in_memory_db?
766
+ def test_transaction_per_thread
767
+ threads = 3.times.map do
768
+ Thread.new do
769
+ Topic.transaction do
770
+ topic = Topic.find(1)
771
+ topic.approved = !topic.approved?
772
+ assert topic.save!
773
+ topic.approved = !topic.approved?
774
+ assert topic.save!
775
+ end
776
+ Topic.connection.close
777
+ end
778
+ end
779
+
780
+ threads.each(&:join)
781
+ end
782
+ end
783
+
784
+ # Test for dirty reads among simultaneous transactions.
785
+ def test_transaction_isolation__read_committed
786
+ # Should be invariant.
787
+ original_salary = Developer.find(1).salary
788
+ temporary_salary = 200000
789
+
790
+ assert_nothing_raised do
791
+ threads = (1..3).map do
792
+ Thread.new do
793
+ Developer.transaction do
794
+ # Expect original salary.
795
+ dev = Developer.find(1)
796
+ assert_equal original_salary, dev.salary
797
+
798
+ dev.salary = temporary_salary
799
+ dev.save!
800
+
801
+ # Expect temporary salary.
802
+ dev = Developer.find(1)
803
+ assert_equal temporary_salary, dev.salary
804
+
805
+ dev.salary = original_salary
806
+ dev.save!
807
+
808
+ # Expect original salary.
809
+ dev = Developer.find(1)
810
+ assert_equal original_salary, dev.salary
811
+ end
812
+ Developer.connection.close
813
+ end
814
+ end
815
+
816
+ # Keep our eyes peeled.
817
+ threads << Thread.new do
818
+ 10.times do
819
+ sleep 0.05
820
+ Developer.transaction do
821
+ # Always expect original salary.
822
+ assert_equal original_salary, Developer.find(1).salary
823
+ end
824
+ end
825
+ Developer.connection.close
826
+ end
827
+
828
+ threads.each(&:join)
829
+ end
830
+
831
+ assert_equal original_salary, Developer.find(1).salary
832
+ end
833
+ end
834
+ end