sequel 3.21.0 → 3.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -37,7 +37,8 @@ describe "A new Database" do
37
37
 
38
38
  specify "should pass the supplied block to the connection pool" do
39
39
  cc = nil
40
- d = Sequel::Database.new {1234}
40
+ d = Sequel::Database.new
41
+ d.meta_def(:connect){|c| 1234}
41
42
  d.synchronize {|c| cc = c}
42
43
  cc.should == 1234
43
44
  end
@@ -74,46 +75,46 @@ describe "A new Database" do
74
75
  Sequel.identifier_input_method = nil
75
76
  Sequel::Database.identifier_input_method.should == ""
76
77
  db = Sequel::Database.new(:identifier_input_method=>nil)
77
- db.identifier_input_method.should == nil
78
+ db.identifier_input_method.should be_nil
78
79
  db.identifier_input_method = :downcase
79
80
  db.identifier_input_method.should == :downcase
80
81
  db = Sequel::Database.new(:identifier_input_method=>:upcase)
81
82
  db.identifier_input_method.should == :upcase
82
83
  db.identifier_input_method = nil
83
- db.identifier_input_method.should == nil
84
+ db.identifier_input_method.should be_nil
84
85
  Sequel.identifier_input_method = :downcase
85
86
  Sequel::Database.identifier_input_method.should == :downcase
86
87
  db = Sequel::Database.new(:identifier_input_method=>nil)
87
- db.identifier_input_method.should == nil
88
+ db.identifier_input_method.should be_nil
88
89
  db.identifier_input_method = :upcase
89
90
  db.identifier_input_method.should == :upcase
90
91
  db = Sequel::Database.new(:identifier_input_method=>:upcase)
91
92
  db.identifier_input_method.should == :upcase
92
93
  db.identifier_input_method = nil
93
- db.identifier_input_method.should == nil
94
+ db.identifier_input_method.should be_nil
94
95
  end
95
96
 
96
97
  specify "should respect the :identifier_output_method option" do
97
98
  Sequel.identifier_output_method = nil
98
99
  Sequel::Database.identifier_output_method.should == ""
99
100
  db = Sequel::Database.new(:identifier_output_method=>nil)
100
- db.identifier_output_method.should == nil
101
+ db.identifier_output_method.should be_nil
101
102
  db.identifier_output_method = :downcase
102
103
  db.identifier_output_method.should == :downcase
103
104
  db = Sequel::Database.new(:identifier_output_method=>:upcase)
104
105
  db.identifier_output_method.should == :upcase
105
106
  db.identifier_output_method = nil
106
- db.identifier_output_method.should == nil
107
+ db.identifier_output_method.should be_nil
107
108
  Sequel.identifier_output_method = :downcase
108
109
  Sequel::Database.identifier_output_method.should == :downcase
109
110
  db = Sequel::Database.new(:identifier_output_method=>nil)
110
- db.identifier_output_method.should == nil
111
+ db.identifier_output_method.should be_nil
111
112
  db.identifier_output_method = :upcase
112
113
  db.identifier_output_method.should == :upcase
113
114
  db = Sequel::Database.new(:identifier_output_method=>:upcase)
114
115
  db.identifier_output_method.should == :upcase
115
116
  db.identifier_output_method = nil
116
- db.identifier_output_method.should == nil
117
+ db.identifier_output_method.should be_nil
117
118
  end
118
119
 
119
120
  specify "should use the default Sequel.quote_identifiers value" do
@@ -193,8 +194,7 @@ describe "A new Database" do
193
194
  end
194
195
 
195
196
  specify "should populate :adapter option when using connection string" do
196
- Sequel::Database.should_receive(:adapter_class).once.with(:do).and_return(Sequel::Database)
197
- Sequel.connect('do:test://host/db_name').opts[:adapter] == :do
197
+ Sequel.connect('mock:/').opts[:adapter].should == "mock"
198
198
  end
199
199
  end
200
200
 
@@ -325,6 +325,10 @@ describe "Database#uri" do
325
325
  @db.uri.should == 'mau://user:pass@localhost:9876/maumau'
326
326
  end
327
327
 
328
+ specify "should return nil if a connection uri was not used" do
329
+ Sequel.mock.uri.should be_nil
330
+ end
331
+
328
332
  specify "should be aliased as #url" do
329
333
  @db.url.should == 'mau://user:pass@localhost:9876/maumau'
330
334
  end
@@ -366,7 +370,7 @@ describe "Database#dataset" do
366
370
  end
367
371
 
368
372
  specify "should provide a filtered #from dataset if a block is given" do
369
- d = @db.from(:mau) {:x.sql_number > 100}
373
+ d = @db.from(:mau){x.sql_number > 100}
370
374
  d.should be_a_kind_of(Sequel::Dataset)
371
375
  d.sql.should == 'SELECT * FROM mau WHERE (x > 100)'
372
376
  end
@@ -384,339 +388,214 @@ describe "Database#dataset" do
384
388
  end
385
389
  end
386
390
 
387
- describe "Database#execute" do
388
- specify "should raise Sequel::NotImplemented" do
389
- proc {Sequel::Database.new.execute('blah blah')}.should raise_error(Sequel::NotImplemented)
390
- proc {Sequel::Database.new << 'blah blah'}.should raise_error(Sequel::NotImplemented)
391
- end
392
- end
393
-
394
- describe "Database#tables" do
395
- specify "should raise Sequel::NotImplemented" do
396
- proc {Sequel::Database.new.tables}.should raise_error(Sequel::NotImplemented)
397
- end
398
- end
399
-
400
- describe "Database#indexes" do
401
- specify "should raise Sequel::NotImplemented" do
402
- proc {Sequel::Database.new.indexes(:table)}.should raise_error(Sequel::NotImplemented)
403
- end
404
- end
405
-
406
- describe "Database#<< and run" do
391
+ describe "Database#dataset_class" do
407
392
  before do
408
- sqls = @sqls = []
409
- @c = Class.new(Sequel::Database) do
410
- define_method(:execute_ddl){|sql, *opts| sqls.clear; sqls << sql; sqls.concat(opts)}
411
- end
412
- @db = @c.new({})
413
- end
414
-
415
- specify "should pass the supplied sql to #execute_ddl" do
416
- (@db << "DELETE FROM items")
417
- @sqls.should == ["DELETE FROM items", {}]
418
- @db.run("DELETE FROM items2")
419
- @sqls.should == ["DELETE FROM items2", {}]
393
+ @db = Sequel::Database.new
394
+ @dsc = Class.new(Sequel::Dataset)
420
395
  end
421
396
 
422
- specify "should return nil" do
423
- (@db << "DELETE FROM items").should == nil
424
- @db.run("DELETE FROM items").should == nil
397
+ specify "should have setter set the class to use to create datasets" do
398
+ @db.dataset_class = @dsc
399
+ ds = @db.dataset
400
+ ds.should be_a_kind_of(@dsc)
401
+ ds.opts.should == {}
402
+ ds.db.should be(@db)
425
403
  end
426
-
427
- specify "should accept options passed to execute_ddl" do
428
- @db.run("DELETE FROM items", :server=>:s1)
429
- @sqls.should == ["DELETE FROM items", {:server=>:s1}]
404
+
405
+ specify "should have getter return the class to use to create datasets" do
406
+ @db.dataset_class.should == Sequel::Dataset
407
+ @db.dataset_class = @dsc
408
+ @db.dataset_class.should == @dsc
430
409
  end
431
410
  end
432
-
433
- describe "Database#synchronize" do
411
+
412
+ describe "Database#extend_datasets" do
434
413
  before do
435
- @db = Sequel::Database.new(:max_connections => 1){12345}
414
+ @db = Sequel::Database.new
415
+ @m = Module.new{def foo() [3] end}
416
+ @m2 = Module.new{def foo() [4] + super end}
417
+ @db.extend_datasets(@m)
436
418
  end
437
419
 
438
- specify "should wrap the supplied block in pool.hold" do
439
- stop = false
440
- c1, c2 = nil
441
- t1 = Thread.new {@db.synchronize {|c| c1 = c; while !stop;sleep 0.1;end}}
442
- while !c1;end
443
- c1.should == 12345
444
- t2 = Thread.new {@db.synchronize {|c| c2 = c}}
445
- sleep 0.2
446
- @db.pool.available_connections.should be_empty
447
- c2.should be_nil
448
- stop = true
449
- t1.join
450
- sleep 0.1
451
- c2.should == 12345
452
- t2.join
420
+ specify "should clear a cached dataset" do
421
+ @db = Sequel::Database.new
422
+ @db.literal(1).should == '1'
423
+ @db.extend_datasets{def literal(v) '2' end}
424
+ @db.literal(1).should == '2'
453
425
  end
454
- end
455
426
 
456
- describe "Database#test_connection" do
457
- before do
458
- @db = Sequel::Database.new{@test = rand(100)}
427
+ specify "should change the dataset class to a subclass the first time it is called" do
428
+ @db.dataset_class.superclass.should == Sequel::Dataset
459
429
  end
460
-
461
- specify "should pool#hold" do
462
- @db.test_connection
463
- @test.should_not be_nil
464
- end
465
-
466
- specify "should return true if successful" do
467
- @db.test_connection.should be_true
430
+
431
+ specify "should not create a subclass of the dataset class if called more than once" do
432
+ @db.extend_datasets(@m2)
433
+ @db.dataset_class.superclass.should == Sequel::Dataset
468
434
  end
469
435
 
470
- specify "should raise an error if the attempting to connect raises an error" do
471
- proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
436
+ specify "should make the dataset class include the module" do
437
+ @db.dataset_class.ancestors.should include(@m)
438
+ @db.dataset_class.ancestors.should_not include(@m2)
439
+ @db.extend_datasets(@m2)
440
+ @db.dataset_class.ancestors.should include(@m)
441
+ @db.dataset_class.ancestors.should include(@m2)
472
442
  end
473
- end
474
443
 
475
- class DummyDataset < Sequel::Dataset
476
- def first
477
- raise if @opts[:from] == [:a]
478
- true
444
+ specify "should have datasets respond to the module's methods" do
445
+ @db.dataset.foo.should == [3]
446
+ @db.extend_datasets(@m2)
447
+ @db.dataset.foo.should == [4, 3]
479
448
  end
480
- end
481
449
 
482
- class DummyDatabase < Sequel::Database
483
- attr_reader :sqls
484
-
485
- def execute(sql, opts={})
486
- @sqls ||= []
487
- @sqls << sql
450
+ specify "should take a block and create a module from it to use" do
451
+ @db.dataset.foo.should == [3]
452
+ @db.extend_datasets{def foo() [5] + super end}
453
+ @db.dataset.foo.should == [5, 3]
488
454
  end
489
-
490
- def transaction; yield; end
491
455
 
492
- def dataset
493
- DummyDataset.new(self)
456
+ specify "should raise an error if both a module and a block are provided" do
457
+ proc{@db.extend_datasets(@m2){def foo() [5] + super end}}.should raise_error(Sequel::Error)
494
458
  end
495
- end
496
459
 
