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