ibm_db 3.0.4-x86-mingw32 → 3.0.5-x86-mingw32

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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -1
  3. data/LICENSE +1 -1
  4. data/MANIFEST +14 -14
  5. data/README +225 -225
  6. data/ext/Makefile.nt32 +181 -181
  7. data/ext/Makefile.nt32.191 +212 -212
  8. data/ext/extconf.rb +291 -291
  9. data/ext/ibm_db.c +11887 -11884
  10. data/ext/ruby_ibm_db.h +241 -241
  11. data/ext/ruby_ibm_db_cli.c +866 -866
  12. data/ext/ruby_ibm_db_cli.h +500 -500
  13. data/init.rb +41 -41
  14. data/lib/IBM_DB.rb +27 -27
  15. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3177 -3177
  16. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +1 -1
  17. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  18. data/lib/mswin32/ibm_db.rb +122 -122
  19. data/lib/mswin32/rb21x/i386/ibm_db.so +0 -0
  20. data/lib/mswin32/rb22x/i386/ibm_db.so +0 -0
  21. data/lib/mswin32/rb23x/i386/ibm_db.so +0 -0
  22. data/test/active_record/connection_adapters/fake_adapter.rb +46 -46
  23. data/test/assets/example.log +1 -1
  24. data/test/assets/test.txt +1 -1
  25. data/test/cases/adapter_test.rb +276 -261
  26. data/test/cases/aggregations_test.rb +158 -158
  27. data/test/cases/ar_schema_test.rb +161 -161
  28. data/test/cases/associations/association_scope_test.rb +21 -21
  29. data/test/cases/associations/belongs_to_associations_test.rb +1029 -1029
  30. data/test/cases/associations/callbacks_test.rb +192 -192
  31. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  32. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +26 -26
  33. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  34. data/test/cases/associations/eager_load_nested_include_test.rb +128 -128
  35. data/test/cases/associations/eager_singularization_test.rb +148 -148
  36. data/test/cases/associations/eager_test.rb +1429 -1411
  37. data/test/cases/associations/extension_test.rb +82 -82
  38. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +972 -932
  39. data/test/cases/associations/has_many_associations_test.rb +2182 -2162
  40. data/test/cases/associations/has_many_through_associations_test.rb +1204 -1204
  41. data/test/cases/associations/has_one_associations_test.rb +610 -610
  42. data/test/cases/associations/has_one_through_associations_test.rb +380 -380
  43. data/test/cases/associations/inner_join_association_test.rb +139 -139
  44. data/test/cases/associations/inverse_associations_test.rb +706 -693
  45. data/test/cases/associations/join_model_test.rb +754 -754
  46. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  47. data/test/cases/associations/required_test.rb +82 -82
  48. data/test/cases/associations_test.rb +380 -380
  49. data/test/cases/attribute_decorators_test.rb +125 -125
  50. data/test/cases/attribute_methods/read_test.rb +60 -60
  51. data/test/cases/attribute_methods/serialization_test.rb +29 -29
  52. data/test/cases/attribute_methods_test.rb +952 -952
  53. data/test/cases/attribute_set_test.rb +210 -200
  54. data/test/cases/attribute_test.rb +180 -180
  55. data/test/cases/attributes_test.rb +136 -136
  56. data/test/cases/autosave_association_test.rb +1595 -1595
  57. data/test/cases/base_test.rb +1664 -1638
  58. data/test/cases/batches_test.rb +212 -212
  59. data/test/cases/binary_test.rb +52 -52
  60. data/test/cases/bind_parameter_test.rb +100 -100
  61. data/test/cases/calculations_test.rb +646 -646
  62. data/test/cases/callbacks_test.rb +543 -543
  63. data/test/cases/clone_test.rb +40 -40
  64. data/test/cases/coders/yaml_column_test.rb +63 -63
  65. data/test/cases/column_alias_test.rb +17 -17
  66. data/test/cases/column_definition_test.rb +123 -123
  67. data/test/cases/connection_adapters/adapter_leasing_test.rb +54 -54
  68. data/test/cases/connection_adapters/connection_handler_test.rb +53 -53
  69. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  70. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +293 -293
  71. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +65 -65
  72. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  73. data/test/cases/connection_adapters/schema_cache_test.rb +56 -56
  74. data/test/cases/connection_adapters/type_lookup_test.rb +110 -110
  75. data/test/cases/connection_management_test.rb +122 -122
  76. data/test/cases/connection_pool_test.rb +346 -346
  77. data/test/cases/connection_specification/resolver_test.rb +116 -116
  78. data/test/cases/core_test.rb +112 -112
  79. data/test/cases/counter_cache_test.rb +209 -209
  80. data/test/cases/custom_locking_test.rb +17 -17
  81. data/test/cases/database_statements_test.rb +19 -19
  82. data/test/cases/date_time_test.rb +61 -61
  83. data/test/cases/defaults_test.rb +223 -223
  84. data/test/cases/dirty_test.rb +785 -775
  85. data/test/cases/disconnected_test.rb +28 -28
  86. data/test/cases/dup_test.rb +157 -157
  87. data/test/cases/enum_test.rb +290 -290
  88. data/test/cases/explain_subscriber_test.rb +64 -64
  89. data/test/cases/explain_test.rb +76 -76
  90. data/test/cases/finder_respond_to_test.rb +60 -60
  91. data/test/cases/finder_test.rb +1169 -1166
  92. data/test/cases/fixture_set/file_test.rb +138 -138
  93. data/test/cases/fixtures_test.rb +908 -897
  94. data/test/cases/forbidden_attributes_protection_test.rb +99 -99
  95. data/test/cases/habtm_destroy_order_test.rb +61 -61
  96. data/test/cases/helper.rb +210 -210
  97. data/test/cases/hot_compatibility_test.rb +54 -54
  98. data/test/cases/i18n_test.rb +45 -45
  99. data/test/cases/inheritance_test.rb +375 -375
  100. data/test/cases/integration_test.rb +139 -139
  101. data/test/cases/invalid_connection_test.rb +22 -22
  102. data/test/cases/invalid_date_test.rb +32 -32
  103. data/test/cases/invertible_migration_test.rb +295 -295
  104. data/test/cases/json_serialization_test.rb +302 -302
  105. data/test/cases/locking_test.rb +477 -477
  106. data/test/cases/log_subscriber_test.rb +136 -136
  107. data/test/cases/migration/change_schema_test - Copy.rb +448 -448
  108. data/test/cases/migration/change_schema_test.rb +512 -472
  109. data/test/cases/migration/change_table_test.rb +224 -224
  110. data/test/cases/migration/column_attributes_test.rb +192 -192
  111. data/test/cases/migration/column_positioning_test.rb +56 -56
  112. data/test/cases/migration/columns_test.rb +304 -304
  113. data/test/cases/migration/command_recorder_test.rb +305 -305
  114. data/test/cases/migration/create_join_table_test.rb +148 -148
  115. data/test/cases/migration/foreign_key_test - Changed.rb +325 -325
  116. data/test/cases/migration/foreign_key_test.rb +328 -360
  117. data/test/cases/migration/helper.rb +39 -39
  118. data/test/cases/migration/index_test.rb +216 -216
  119. data/test/cases/migration/logger_test.rb +36 -36
  120. data/test/cases/migration/pending_migrations_test.rb +53 -53
  121. data/test/cases/migration/references_foreign_key_test.rb +169 -214
  122. data/test/cases/migration/references_index_test.rb +101 -101
  123. data/test/cases/migration/references_statements_test.rb +116 -116
  124. data/test/cases/migration/rename_table_test.rb +93 -93
  125. data/test/cases/migration/table_and_index_test.rb +24 -24
  126. data/test/cases/migration_test.rb +959 -959
  127. data/test/cases/migrator_test.rb +388 -388
  128. data/test/cases/mixin_test.rb +70 -70
  129. data/test/cases/modules_test.rb +173 -173
  130. data/test/cases/multiparameter_attributes_test.rb +350 -350
  131. data/test/cases/multiple_db_test.rb +115 -115
  132. data/test/cases/nested_attributes_test.rb +1070 -1057
  133. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  134. data/test/cases/persistence_test.rb +909 -909
  135. data/test/cases/pooled_connections_test.rb +81 -81
  136. data/test/cases/primary_keys_test.rb +237 -237
  137. data/test/cases/query_cache_test.rb +326 -326
  138. data/test/cases/quoting_test.rb +156 -156
  139. data/test/cases/readonly_test.rb +118 -118
  140. data/test/cases/reaper_test.rb +85 -85
  141. data/test/cases/reflection_test.rb +463 -454
  142. data/test/cases/relation/delegation_test.rb +68 -68
  143. data/test/cases/relation/merging_test.rb +161 -161
  144. data/test/cases/relation/mutation_test.rb +165 -165
  145. data/test/cases/relation/predicate_builder_test.rb +14 -14
  146. data/test/cases/relation/where_chain_test.rb +181 -181
  147. data/test/cases/relation/where_test.rb +300 -300
  148. data/test/cases/relation/where_test2.rb +36 -36
  149. data/test/cases/relation_test.rb +319 -297
  150. data/test/cases/relations_test.rb +1815 -1815
  151. data/test/cases/reload_models_test.rb +22 -22
  152. data/test/cases/result_test.rb +80 -80
  153. data/test/cases/sanitize_test.rb +83 -83
  154. data/test/cases/schema_dumper_test.rb +463 -463
  155. data/test/cases/scoping/default_scoping_test.rb +454 -454
  156. data/test/cases/scoping/named_scoping_test.rb +524 -524
  157. data/test/cases/scoping/relation_scoping_test.rb +357 -357
  158. data/test/cases/serialization_test.rb +104 -104
  159. data/test/cases/serialized_attribute_test.rb +277 -277
  160. data/test/cases/statement_cache_test.rb +98 -98
  161. data/test/cases/store_test.rb +194 -194
  162. data/test/cases/tasks/database_tasks_test.rb +398 -396
  163. data/test/cases/tasks/mysql_rake_test.rb +324 -311
  164. data/test/cases/tasks/postgresql_rake_test.rb +250 -245
  165. data/test/cases/tasks/sqlite_rake_test.rb +193 -193
  166. data/test/cases/test_case.rb +123 -123
  167. data/test/cases/timestamp_test.rb +467 -468
  168. data/test/cases/transaction_callbacks_test.rb +452 -452
  169. data/test/cases/transaction_isolation_test.rb +106 -106
  170. data/test/cases/transactions_test.rb +817 -817
  171. data/test/cases/type/decimal_test.rb +56 -51
  172. data/test/cases/type/integer_test.rb +121 -121
  173. data/test/cases/type/string_test.rb +36 -36
  174. data/test/cases/type/type_map_test.rb +177 -177
  175. data/test/cases/type/unsigned_integer_test.rb +18 -18
  176. data/test/cases/types_test.rb +141 -141
  177. data/test/cases/unconnected_test.rb +33 -33
  178. data/test/cases/validations/association_validation_test.rb +86 -86
  179. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  180. data/test/cases/validations/i18n_validation_test.rb +90 -90
  181. data/test/cases/validations/length_validation_test.rb +47 -47
  182. data/test/cases/validations/presence_validation_test.rb +68 -68
  183. data/test/cases/validations/uniqueness_validation_test.rb +457 -434
  184. data/test/cases/validations_repair_helper.rb +23 -23
  185. data/test/cases/validations_test.rb +165 -165
  186. data/test/cases/view_test.rb +119 -113
  187. data/test/cases/xml_serialization_test.rb +457 -457
  188. data/test/cases/yaml_serialization_test.rb +126 -86
  189. data/test/config.rb +5 -5
  190. data/test/config.yml +154 -154
  191. data/test/connections/native_ibm_db/connection.rb +43 -43
  192. data/test/fixtures/accounts.yml +29 -29
  193. data/test/fixtures/admin/accounts.yml +2 -2
  194. data/test/fixtures/admin/randomly_named_a9.yml +7 -7
  195. data/test/fixtures/admin/randomly_named_b0.yml +7 -7
  196. data/test/fixtures/admin/users.yml +10 -10
  197. data/test/fixtures/author_addresses.yml +17 -17
  198. data/test/fixtures/author_favorites.yml +3 -3
  199. data/test/fixtures/authors.yml +23 -23
  200. data/test/fixtures/binaries.yml +133 -133
  201. data/test/fixtures/books.yml +11 -11
  202. data/test/fixtures/bulbs.yml +5 -5
  203. data/test/fixtures/cars.yml +9 -9
  204. data/test/fixtures/categories.yml +19 -19
  205. data/test/fixtures/categories/special_categories.yml +9 -9
  206. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  207. data/test/fixtures/categories_ordered.yml +7 -7
  208. data/test/fixtures/categories_posts.yml +31 -31
  209. data/test/fixtures/categorizations.yml +23 -23
  210. data/test/fixtures/clubs.yml +8 -8
  211. data/test/fixtures/collections.yml +3 -3
  212. data/test/fixtures/colleges.yml +3 -3
  213. data/test/fixtures/comments.yml +65 -65
  214. data/test/fixtures/companies.yml +67 -67
  215. data/test/fixtures/computers.yml +10 -10
  216. data/test/fixtures/courses.yml +8 -8
  217. data/test/fixtures/customers.yml +25 -25
  218. data/test/fixtures/dashboards.yml +6 -6
  219. data/test/fixtures/developers.yml +21 -21
  220. data/test/fixtures/developers_projects.yml +16 -16
  221. data/test/fixtures/dog_lovers.yml +7 -7
  222. data/test/fixtures/dogs.yml +4 -4
  223. data/test/fixtures/doubloons.yml +3 -3
  224. data/test/fixtures/edges.yml +5 -5
  225. data/test/fixtures/entrants.yml +14 -14
  226. data/test/fixtures/essays.yml +6 -6
  227. data/test/fixtures/faces.yml +11 -11
  228. data/test/fixtures/fk_test_has_fk.yml +3 -3
  229. data/test/fixtures/fk_test_has_pk.yml +1 -1
  230. data/test/fixtures/friendships.yml +4 -4
  231. data/test/fixtures/funny_jokes.yml +10 -10
  232. data/test/fixtures/interests.yml +33 -33
  233. data/test/fixtures/items.yml +3 -3
  234. data/test/fixtures/jobs.yml +7 -7
  235. data/test/fixtures/legacy_things.yml +3 -3
  236. data/test/fixtures/mateys.yml +4 -4
  237. data/test/fixtures/member_details.yml +8 -8
  238. data/test/fixtures/member_types.yml +6 -6
  239. data/test/fixtures/members.yml +11 -11
  240. data/test/fixtures/memberships.yml +34 -34
  241. data/test/fixtures/men.yml +5 -5
  242. data/test/fixtures/minimalistics.yml +2 -2
  243. data/test/fixtures/minivans.yml +5 -5
  244. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  245. data/test/fixtures/mixins.yml +29 -29
  246. data/test/fixtures/movies.yml +7 -7
  247. data/test/fixtures/naked/csv/accounts.csv +1 -1
  248. data/test/fixtures/naked/yml/accounts.yml +1 -1
  249. data/test/fixtures/naked/yml/companies.yml +1 -1
  250. data/test/fixtures/naked/yml/courses.yml +1 -1
  251. data/test/fixtures/organizations.yml +5 -5
  252. data/test/fixtures/other_topics.yml +42 -42
  253. data/test/fixtures/owners.yml +9 -9
  254. data/test/fixtures/parrots.yml +27 -27
  255. data/test/fixtures/parrots_pirates.yml +7 -7
  256. data/test/fixtures/people.yml +24 -24
  257. data/test/fixtures/peoples_treasures.yml +3 -3
  258. data/test/fixtures/pets.yml +19 -19
  259. data/test/fixtures/pirates.yml +12 -12
  260. data/test/fixtures/posts.yml +80 -80
  261. data/test/fixtures/price_estimates.yml +7 -7
  262. data/test/fixtures/products.yml +4 -4
  263. data/test/fixtures/projects.yml +7 -7
  264. data/test/fixtures/randomly_named_a9.yml +7 -7
  265. data/test/fixtures/ratings.yml +14 -14
  266. data/test/fixtures/readers.yml +11 -11
  267. data/test/fixtures/references.yml +17 -17
  268. data/test/fixtures/reserved_words/distinct.yml +5 -5
  269. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  270. data/test/fixtures/reserved_words/group.yml +14 -14
  271. data/test/fixtures/reserved_words/select.yml +8 -8
  272. data/test/fixtures/reserved_words/values.yml +7 -7
  273. data/test/fixtures/ships.yml +6 -6
  274. data/test/fixtures/speedometers.yml +8 -8
  275. data/test/fixtures/sponsors.yml +12 -12
  276. data/test/fixtures/string_key_objects.yml +7 -7
  277. data/test/fixtures/subscribers.yml +10 -10
  278. data/test/fixtures/subscriptions.yml +12 -12
  279. data/test/fixtures/taggings.yml +78 -78
  280. data/test/fixtures/tags.yml +11 -11
  281. data/test/fixtures/tasks.yml +7 -7
  282. data/test/fixtures/teapots.yml +3 -3
  283. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  284. data/test/fixtures/to_be_linked/users.yml +10 -10
  285. data/test/fixtures/topics.yml +49 -49
  286. data/test/fixtures/toys.yml +14 -14
  287. data/test/fixtures/traffic_lights.yml +9 -9
  288. data/test/fixtures/treasures.yml +10 -10
  289. data/test/fixtures/uuid_children.yml +3 -3
  290. data/test/fixtures/uuid_parents.yml +2 -2
  291. data/test/fixtures/variants.yml +4 -4
  292. data/test/fixtures/vegetables.yml +19 -19
  293. data/test/fixtures/vertices.yml +3 -3
  294. data/test/fixtures/warehouse_things.yml +2 -2
  295. data/test/fixtures/zines.yml +5 -5
  296. data/test/ibm_db_test.rb +24 -24
  297. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  298. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  299. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  300. data/test/migrations/missing/1000_people_have_middle_names.rb +8 -8
  301. data/test/migrations/missing/1_people_have_last_names.rb +8 -8
  302. data/test/migrations/missing/3_we_need_reminders.rb +11 -11
  303. data/test/migrations/missing/4_innocent_jointable.rb +11 -11
  304. data/test/migrations/rename/1_we_need_things.rb +10 -10
  305. data/test/migrations/rename/2_rename_things.rb +8 -8
  306. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  307. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  308. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  309. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  310. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  311. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  312. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  313. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  314. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  315. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  316. data/test/migrations/valid/2_we_need_reminders.rb +11 -11
  317. data/test/migrations/valid/3_innocent_jointable.rb +11 -11
  318. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  319. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +11 -11
  320. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +11 -11
  321. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  322. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  323. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  324. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  325. data/test/models/admin.rb +4 -4
  326. data/test/models/admin/account.rb +2 -2
  327. data/test/models/admin/randomly_named_c1.rb +3 -3
  328. data/test/models/admin/user.rb +40 -40
  329. data/test/models/aircraft.rb +4 -4
  330. data/test/models/arunit2_model.rb +3 -3
  331. data/test/models/author.rb +212 -212
  332. data/test/models/auto_id.rb +4 -4
  333. data/test/models/autoloadable/extra_firm.rb +2 -2
  334. data/test/models/binary.rb +1 -1
  335. data/test/models/bird.rb +12 -12
  336. data/test/models/book.rb +18 -18
  337. data/test/models/boolean.rb +2 -2
  338. data/test/models/bulb.rb +51 -51
  339. data/test/models/cake_designer.rb +3 -3
  340. data/test/models/car.rb +26 -26
  341. data/test/models/carrier.rb +2 -2
  342. data/test/models/categorization.rb +19 -19
  343. data/test/models/category.rb +35 -35
  344. data/test/models/chef.rb +7 -3
  345. data/test/models/citation.rb +3 -3
  346. data/test/models/club.rb +23 -23
  347. data/test/models/college.rb +10 -10
  348. data/test/models/column.rb +3 -3
  349. data/test/models/column_name.rb +3 -3
  350. data/test/models/comment.rb +64 -64
  351. data/test/models/company.rb +228 -225
  352. data/test/models/company_in_module.rb +98 -98
  353. data/test/models/computer.rb +3 -3
  354. data/test/models/contact.rb +41 -41
  355. data/test/models/contract.rb +20 -20
  356. data/test/models/country.rb +7 -7
  357. data/test/models/course.rb +6 -6
  358. data/test/models/customer.rb +77 -77
  359. data/test/models/customer_carrier.rb +14 -14
  360. data/test/models/dashboard.rb +3 -3
  361. data/test/models/default.rb +2 -2
  362. data/test/models/department.rb +4 -4
  363. data/test/models/developer.rb +255 -252
  364. data/test/models/dog.rb +5 -5
  365. data/test/models/dog_lover.rb +5 -5
  366. data/test/models/doubloon.rb +12 -12
  367. data/test/models/drink_designer.rb +3 -3
  368. data/test/models/edge.rb +5 -5
  369. data/test/models/electron.rb +5 -5
  370. data/test/models/engine.rb +4 -4
  371. data/test/models/entrant.rb +3 -3
  372. data/test/models/essay.rb +5 -5
  373. data/test/models/event.rb +2 -2
  374. data/test/models/eye.rb +37 -37
  375. data/test/models/face.rb +9 -9
  376. data/test/models/friendship.rb +6 -6
  377. data/test/models/guid.rb +1 -1
  378. data/test/models/hotel.rb +9 -6
  379. data/test/models/image.rb +3 -3
  380. data/test/models/interest.rb +5 -5
  381. data/test/models/invoice.rb +4 -4
  382. data/test/models/item.rb +7 -7
  383. data/test/models/job.rb +7 -7
  384. data/test/models/joke.rb +7 -7
  385. data/test/models/keyboard.rb +3 -3
  386. data/test/models/legacy_thing.rb +3 -3
  387. data/test/models/lesson.rb +11 -11
  388. data/test/models/line_item.rb +3 -3
  389. data/test/models/liquid.rb +4 -4
  390. data/test/models/man.rb +11 -11
  391. data/test/models/matey.rb +4 -4
  392. data/test/models/member.rb +41 -41
  393. data/test/models/member_detail.rb +7 -7
  394. data/test/models/member_type.rb +3 -3
  395. data/test/models/membership.rb +35 -35
  396. data/test/models/minimalistic.rb +2 -2
  397. data/test/models/minivan.rb +9 -9
  398. data/test/models/mixed_case_monkey.rb +3 -3
  399. data/test/models/molecule.rb +6 -6
  400. data/test/models/movie.rb +5 -5
  401. data/test/models/order.rb +4 -4
  402. data/test/models/organization.rb +14 -14
  403. data/test/models/owner.rb +34 -34
  404. data/test/models/parrot.rb +29 -29
  405. data/test/models/person.rb +143 -143
  406. data/test/models/personal_legacy_thing.rb +4 -4
  407. data/test/models/pet.rb +15 -15
  408. data/test/models/pirate.rb +92 -92
  409. data/test/models/possession.rb +3 -3
  410. data/test/models/post.rb +264 -264
  411. data/test/models/price_estimate.rb +4 -4
  412. data/test/models/professor.rb +5 -5
  413. data/test/models/project.rb +31 -29
  414. data/test/models/publisher.rb +2 -2
  415. data/test/models/publisher/article.rb +4 -4
  416. data/test/models/publisher/magazine.rb +3 -3
  417. data/test/models/randomly_named_c1.rb +3 -3
  418. data/test/models/rating.rb +4 -4
  419. data/test/models/reader.rb +23 -23
  420. data/test/models/record.rb +2 -2
  421. data/test/models/reference.rb +22 -22
  422. data/test/models/reply.rb +61 -61
  423. data/test/models/ship.rb +33 -33
  424. data/test/models/ship_part.rb +7 -7
  425. data/test/models/shop.rb +17 -17
  426. data/test/models/shop_account.rb +6 -6
  427. data/test/models/speedometer.rb +6 -6
  428. data/test/models/sponsor.rb +7 -7
  429. data/test/models/string_key_object.rb +3 -3
  430. data/test/models/student.rb +4 -4
  431. data/test/models/subject.rb +16 -16
  432. data/test/models/subscriber.rb +8 -8
  433. data/test/models/subscription.rb +4 -4
  434. data/test/models/tag.rb +7 -7
  435. data/test/models/tagging.rb +13 -13
  436. data/test/models/task.rb +5 -5
  437. data/test/models/topic.rb +124 -124
  438. data/test/models/toy.rb +6 -6
  439. data/test/models/traffic_light.rb +4 -4
  440. data/test/models/treasure.rb +14 -14
  441. data/test/models/treaty.rb +7 -7
  442. data/test/models/tyre.rb +11 -11
  443. data/test/models/uuid_child.rb +3 -3
  444. data/test/models/uuid_parent.rb +3 -3
  445. data/test/models/vegetables.rb +24 -24
  446. data/test/models/vehicle.rb +6 -6
  447. data/test/models/vertex.rb +9 -9
  448. data/test/models/warehouse_thing.rb +5 -5
  449. data/test/models/wheel.rb +3 -3
  450. data/test/models/without_table.rb +3 -3
  451. data/test/models/zine.rb +3 -3
  452. data/test/schema/mysql2_specific_schema.rb +58 -58
  453. data/test/schema/mysql_specific_schema.rb +70 -70
  454. data/test/schema/oracle_specific_schema.rb +43 -43
  455. data/test/schema/postgresql_specific_schema.rb +202 -202
  456. data/test/schema/schema.rb +952 -938
  457. data/test/schema/sqlite_specific_schema.rb +21 -21
  458. data/test/support/config.rb +43 -43
  459. data/test/support/connection.rb +22 -22
  460. data/test/support/connection_helper.rb +14 -14
  461. data/test/support/ddl_helper.rb +8 -8
  462. data/test/support/schema_dumping_helper.rb +20 -20
  463. metadata +2 -2
@@ -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_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,817 +1,817 @@
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_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