497
- describe "Database#create_table" do
498
- before do
499
- @db = DummyDatabase.new
460
+ specify "should be able to override methods defined in the original Dataset class" do
461
+ @db.extend_datasets(Module.new{def select(*a, &block) super.order(*a, &block) end})
462
+ @db[:t].select(:a, :b).sql.should == 'SELECT a, b FROM t ORDER BY a, b'
500
463
  end
501
-
502
- specify "should construct proper SQL" do
503
- @db.create_table :test do
504
- primary_key :id, :integer, :null => false
505
- column :name, :text
506
- index :name, :unique => true
507
- end
508
- @db.sqls.should == [
509
- 'CREATE TABLE test (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
510
- 'CREATE UNIQUE INDEX test_name_index ON test (name)'
511
- ]
464
+
465
+ specify "should reapply settings if dataset_class is chagned" do
466
+ c = Class.new(Sequel::Dataset)
467
+ @db.dataset_class = c
468
+ @db.dataset_class.superclass.should == c
469
+ @db.dataset_class.ancestors.should include(@m)
470
+ @db.dataset.foo.should == [3]
512
471
  end
472
+ end
513
473
 
514
- specify "should create a temporary table" do
515
- @db.create_table :test_tmp, :temp => true do
516
- primary_key :id, :integer, :null => false
517
- column :name, :text
518
- index :name, :unique => true
519
- end
520
-
521
- @db.sqls.should == [
522
- 'CREATE TEMPORARY TABLE test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
523
- 'CREATE UNIQUE INDEX test_tmp_name_index ON test_tmp (name)'
524
- ]
474
+ describe "Database#execute" do
475
+ specify "should raise Sequel::NotImplemented" do
476
+ proc {Sequel::Database.new.execute('blah blah')}.should raise_error(Sequel::NotImplemented)
525
477
  end
526
478
  end
527
479
 
528
- describe "Database#alter_table" do
529
- before do
530
- @db = DummyDatabase.new
531
- end
532
-
533
- specify "should construct proper SQL" do
534
- @db.alter_table :xyz do
535
- add_column :aaa, :text, :null => false, :unique => true
536
- drop_column :bbb
537
- rename_column :ccc, :ddd
538
- set_column_type :eee, :integer
539
- set_column_default :hhh, 'abcd'
540
-
541
- add_index :fff, :unique => true
542
- drop_index :ggg
543
- end
544
-
545
- @db.sqls.should == [
546
- 'ALTER TABLE xyz ADD COLUMN aaa text NOT NULL UNIQUE',
547
- 'ALTER TABLE xyz DROP COLUMN bbb',
548
- 'ALTER TABLE xyz RENAME COLUMN ccc TO ddd',
549
- 'ALTER TABLE xyz ALTER COLUMN eee TYPE integer',
550
- "ALTER TABLE xyz ALTER COLUMN hhh SET DEFAULT 'abcd'",
551
-
552
- 'CREATE UNIQUE INDEX xyz_fff_index ON xyz (fff)',
553
- 'DROP INDEX xyz_ggg_index'
554
- ]
480
+ describe "Database#tables" do
481
+ specify "should raise Sequel::NotImplemented" do
482
+ proc {Sequel::Database.new.tables}.should raise_error(Sequel::NotImplemented)
555
483
  end
556
484
  end
557
485
 
558
- describe "Database#add_column" do
559
- before do
560
- @db = DummyDatabase.new
561
- end
562
-
563
- specify "should construct proper SQL" do
564
- @db.add_column :test, :name, :text, :unique => true
565
- @db.sqls.should == [
566
- 'ALTER TABLE test ADD COLUMN name text UNIQUE'
567
- ]
486
+ describe "Database#views" do
487
+ specify "should raise Sequel::NotImplemented" do
488
+ proc {Sequel::Database.new.views}.should raise_error(Sequel::NotImplemented)
568
489
  end
569
490
  end
570
491
 
571
- describe "Database#drop_column" do
572
- before do
573
- @db = DummyDatabase.new
574
- end
575
-
576
- specify "should construct proper SQL" do
577
- @db.drop_column :test, :name
578
- @db.sqls.should == [
579
- 'ALTER TABLE test DROP COLUMN name'
580
- ]
492
+ describe "Database#indexes" do
493
+ specify "should raise Sequel::NotImplemented" do
494
+ proc {Sequel::Database.new.indexes(:table)}.should raise_error(Sequel::NotImplemented)
581
495
  end
582
496
  end
583
497
 
584
- describe "Database#rename_column" do
585
- before do
586
- @db = DummyDatabase.new
587
- end
588
-
589
- specify "should construct proper SQL" do
590
- @db.rename_column :test, :abc, :def
591
- @db.sqls.should == [
592
- 'ALTER TABLE test RENAME COLUMN abc TO def'
593
- ]
498
+ describe "Database#foreign_key_list" do
499
+ specify "should raise Sequel::NotImplemented" do
500
+ proc {Sequel::Database.new.foreign_key_list(:table)}.should raise_error(Sequel::NotImplemented)
594
501
  end
595
502
  end
596
503
 
597
- describe "Database#set_column_type" do
504
+ describe "Database#run" do
598
505
  before do
599
- @db = DummyDatabase.new
506
+ @db = Sequel.mock(:servers=>{:s1=>{}})
600
507
  end
601
508
 
602
- specify "should construct proper SQL" do
603
- @db.set_column_type :test, :name, :integer
604
- @db.sqls.should == [
605
- 'ALTER TABLE test ALTER COLUMN name TYPE integer'
606
- ]
509
+ specify "should execute the code on the database" do
510
+ @db.run("DELETE FROM items")
511
+ @db.sqls.should == ["DELETE FROM items"]
607
512
  end
608
- end
609
-
610
- describe "Database#set_column_default" do
611
- before do
612
- @db = DummyDatabase.new
513
+
514
+ specify "should return nil" do
515
+ @db.run("DELETE FROM items").should be_nil
613
516
  end
614
517
 
615
- specify "should construct proper SQL" do
616
- @db.set_column_default :test, :name, 'zyx'
617
- @db.sqls.should == [
618
- "ALTER TABLE test ALTER COLUMN name SET DEFAULT 'zyx'"
619
- ]
518
+ specify "should accept options passed to execute_ddl" do
519
+ @db.run("DELETE FROM items", :server=>:s1)
520
+ @db.sqls.should == ["DELETE FROM items -- s1"]
620
521
  end
621
522
  end
622
523
 
623
- describe "Database#add_index" do
524
+ describe "Database#<<" do
624
525
  before do
625
- @db = DummyDatabase.new
526
+ @db = Sequel.mock
626
527
  end
627
-
628
- specify "should construct proper SQL" do
629
- @db.add_index :test, :name, :unique => true
630
- @db.sqls.should == [
631
- 'CREATE UNIQUE INDEX test_name_index ON test (name)'
632
- ]
528
+
529
+ specify "should execute the code on the database" do
530
+ @db << "DELETE FROM items"
531
+ @db.sqls.should == ["DELETE FROM items"]
633
532
  end
634
533
 
635
- specify "should accept multiple columns" do
636
- @db.add_index :test, [:one, :two]
637
- @db.sqls.should == [
638
- 'CREATE INDEX test_one_two_index ON test (one, two)'
639
- ]
534
+ specify "should be chainable" do
535
+ @db << "DELETE FROM items" << "DELETE FROM items2"
536
+ @db.sqls.should == ["DELETE FROM items", "DELETE FROM items2"]
640
537
  end
641
538
  end
642
539
 
643
- describe "Database#drop_index" do
540
+ describe "Database#synchronize" do
644
541
  before do
645
- @db = DummyDatabase.new
542
+ @db = Sequel::Database.new(:max_connections => 1)
543
+ @db.meta_def(:connect){|c| 12345}
646
544
  end
647
545
 
648
- specify "should construct proper SQL" do
649
- @db.drop_index :test, :name
650
- @db.sqls.should == [
651
- 'DROP INDEX test_name_index'
652
- ]
546
+ specify "should wrap the supplied block in pool.hold" do
547
+ q, q1, q2, q3 = Queue.new, Queue.new, Queue.new, Queue.new
548
+ c1, c2 = nil
549
+ t1 = Thread.new{@db.synchronize{|c| c1 = c; q.push nil; q1.pop}; q.push nil}
550
+ q.pop
551
+ c1.should == 12345
552
+ t2 = Thread.new{@db.synchronize{|c| c2 = c; q2.push nil}}
553
+ @db.pool.available_connections.should be_empty
554
+ c2.should be_nil
555
+ q1.push nil
556
+ q.pop
557
+ q2.pop
558
+ c2.should == 12345
559
+ t1.join
560
+ t2.join
653
561
  end
654
-
655
562
  end
656
563
 
657
- class Dummy2Database < Sequel::Database
658
- attr_reader :sql
659
- def execute(sql); @sql = sql; end
660
- def transaction; yield; end
661
- end
662
-
663
- describe "Database#drop_table" do
564
+ describe "Database#test_connection" do
664
565
  before do
665
- @db = DummyDatabase.new
566
+ @db = Sequel::Database.new
567
+ pr = proc{@test = rand(100)}
568
+ @db.meta_def(:connect){|c| pr.call}
666
569
  end
667
570
 
668
- specify "should construct proper SQL" do
669
- @db.drop_table :test
670
- @db.sqls.should == ['DROP TABLE test']
571
+ specify "should attempt to get a connection" do
572
+ @db.test_connection
573
+ @test.should_not be_nil
671
574
  end
672
575
 
673
- specify "should accept multiple table names" do
674
- @db.drop_table :a, :bb, :ccc
675
- @db.sqls.should == [
676
- 'DROP TABLE a',
677
- 'DROP TABLE bb',
678
- 'DROP TABLE ccc'
679
- ]
576
+ specify "should return true if successful" do
577
+ @db.test_connection.should be_true
680
578
  end
681
- end
682
579
 
683
- describe "Database#rename_table" do
684
- before do
685
- @db = DummyDatabase.new
686
- end
687
-
688
- specify "should construct proper SQL" do
689
- @db.rename_table :abc, :xyz
690
- @db.sqls.should == ['ALTER TABLE abc RENAME TO xyz']
580
+ specify "should raise an error if the attempting to connect raises an error" do
581
+ proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
691
582
  end
692
583
  end
693
584
 
694
585
  describe "Database#table_exists?" do
695
586
  specify "should try to select the first record from the table's dataset" do
696
- db2 = DummyDatabase.new
697
- db2.table_exists?(:a).should be_false
698
- db2.table_exists?(:b).should be_true
587
+ db = Sequel.mock(:fetch=>[Sequel::Error, [], [{:a=>1}]])
588
+ db.table_exists?(:a).should be_false
589
+ db.sqls.should == ["SELECT NULL FROM a LIMIT 1"]
590
+ db.table_exists?(:b).should be_true
591
+ db.table_exists?(:c).should be_true
699
592
  end
700
593
  end
701
594
 
