ibm_db 3.0.5-x86-mingw32 → 4.0.0-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 (586) 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/lib/mswin32/ibm_db.rb +91 -123
  20. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  21. data/test/active_record/connection_adapters/fake_adapter.rb +49 -46
  22. data/test/assets/example.log +1 -1
  23. data/test/assets/test.txt +1 -1
  24. data/test/cases/adapter_test.rb +351 -276
  25. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -0
  26. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -0
  27. data/test/cases/adapters/mysql2/boolean_test.rb +100 -0
  28. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -0
  29. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -0
  30. data/test/cases/adapters/mysql2/connection_test.rb +210 -0
  31. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -0
  32. data/test/cases/adapters/mysql2/enum_test.rb +26 -0
  33. data/test/cases/adapters/mysql2/explain_test.rb +21 -0
  34. data/test/cases/adapters/mysql2/json_test.rb +195 -0
  35. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -0
  36. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
  37. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -0
  38. data/test/cases/adapters/mysql2/schema_test.rb +126 -0
  39. data/test/cases/adapters/mysql2/sp_test.rb +36 -0
  40. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -0
  41. data/test/cases/adapters/mysql2/table_options_test.rb +42 -0
  42. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -0
  43. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -0
  44. data/test/cases/adapters/postgresql/array_test.rb +339 -0
  45. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -0
  46. data/test/cases/adapters/postgresql/bytea_test.rb +134 -0
  47. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -0
  48. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -0
  49. data/test/cases/adapters/postgresql/cidr_test.rb +25 -0
  50. data/test/cases/adapters/postgresql/citext_test.rb +78 -0
  51. data/test/cases/adapters/postgresql/collation_test.rb +53 -0
  52. data/test/cases/adapters/postgresql/composite_test.rb +132 -0
  53. data/test/cases/adapters/postgresql/connection_test.rb +257 -0
  54. data/test/cases/adapters/postgresql/datatype_test.rb +92 -0
  55. data/test/cases/adapters/postgresql/domain_test.rb +47 -0
  56. data/test/cases/adapters/postgresql/enum_test.rb +91 -0
  57. data/test/cases/adapters/postgresql/explain_test.rb +20 -0
  58. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -0
  59. data/test/cases/adapters/postgresql/full_text_test.rb +44 -0
  60. data/test/cases/adapters/postgresql/geometric_test.rb +378 -0
  61. data/test/cases/adapters/postgresql/hstore_test.rb +382 -0
  62. data/test/cases/adapters/postgresql/infinity_test.rb +69 -0
  63. data/test/cases/adapters/postgresql/integer_test.rb +25 -0
  64. data/test/cases/adapters/postgresql/json_test.rb +237 -0
  65. data/test/cases/adapters/postgresql/ltree_test.rb +53 -0
  66. data/test/cases/adapters/postgresql/money_test.rb +96 -0
  67. data/test/cases/adapters/postgresql/network_test.rb +94 -0
  68. data/test/cases/adapters/postgresql/numbers_test.rb +49 -0
  69. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -0
  70. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
  71. data/test/cases/adapters/postgresql/quoting_test.rb +44 -0
  72. data/test/cases/adapters/postgresql/range_test.rb +343 -0
  73. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -0
  74. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -0
  75. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -0
  76. data/test/cases/adapters/postgresql/schema_test.rb +597 -0
  77. data/test/cases/adapters/postgresql/serial_test.rb +154 -0
  78. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -0
  79. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -0
  80. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -0
  81. data/test/cases/adapters/postgresql/utils_test.rb +62 -0
  82. data/test/cases/adapters/postgresql/uuid_test.rb +294 -0
  83. data/test/cases/adapters/postgresql/xml_test.rb +54 -0
  84. data/test/cases/adapters/sqlite3/collation_test.rb +53 -0
  85. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -0
  86. data/test/cases/adapters/sqlite3/explain_test.rb +21 -0
  87. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -0
  88. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -0
  89. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
  90. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -0
  91. data/test/cases/aggregations_test.rb +168 -158
  92. data/test/cases/ar_schema_test.rb +146 -161
  93. data/test/cases/associations/association_scope_test.rb +16 -21
  94. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1029
  95. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -0
  96. data/test/cases/associations/callbacks_test.rb +190 -192
  97. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  98. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  99. data/test/cases/associations/eager_load_nested_include_test.rb +126 -128
  100. data/test/cases/associations/eager_singularization_test.rb +148 -148
  101. data/test/cases/associations/eager_test.rb +1514 -1429
  102. data/test/cases/associations/extension_test.rb +87 -82
  103. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -972
  104. data/test/cases/associations/has_many_associations_test.rb +2501 -2182
  105. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1204
  106. data/test/cases/associations/has_one_associations_test.rb +707 -610
  107. data/test/cases/associations/has_one_through_associations_test.rb +383 -380
  108. data/test/cases/associations/inner_join_association_test.rb +139 -139
  109. data/test/cases/associations/inverse_associations_test.rb +733 -706
  110. data/test/cases/associations/join_model_test.rb +777 -754
  111. data/test/cases/associations/left_outer_join_association_test.rb +88 -0
  112. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  113. data/test/cases/associations/required_test.rb +102 -82
  114. data/test/cases/associations_test.rb +385 -380
  115. data/test/cases/attribute_decorators_test.rb +125 -125
  116. data/test/cases/attribute_methods/read_test.rb +60 -60
  117. data/test/cases/attribute_methods_test.rb +1009 -952
  118. data/test/cases/attribute_set_test.rb +270 -210
  119. data/test/cases/attribute_test.rb +246 -180
  120. data/test/cases/attributes_test.rb +253 -136
  121. data/test/cases/autosave_association_test.rb +1708 -1595
  122. data/test/cases/base_test.rb +1713 -1664
  123. data/test/cases/batches_test.rb +489 -212
  124. data/test/cases/binary_test.rb +44 -52
  125. data/test/cases/bind_parameter_test.rb +110 -100
  126. data/test/cases/cache_key_test.rb +25 -0
  127. data/test/cases/calculations_test.rb +798 -646
  128. data/test/cases/callbacks_test.rb +636 -543
  129. data/test/cases/clone_test.rb +40 -40
  130. data/test/cases/coders/json_test.rb +15 -0
  131. data/test/cases/coders/yaml_column_test.rb +63 -63
  132. data/test/cases/collection_cache_key_test.rb +115 -0
  133. data/test/cases/column_alias_test.rb +17 -17
  134. data/test/cases/column_definition_test.rb +92 -123
  135. data/test/cases/comment_test.rb +143 -0
  136. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -54
  137. data/test/cases/connection_adapters/connection_handler_test.rb +160 -53
  138. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  139. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -293
  140. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -65
  141. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  142. data/test/cases/connection_adapters/schema_cache_test.rb +61 -56
  143. data/test/cases/connection_adapters/type_lookup_test.rb +118 -110
  144. data/test/cases/connection_management_test.rb +112 -122
  145. data/test/cases/connection_pool_test.rb +521 -346
  146. data/test/cases/connection_specification/resolver_test.rb +131 -116
  147. data/test/cases/core_test.rb +112 -112
  148. data/test/cases/counter_cache_test.rb +214 -209
  149. data/test/cases/custom_locking_test.rb +17 -17
  150. data/test/cases/database_statements_test.rb +34 -19
  151. data/test/cases/{invalid_date_test.rb → date_test.rb} +44 -32
  152. data/test/cases/date_time_precision_test.rb +106 -0
  153. data/test/cases/date_time_test.rb +61 -61
  154. data/test/cases/defaults_test.rb +218 -223
  155. data/test/cases/dirty_test.rb +763 -785
  156. data/test/cases/disconnected_test.rb +30 -28
  157. data/test/cases/dup_test.rb +157 -157
  158. data/test/cases/enum_test.rb +444 -290
  159. data/test/cases/errors_test.rb +16 -0
  160. data/test/cases/explain_subscriber_test.rb +64 -64
  161. data/test/cases/explain_test.rb +87 -76
  162. data/test/cases/finder_respond_to_test.rb +60 -60
  163. data/test/cases/finder_test.rb +1294 -1169
  164. data/test/cases/fixture_set/file_test.rb +156 -138
  165. data/test/cases/fixtures_test.rb +988 -908
  166. data/test/cases/forbidden_attributes_protection_test.rb +165 -99
  167. data/test/cases/habtm_destroy_order_test.rb +61 -61
  168. data/test/cases/helper.rb +204 -210
  169. data/test/cases/hot_compatibility_test.rb +142 -54
  170. data/test/cases/i18n_test.rb +45 -45
  171. data/test/cases/inheritance_test.rb +606 -375
  172. data/test/cases/integration_test.rb +155 -139
  173. data/test/cases/invalid_connection_test.rb +24 -22
  174. data/test/cases/invertible_migration_test.rb +387 -295
  175. data/test/cases/json_serialization_test.rb +311 -302
  176. data/test/cases/locking_test.rb +493 -477
  177. data/test/cases/log_subscriber_test.rb +225 -136
  178. data/test/cases/migration/change_schema_test.rb +458 -512
  179. data/test/cases/migration/change_table_test.rb +256 -224
  180. data/test/cases/migration/column_attributes_test.rb +176 -192
  181. data/test/cases/migration/column_positioning_test.rb +56 -56
  182. data/test/cases/migration/columns_test.rb +310 -304
  183. data/test/cases/migration/command_recorder_test.rb +350 -305
  184. data/test/cases/migration/compatibility_test.rb +118 -0
  185. data/test/cases/migration/create_join_table_test.rb +157 -148
  186. data/test/cases/migration/foreign_key_test.rb +360 -328
  187. data/test/cases/migration/helper.rb +39 -39
  188. data/test/cases/migration/index_test.rb +218 -216
  189. data/test/cases/migration/logger_test.rb +36 -36
  190. data/test/cases/migration/pending_migrations_test.rb +52 -53
  191. data/test/cases/migration/references_foreign_key_test.rb +216 -169
  192. data/test/cases/migration/references_index_test.rb +101 -101
  193. data/test/cases/migration/references_statements_test.rb +136 -116
  194. data/test/cases/migration/rename_table_test.rb +93 -93
  195. data/test/cases/migration_test.rb +1157 -959
  196. data/test/cases/migrator_test.rb +470 -388
  197. data/test/cases/mixin_test.rb +68 -70
  198. data/test/cases/modules_test.rb +172 -173
  199. data/test/cases/multiparameter_attributes_test.rb +372 -350
  200. data/test/cases/multiple_db_test.rb +122 -115
  201. data/test/cases/nested_attributes_test.rb +1098 -1070
  202. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  203. data/test/cases/persistence_test.rb +1001 -909
  204. data/test/cases/pooled_connections_test.rb +81 -81
  205. data/test/cases/primary_keys_test.rb +376 -237
  206. data/test/cases/query_cache_test.rb +446 -326
  207. data/test/cases/quoting_test.rb +202 -156
  208. data/test/cases/readonly_test.rb +119 -118
  209. data/test/cases/reaper_test.rb +85 -85
  210. data/test/cases/reflection_test.rb +509 -463
  211. data/test/cases/relation/delegation_test.rb +63 -68
  212. data/test/cases/relation/merging_test.rb +157 -161
  213. data/test/cases/relation/mutation_test.rb +183 -165
  214. data/test/cases/relation/or_test.rb +92 -0
  215. data/test/cases/relation/predicate_builder_test.rb +16 -14
  216. data/test/cases/relation/record_fetch_warning_test.rb +40 -0
  217. data/test/cases/relation/where_chain_test.rb +105 -181
  218. data/test/cases/relation/where_clause_test.rb +182 -0
  219. data/test/cases/relation/where_test.rb +322 -300
  220. data/test/cases/relation_test.rb +328 -319
  221. data/test/cases/relations_test.rb +2026 -1815
  222. data/test/cases/reload_models_test.rb +22 -22
  223. data/test/cases/result_test.rb +90 -80
  224. data/test/cases/sanitize_test.rb +176 -83
  225. data/test/cases/schema_dumper_test.rb +457 -463
  226. data/test/cases/schema_loading_test.rb +52 -0
  227. data/test/cases/scoping/default_scoping_test.rb +528 -454
  228. data/test/cases/scoping/named_scoping_test.rb +561 -524
  229. data/test/cases/scoping/relation_scoping_test.rb +400 -357
  230. data/test/cases/secure_token_test.rb +32 -0
  231. data/test/cases/serialization_test.rb +104 -104
  232. data/test/cases/serialized_attribute_test.rb +364 -277
  233. data/test/cases/statement_cache_test.rb +136 -98
  234. data/test/cases/store_test.rb +195 -194
  235. data/test/cases/suppressor_test.rb +63 -0
  236. data/test/cases/tasks/database_tasks_test.rb +462 -398
  237. data/test/cases/tasks/mysql_rake_test.rb +345 -324
  238. data/test/cases/tasks/postgresql_rake_test.rb +304 -250
  239. data/test/cases/tasks/sqlite_rake_test.rb +220 -193
  240. data/test/cases/test_case.rb +131 -123
  241. data/test/cases/test_fixtures_test.rb +36 -0
  242. data/test/cases/time_precision_test.rb +102 -0
  243. data/test/cases/timestamp_test.rb +501 -467
  244. data/test/cases/touch_later_test.rb +121 -0
  245. data/test/cases/transaction_callbacks_test.rb +518 -452
  246. data/test/cases/transaction_isolation_test.rb +106 -106
  247. data/test/cases/transactions_test.rb +834 -817
  248. data/test/cases/type/adapter_specific_registry_test.rb +133 -0
  249. data/test/cases/type/date_time_test.rb +14 -0
  250. data/test/cases/type/integer_test.rb +27 -121
  251. data/test/cases/type/string_test.rb +22 -36
  252. data/test/cases/type/type_map_test.rb +177 -177
  253. data/test/cases/type_test.rb +39 -0
  254. data/test/cases/types_test.rb +24 -141
  255. data/test/cases/unconnected_test.rb +33 -33
  256. data/test/cases/validations/absence_validation_test.rb +73 -0
  257. data/test/cases/validations/association_validation_test.rb +97 -86
  258. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  259. data/test/cases/validations/i18n_validation_test.rb +86 -90
  260. data/test/cases/validations/length_validation_test.rb +79 -47
  261. data/test/cases/validations/presence_validation_test.rb +103 -68
  262. data/test/cases/validations/uniqueness_validation_test.rb +548 -457
  263. data/test/cases/validations_repair_helper.rb +19 -23
  264. data/test/cases/validations_test.rb +194 -165
  265. data/test/cases/view_test.rb +216 -119
  266. data/test/cases/yaml_serialization_test.rb +121 -126
  267. data/test/config.example.yml +97 -0
  268. data/test/config.rb +5 -5
  269. data/test/fixtures/accounts.yml +29 -29
  270. data/test/fixtures/admin/accounts.yml +2 -2
  271. data/test/fixtures/admin/users.yml +10 -10
  272. data/test/fixtures/author_addresses.original +11 -0
  273. data/test/fixtures/author_addresses.yml +17 -17
  274. data/test/fixtures/author_favorites.yml +3 -3
  275. data/test/fixtures/authors.original +17 -0
  276. data/test/fixtures/authors.yml +23 -23
  277. data/test/fixtures/bad_posts.yml +9 -0
  278. data/test/fixtures/binaries.yml +133 -133
  279. data/test/fixtures/books.yml +31 -11
  280. data/test/fixtures/bulbs.yml +5 -5
  281. data/test/fixtures/cars.yml +9 -9
  282. data/test/fixtures/categories.yml +19 -19
  283. data/test/fixtures/categories/special_categories.yml +9 -9
  284. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  285. data/test/fixtures/categories_ordered.yml +7 -7
  286. data/test/fixtures/categories_posts.yml +31 -31
  287. data/test/fixtures/categorizations.yml +23 -23
  288. data/test/fixtures/clubs.yml +8 -8
  289. data/test/fixtures/collections.yml +3 -3
  290. data/test/fixtures/colleges.yml +3 -3
  291. data/test/fixtures/comments.yml +65 -65
  292. data/test/fixtures/companies.yml +67 -67
  293. data/test/fixtures/computers.yml +10 -10
  294. data/test/fixtures/content.yml +3 -0
  295. data/test/fixtures/content_positions.yml +3 -0
  296. data/test/fixtures/courses.yml +8 -8
  297. data/test/fixtures/customers.yml +25 -25
  298. data/test/fixtures/dashboards.yml +6 -6
  299. data/test/fixtures/dead_parrots.yml +5 -0
  300. data/test/fixtures/developers.yml +22 -22
  301. data/test/fixtures/developers_projects.yml +16 -16
  302. data/test/fixtures/dog_lovers.yml +7 -7
  303. data/test/fixtures/dogs.yml +4 -4
  304. data/test/fixtures/doubloons.yml +3 -3
  305. data/test/fixtures/edges.yml +5 -5
  306. data/test/fixtures/entrants.yml +14 -14
  307. data/test/fixtures/essays.yml +6 -6
  308. data/test/fixtures/faces.yml +11 -11
  309. data/test/fixtures/fk_test_has_fk.yml +3 -3
  310. data/test/fixtures/fk_test_has_pk.yml +1 -1
  311. data/test/fixtures/friendships.yml +4 -4
  312. data/test/fixtures/funny_jokes.yml +10 -10
  313. data/test/fixtures/interests.yml +33 -33
  314. data/test/fixtures/items.yml +3 -3
  315. data/test/fixtures/jobs.yml +7 -7
  316. data/test/fixtures/legacy_things.yml +3 -3
  317. data/test/fixtures/live_parrots.yml +4 -0
  318. data/test/fixtures/mateys.yml +4 -4
  319. data/test/fixtures/member_details.yml +8 -8
  320. data/test/fixtures/member_types.yml +6 -6
  321. data/test/fixtures/members.yml +11 -11
  322. data/test/fixtures/memberships.yml +34 -34
  323. data/test/fixtures/men.yml +5 -5
  324. data/test/fixtures/minimalistics.yml +2 -2
  325. data/test/fixtures/minivans.yml +5 -5
  326. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  327. data/test/fixtures/mixins.yml +29 -29
  328. data/test/fixtures/movies.yml +7 -7
  329. data/test/fixtures/naked/yml/accounts.yml +1 -1
  330. data/test/fixtures/naked/yml/companies.yml +1 -1
  331. data/test/fixtures/naked/yml/courses.yml +1 -1
  332. data/test/fixtures/naked/yml/parrots.yml +2 -0
  333. data/test/fixtures/naked/yml/trees.yml +3 -0
  334. data/test/fixtures/nodes.yml +29 -0
  335. data/test/fixtures/organizations.yml +5 -5
  336. data/test/fixtures/other_comments.yml +6 -0
  337. data/test/fixtures/other_dogs.yml +2 -0
  338. data/test/fixtures/other_posts.yml +7 -0
  339. data/test/fixtures/other_topics.yml +42 -42
  340. data/test/fixtures/owners.yml +9 -9
  341. data/test/fixtures/parrots.yml +27 -27
  342. data/test/fixtures/parrots_pirates.yml +7 -7
  343. data/test/fixtures/people.yml +24 -24
  344. data/test/fixtures/peoples_treasures.yml +3 -3
  345. data/test/fixtures/pets.yml +19 -19
  346. data/test/fixtures/pirates.yml +15 -12
  347. data/test/fixtures/posts.yml +80 -80
  348. data/test/fixtures/price_estimates.yml +16 -7
  349. data/test/fixtures/products.yml +4 -4
  350. data/test/fixtures/projects.yml +7 -7
  351. data/test/fixtures/ratings.yml +14 -14
  352. data/test/fixtures/readers.yml +11 -11
  353. data/test/fixtures/references.yml +17 -17
  354. data/test/fixtures/reserved_words/distinct.yml +5 -5
  355. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  356. data/test/fixtures/reserved_words/group.yml +14 -14
  357. data/test/fixtures/reserved_words/select.yml +8 -8
  358. data/test/fixtures/reserved_words/values.yml +7 -7
  359. data/test/fixtures/ships.yml +6 -6
  360. data/test/fixtures/speedometers.yml +8 -8
  361. data/test/fixtures/sponsors.yml +12 -12
  362. data/test/fixtures/string_key_objects.yml +7 -7
  363. data/test/fixtures/subscribers.yml +10 -10
  364. data/test/fixtures/subscriptions.yml +12 -12
  365. data/test/fixtures/taggings.yml +78 -78
  366. data/test/fixtures/tags.yml +11 -11
  367. data/test/fixtures/tasks.yml +7 -7
  368. data/test/fixtures/teapots.yml +3 -3
  369. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  370. data/test/fixtures/to_be_linked/users.yml +10 -10
  371. data/test/fixtures/topics.yml +49 -49
  372. data/test/fixtures/toys.yml +14 -14
  373. data/test/fixtures/traffic_lights.yml +9 -9
  374. data/test/fixtures/treasures.yml +10 -10
  375. data/test/fixtures/trees.yml +3 -0
  376. data/test/fixtures/uuid_children.yml +3 -3
  377. data/test/fixtures/uuid_parents.yml +2 -2
  378. data/test/fixtures/variants.yml +4 -4
  379. data/test/fixtures/vegetables.yml +19 -19
  380. data/test/fixtures/vertices.yml +3 -3
  381. data/test/fixtures/warehouse_things.yml +2 -2
  382. data/test/fixtures/zines.yml +5 -5
  383. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  384. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  385. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  386. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  387. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  388. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  389. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  390. data/test/migrations/rename/1_we_need_things.rb +11 -11
  391. data/test/migrations/rename/2_rename_things.rb +9 -9
  392. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  393. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  394. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  395. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  396. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  397. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  398. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  399. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  400. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  401. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  402. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  403. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  404. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  405. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  406. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  407. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  408. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  409. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  410. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  411. data/test/models/admin.rb +5 -5
  412. data/test/models/admin/account.rb +3 -3
  413. data/test/models/admin/randomly_named_c1.rb +6 -2
  414. data/test/models/admin/user.rb +40 -40
  415. data/test/models/aircraft.rb +5 -4
  416. data/test/models/arunit2_model.rb +3 -3
  417. data/test/models/author.rb +209 -212
  418. data/test/models/auto_id.rb +4 -4
  419. data/test/models/autoloadable/extra_firm.rb +2 -2
  420. data/test/models/binary.rb +2 -2
  421. data/test/models/bird.rb +12 -12
  422. data/test/models/book.rb +23 -18
  423. data/test/models/boolean.rb +2 -2
  424. data/test/models/bulb.rb +52 -51
  425. data/test/models/cake_designer.rb +3 -3
  426. data/test/models/car.rb +29 -26
  427. data/test/models/carrier.rb +2 -2
  428. data/test/models/cat.rb +10 -0
  429. data/test/models/categorization.rb +19 -19
  430. data/test/models/category.rb +35 -35
  431. data/test/models/chef.rb +8 -7
  432. data/test/models/citation.rb +3 -3
  433. data/test/models/club.rb +25 -23
  434. data/test/models/college.rb +10 -10
  435. data/test/models/column.rb +3 -3
  436. data/test/models/column_name.rb +3 -3
  437. data/test/models/comment.rb +76 -64
  438. data/test/models/company.rb +230 -228
  439. data/test/models/company_in_module.rb +98 -98
  440. data/test/models/computer.rb +3 -3
  441. data/test/models/contact.rb +41 -41
  442. data/test/models/content.rb +40 -0
  443. data/test/models/contract.rb +20 -20
  444. data/test/models/country.rb +7 -7
  445. data/test/models/course.rb +6 -6
  446. data/test/models/customer.rb +83 -77
  447. data/test/models/customer_carrier.rb +14 -14
  448. data/test/models/dashboard.rb +3 -3
  449. data/test/models/default.rb +2 -2
  450. data/test/models/department.rb +4 -4
  451. data/test/models/developer.rb +274 -255
  452. data/test/models/dog.rb +5 -5
  453. data/test/models/dog_lover.rb +5 -5
  454. data/test/models/doubloon.rb +12 -12
  455. data/test/models/drink_designer.rb +3 -3
  456. data/test/models/edge.rb +5 -5
  457. data/test/models/electron.rb +5 -5
  458. data/test/models/engine.rb +4 -4
  459. data/test/models/entrant.rb +3 -3
  460. data/test/models/essay.rb +5 -5
  461. data/test/models/event.rb +3 -3
  462. data/test/models/eye.rb +37 -37
  463. data/test/models/face.rb +9 -9
  464. data/test/models/friendship.rb +6 -6
  465. data/test/models/guid.rb +2 -2
  466. data/test/models/guitar.rb +4 -0
  467. data/test/models/hotel.rb +11 -9
  468. data/test/models/image.rb +3 -3
  469. data/test/models/interest.rb +5 -5
  470. data/test/models/invoice.rb +4 -4
  471. data/test/models/item.rb +7 -7
  472. data/test/models/job.rb +7 -7
  473. data/test/models/joke.rb +7 -7
  474. data/test/models/keyboard.rb +3 -3
  475. data/test/models/legacy_thing.rb +3 -3
  476. data/test/models/lesson.rb +11 -11
  477. data/test/models/line_item.rb +3 -3
  478. data/test/models/liquid.rb +4 -4
  479. data/test/models/man.rb +11 -11
  480. data/test/models/matey.rb +4 -4
  481. data/test/models/member.rb +42 -41
  482. data/test/models/member_detail.rb +8 -7
  483. data/test/models/member_type.rb +3 -3
  484. data/test/models/membership.rb +35 -35
  485. data/test/models/mentor.rb +3 -0
  486. data/test/models/minimalistic.rb +2 -2
  487. data/test/models/minivan.rb +9 -9
  488. data/test/models/mixed_case_monkey.rb +3 -3
  489. data/test/models/mocktail_designer.rb +2 -0
  490. data/test/models/molecule.rb +6 -6
  491. data/test/models/movie.rb +5 -5
  492. data/test/models/node.rb +5 -0
  493. data/test/models/non_primary_key.rb +2 -0
  494. data/test/models/notification.rb +3 -0
  495. data/test/models/order.rb +4 -4
  496. data/test/models/organization.rb +14 -14
  497. data/test/models/other_dog.rb +5 -0
  498. data/test/models/owner.rb +37 -34
  499. data/test/models/parrot.rb +28 -29
  500. data/test/models/person.rb +142 -143
  501. data/test/models/personal_legacy_thing.rb +4 -4
  502. data/test/models/pet.rb +18 -15
  503. data/test/models/pet_treasure.rb +6 -0
  504. data/test/models/pirate.rb +92 -92
  505. data/test/models/possession.rb +3 -3
  506. data/test/models/post.rb +273 -264
  507. data/test/models/price_estimate.rb +4 -4
  508. data/test/models/professor.rb +5 -5
  509. data/test/models/project.rb +40 -31
  510. data/test/models/publisher.rb +2 -2
  511. data/test/models/publisher/article.rb +4 -4
  512. data/test/models/publisher/magazine.rb +3 -3
  513. data/test/models/randomly_named_c1.rb +1 -1
  514. data/test/models/rating.rb +4 -4
  515. data/test/models/reader.rb +23 -23
  516. data/test/models/recipe.rb +3 -0
  517. data/test/models/record.rb +2 -2
  518. data/test/models/reference.rb +22 -22
  519. data/test/models/reply.rb +61 -61
  520. data/test/models/ship.rb +39 -33
  521. data/test/models/ship_part.rb +8 -8
  522. data/test/models/shop.rb +17 -17
  523. data/test/models/shop_account.rb +6 -6
  524. data/test/models/speedometer.rb +6 -6
  525. data/test/models/sponsor.rb +7 -7
  526. data/test/models/string_key_object.rb +3 -3
  527. data/test/models/student.rb +4 -4
  528. data/test/models/subject.rb +16 -16
  529. data/test/models/subscriber.rb +8 -8
  530. data/test/models/subscription.rb +4 -4
  531. data/test/models/tag.rb +13 -7
  532. data/test/models/tagging.rb +13 -13
  533. data/test/models/task.rb +5 -5
  534. data/test/models/topic.rb +118 -124
  535. data/test/models/toy.rb +6 -6
  536. data/test/models/traffic_light.rb +4 -4
  537. data/test/models/treasure.rb +14 -14
  538. data/test/models/treaty.rb +7 -7
  539. data/test/models/tree.rb +3 -0
  540. data/test/models/tuning_peg.rb +4 -0
  541. data/test/models/tyre.rb +11 -11
  542. data/test/models/user.rb +14 -0
  543. data/test/models/uuid_child.rb +3 -3
  544. data/test/models/uuid_item.rb +6 -0
  545. data/test/models/uuid_parent.rb +3 -3
  546. data/test/models/vegetables.rb +24 -24
  547. data/test/models/vehicle.rb +6 -6
  548. data/test/models/vertex.rb +9 -9
  549. data/test/models/warehouse_thing.rb +5 -5
  550. data/test/models/wheel.rb +3 -3
  551. data/test/models/without_table.rb +3 -3
  552. data/test/models/zine.rb +3 -3
  553. data/test/schema/mysql2_specific_schema.rb +68 -58
  554. data/test/schema/oracle_specific_schema.rb +40 -43
  555. data/test/schema/postgresql_specific_schema.rb +114 -202
  556. data/test/schema/schema.rb +1057 -952
  557. data/test/schema/schema.rb.original +1057 -0
  558. data/test/schema/sqlite_specific_schema.rb +18 -22
  559. data/test/support/config.rb +43 -43
  560. data/test/support/connection.rb +23 -22
  561. data/test/support/connection_helper.rb +14 -14
  562. data/test/support/ddl_helper.rb +8 -8
  563. data/test/support/schema_dumping_helper.rb +20 -20
  564. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
  565. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
  566. metadata +129 -28
  567. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  568. data/lib/mswin32/rb21x/i386/ibm_db.so +0 -0
  569. data/lib/mswin32/rb22x/i386/ibm_db.so +0 -0
  570. data/lib/mswin32/rb23x/i386/ibm_db.so +0 -0
  571. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +0 -26
  572. data/test/cases/attribute_methods/serialization_test.rb +0 -29
  573. data/test/cases/migration/change_schema_test - Copy.rb +0 -448
  574. data/test/cases/migration/foreign_key_test - Changed.rb +0 -325
  575. data/test/cases/migration/table_and_index_test.rb +0 -24
  576. data/test/cases/relation/where_test2.rb +0 -36
  577. data/test/cases/type/decimal_test.rb +0 -56
  578. data/test/cases/type/unsigned_integer_test.rb +0 -18
  579. data/test/cases/xml_serialization_test.rb +0 -457
  580. data/test/connections/native_ibm_db/connection.rb +0 -44
  581. data/test/fixtures/naked/csv/accounts.csv +0 -1
  582. data/test/schema/i5/ibm_db_specific_schema.rb +0 -137
  583. data/test/schema/ids/ibm_db_specific_schema.rb +0 -140
  584. data/test/schema/luw/ibm_db_specific_schema.rb +0 -137
  585. data/test/schema/mysql_specific_schema.rb +0 -70
  586. 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