702
- class Dummy3Database < Sequel::Database
703
- attr_reader :sql, :transactions
704
- def execute(sql, opts={}); @sql ||= []; @sql << sql; end
705
-
706
- class DummyConnection
707
- def initialize(db); @db = db; end
708
- def execute(sql); @db.execute(sql); end
709
- end
710
- end
711
-
712
- describe "Database#transaction" do
713
- before do
714
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
715
- end
716
-
595
+ shared_examples_for "Database#transaction" do
717
596
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
718
597
  @db.transaction{@db.execute 'DROP TABLE test;'}
719
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
598
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
720
599
  end
721
600
 
722
601
  specify "should support transaction isolation levels" do
@@ -724,7 +603,7 @@ describe "Database#transaction" do
724
603
  [:uncommitted, :committed, :repeatable, :serializable].each do |l|
725
604
  @db.transaction(:isolation=>l){@db.run "DROP TABLE #{l}"}
726
605
  end
727
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
606
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
728
607
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
729
608
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
730
609
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -736,12 +615,24 @@ describe "Database#transaction" do
736
615
  @db.transaction_isolation_level = l
737
616
  @db.transaction{@db.run "DROP TABLE #{l}"}
738
617
  end
739
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
618
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
740
619
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
741
620
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
742
621
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
743
622
  end
744
623
 
624
+ specify "should support :disconnect=>:retry option for automatically retrying on disconnect" do
625
+ a = []
626
+ @db.transaction(:disconnect=>:retry){a << 1; raise Sequel::DatabaseDisconnectError if a.length < 2}
627
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'COMMIT']
628
+ a.should == [1, 1]
629
+ end
630
+
631
+ specify "should raise an error if attempting to use :disconnect=>:retry inside another transaction" do
632
+ proc{@db.transaction{@db.transaction(:disconnect=>:retry){}}}.should raise_error(Sequel::Error)
633
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
634
+ end
635
+
745
636
  specify "should handle returning inside of the block by committing" do
746
637
  def @db.ret_commit
747
638
  transaction do
@@ -751,16 +642,55 @@ describe "Database#transaction" do
751
642
  end
752
643
  end
753
644
  @db.ret_commit
754
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
645
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
755
646
  end
756
647
 
757
648
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
758
649
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
759
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
650
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
760
651
 
761
652
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
762
653
  end
763
654
 
655
+ specify "should handle errors when sending BEGIN" do
656
+ ec = Class.new(StandardError)
657
+ @db.meta_def(:database_error_classes){[ec]}
658
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /BEGIN/ ? raise(ec, 'bad') : super(c, sql)}
659
+ begin
660
+ @db.transaction{@db.execute 'DROP TABLE test;'}
661
+ rescue Sequel::DatabaseError => e
662
+ end
663
+ e.should_not be_nil
664
+ e.wrapped_exception.should be_a_kind_of(ec)
665
+ @db.sqls.should == ['ROLLBACK']
666
+ end
667
+
668
+ specify "should handle errors when sending COMMIT" do
669
+ ec = Class.new(StandardError)
670
+ @db.meta_def(:database_error_classes){[ec]}
671
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /COMMIT/ ? raise(ec, 'bad') : super(c, sql)}
672
+ begin
673
+ @db.transaction{@db.execute 'DROP TABLE test;'}
674
+ rescue Sequel::DatabaseError => e
675
+ end
676
+ e.should_not be_nil
677
+ e.wrapped_exception.should be_a_kind_of(ec)
678
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;']
679
+ end
680
+
681
+ specify "should handle errors when sending ROLLBACK" do
682
+ ec = Class.new(StandardError)
683
+ @db.meta_def(:database_error_classes){[ec]}
684
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /ROLLBACK/ ? raise(ec, 'bad') : super(c, sql)}
685
+ begin
686
+ @db.transaction{raise ArgumentError, 'asdf'}
687
+ rescue Sequel::DatabaseError => e
688
+ end
689
+ e.should_not be_nil
690
+ e.wrapped_exception.should be_a_kind_of(ec)
691
+ @db.sqls.should == ['BEGIN']
692
+ end
693
+
764
694
  specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
765
695
  @db.transaction do
766
696
  @db.drop_table(:a)
@@ -768,7 +698,50 @@ describe "Database#transaction" do
768
698
  @db.drop_table(:b)
769
699
  end
770
700
 
771
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
701
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
702
+ end
703
+
704
+ specify "should have in_transaction? return true if inside a transaction" do
705
+ c = nil
706
+ @db.transaction{c = @db.in_transaction?}
707
+ c.should be_true
708
+ end
709
+
710
+ specify "should have in_transaction? handle sharding correctly" do
711
+ c = []
712
+ @db.transaction(:server=>:test){c << @db.in_transaction?}
713
+ @db.transaction(:server=>:test){c << @db.in_transaction?(:server=>:test)}
714
+ c.should == [false, true]
715
+ end
716
+
717
+ specify "should have in_transaction? return false if not in a transaction" do
718
+ @db.in_transaction?.should be_false
719
+ end
720
+
721
+ specify "should return nil if Sequel::Rollback is called in the transaction" do
722
+ @db.transaction{raise Sequel::Rollback}.should be_nil
723
+ end
724
+
725
+ specify "should reraise Sequel::Rollback errors when using :rollback=>:reraise option is given" do
726
+ proc {@db.transaction(:rollback=>:reraise){raise Sequel::Rollback}}.should raise_error(Sequel::Rollback)
727
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
728
+ proc {@db.transaction(:rollback=>:reraise){raise ArgumentError}}.should raise_error(ArgumentError)
729
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
730
+ @db.transaction(:rollback=>:reraise){1}.should == 1
731
+ @db.sqls.should == ['BEGIN', 'COMMIT']
732
+ end
733
+
734
+ specify "should always rollback if :rollback=>:always option is given" do
735
+ proc {@db.transaction(:rollback=>:always){raise ArgumentError}}.should raise_error(ArgumentError)
736
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
737
+ @db.transaction(:rollback=>:always){raise Sequel::Rollback}.should be_nil
738
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
739
+ @db.transaction(:rollback=>:always){1}.should be_nil
740
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
741
+ catch (:foo) do
742
+ @db.transaction(:rollback=>:always){throw :foo}
743
+ end
744
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
772
745
  end
773
746
 
774
747
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -780,47 +753,242 @@ describe "Database#transaction" do
780
753
  end
781
754
 
782
755
  specify "should be re-entrant" do
783
- stop = false
756
+ q, q1 = Queue.new, Queue.new
784
757
  cc = nil
785
758
  t = Thread.new do
786
759
  @db.transaction {@db.transaction {@db.transaction {|c|
787
760
  cc = c
788
- while !stop; sleep 0.1; end
761
+ q.pop
762
+ q1.push nil
763
+ q.pop
789
764
  }}}
790
765
  end
791
- while cc.nil?; sleep 0.1; end
792
- cc.should be_a_kind_of(Dummy3Database::DummyConnection)
793
- @db.transactions.should == [t]
794
- stop = true
766
+ q.push nil
767
+ q1.pop
768
+ cc.should be_a_kind_of(Sequel::Mock::Connection)
769
+ tr = @db.instance_variable_get(:@transactions)
770
+ tr.keys.should == [cc]
771
+ q.push nil
795
772
  t.join
796
- @db.transactions.should be_empty
773
+ tr.should be_empty
774
+ end
775
+
776
+ specify "should correctly handle nested transacation use with separate shards" do
777
+ @db.transaction do |c1|
778
+ @db.transaction(:server=>:test) do |c2|
779
+ c1.should_not == c2
780
+ @db.execute 'DROP TABLE test;'
781
+ end
782
+ end
783
+ @db.sqls.should == ['BEGIN', 'BEGIN -- test', 'DROP TABLE test;', 'COMMIT -- test', 'COMMIT']
784
+ end
785
+
786
+ if (!defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
787
+ specify "should handle Thread#kill for transactions inside threads" do
788
+ q = Queue.new
789
+ q1 = Queue.new
790
+ t = Thread.new do
791
+ @db.transaction do
792
+ @db.execute 'DROP TABLE test'
793
+ q1.push nil
794
+ q.pop
795
+ @db.execute 'DROP TABLE test2'
796
+ end
797
+ end
798
+ q1.pop
799
+ t.kill
800
+ t.join
801
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
802
+ end
803
+ end
804
+
805
+ specify "should raise an Error if after_commit or after_rollback is called without a block" do
806
+ proc{@db.after_commit}.should raise_error(Sequel::Error)
807
+ proc{@db.after_rollback}.should raise_error(Sequel::Error)
808
+ end
809
+
810
+ specify "should have after_commit and after_rollback respect :server option" do
811
+ @db.transaction(:server=>:test){@db.after_commit(:server=>:test){@db.execute('foo', :server=>:test)}}
812
+ @db.sqls.should == ['BEGIN -- test', 'COMMIT -- test', 'foo -- test']
813
+ @db.transaction(:server=>:test){@db.after_rollback(:server=>:test){@db.execute('foo', :server=>:test)}; raise Sequel::Rollback}
814
+ @db.sqls.should == ['BEGIN -- test', 'ROLLBACK -- test', 'foo -- test']
815
+ end
816
+
817
+ specify "should execute after_commit outside transactions" do
818
+ @db.after_commit{@db.execute('foo')}
819
+ @db.sqls.should == ['foo']
820
+ end
821
+
822
+ specify "should ignore after_rollback outside transactions" do
823
+ @db.after_rollback{@db.execute('foo')}
824
+ @db.sqls.should == []
825
+ end
826
+
827
+ specify "should support after_commit inside transactions" do
828
+ @db.transaction{@db.after_commit{@db.execute('foo')}}
829
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
830
+ end
831
+
832
+ specify "should support after_rollback inside transactions" do
833
+ @db.transaction{@db.after_rollback{@db.execute('foo')}}
834
+ @db.sqls.should == ['BEGIN', 'COMMIT']
835
+ end
836
+
837
+ specify "should not call after_commit if the transaction rolls back" do
838
+ @db.transaction{@db.after_commit{@db.execute('foo')}; raise Sequel::Rollback}
839
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
840
+ end
841
+
842
+ specify "should call after_rollback if the transaction rolls back" do
843
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; raise Sequel::Rollback}
844
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
845
+ end
846
+
847
+ specify "should call multiple after_commit blocks in order if called inside transactions" do
848
+ @db.transaction{@db.after_commit{@db.execute('foo')}; @db.after_commit{@db.execute('bar')}}
849
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo', 'bar']
850
+ end
851
+
852
+ specify "should call multiple after_rollback blocks in order if called inside transactions" do
853
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; @db.after_rollback{@db.execute('bar')}; raise Sequel::Rollback}
854
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo', 'bar']
855
+ end
856
+
857
+ specify "should support after_commit inside nested transactions" do
858
+ @db.transaction{@db.transaction{@db.after_commit{@db.execute('foo')}}}
859
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
860
+ end
861
+
862
+ specify "should support after_rollback inside nested transactions" do
863
+ @db.transaction{@db.transaction{@db.after_rollback{@db.execute('foo')}}; raise Sequel::Rollback}
864
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
865
+ end
866
+
867
+ specify "should raise an error if you attempt to use after_commit inside a prepared transaction" do
868
+ @db.meta_def(:supports_prepared_transactions?){true}
869
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_commit{@db.execute('foo')}}}.should raise_error(Sequel::Error)
870
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
871
+ end
872
+
873
+ specify "should raise an error if you attempt to use after_rollback inside a prepared transaction" do
874
+ @db.meta_def(:supports_prepared_transactions?){true}
875
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_rollback{@db.execute('foo')}}}.should raise_error(Sequel::Error)
876
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
797
877
  end
798
878
  end
799
879
 
800
- describe "Database#transaction with savepoints" do
880
+ describe "Database#transaction with savepoint support" do
801
881
  before do
802
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
882
+ @db = Sequel.mock(:servers=>{:test=>{}})
883
+ end
884
+
885
+ it_should_behave_like "Database#transaction"
886
+
887
+ specify "should support after_commit inside savepoints" do
803
888
  @db.meta_def(:supports_savepoints?){true}
889
+ @db.transaction do
890
+ @db.after_commit{@db.execute('foo')}
891
+ @db.transaction(:savepoint=>true){@db.after_commit{@db.execute('bar')}}
892
+ @db.after_commit{@db.execute('baz')}
893
+ end
894
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar', 'baz']
895
+ end
896
+
897
+ specify "should support after_rollback inside savepoints" do
898
+ @db.meta_def(:supports_savepoints?){true}
899
+ @db.transaction do
900
+ @db.after_rollback{@db.execute('foo')}
901
+ @db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
902
+ @db.after_rollback{@db.execute('baz')}
903
+ raise Sequel::Rollback
904
+ end
905
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
906
+ end
907
+
908
+ specify "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
909
+ @db.meta_def(:supports_savepoints?){true}
910
+ @db.meta_def(:supports_prepared_transactions?){true}
911
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
912
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
913
+ end
914
+
915
+ specify "should raise an error if you attempt to use after_rollback inside a savepoint in a prepared transaction" do
916
+ @db.meta_def(:supports_savepoints?){true}
917
+ @db.meta_def(:supports_prepared_transactions?){true}
918
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
919
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
920
+ end
921
+ end
922
+
923
+ describe "Database#transaction without savepoint support" do
924
+ before do
925
+ @db = Sequel.mock(:servers=>{:test=>{}})
926
+ @db.meta_def(:supports_savepoints?){false}
927
+ end
928
+
929
+ it_should_behave_like "Database#transaction"
930
+ end
931
+
932
+ describe "Sequel.transaction" do
933
+ before do
934
+ @sqls = []
935
+ @db1 = Sequel.mock(:append=>'1', :sqls=>@sqls)
936
+ @db2 = Sequel.mock(:append=>'2', :sqls=>@sqls)
937
+ @db3 = Sequel.mock(:append=>'3', :sqls=>@sqls)
938
+ end
939
+
940
+ specify "should run the block inside transacitons on all three databases" do
941
+ Sequel.transaction([@db1, @db2, @db3]){1}.should == 1
942
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
943
+ end
944
+
945
+ specify "should pass options to all the blocks" do
946
+ Sequel.transaction([@db1, @db2, @db3], :rollback=>:always){1}.should be_nil
947
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
948
+ end
949
+
950
+ specify "should handle Sequel::Rollback exceptions raised by the block to rollback on all databases" do
951
+ Sequel.transaction([@db1, @db2, @db3]){raise Sequel::Rollback}.should be_nil
952
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
953
+ end
954
+
955
+ specify "should handle nested transactions" do
956
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3]){1}}.should == 1
957
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
958
+ end
959
+
960
+ specify "should handle savepoints" do
961
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3], :savepoint=>true){1}}.should == 1
962
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3',
963
+ 'SAVEPOINT autopoint_1 -- 1', 'SAVEPOINT autopoint_1 -- 2', 'SAVEPOINT autopoint_1 -- 3',
964
+ 'RELEASE SAVEPOINT autopoint_1 -- 3', 'RELEASE SAVEPOINT autopoint_1 -- 2', 'RELEASE SAVEPOINT autopoint_1 -- 1',
965
+ 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
966
+ end
967
+ end
968
+
969
+ describe "Database#transaction with savepoints" do
970
+ before do
971
+ @db = Sequel.mock
804
972
  end
805
973
 
806
974
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
807
975
  @db.transaction {@db.execute 'DROP TABLE test;'}
808
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
976
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
809
977
  end
810
978
 
811
979
  specify "should use savepoints if given the :savepoint option" do
812
980
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}}
813
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
981
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
814
982
  end
815
983
 
816
984
  specify "should not use a savepoints if no transaction is in progress" do
817
985
  @db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
818
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
986
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
819
987
  end
820
988
 
821
989
  specify "should reuse the current transaction if no :savepoint option is given" do
822
990
  @db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
823
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
991
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
824
992
  end
825
993
 
826
994
  specify "should handle returning inside of the block by committing" do
@@ -832,7 +1000,7 @@ describe "Database#transaction with savepoints" do
832
1000
  end
833
1001
  end
834
1002
  @db.ret_commit
835
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
1003
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
836
1004
  end
837
1005
 
838
1006
  specify "should handle returning inside of a savepoint by committing" do
@@ -846,34 +1014,34 @@ describe "Database#transaction with savepoints" do
846
1014
  end
847
1015
  end
848
1016
  @db.ret_commit
849
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
1017
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
850
1018
  end
851
1019
 
852
1020
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
853
1021
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
854
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
1022
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
855
1023
 
856
1024
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
857
1025
  end
858
1026
 
859
1027
  specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
860
1028
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test'; raise RuntimeError}} rescue nil
861
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
1029
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
862
1030
 
863
1031
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
864
1032
  end
865
1033
 
866
- specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
1034
+ specify "should issue ROLLBACK if Sequel::Rollback is raised in the transaction" do
867
1035
  @db.transaction do
868
1036
  @db.drop_table(:a)
869
1037
  raise Sequel::Rollback
870
1038
  @db.drop_table(:b)
871
1039
  end
872
1040
 
873
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
1041
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
874
1042
  end
875
1043
 
876
- specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is called in a savepoint" do
1044
+ specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is raised in a savepoint" do
877
1045
  @db.transaction do
878
1046
  @db.transaction(:savepoint=>true) do
879
1047
  @db.drop_table(:a)
@@ -882,7 +1050,7 @@ describe "Database#transaction with savepoints" do
882
1050
  @db.drop_table(:b)
883
1051
  end
884
1052
 
885
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
1053
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
886
1054
  end
887
1055
 
888
1056
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -898,37 +1066,28 @@ end
898
1066
 
899
1067
  describe "A Database adapter with a scheme" do
900
1068
  before do
901
- class CCC < Sequel::Database
902
- if defined?(DISCONNECTS)
903
- DISCONNECTS.clear
904
- else
905
- DISCONNECTS = []
906
- end
907
- set_adapter_scheme :ccc
908
- def disconnect
909
- DISCONNECTS << self
910
- end
911
- end
1069
+ @ccc = Class.new(Sequel::Database)
1070
+ @ccc.send(:set_adapter_scheme, :ccc)
912
1071
  end
913
1072
 
914
1073
  specify "should be registered in the ADAPTER_MAP" do
915
- Sequel::ADAPTER_MAP[:ccc].should == CCC
1074
+ Sequel::ADAPTER_MAP[:ccc].should == @ccc
916
1075
  end
917
1076
 
918
1077
  specify "should give the database_type as the adapter scheme by default" do
919
- CCC.new.database_type.should == :ccc
1078
+ @ccc.new.database_type.should == :ccc
920
1079
  end
921
1080
 
922
1081
  specify "should be instantiated when its scheme is specified" do
923
1082
  c = Sequel::Database.connect('ccc://localhost/db')
924
- c.should be_a_kind_of(CCC)
1083
+ c.should be_a_kind_of(@ccc)
925
1084
  c.opts[:host].should == 'localhost'
926
1085
  c.opts[:database].should == 'db'
927
1086
  end
928
1087
 
929
1088
  specify "should be accessible through Sequel.connect" do
930
1089
  c = Sequel.connect 'ccc://localhost/db'
931
- c.should be_a_kind_of(CCC)
1090
+ c.should be_a_kind_of(@ccc)
932
1091
  c.opts[:host].should == 'localhost'
933
1092
  c.opts[:database].should == 'db'
934
1093
  end
@@ -940,7 +1099,7 @@ describe "A Database adapter with a scheme" do
940
1099
  returnValue = 'anything'
941
1100
 
942
1101
  p = proc do |c|
943
- c.should be_a_kind_of(CCC)
1102
+ c.should be_a_kind_of(@ccc)
944
1103
  c.opts[:host].should == 'localhost'
945
1104
  c.opts[:database].should == 'db'
946
1105
  z = y
@@ -948,15 +1107,22 @@ describe "A Database adapter with a scheme" do
948
1107
  x = c
949
1108
  returnValue
950
1109
  end
1110
+
1111
+ @ccc.class_eval do
1112
+ self::DISCONNECTS = []
1113
+ def disconnect
1114
+ self.class::DISCONNECTS << self
1115
+ end
1116
+ end
951
1117
  Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
952
- CCC::DISCONNECTS.should == [x]
1118
+ @ccc::DISCONNECTS.should == [x]
953
1119
 
954
1120
  Sequel.connect('ccc://localhost/db', &p).should == returnValue
955
- CCC::DISCONNECTS.should == [y, x]
1121
+ @ccc::DISCONNECTS.should == [y, x]
956
1122
 
957
1123
  Sequel.send(:def_adapter_method, :ccc)
958
1124
  Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
959
- CCC::DISCONNECTS.should == [z, y, x]
1125
+ @ccc::DISCONNECTS.should == [z, y, x]
960
1126
  end
961
1127
 
962
1128
  specify "should be accessible through Sequel.<adapter>" do
@@ -966,32 +1132,31 @@ describe "A Database adapter with a scheme" do
966
1132
  proc {Sequel.ccc('abc', 'def')}.should raise_error(Sequel::Error)
967
1133
 
968
1134
  c = Sequel.ccc('mydb')
969
- p = proc{c.opts.delete_if{|k,v| k == :disconnection_proc || k == :single_threaded}}
970
- c.should be_a_kind_of(CCC)
971
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>CCC}
1135
+ c.should be_a_kind_of(@ccc)
1136
+ c.opts.values_at(:adapter, :database, :adapter_class).should == [:ccc, 'mydb', @ccc]
972
1137
 
973
1138
  c = Sequel.ccc('mydb', :host => 'localhost')
974
- c.should be_a_kind_of(CCC)
975
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1139
+ c.should be_a_kind_of(@ccc)
1140
+ c.opts.values_at(:adapter, :database, :host, :adapter_class).should == [:ccc, 'mydb', 'localhost', @ccc]
976
1141
 
977
1142
  c = Sequel.ccc
978
- c.should be_a_kind_of(CCC)
979
- p.call.should == {:adapter=>:ccc, :adapter_class=>CCC}
1143
+ c.should be_a_kind_of(@ccc)
1144
+ c.opts.values_at(:adapter, :adapter_class).should == [:ccc, @ccc]
980
1145
 
981
1146
  c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
982
- c.should be_a_kind_of(CCC)
983
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1147
+ c.should be_a_kind_of(@ccc)
1148
+ c.opts.values_at(:adapter, :database, :host, :adapter_class).should == [:ccc, 'mydb', 'localhost', @ccc]
984
1149
  end
985
1150
 
986
1151
  specify "should be accessible through Sequel.connect with options" do
987
1152
  c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
988
- c.should be_a_kind_of(CCC)
1153
+ c.should be_a_kind_of(@ccc)
989
1154
  c.opts[:adapter].should == :ccc
990
1155
  end
991
1156
 
992
1157
  specify "should be accessible through Sequel.connect with URL parameters" do
993
1158
  c = Sequel.connect 'ccc:///db?host=/tmp&user=test'
994
- c.should be_a_kind_of(CCC)
1159
+ c.should be_a_kind_of(@ccc)
995
1160
  c.opts[:host].should == '/tmp'
996
1161
  c.opts[:database].should == 'db'
997
1162
  c.opts[:user].should == 'test'
@@ -999,14 +1164,14 @@ describe "A Database adapter with a scheme" do
999
1164
 
1000
1165
  specify "should have URL parameters take precedence over fixed URL parts" do
1001
1166
  c = Sequel.connect 'ccc://localhost/db?host=a&database=b'
1002
- c.should be_a_kind_of(CCC)
1167
+ c.should be_a_kind_of(@ccc)
1003
1168
  c.opts[:host].should == 'a'
1004
1169
  c.opts[:database].should == 'b'
1005
1170
  end
1006
1171
 
1007
1172
  specify "should have hash options take predence over URL parameters or parts" do
1008
1173
  c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=>'a', :database=>'b', :user=>'c'
1009
- c.should be_a_kind_of(CCC)
1174
+ c.should be_a_kind_of(@ccc)
1010
1175
  c.opts[:host].should == 'a'
1011
1176
  c.opts[:database].should == 'b'
1012
1177
  c.opts[:user].should == 'c'
@@ -1014,7 +1179,7 @@ describe "A Database adapter with a scheme" do
1014
1179
 
1015
1180
  specify "should unescape values of URL parameters and parts" do
1016
1181
  c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
1017
- c.should be_a_kind_of(CCC)
1182
+ c.should be_a_kind_of(@ccc)
1018
1183
  c.opts[:database].should == 'd[b]'
1019
1184
  c.opts[:host].should == 'domain\\instance'
1020
1185
  end
@@ -1097,7 +1262,8 @@ end
1097
1262
  describe "A single threaded database" do
1098
1263
  before do
1099
1264
  conn = 1234567
1100
- @db = Sequel::Database.new(:single_threaded => true) do
1265
+ @db = Sequel::Database.new(:single_threaded => true)
1266
+ @db.meta_def(:connect) do |c|
1101
1267
  conn += 1
1102
1268
  end
1103
1269
  end
@@ -1129,10 +1295,6 @@ describe "A single threaded database" do
1129
1295
  end
1130
1296
 
1131
1297
  describe "A database" do
1132
- before do
1133
- Sequel::Database.single_threaded = false
1134
- end
1135
-
1136
1298
  after do
1137
1299
  Sequel::Database.single_threaded = false
1138
1300
  end
@@ -1177,11 +1339,7 @@ end
1177
1339
 
1178
1340
  describe "Database#fetch" do
1179
1341
  before do
1180
- @db = Sequel::Database.new
1181
- c = Class.new(Sequel::Dataset) do
1182
- def fetch_rows(sql); yield({:sql => sql}); end
1183
- end
1184
- @db.meta_def(:dataset) {c.new(self)}
1342
+ @db = Sequel.mock(:fetch=>proc{|sql| {:sql => sql}})
1185
1343
  end
1186
1344
 
1187
1345
  specify "should create a dataset and invoke its fetch_rows method with the given sql" do
@@ -1219,7 +1377,7 @@ describe "Database#fetch" do
1219
1377
  ds.select_sql.should == 'select * from xyz'
1220
1378
  ds.sql.should == 'select * from xyz'
1221
1379
 
1222
- ds.filter!(:price.sql_number < 100)
1380
+ ds.filter!{price.sql_number < 100}
1223
1381
  ds.select_sql.should == 'select * from xyz'
1224
1382
  ds.sql.should == 'select * from xyz'
1225
1383
  end
@@ -1228,143 +1386,62 @@ end
1228
1386
 
1229
1387
  describe "Database#[]" do
1230
1388
  before do
1231
- @db = Sequel::Database.new
1389
+ @db = Sequel.mock
1232
1390
  end
1233
1391
 
1234
1392
  specify "should return a dataset when symbols are given" do
1235
1393
  ds = @db[:items]
1236
- ds.class.should == Sequel::Dataset
1394
+ ds.should be_a_kind_of(Sequel::Dataset)
1237
1395
  ds.opts[:from].should == [:items]
1238
1396
  end
1239
1397
 
1240
1398
  specify "should return a dataset when a string is given" do
1241
- c = Class.new(Sequel::Dataset) do
1242
- def fetch_rows(sql); yield({:sql => sql}); end
1243
- end
1244
- @db.meta_def(:dataset) {c.new(self)}
1245
-
1399
+ @db.fetch = proc{|sql| {:sql=>sql}}
1246
1400
  sql = nil
1247
1401
  @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
1248
1402
  sql.should == "select * from xyz where x = 15 and y = 'abc'"
1249
1403
  end
1250
1404
  end
1251
1405
 
1252
- describe "Database#create_view" do
1253
- before do
1254
- @db = DummyDatabase.new
1255
- end
1256
-
1257
- specify "should construct proper SQL with raw SQL" do
1258
- @db.create_view :test, "SELECT * FROM xyz"
1259
- @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1260
- @db.sqls.clear
1261
- @db.create_view :test.identifier, "SELECT * FROM xyz"
1262
- @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1263
- end
1264
-
1265
- specify "should construct proper SQL with dataset" do
1266
- @db.create_view :test, @db[:items].select(:a, :b).order(:c)
1267
- @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1268
- @db.sqls.clear
1269
- @db.create_view :test.qualify(:sch), @db[:items].select(:a, :b).order(:c)
1270
- @db.sqls.should == ['CREATE VIEW sch.test AS SELECT a, b FROM items ORDER BY c']
1271
- end
1272
- end
1273
-
1274
- describe "Database#create_or_replace_view" do
1275
- before do
1276
- @db = DummyDatabase.new
1277
- end
1278
-
1279
- specify "should construct proper SQL with raw SQL" do
1280
- @db.create_or_replace_view :test, "SELECT * FROM xyz"
1281
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT * FROM xyz']
1282
- @db.sqls.clear
1283
- @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1284
- @db.sqls.should == ['CREATE OR REPLACE VIEW sch.test AS SELECT * FROM xyz']
1285
- end
1286
-
1287
- specify "should construct proper SQL with dataset" do
1288
- @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1289
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1290
- @db.sqls.clear
1291
- @db.create_or_replace_view :test.identifier, @db[:items].select(:a, :b).order(:c)
1292
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1293
- end
1294
- end
1295
-
1296
- describe "Database#drop_view" do
1297
- before do
1298
- @db = DummyDatabase.new
1299
- end
1300
-
1301
- specify "should construct proper SQL" do
1302
- @db.drop_view :test
1303
- @db.drop_view :test.identifier
1304
- @db.drop_view :sch__test
1305
- @db.drop_view :test.qualify(:sch)
1306
- @db.sqls.should == ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test', 'DROP VIEW sch.test']
1406
+ describe "Database#inspect" do
1407
+ specify "should include the class name and the connection url" do
1408
+ Sequel.connect('mock://foo/bar').inspect.should == '#<Sequel::Mock::Database: "mock://foo/bar">'
1307
1409
  end
1308
- end
1309
1410
 
1310
- describe "Database#alter_table_sql" do
1311
- before do
1312
- @db = DummyDatabase.new
1313
- end
1314
-
1315
- specify "should raise error for an invalid op" do
1316
- proc {@db.send(:alter_table_sql, :mau, :op => :blah)}.should raise_error(Sequel::Error)
1411
+ specify "should include the class name and the connection options if an options hash was given" do
1412
+ Sequel.connect(:adapter=>:mock).inspect.should =~ /#<Sequel::Mock::Database: \{:adapter=>:mock\}>/
1317
1413
  end
1318
- end
1319
1414
 
1320
- describe "Database#inspect" do
1321
- before do
1322
- @db = DummyDatabase.new
1323
-
1324
- @db.meta_def(:uri) {'blah://blahblah/blah'}
1325
- end
1326
-
1327
- specify "should include the class name and the connection url" do
1328
- @db.inspect.should == '#<DummyDatabase: "blah://blahblah/blah">'
1415
+ specify "should include the class name, uri, and connection options if uri and options hash was given" do
1416
+ Sequel.connect('mock://foo', :database=>'bar').inspect.should =~ /#<Sequel::Mock::Database: "mock:\/\/foo" \{:database=>"bar"\}>/
1329
1417
  end
1330
1418
  end
1331
1419
 
1332
1420
  describe "Database#get" do
1333
1421
  before do
1334
- @c = Class.new(DummyDatabase) do
1335
- def dataset
1336
- ds = super
1337
- def ds.get(*args, &block)
1338
- @db.execute select(*args, &block).sql
1339
- args
1340
- end
1341
- ds
1342
- end
1343
- end
1344
-
1345
- @db = @c.new
1422
+ @db = Sequel.mock(:fetch=>{:a=>1})
1346
1423
  end
1347
1424
 
1348
1425
  specify "should use Dataset#get to get a single value" do
1349
- @db.get(1).should == [1]
1350
- @db.sqls.last.should == 'SELECT 1'
1426
+ @db.get(1).should == 1
1427
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1351
1428
 
1352
- @db.get(:version.sql_function)
1353
- @db.sqls.last.should == 'SELECT version()'
1429
+ @db.get(Sequel.function(:version))
1430
+ @db.sqls.should == ['SELECT version() LIMIT 1']
1354
1431
  end
1355
1432
 
1356
1433
  specify "should accept a block" do
1357
1434
  @db.get{1}
1358
- @db.sqls.last.should == 'SELECT 1'
1435
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1359
1436
 
1360
1437
  @db.get{version(1)}
1361
- @db.sqls.last.should == 'SELECT version(1)'
1438
+ @db.sqls.should == ['SELECT version(1) LIMIT 1']
1362
1439
  end
1363
1440
  end
1364
1441
 
1365
1442
  describe "Database#call" do
1366
1443
  specify "should call the prepared statement with the given name" do
1367
- db = MockDatabase.new
1444
+ db = Sequel.mock(:fetch=>{:id => 1, :x => 1})
1368
1445
  db[:items].prepare(:select, :select_all)
1369
1446
  db.call(:select_all).should == [{:id => 1, :x => 1}]
1370
1447
  db[:items].filter(:n=>:$n).prepare(:select, :select_n)
@@ -1376,146 +1453,134 @@ end
1376
1453
  describe "Database#server_opts" do
1377
1454
  specify "should return the general opts if no :servers option is used" do
1378
1455
  opts = {:host=>1, :database=>2}
1379
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1456
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1380
1457
  end
1381
1458
 
1382
1459
  specify "should return the general opts if entry for the server is present in the :servers option" do
1383
1460
  opts = {:host=>1, :database=>2, :servers=>{}}
1384
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1461
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1385
1462
  end
1386
1463
 
1387
1464
  specify "should return the general opts merged with the specific opts if given as a hash" do
1388
1465
  opts = {:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}}}
1389
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 3
1466
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 3
1390
1467
  end
1391
1468
 
1392
1469
  specify "should return the sgeneral opts merged with the specific opts if given as a proc" do
1393
1470
  opts = {:host=>1, :database=>2, :servers=>{:server1=>proc{|db| {:host=>4}}}}
1394
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 4
1471
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 4
1395
1472
  end
1396
1473
 
1397
1474
  specify "should raise an error if the specific opts is not a proc or hash" do
1398
1475
  opts = {:host=>1, :database=>2, :servers=>{:server1=>2}}
1399
- proc{MockDatabase.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1476
+ proc{Sequel::Database.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1477
+ end
1478
+
1479
+ specify "should return the general opts merged with given opts if given opts is a Hash" do
1480
+ opts = {:host=>1, :database=>2}
1481
+ Sequel::Database.new(opts).send(:server_opts, :host=>2)[:host].should == 2
1400
1482
  end
1401
1483
  end
1402
1484
 
1403
1485
  describe "Database#add_servers" do
1404
1486
  before do
1405
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1406
- def @db.connect(server)
1407
- server_opts(server)
1408
- end
1409
- def @db.disconnect_connection(c)
1410
- end
1487
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1411
1488
  end
1412
1489
 
1413
1490
  specify "should add new servers to the connection pool" do
1414
- @db.synchronize{|c| c[:host].should == 1}
1415
- @db.synchronize(:server1){|c| c[:host].should == 3}
1416
- @db.synchronize(:server2){|c| c[:host].should == 1}
1491
+ @db.synchronize{|c| c.opts[:host].should == 1}
1492
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1493
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1417
1494
 
1418
1495
  @db.add_servers(:server2=>{:host=>6})
1419
- @db.synchronize{|c| c[:host].should == 1}
1420
- @db.synchronize(:server1){|c| c[:host].should == 3}
1421
- @db.synchronize(:server2){|c| c[:host].should == 6}
1496
+ @db.synchronize{|c| c.opts[:host].should == 1}
1497
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1498
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1422
1499
 
1423
1500
  @db.disconnect
1424
- @db.synchronize{|c| c[:host].should == 1}
1425
- @db.synchronize(:server1){|c| c[:host].should == 3}
1426
- @db.synchronize(:server2){|c| c[:host].should == 6}
1501
+ @db.synchronize{|c| c.opts[:host].should == 1}
1502
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1503
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1427
1504
  end
1428
1505
 
1429
1506
  specify "should replace options for future connections to existing servers" do
1430
- @db.synchronize{|c| c[:host].should == 1}
1431
- @db.synchronize(:server1){|c| c[:host].should == 3}
1432
- @db.synchronize(:server2){|c| c[:host].should == 1}
1507
+ @db.synchronize{|c| c.opts[:host].should == 1}
1508
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1509
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1433
1510
 
1434
1511
  @db.add_servers(:default=>proc{{:host=>4}}, :server1=>{:host=>8})
1435
- @db.synchronize{|c| c[:host].should == 1}
1436
- @db.synchronize(:server1){|c| c[:host].should == 3}
1437
- @db.synchronize(:server2){|c| c[:host].should == 1}
1512
+ @db.synchronize{|c| c.opts[:host].should == 1}
1513
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1514
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1438
1515
 
1439
1516
  @db.disconnect
1440
- @db.synchronize{|c| c[:host].should == 4}
1441
- @db.synchronize(:server1){|c| c[:host].should == 8}
1442
- @db.synchronize(:server2){|c| c[:host].should == 4}
1517
+ @db.synchronize{|c| c.opts[:host].should == 4}
1518
+ @db.synchronize(:server1){|c| c.opts[:host].should == 8}
1519
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1443
1520
  end
1444
1521
  end
1445
1522
 
1446
1523
  describe "Database#remove_servers" do
1447
1524
  before do
1448
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1449
- def @db.connect(server)
1450
- server_opts(server)
1451
- end
1452
- def @db.disconnect_connection(c)
1453
- end
1525
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1454
1526
  end
1455
1527
 
1456
1528
  specify "should remove servers from the connection pool" do
1457
- @db.synchronize{|c| c[:host].should == 1}
1458
- @db.synchronize(:server1){|c| c[:host].should == 3}
1459
- @db.synchronize(:server2){|c| c[:host].should == 4}
1529
+ @db.synchronize{|c| c.opts[:host].should == 1}
1530
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1531
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1460
1532
 
1461
1533
  @db.remove_servers(:server1, :server2)
1462
- @db.synchronize{|c| c[:host].should == 1}
1463
- @db.synchronize(:server1){|c| c[:host].should == 1}
1464
- @db.synchronize(:server2){|c| c[:host].should == 1}
1534
+ @db.synchronize{|c| c.opts[:host].should == 1}
1535
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1536
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1465
1537
  end
1466
1538
 
1467
1539
  specify "should accept arrays of symbols" do
1468
1540
  @db.remove_servers([:server1, :server2])
1469
- @db.synchronize{|c| c[:host].should == 1}
1470
- @db.synchronize(:server1){|c| c[:host].should == 1}
1471
- @db.synchronize(:server2){|c| c[:host].should == 1}
1541
+ @db.synchronize{|c| c.opts[:host].should == 1}
1542
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1543
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1472
1544
  end
1473
1545
 
1474
1546
  specify "should allow removal while connections are still open" do
1475
1547
  @db.synchronize do |c1|
1476
- c1[:host].should == 1
1548
+ c1.opts[:host].should == 1
1477
1549
  @db.synchronize(:server1) do |c2|
1478
- c2[:host].should == 3
1550
+ c2.opts[:host].should == 3
1479
1551
  @db.synchronize(:server2) do |c3|
1480
- c3[:host].should == 4
1552
+ c3.opts[:host].should == 4
1481
1553
  @db.remove_servers(:server1, :server2)
1482
1554
  @db.synchronize(:server1) do |c4|
1483
1555
  c4.should_not == c2
1484
1556
  c4.should == c1
1485
- c4[:host].should == 1
1557
+ c4.opts[:host].should == 1
1486
1558
  @db.synchronize(:server2) do |c5|
1487
1559
  c5.should_not == c3
1488
1560
  c5.should == c1
1489
- c5[:host].should == 1
1561
+ c5.opts[:host].should == 1
1490
1562
  end
1491
1563
  end
1492
- c3[:host].should == 4
1564
+ c3.opts[:host].should == 4
1493
1565
  end
1494
- c2[:host].should == 3
1566
+ c2.opts[:host].should == 3
1495
1567
  end
1496
- c1[:host].should == 1
1568
+ c1.opts[:host].should == 1
1497
1569
  end
1498
1570
  end
1499
1571
  end
1500
1572
 
1501
1573
  describe "Database#each_server with do/jdbc adapter connection string without :adapter option" do
1502
- before do
1574
+ specify "should yield a separate database object for each server" do
1503
1575
  klass = Class.new(Sequel::Database)
1504
- klass.should_receive(:adapter_class).once.with(:jdbc).and_return(MockDatabase)
1576
+ klass.should_receive(:adapter_class).once.with(:jdbc).and_return(Sequel::Mock::Database)
1505
1577
  @db = klass.connect('jdbc:blah:', :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1506
- def @db.connect(server)
1507
- server_opts(server)
1508
- end
1509
- def @db.disconnect_connection(c)
1510
- end
1511
- end
1512
1578
 
1513
- specify "should yield a separate database object for each server" do
1514
1579
  hosts = []
1515
1580
  @db.each_server do |db|
1516
1581
  db.should be_a_kind_of(Sequel::Database)
1517
1582
  db.should_not == @db
1518
- db.opts[:adapter_class].should == MockDatabase
1583
+ db.opts[:adapter_class].should == Sequel::Mock::Database
1519
1584
  db.opts[:database].should == 2
1520
1585
  hosts << db.opts[:host]
1521
1586
  end
@@ -1525,12 +1590,7 @@ end
1525
1590
 
1526
1591
  describe "Database#each_server" do
1527
1592
  before do
1528
- @db = Sequel.connect(:adapter=>:mock, :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1529
- def @db.connect(server)
1530
- server_opts(server)
1531
- end
1532
- def @db.disconnect_connection(c)
1533
- end
1593
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1534
1594
  end
1535
1595
 
1536
1596
  specify "should yield a separate database object for each server" do
@@ -1561,21 +1621,25 @@ describe "Database#each_server" do
1561
1621
  end
1562
1622
 
1563
1623
  describe "Database#raise_error" do
1624
+ before do
1625
+ @db = Sequel.mock
1626
+ end
1627
+
1564
1628
  specify "should reraise if the exception class is not in opts[:classes]" do
1565
1629
  e = Class.new(StandardError)
1566
- proc{MockDatabase.new.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1630
+ proc{@db.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1567
1631
  end
1568
1632
 
1569
- specify "should convert the exception to a DatabaseError if the exception class is not in opts[:classes]" do
1570
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1633
+ specify "should convert the exception to a DatabaseError if the exception class is in opts[:classes]" do
1634
+ proc{@db.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1571
1635
  end
1572
1636
 
1573
1637
  specify "should convert the exception to a DatabaseError if opts[:classes] if not present" do
1574
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1638
+ proc{@db.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1575
1639
  end
1576
1640
 
1577
1641
  specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
1578
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1642
+ proc{@db.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1579
1643
  end
1580
1644
  end
1581
1645
 
@@ -1594,6 +1658,305 @@ describe "Database#typecast_value" do
1594
1658
  proc{@db.typecast_value(:datetime, 4)}.should raise_error(Sequel::InvalidValue)
1595
1659
  end
1596
1660
 
1661
+ specify "should handle integers with leading 0 as base 10" do
1662
+ @db.typecast_value(:integer, "013").should == 13
1663
+ @db.typecast_value(:integer, "08").should == 8
1664
+ @db.typecast_value(:integer, "000013").should == 13
1665
+ @db.typecast_value(:integer, "000008").should == 8
1666
+ end
1667
+
1668
+ specify "should handle integers with leading 0x as base 16" do
1669
+ @db.typecast_value(:integer, "0x013").should == 19
1670
+ @db.typecast_value(:integer, "0x80").should == 128
1671
+ end
1672
+
1673
+ specify "should typecast blobs as as Sequel::SQL::Blob" do
1674
+ v = @db.typecast_value(:blob, "0x013")
1675
+ v.should be_a_kind_of(Sequel::SQL::Blob)
1676
+ v.should == Sequel::SQL::Blob.new("0x013")
1677
+ @db.typecast_value(:blob, v).object_id.should == v.object_id
1678
+ end
1679
+
1680
+ specify "should typecast boolean values to true, false, or nil" do
1681
+ @db.typecast_value(:boolean, false).should be_false
1682
+ @db.typecast_value(:boolean, 0).should be_false
1683
+ @db.typecast_value(:boolean, "0").should be_false
1684
+ @db.typecast_value(:boolean, 'f').should be_false
1685
+ @db.typecast_value(:boolean, 'false').should be_false
1686
+ @db.typecast_value(:boolean, true).should be_true
1687
+ @db.typecast_value(:boolean, 1).should be_true
1688
+ @db.typecast_value(:boolean, '1').should be_true
1689
+ @db.typecast_value(:boolean, 't').should be_true
1690
+ @db.typecast_value(:boolean, 'true').should be_true
1691
+ @db.typecast_value(:boolean, '').should be_nil
1692
+ end
1693
+
1694
+ specify "should typecast date values to Date" do
1695
+ @db.typecast_value(:date, Date.today).should == Date.today
1696
+ @db.typecast_value(:date, DateTime.now).should == Date.today
1697
+ @db.typecast_value(:date, Time.now).should == Date.today
1698
+ @db.typecast_value(:date, Date.today.to_s).should == Date.today
1699
+ @db.typecast_value(:date, :year=>Date.today.year, :month=>Date.today.month, :day=>Date.today.day).should == Date.today
1700
+ end
1701
+
1702
+ specify "should have Sequel.application_to_database_timestamp convert to Sequel.database_timezone" do
1703
+ begin
1704
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1705
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1706
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1707
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1708
+ Sequel.application_timezone = :utc
1709
+ Sequel.database_timezone = :local
1710
+ Sequel.application_to_database_timestamp(t).should == t4
1711
+ Sequel.application_timezone = :local
1712
+ Sequel.database_timezone = :utc
1713
+ Sequel.application_to_database_timestamp(t2).should == t3
1714
+ ensure
1715
+ Sequel.default_timezone = nil
1716
+ end
1717
+ end
1718
+
1719
+ specify "should have Database#to_application_timestamp convert values using the database's timezone" do
1720
+ begin
1721
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1722
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1723
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1724
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1725
+ Sequel.default_timezone = :utc
1726
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t
1727
+ Sequel.database_timezone = :local
1728
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1729
+ Sequel.default_timezone = :local
1730
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t2
1731
+ Sequel.database_timezone = :utc
1732
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1733
+
1734
+ Sequel.default_timezone = :utc
1735
+ @db.timezone = :local
1736
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1737
+ Sequel.default_timezone = :local
1738
+ @db.timezone = :utc
1739
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1740
+ ensure
1741
+ Sequel.default_timezone = nil
1742
+ end
1743
+ end
1744
+
1745
+ specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1746
+ t = Time.utc(2011, 1, 2, 3, 4, 5, 500000) # UTC Time
1747
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5, 500000) # Local Time
1748
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5, 500000) - (t - t2) # Local Time in UTC Time
1749
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5, 500000) + (t - t2) # UTC Time in Local Time
1750
+ secs = defined?(Rational) ? Rational(11, 2) : 5.5
1751
+ r1 = defined?(Rational) ? Rational(t2.utc_offset, 86400) : t2.utc_offset/86400.0
1752
+ r2 = defined?(Rational) ? Rational((t - t2).to_i, 86400) : (t - t2).to_i/86400.0
1753
+ dt = DateTime.civil(2011, 1, 2, 3, 4, secs)
1754
+ dt2 = DateTime.civil(2011, 1, 2, 3, 4, secs, r1)
1755
+ dt3 = DateTime.civil(2011, 1, 2, 3, 4, secs) - r2
1756
+ dt4 = DateTime.civil(2011, 1, 2, 3, 4, secs, r1) + r2
1757
+
1758
+ t.should == t4
1759
+ t2.should == t3
1760
+ dt.should == dt4
1761
+ dt2.should == dt3
1762
+
1763
+ check = proc do |i, o|
1764
+ v = @db.typecast_value(:datetime, i)
1765
+ v.should == o
1766
+ if o.is_a?(Time)
1767
+ v.utc_offset.should == o.utc_offset
1768
+ else
1769
+ v.offset.should == o.offset
1770
+ end
1771
+ end
1772
+ @db.extend_datasets(Module.new{def supports_timestamp_timezones?; true; end})
1773
+ begin
1774
+ @db.typecast_value(:datetime, dt).should == t
1775
+ @db.typecast_value(:datetime, dt2).should == t2
1776
+ @db.typecast_value(:datetime, t).should == t
1777
+ @db.typecast_value(:datetime, t2).should == t2
1778
+ @db.typecast_value(:datetime, @db.literal(dt)[1...-1]).should == t
1779
+ @db.typecast_value(:datetime, dt.strftime('%F %T.%N')).should == t2
1780
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == Time.mktime(2011, 1, 2, 0, 0, 0)
1781
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000).should == t2
1782
+
1783
+ Sequel.datetime_class = DateTime
1784
+ @db.typecast_value(:datetime, dt).should == dt
1785
+ @db.typecast_value(:datetime, dt2).should == dt2
1786
+ @db.typecast_value(:datetime, t).should == dt
1787
+ @db.typecast_value(:datetime, t2).should == dt2
1788
+ @db.typecast_value(:datetime, @db.literal(dt)[1...-1]).should == dt
1789
+ @db.typecast_value(:datetime, dt.strftime('%F %T.%N')).should == dt
1790
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == DateTime.civil(2011, 1, 2, 0, 0, 0)
1791
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000).should == dt
1792
+
1793
+ Sequel.application_timezone = :utc
1794
+ Sequel.typecast_timezone = :local
1795
+ Sequel.datetime_class = Time
1796
+ check[dt, t]
1797
+ check[dt2, t3]
1798
+ check[t, t]
1799
+ check[t2, t3]
1800
+ check[@db.literal(dt)[1...-1], t]
1801
+ check[dt.strftime('%F %T.%N'), t3]
1802
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1803
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t3]
1804
+
1805
+ Sequel.datetime_class = DateTime
1806
+ check[dt, dt]
1807
+ check[dt2, dt3]
1808
+ check[t, dt]
1809
+ check[t2, dt3]
1810
+ check[@db.literal(dt)[1...-1], dt]
1811
+ check[dt.strftime('%F %T.%N'), dt3]
1812
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1813
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt3]
1814
+
1815
+ Sequel.typecast_timezone = :utc
1816
+ Sequel.datetime_class = Time
1817
+ check[dt, t]
1818
+ check[dt2, t3]
1819
+ check[t, t]
1820
+ check[t2, t3]
1821
+ check[@db.literal(dt)[1...-1], t]
1822
+ check[dt.strftime('%F %T.%N'), t]
1823
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1824
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t]
1825
+
1826
+ Sequel.datetime_class = DateTime
1827
+ check[dt, dt]
1828
+ check[dt2, dt3]
1829
+ check[t, dt]
1830
+ check[t2, dt3]
1831
+ check[@db.literal(dt)[1...-1], dt]
1832
+ check[dt.strftime('%F %T.%N'), dt]
1833
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1834
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt]
1835
+
1836
+ Sequel.application_timezone = :local
1837
+ Sequel.datetime_class = Time
1838
+ check[dt, t4]
1839
+ check[dt2, t2]
1840
+ check[t, t4]
1841
+ check[t2, t2]
1842
+ check[@db.literal(dt)[1...-1], t4]
1843
+ check[dt.strftime('%F %T.%N'), t4]
1844
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1845
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t4]
1846
+
1847
+ Sequel.datetime_class = DateTime
1848
+ check[dt, dt4]
1849
+ check[dt2, dt2]
1850
+ check[t, dt4]
1851
+ check[t2, dt2]
1852
+ check[@db.literal(dt)[1...-1], dt4]
1853
+ check[dt.strftime('%F %T.%N'), dt4]
1854
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1855
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt4]
1856
+
1857
+ Sequel.typecast_timezone = :local
1858
+ Sequel.datetime_class = Time
1859
+ check[dt, t4]
1860
+ check[dt2, t2]
1861
+ check[t, t4]
1862
+ check[t2, t2]
1863
+ check[@db.literal(dt)[1...-1], t4]
1864
+ check[dt.strftime('%F %T.%N'), t2]
1865
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1866
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t2]
1867
+
1868
+ Sequel.datetime_class = DateTime
1869
+ check[dt, dt4]
1870
+ check[dt2, dt2]
1871
+ check[t, dt4]
1872
+ check[t2, dt2]
1873
+ check[@db.literal(dt)[1...-1], dt4]
1874
+ check[dt.strftime('%F %T.%N'), dt2]
1875
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1876
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt2]
1877
+
1878
+ ensure
1879
+ Sequel.default_timezone = nil
1880
+ Sequel.datetime_class = Time
1881
+ end
1882
+ end
1883
+
1884
+ specify "should handle arrays when typecasting timestamps" do
1885
+ begin
1886
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == Time.local(2011, 10, 11, 12, 13, 14)
1887
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1888
+
1889
+ Sequel.datetime_class = DateTime
1890
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1891
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1892
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000, (defined?(Rational) ? Rational(1, 2) : 0.5)]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1893
+ ensure
1894
+ Sequel.datetime_class = Time
1895
+ end
1896
+ end
1897
+
1898
+ specify "should handle hashes when typecasting timestamps" do
1899
+ begin
1900
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
1901
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1902
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
1903
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1904
+
1905
+ Sequel.datetime_class = DateTime
1906
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1907
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1908
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1909
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1910
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
1911
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1912
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
1913
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1914
+ ensure
1915
+ Sequel.datetime_class = Time
1916
+ end
1917
+ end
1918
+
1919
+ specify "should typecast decimal values to BigDecimal" do
1920
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1921
+ v = @db.typecast_value(:decimal, i)
1922
+ v.should be_a_kind_of(BigDecimal)
1923
+ v.should == BigDecimal.new('1.0')
1924
+ end
1925
+ end
1926
+
1927
+ specify "should typecast float values to Float" do
1928
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1929
+ v = @db.typecast_value(:float, i)
1930
+ v.should be_a_kind_of(Float)
1931
+ v.should == 1.0
1932
+ end
1933
+ end
1934
+
1935
+ specify "should typecast string values to String" do
1936
+ [1.0, '1.0', Sequel.blob('1.0')].each do |i|
1937
+ v = @db.typecast_value(:string, i)
1938
+ v.should be_an_instance_of(String)
1939
+ v.should == "1.0"
1940
+ end
1941
+ end
1942
+
1943
+ specify "should typecast time values to SQLTime" do
1944
+ t = Time.now
1945
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3)
1946
+ [st, Time.utc(t.year, t.month, t.day, 1, 2, 3), Time.local(t.year, t.month, t.day, 1, 2, 3), '01:02:03', {:hour=>1, :minute=>2, :second=>3}].each do |i|
1947
+ v = @db.typecast_value(:time, i)
1948
+ v.should be_an_instance_of(Sequel::SQLTime)
1949
+ v.should == st
1950
+ end
1951
+ end
1952
+
1953
+ specify "should correctly handle time value conversion to SQLTime with fractional seconds" do
1954
+ t = Time.now
1955
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1956
+ t = Time.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1957
+ @db.typecast_value(:time, t).should == st
1958
+ end
1959
+
1597
1960
  specify "should have an underlying exception class available at wrapped_exception" do
1598
1961
  begin
1599
1962
  @db.typecast_value(:date, 'a')
@@ -1639,10 +2002,23 @@ describe "Database#blank_object?" do
1639
2002
  end
1640
2003
 
1641
2004
  describe "Database#schema_autoincrementing_primary_key?" do
1642
- specify "should whether the parsed schema row indicates a primary key" do
2005
+ specify "should indicate whether the parsed schema row indicates a primary key" do
1643
2006
  m = Sequel::Database.new.method(:schema_autoincrementing_primary_key?)
1644
- m.call(:primary_key=>true).should == true
1645
- m.call(:primary_key=>false).should == false
2007
+ m.call(:primary_key=>true, :db_type=>'integer').should == true
2008
+ m.call(:primary_key=>true, :db_type=>'varchar(255)').should == false
2009
+ m.call(:primary_key=>false, :db_type=>'integer').should == false
2010
+ end
2011
+ end
2012
+
2013
+ describe "Database#supports_transactional_ddl?" do
2014
+ specify "should be false by default" do
2015
+ Sequel::Database.new.supports_transactional_ddl?.should == false
2016
+ end
2017
+ end
2018
+
2019
+ describe "Database#global_index_namespace?" do
2020
+ specify "should be true by default" do
2021
+ Sequel::Database.new.global_index_namespace?.should == true
1646
2022
  end
1647
2023
  end
1648
2024
 
@@ -1652,6 +2028,19 @@ describe "Database#supports_savepoints?" do
1652
2028
  end
1653
2029
  end
1654
2030
 
2031
+ describe "Database#supports_savepoints_in_prepared_transactions?" do
2032
+ specify "should be false by default" do
2033
+ Sequel::Database.new.supports_savepoints_in_prepared_transactions?.should == false
2034
+ end
2035
+
2036
+ specify "should be true if both savepoints and prepared transactions are supported" do
2037
+ db = Sequel::Database.new
2038
+ db.meta_def(:supports_savepoints?){true}
2039
+ db.meta_def(:supports_prepared_transactions?){true}
2040
+ db.supports_savepoints_in_prepared_transactions?.should == true
2041
+ end
2042
+ end
2043
+
1655
2044
  describe "Database#supports_prepared_transactions?" do
1656
2045
  specify "should be false by default" do
1657
2046
  Sequel::Database.new.supports_prepared_transactions?.should == false
@@ -1693,15 +2082,18 @@ end
1693
2082
  describe "Database#column_schema_to_ruby_default" do
1694
2083
  specify "should handle converting many default formats" do
1695
2084
  db = Sequel::Database.new
1696
- m = db.method(:column_schema_to_ruby_default)
1697
- p = lambda{|d,t| m.call(d,t)}
1698
- p[nil, :integer].should == nil
2085
+ p = lambda{|d,t| db.send(:column_schema_to_ruby_default, d, t)}
2086
+ p[nil, :integer].should be_nil
2087
+ p[1, :integer].should == 1
1699
2088
  p['1', :integer].should == 1
1700
2089
  p['-1', :integer].should == -1
2090
+ p[1.0, :float].should == 1.0
1701
2091
  p['1.0', :float].should == 1.0
1702
2092
  p['-1.0', :float].should == -1.0
1703
2093
  p['1.0', :decimal].should == BigDecimal.new('1.0')
1704
2094
  p['-1.0', :decimal].should == BigDecimal.new('-1.0')
2095
+ p[true, :boolean].should == true
2096
+ p[false, :boolean].should == false
1705
2097
  p['1', :boolean].should == true
1706
2098
  p['0', :boolean].should == false
1707
2099
  p['true', :boolean].should == true
@@ -1709,33 +2101,40 @@ describe "Database#column_schema_to_ruby_default" do
1709
2101
  p["'t'", :boolean].should == true
1710
2102
  p["'f'", :boolean].should == false
1711
2103
  p["'a'", :string].should == 'a'
1712
- p["'a'", :blob].should == 'a'.to_sequel_blob
2104
+ p["'a'", :blob].should == Sequel.blob('a')
1713
2105
  p["'a'", :blob].should be_a_kind_of(Sequel::SQL::Blob)
1714
2106
  p["''", :string].should == ''
1715
2107
  p["'\\a''b'", :string].should == "\\a'b"
1716
2108
  p["'NULL'", :string].should == "NULL"
2109
+ p[Date.today, :date].should == Date.today
1717
2110
  p["'2009-10-29'", :date].should == Date.new(2009,10,29)
1718
- p["CURRENT_TIMESTAMP", :date].should == nil
1719
- p["today()", :date].should == nil
2111
+ p["CURRENT_TIMESTAMP", :date].should == Sequel::CURRENT_DATE
2112
+ p["CURRENT_DATE", :date].should == Sequel::CURRENT_DATE
2113
+ p["now()", :date].should == Sequel::CURRENT_DATE
2114
+ p["getdate()", :date].should == Sequel::CURRENT_DATE
2115
+ p["CURRENT_TIMESTAMP", :datetime].should == Sequel::CURRENT_TIMESTAMP
2116
+ p["CURRENT_DATE", :datetime].should == Sequel::CURRENT_TIMESTAMP
2117
+ p["now()", :datetime].should == Sequel::CURRENT_TIMESTAMP
2118
+ p["getdate()", :datetime].should == Sequel::CURRENT_TIMESTAMP
1720
2119
  p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
1721
2120
  p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1722
2121
  p["'10:20:30'", :time].should == Time.parse('10:20:30')
1723
- p["NaN", :float].should == nil
2122
+ p["NaN", :float].should be_nil
1724
2123
 
1725
- db.meta_def(:database_type){:postgres}
2124
+ db = Sequel.mock(:host=>'postgres')
1726
2125
  p["''::text", :string].should == ""
1727
2126
  p["'\\a''b'::character varying", :string].should == "\\a'b"
1728
2127
  p["'a'::bpchar", :string].should == "a"
1729
2128
  p["(-1)", :integer].should == -1
1730
2129
  p["(-1.0)", :float].should == -1.0
1731
2130
  p['(-1.0)', :decimal].should == BigDecimal.new('-1.0')
1732
- p["'a'::bytea", :blob].should == 'a'.to_sequel_blob
2131
+ p["'a'::bytea", :blob].should == Sequel.blob('a')
1733
2132
  p["'a'::bytea", :blob].should be_a_kind_of(Sequel::SQL::Blob)
1734
2133
  p["'2009-10-29'::date", :date].should == Date.new(2009,10,29)
1735
2134
  p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
1736
2135
  p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
1737
2136
 
1738
- db.meta_def(:database_type){:mysql}
2137
+ db = Sequel.mock(:host=>'mysql')
1739
2138
  p["\\a'b", :string].should == "\\a'b"
1740
2139
  p["a", :string].should == "a"
1741
2140
  p["NULL", :string].should == "NULL"
@@ -1744,14 +2143,71 @@ describe "Database#column_schema_to_ruby_default" do
1744
2143
  p["2009-10-29", :date].should == Date.new(2009,10,29)
1745
2144
  p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1746
2145
  p["10:20:30", :time].should == Time.parse('10:20:30')
1747
- p["CURRENT_DATE", :date].should == nil
1748
- p["CURRENT_TIMESTAMP", :datetime].should == nil
1749
2146
  p["a", :enum].should == "a"
2147
+ p["a,b", :set].should == "a,b"
1750
2148
 
1751
- db.meta_def(:database_type){:mssql}
2149
+ db = Sequel.mock(:host=>'mssql')
1752
2150
  p["(N'a')", :string].should == "a"
1753
2151
  p["((-12))", :integer].should == -12
1754
2152
  p["((12.1))", :float].should == 12.1
1755
2153
  p["((-12.1))", :decimal].should == BigDecimal.new('-12.1')
1756
2154
  end
1757
2155
  end
2156
+
2157
+ describe "Database extensions" do
2158
+ before(:all) do
2159
+ class << Sequel
2160
+ alias _extension extension
2161
+ def extension(*)
2162
+ end
2163
+ end
2164
+ end
2165
+ after(:all) do
2166
+ class << Sequel
2167
+ alias extension _extension
2168
+ end
2169
+ end
2170
+ before do
2171
+ @db = Sequel.mock
2172
+ end
2173
+
2174
+ specify "should be able to register an extension with a module Database#extension extend the module" do
2175
+ Sequel::Database.register_extension(:foo, Module.new{def a; 1; end})
2176
+ @db.extension(:foo).a.should == 1
2177
+ end
2178
+
2179
+ specify "should be able to register an extension with a block and Database#extension call the block" do
2180
+ @db.quote_identifiers = false
2181
+ Sequel::Database.register_extension(:foo){|db| db.quote_identifiers = true}
2182
+ @db.extension(:foo).quote_identifiers?.should be_true
2183
+ end
2184
+
2185
+ specify "should be able to register an extension with a callable and Database#extension call the callable" do
2186
+ @db.quote_identifiers = false
2187
+ Sequel::Database.register_extension(:foo, proc{|db| db.quote_identifiers = true})
2188
+ @db.extension(:foo).quote_identifiers?.should be_true
2189
+ end
2190
+
2191
+ specify "should be able to load multiple extensions in the same call" do
2192
+ @db.quote_identifiers = false
2193
+ @db.identifier_input_method = :downcase
2194
+ Sequel::Database.register_extension(:foo, proc{|db| db.quote_identifiers = true})
2195
+ Sequel::Database.register_extension(:bar, proc{|db| db.identifier_input_method = nil})
2196
+ @db.extension(:foo, :bar)
2197
+ @db.quote_identifiers?.should be_true
2198
+ @db.identifier_input_method.should be_nil
2199
+ end
2200
+
2201
+ specify "should return the receiver" do
2202
+ Sequel::Database.register_extension(:foo, Module.new{def a; 1; end})
2203
+ @db.extension(:foo).should equal(@db)
2204
+ end
2205
+
2206
+ specify "should raise an Error if registering with both a module and a block" do
2207
+ proc{Sequel::Database.register_extension(:foo, Module.new){}}.should raise_error(Sequel::Error)
2208
+ end
2209
+
2210
+ specify "should raise an Error if attempting to load an incompatible extension" do
2211
+ proc{@db.extension(:foo2)}.should raise_error(Sequel::Error)
2212
+ end
2213
+ end