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
@@ -60,12 +60,6 @@ describe "Dataset" do
60
60
  db = Sequel::Database.new(:identifier_output_method=>:downcase)
61
61
  db[:a].identifier_output_method.should == :downcase
62
62
  end
63
- end
64
-
65
- describe "Dataset" do
66
- before do
67
- @dataset = Sequel::Dataset.new("db")
68
- end
69
63
 
70
64
  specify "should have quote_identifiers= method which changes literalization of identifiers" do
71
65
  @dataset.quote_identifiers = true
@@ -111,27 +105,25 @@ describe "Dataset#clone" do
111
105
 
112
106
  specify "should create an exact copy of the dataset" do
113
107
  @dataset.row_proc = Proc.new{|r| r}
114
- @clone = @dataset.clone
108
+ clone = @dataset.clone
115
109
 
116
- @clone.object_id.should_not === @dataset.object_id
117
- @clone.class.should == @dataset.class
118
- @clone.opts.should == @dataset.opts
119
- @clone.row_proc.should == @dataset.row_proc
110
+ clone.object_id.should_not === @dataset.object_id
111
+ clone.class.should == @dataset.class
112
+ clone.db.should == @dataset.db
113
+ clone.opts.should == @dataset.opts
114
+ clone.row_proc.should == @dataset.row_proc
120
115
  end
121
116
 
122
117
  specify "should deep-copy the dataset opts" do
123
- @clone = @dataset.clone
118
+ clone = @dataset.clone
124
119
 
125
- @clone.opts.should_not equal(@dataset.opts)
120
+ clone.opts.should_not equal(@dataset.opts)
126
121
  @dataset.filter!(:a => 'b')
127
- @clone.opts[:filter].should be_nil
128
- end
129
-
130
- specify "should return a clone self" do
131
- clone = @dataset.clone({})
132
- clone.class.should == @dataset.class
133
- clone.db.should == @dataset.db
134
- clone.opts.should == @dataset.opts
122
+ clone.opts[:filter].should be_nil
123
+
124
+ clone = @dataset.clone(:from => [:other])
125
+ @dataset.opts[:from].should == [:items]
126
+ clone.opts[:from].should == [:other]
135
127
  end
136
128
 
137
129
  specify "should merge the specified options" do
@@ -144,12 +136,6 @@ describe "Dataset#clone" do
144
136
  clone.opts.should == {:from => [:other]}
145
137
  end
146
138
 
147
- specify "should create a clone with a deep copy of options" do
148
- clone = @dataset.clone(:from => [:other])
149
- @dataset.opts[:from].should == [:items]
150
- clone.opts[:from].should == [:other]
151
- end
152
-
153
139
  specify "should return an object with the same modules included" do
154
140
  m = Module.new do
155
141
  def __xyz__; "xyz"; end
@@ -161,7 +147,7 @@ end
161
147
 
162
148
  describe "Dataset#==" do
163
149
  before do
164
- @db = MockDatabase.new
150
+ @db = Sequel.mock
165
151
  @h = {}
166
152
  end
167
153
 
@@ -170,7 +156,7 @@ describe "Dataset#==" do
170
156
  end
171
157
 
172
158
  specify "should be different for datasets with different dbs" do
173
- @db[:t].should_not == MockDatabase.new[:t]
159
+ @db[:t].should_not == Sequel.mock[:t]
174
160
  end
175
161
 
176
162
  specify "should be different for datasets with different opts" do
@@ -186,7 +172,7 @@ end
186
172
 
187
173
  describe "Dataset#hash" do
188
174
  before do
189
- @db = MockDatabase.new
175
+ @db = Sequel.mock
190
176
  @h = {}
191
177
  end
192
178
 
@@ -197,7 +183,7 @@ describe "Dataset#hash" do
197
183
  end
198
184
 
199
185
  specify "should be different for datasets with different dbs" do
200
- @db[:t].hash.should_not == MockDatabase.new[:t].hash
186
+ @db[:t].hash.should_not == Sequel.mock[:t].hash
201
187
  end
202
188
 
203
189
  specify "should be different for datasets with different opts" do
@@ -228,10 +214,21 @@ describe "A simple dataset" do
228
214
  @dataset.truncate_sql.should == 'TRUNCATE TABLE test'
229
215
  end
230
216
 
217
+ specify "should format a truncate statement with multiple tables if supported" do
218
+ @dataset.meta_def(:check_truncation_allowed!){}
219
+ @dataset.from(:test, :test2).truncate_sql.should == 'TRUNCATE TABLE test, test2'
220
+ end
221
+
231
222
  specify "should format an insert statement with default values" do
232
223
  @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
233
224
  end
234
225
 
226
+ specify "should use a single column with a default value when the dataset doesn't support using insert statement with default values" do
227
+ @dataset.meta_def(:insert_supports_empty_values?){false}
228
+ @dataset.meta_def(:columns){[:a, :b]}
229
+ @dataset.insert_sql.should == 'INSERT INTO test (b) VALUES (DEFAULT)'
230
+ end
231
+
235
232
  specify "should format an insert statement with hash" do
236
233
  @dataset.insert_sql(:name => 'wxyz', :price => 342).
237
234
  should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
@@ -244,46 +241,29 @@ describe "A simple dataset" do
244
241
  should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
245
242
  end
246
243
 
247
- specify "should format an insert statement with an object that respond_to? :values" do
248
- dbb = Sequel::Database.new
249
-
250
- v = Object.new
251
- def v.values; {:a => 1}; end
252
-
253
- @dataset.insert_sql(v).should == "INSERT INTO test (a) VALUES (1)"
254
-
255
- def v.values; {}; end
256
- @dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
257
- end
258
-
259
244
  specify "should format an insert statement with an arbitrary value" do
260
245
  @dataset.insert_sql(123).should == "INSERT INTO test VALUES (123)"
261
246
  end
262
247
 
263
248
  specify "should format an insert statement with sub-query" do
264
- @sub = Sequel::Dataset.new(nil).from(:something).filter(:x => 2)
265
- @dataset.insert_sql(@sub).should == \
266
- "INSERT INTO test SELECT * FROM something WHERE (x = 2)"
249
+ @dataset.insert_sql(@dataset.from(:something).filter(:x => 2)).should == "INSERT INTO test SELECT * FROM something WHERE (x = 2)"
267
250
  end
268
251
 
269
252
  specify "should format an insert statement with array" do
270
- @dataset.insert_sql('a', 2, 6.5).should ==
271
- "INSERT INTO test VALUES ('a', 2, 6.5)"
253
+ @dataset.insert_sql('a', 2, 6.5).should == "INSERT INTO test VALUES ('a', 2, 6.5)"
272
254
  end
273
255
 
274
256
  specify "should format an update statement" do
275
- @dataset.update_sql(:name => 'abc').should ==
276
- "UPDATE test SET name = 'abc'"
257
+ @dataset.update_sql(:name => 'abc').should == "UPDATE test SET name = 'abc'"
277
258
  end
278
259
 
279
260
  specify "should be able to return rows for arbitrary SQL" do
280
- @dataset.clone(:sql => 'xxx yyy zzz').select_sql.should ==
281
- "xxx yyy zzz"
261
+ @dataset.clone(:sql => 'xxx yyy zzz').select_sql.should == "xxx yyy zzz"
282
262
  end
283
263
 
284
264
  specify "should use the :sql option for all sql methods" do
285
265
  sql = "X"
286
- ds = Sequel::Dataset.new(nil, :sql=>sql)
266
+ ds = @dataset.clone(:sql=>sql)
287
267
  ds.sql.should == sql
288
268
  ds.select_sql.should == sql
289
269
  ds.insert_sql.should == sql
@@ -338,37 +318,37 @@ describe "Dataset#unused_table_alias" do
338
318
  @ds.unused_table_alias('test').should == :test_0
339
319
  @ds.unused_table_alias(:b__t___test).should == :test_0
340
320
  @ds.unused_table_alias(:b__test).should == :test_0
341
- @ds.unused_table_alias(:test.qualify(:b)).should == :test_0
342
- @ds.unused_table_alias(:b.as(:test)).should == :test_0
343
- @ds.unused_table_alias(:b.as(:test.identifier)).should == :test_0
344
- @ds.unused_table_alias(:b.as('test')).should == :test_0
345
- @ds.unused_table_alias(:test.identifier).should == :test_0
321
+ @ds.unused_table_alias(Sequel.qualify(:b, :test)).should == :test_0
322
+ @ds.unused_table_alias(Sequel.expr(:b).as(:test)).should == :test_0
323
+ @ds.unused_table_alias(Sequel.expr(:b).as(Sequel.identifier(:test))).should == :test_0
324
+ @ds.unused_table_alias(Sequel.expr(:b).as('test')).should == :test_0
325
+ @ds.unused_table_alias(Sequel.identifier(:test)).should == :test_0
346
326
  end
347
327
  end
348
328
 
349
329
  describe "Dataset#exists" do
350
330
  before do
351
- @ds1 = Sequel::Dataset.new(nil).from(:test)
352
- @ds2 = @ds1.filter(:price.sql_number < 100)
353
- @ds3 = @ds1.filter(:price.sql_number > 50)
331
+ @ds1 = Sequel.mock[:test]
332
+ @ds2 = @ds1.filter(Sequel.expr(:price) < 100)
333
+ @ds3 = @ds1.filter(Sequel.expr(:price) > 50)
354
334
  end
355
335
 
356
336
  specify "should work in filters" do
357
337
  @ds1.filter(@ds2.exists).sql.should ==
358
338
  'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
359
339
  @ds1.filter(@ds2.exists & @ds3.exists).sql.should ==
360
- 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)) AND EXISTS (SELECT * FROM test WHERE (price > 50)))'
340
+ 'SELECT * FROM test WHERE ((EXISTS (SELECT * FROM test WHERE (price < 100))) AND (EXISTS (SELECT * FROM test WHERE (price > 50))))'
361
341
  end
362
342
 
363
343
  specify "should work in select" do
364
344
  @ds1.select(@ds2.exists.as(:a), @ds3.exists.as(:b)).sql.should ==
365
- 'SELECT EXISTS (SELECT * FROM test WHERE (price < 100)) AS a, EXISTS (SELECT * FROM test WHERE (price > 50)) AS b FROM test'
345
+ 'SELECT (EXISTS (SELECT * FROM test WHERE (price < 100))) AS a, (EXISTS (SELECT * FROM test WHERE (price > 50))) AS b FROM test'
366
346
  end
367
347
  end
368
348
 
369
349
  describe "Dataset#where" do
370
350
  before do
371
- @dataset = Sequel::Dataset.new(nil).from(:test)
351
+ @dataset = Sequel.mock[:test]
372
352
  @d1 = @dataset.where(:region => 'Asia')
373
353
  @d2 = @dataset.where('region = ?', 'Asia')
374
354
  @d3 = @dataset.where("a = 1")
@@ -390,8 +370,7 @@ describe "Dataset#where" do
390
370
  end
391
371
 
392
372
  specify "should work with a string with placeholders and arguments for those placeholders" do
393
- @dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.should ==
394
- "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
373
+ @dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.should == "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
395
374
  end
396
375
 
397
376
  specify "should not modify passed array with placeholders" do
@@ -418,14 +397,12 @@ describe "Dataset#where" do
418
397
  b.should == a
419
398
  end
420
399
 
421
- specify "should not replace named placeholders that don't existin in the hash" do
422
- @dataset.where('price < :price AND id in :ids', :price=>100).select_sql.should ==
423
- "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
400
+ specify "should not replace named placeholders that don't exist in the hash" do
401
+ @dataset.where('price < :price AND id in :ids', :price=>100).select_sql.should == "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
424
402
  end
425
403
 
426
404
  specify "should handle partial names" do
427
- @dataset.where('price < :price AND id = :p', :p=>2, :price=>100).select_sql.should ==
428
- "SELECT * FROM test WHERE (price < 100 AND id = 2)"
405
+ @dataset.where('price < :price AND id = :p', :p=>2, :price=>100).select_sql.should == "SELECT * FROM test WHERE (price < 100 AND id = 2)"
429
406
  end
430
407
 
431
408
  specify "should affect select, delete and update statements" do
@@ -445,180 +422,182 @@ describe "Dataset#where" do
445
422
 
446
423
  specify "should be composable using AND operator (for scoping)" do
447
424
  # hashes are merged, no problem
448
- @d1.where(:size => 'big').select_sql.should ==
449
- "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
425
+ @d1.where(:size => 'big').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
450
426
 
451
427
  # hash and string
452
- @d1.where('population > 1000').select_sql.should ==
453
- "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
454
- @d1.where('(a > 1) OR (b < 2)').select_sql.should ==
455
- "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
428
+ @d1.where('population > 1000').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
429
+ @d1.where('(a > 1) OR (b < 2)').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
456
430
 
457
431
  # hash and array
458
- @d1.where('GDP > ?', 1000).select_sql.should ==
459
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
432
+ @d1.where('GDP > ?', 1000).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
460
433
 
461
434
  # array and array
462
- @d2.where('GDP > ?', 1000).select_sql.should ==
463
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
435
+ @d2.where('GDP > ?', 1000).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
464
436
 
465
437
  # array and hash
466
- @d2.where(:name => ['Japan', 'China']).select_sql.should ==
467
- "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
438
+ @d2.where(:name => ['Japan', 'China']).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
468
439
 
469
440
  # array and string
470
- @d2.where('GDP > ?').select_sql.should ==
471
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
441
+ @d2.where('GDP > ?').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
472
442
 
473
443
  # string and string
474
- @d3.where('b = 2').select_sql.should ==
475
- "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
444
+ @d3.where('b = 2').select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
476
445
 
477
446
  # string and hash
478
- @d3.where(:c => 3).select_sql.should ==
479
- "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
447
+ @d3.where(:c => 3).select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
480
448
 
481
449
  # string and array
482
- @d3.where('d = ?', 4).select_sql.should ==
483
- "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
450
+ @d3.where('d = ?', 4).select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
484
451
  end
485
452
 
486
453
  specify "should be composable using AND operator (for scoping) with block" do
487
- @d3.where{:e.sql_number < 5}.select_sql.should ==
488
- "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
454
+ @d3.where{e < 5}.select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
489
455
  end
490
456
 
491
457
  specify "should accept ranges" do
492
- @dataset.filter(:id => 4..7).sql.should ==
493
- 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
494
- @dataset.filter(:id => 4...7).sql.should ==
495
- 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
458
+ @dataset.filter(:id => 4..7).sql.should == 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
459
+ @dataset.filter(:id => 4...7).sql.should == 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
496
460
 
497
- @dataset.filter(:table__id => 4..7).sql.should ==
498
- 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id <= 7))'
499
- @dataset.filter(:table__id => 4...7).sql.should ==
500
- 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id < 7))'
461
+ @dataset.filter(:table__id => 4..7).sql.should == 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id <= 7))'
462
+ @dataset.filter(:table__id => 4...7).sql.should == 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id < 7))'
501
463
  end
502
464
 
503
465
  specify "should accept nil" do
504
- @dataset.filter(:owner_id => nil).sql.should ==
505
- 'SELECT * FROM test WHERE (owner_id IS NULL)'
466
+ @dataset.filter(:owner_id => nil).sql.should == 'SELECT * FROM test WHERE (owner_id IS NULL)'
506
467
  end
507
468
 
508
469
  specify "should accept a subquery" do
509
- @dataset.filter('gdp > ?', @d1.select(:avg.sql_function(:gdp))).sql.should ==
510
- "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
470
+ @dataset.filter('gdp > ?', @d1.select(Sequel.function(:avg, :gdp))).sql.should == "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
511
471
  end
512
472
 
473
+ specify "should handle all types of IN/NOT IN queries with empty arrays" do
474
+ @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (id != id)"
475
+ @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
476
+ @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (id = id)"
477
+ @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
478
+ end
479
+
480
+ specify "should handle all types of IN/NOT IN queries with empty arrays" do
481
+ begin
482
+ Sequel.empty_array_handle_nulls = false
483
+ @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 0)"
484
+ @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 0)"
485
+ @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
486
+ @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
487
+ ensure
488
+ Sequel.empty_array_handle_nulls = true
489
+ end
490
+ end
491
+
513
492
  specify "should handle all types of IN/NOT IN queries" do
514
493
  @dataset.filter(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
515
- @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (id != id)"
516
494
  @dataset.filter(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id IN (1, 2))"
517
495
  @dataset.filter([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
518
- @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
519
- @dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
496
+ @dataset.filter([:id1, :id2] => Sequel.value_list([[1, 2], [3,4]])).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
520
497
  @dataset.filter([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
521
498
 
522
499
  @dataset.exclude(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id NOT IN (SELECT id FROM test WHERE (region = 'Asia')))"
523
- @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
524
500
  @dataset.exclude(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id NOT IN (1, 2))"
525
501
  @dataset.exclude([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
526
- @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
527
- @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
502
+ @dataset.exclude([:id1, :id2] => Sequel.value_list([[1, 2], [3,4]])).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
528
503
  @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
529
504
  end
530
505
 
531
506
  specify "should handle IN/NOT IN queries with multiple columns and an array where the database doesn't support it" do
532
507
  @dataset.meta_def(:supports_multiple_column_in?){false}
533
- @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
534
- @dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
535
- @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
536
- @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
508
+ @dataset.filter([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
509
+ @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
510
+ @dataset.filter([:id1, :id2] => Sequel.value_list([[1, 2], [3,4]])).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
511
+ @dataset.exclude([:id1, :id2] => Sequel.value_list([[1, 2], [3,4]])).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
537
512
  end
538
513
 
539
514
  specify "should handle IN/NOT IN queries with multiple columns and a dataset where the database doesn't support it" do
540
515
  @dataset.meta_def(:supports_multiple_column_in?){false}
541
- d1 = @d1.select(:id1, :id2)
542
- def d1.fetch_rows(sql)
543
- @sql_used = sql
544
- @columns = [:id1, :id2]
545
- yield(:id1=>1, :id2=>2)
546
- yield(:id1=>3, :id2=>4)
547
- end
548
- d1.instance_variable_get(:@sql_used).should == nil
516
+ db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
517
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
549
518
  @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
550
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
551
- d1.instance_variable_set(:@sql_used, nil)
519
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
552
520
  @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
553
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
521
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
554
522
  end
555
523
 
556
524
  specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it" do
557
525
  @dataset.meta_def(:supports_multiple_column_in?){false}
558
- d1 = @d1.select(:id1, :id2)
559
- def d1.fetch_rows(sql)
560
- @sql_used = sql
561
- @columns = [:id1, :id2]
562
- end
563
- d1.instance_variable_get(:@sql_used).should == nil
526
+ db = Sequel.mock
527
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
564
528
  @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
565
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
566
- d1.instance_variable_set(:@sql_used, nil)
567
- @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
568
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
529
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
530
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
531
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
532
+ end
533
+
534
+ specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it with correct NULL handling" do
535
+ begin
536
+ Sequel.empty_array_handle_nulls = false
537
+ @dataset.meta_def(:supports_multiple_column_in?){false}
538
+ db = Sequel.mock
539
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
540
+ @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 0)"
541
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
542
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
543
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
544
+ ensure
545
+ Sequel.empty_array_handle_nulls = true
546
+ end
547
+ end
548
+
549
+ specify "should handle IN/NOT IN queries for datasets with row_procs" do
550
+ @dataset.meta_def(:supports_multiple_column_in?){false}
551
+ db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
552
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
553
+ d1.row_proc = proc{|h| Object.new}
554
+ @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
555
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
556
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
557
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
569
558
  end
570
559
 
571
560
  specify "should accept a subquery for an EXISTS clause" do
572
- a = @dataset.filter(:price.sql_number < 100)
573
- @dataset.filter(a.exists).sql.should ==
574
- 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
561
+ a = @dataset.filter(Sequel.expr(:price) < 100)
562
+ @dataset.filter(a.exists).sql.should == 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
575
563
  end
576
564
 
577
565
  specify "should accept proc expressions" do
578
- d = @d1.select(:avg.sql_function(:gdp))
579
- @dataset.filter {:gdp.sql_number > d}.sql.should ==
580
- "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
581
-
582
- @dataset.filter {:a.sql_number < 1}.sql.should ==
583
- 'SELECT * FROM test WHERE (a < 1)'
584
-
585
- @dataset.filter {(:a.sql_number >= 1) & (:b.sql_number <= 2)}.sql.should ==
586
- 'SELECT * FROM test WHERE ((a >= 1) AND (b <= 2))'
587
-
588
- @dataset.filter {:c.like 'ABC%'}.sql.should ==
589
- "SELECT * FROM test WHERE (c LIKE 'ABC%')"
590
-
591
- @dataset.filter {:c.like 'ABC%'}.sql.should ==
592
- "SELECT * FROM test WHERE (c LIKE 'ABC%')"
593
-
594
- @dataset.filter {:c.like 'ABC%', '%XYZ'}.sql.should ==
595
- "SELECT * FROM test WHERE ((c LIKE 'ABC%') OR (c LIKE '%XYZ'))"
566
+ d = @d1.select(Sequel.function(:avg, :gdp))
567
+ @dataset.filter{gdp > d}.sql.should == "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
568
+ @dataset.filter{a < 1}.sql.should == 'SELECT * FROM test WHERE (a < 1)'
569
+ @dataset.filter{(a >= 1) & (b <= 2)}.sql.should == 'SELECT * FROM test WHERE ((a >= 1) AND (b <= 2))'
570
+ @dataset.filter{c.like 'ABC%'}.sql.should == "SELECT * FROM test WHERE (c LIKE 'ABC%')"
571
+ @dataset.filter{c.like 'ABC%', '%XYZ'}.sql.should == "SELECT * FROM test WHERE ((c LIKE 'ABC%') OR (c LIKE '%XYZ'))"
596
572
  end
597
573
 
598
574
  specify "should work for grouped datasets" do
599
- @dataset.group(:a).filter(:b => 1).sql.should ==
600
- 'SELECT * FROM test WHERE (b = 1) GROUP BY a'
575
+ @dataset.group(:a).filter(:b => 1).sql.should == 'SELECT * FROM test WHERE (b = 1) GROUP BY a'
601
576
  end
602
577
 
603
578
  specify "should accept true and false as arguments" do
604
- @dataset.filter(true).sql.should ==
605
- "SELECT * FROM test WHERE 't'"
606
- @dataset.filter(false).sql.should ==
607
- "SELECT * FROM test WHERE 'f'"
579
+ @dataset.filter(true).sql.should == "SELECT * FROM test WHERE 't'"
580
+ @dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE 't'"
581
+ @dataset.filter(false).sql.should == "SELECT * FROM test WHERE 'f'"
582
+ @dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE 'f'"
583
+ end
584
+
585
+ specify "should use boolean expression if dataset does not support where true/false" do
586
+ def @dataset.supports_where_true?() false end
587
+ @dataset.filter(true).sql.should == "SELECT * FROM test WHERE (1 = 1)"
588
+ @dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE (1 = 1)"
589
+ @dataset.filter(false).sql.should == "SELECT * FROM test WHERE (1 = 0)"
590
+ @dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE (1 = 0)"
608
591
  end
609
592
 
610
593
  specify "should allow the use of multiple arguments" do
611
- @dataset.filter(:a, :b).sql.should ==
612
- 'SELECT * FROM test WHERE (a AND b)'
613
- @dataset.filter(:a, :b=>1).sql.should ==
614
- 'SELECT * FROM test WHERE (a AND (b = 1))'
615
- @dataset.filter(:a, :c.sql_number > 3, :b=>1).sql.should ==
616
- 'SELECT * FROM test WHERE (a AND (c > 3) AND (b = 1))'
594
+ @dataset.filter(:a, :b).sql.should == 'SELECT * FROM test WHERE (a AND b)'
595
+ @dataset.filter(:a, :b=>1).sql.should == 'SELECT * FROM test WHERE (a AND (b = 1))'
596
+ @dataset.filter(:a, Sequel.expr(:c) > 3, :b=>1).sql.should == 'SELECT * FROM test WHERE (a AND (c > 3) AND (b = 1))'
617
597
  end
618
598
 
619
599
  specify "should allow the use of blocks and arguments simultaneously" do
620
- @dataset.filter(:zz.sql_number < 3){:yy.sql_number > 3}.sql.should ==
621
- 'SELECT * FROM test WHERE ((zz < 3) AND (yy > 3))'
600
+ @dataset.filter(Sequel.expr(:zz) < 3){yy > 3}.sql.should == 'SELECT * FROM test WHERE ((zz < 3) AND (yy > 3))'
622
601
  end
623
602
 
624
603
  specify "should yield a VirtualRow to the block" do
@@ -642,8 +621,8 @@ describe "Dataset#where" do
642
621
  end
643
622
 
644
623
  specify "should raise an error if a NumericExpression or StringExpression is used" do
645
- proc{@dataset.filter(:x + 1)}.should raise_error(Sequel::Error)
646
- proc{@dataset.filter(:x.sql_string)}.should raise_error(Sequel::Error)
624
+ proc{@dataset.filter(Sequel.expr(:x) + 1)}.should raise_error(Sequel::Error)
625
+ proc{@dataset.filter(Sequel.expr(:x).sql_string)}.should raise_error(Sequel::Error)
647
626
  end
648
627
  end
649
628
 
@@ -658,33 +637,25 @@ describe "Dataset#or" do
658
637
  end
659
638
 
660
639
  specify "should add an alternative expression to the where clause" do
661
- @d1.or(:y => 2).sql.should ==
662
- 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
640
+ @d1.or(:y => 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
663
641
  end
664
642
 
665
643
  specify "should accept all forms of filters" do
666
- @d1.or('y > ?', 2).sql.should ==
667
- 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
668
- @d1.or(:yy.sql_number > 3).sql.should ==
669
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
644
+ @d1.or('y > ?', 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
645
+ @d1.or(Sequel.expr(:yy) > 3).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
670
646
  end
671
647
 
672
648
  specify "should accept blocks passed to filter" do
673
- @d1.or{:yy.sql_number > 3}.sql.should ==
674
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
649
+ @d1.or{yy > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
675
650
  end
676
651
 
677
652
  specify "should correctly add parens to give predictable results" do
678
- @d1.filter(:y => 2).or(:z => 3).sql.should ==
679
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
680
-
681
- @d1.or(:y => 2).filter(:z => 3).sql.should ==
682
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
653
+ @d1.filter(:y => 2).or(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
654
+ @d1.or(:y => 2).filter(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
683
655
  end
684
656
 
685
657
  specify "should allow the use of blocks and arguments simultaneously" do
686
- @d1.or(:zz.sql_number < 3){:yy.sql_number > 3}.sql.should ==
687
- 'SELECT * FROM test WHERE ((x = 1) OR ((zz < 3) AND (yy > 3)))'
658
+ @d1.or(Sequel.expr(:zz) < 3){yy > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) OR ((zz < 3) AND (yy > 3)))'
688
659
  end
689
660
  end
690
661
 
@@ -697,34 +668,25 @@ describe "Dataset#and" do
697
668
  specify "should raise if no filter exists" do
698
669
  proc {@dataset.and(:a => 1)}.should raise_error(Sequel::Error)
699
670
  proc {@dataset.where(:a => 1).group(:t).and(:b => 2)}.should_not raise_error(Sequel::Error)
700
- @dataset.where(:a => 1).group(:t).and(:b => 2).sql ==
701
- "SELECT * FROM test WHERE (a = 1) AND (b = 2) GROUP BY t"
671
+ @dataset.where(:a => 1).group(:t).and(:b => 2).sql.should == "SELECT * FROM test WHERE ((a = 1) AND (b = 2)) GROUP BY t"
702
672
  end
703
673
 
704
674
  specify "should add an alternative expression to the where clause" do
705
- @d1.and(:y => 2).sql.should ==
706
- 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
675
+ @d1.and(:y => 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
707
676
  end
708
677
 
709
- specify "should accept all forms of filters" do
710
- # probably not exhaustive, but good enough
711
- @d1.and('y > ?', 2).sql.should ==
712
- 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
713
- @d1.and(:yy.sql_number > 3).sql.should ==
714
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
678
+ specify "should accept different types of filters" do
679
+ @d1.and('y > ?', 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
680
+ @d1.and(Sequel.expr(:yy) > 3).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
715
681
  end
716
682
 
717
683
  specify "should accept blocks passed to filter" do
718
- @d1.and {:yy.sql_number > 3}.sql.should ==
719
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
684
+ @d1.and{yy > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
720
685
  end
721
686
 
722
687
  specify "should correctly add parens to give predictable results" do
723
- @d1.or(:y => 2).and(:z => 3).sql.should ==
724
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
725
-
726
- @d1.and(:y => 2).or(:z => 3).sql.should ==
727
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
688
+ @d1.or(:y => 2).and(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
689
+ @d1.and(:y => 2).or(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
728
690
  end
729
691
  end
730
692
 
@@ -734,8 +696,11 @@ describe "Dataset#exclude" do
734
696
  end
735
697
 
736
698
  specify "should correctly negate the expression when one condition is given" do
737
- @dataset.exclude(:region=>'Asia').select_sql.should ==
738
- "SELECT * FROM test WHERE (region != 'Asia')"
699
+ @dataset.exclude(:region=>'Asia').select_sql.should == "SELECT * FROM test WHERE (region != 'Asia')"
700
+ end
701
+
702
+ specify "should affect the having clause if having clause is already used" do
703
+ @dataset.group_and_count(:name).having{count > 2}.exclude{count > 5}.sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name HAVING ((count > 2) AND (count <= 5))"
739
704
  end
740
705
 
741
706
  specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
@@ -745,30 +710,46 @@ describe "Dataset#exclude" do
745
710
  end
746
711
 
747
712
  specify "should parenthesize a single string condition correctly" do
748
- @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should ==
749
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
713
+ @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should == "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
750
714
  end
751
715
 
752
716
  specify "should parenthesize an array condition correctly" do
753
- @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should ==
754
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
717
+ @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should == "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
755
718
  end
756
719
 
757
720
  specify "should correctly parenthesize when it is used twice" do
758
- @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should ==
759
- "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
721
+ @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should == "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
760
722
  end
761
723
 
762
724
  specify "should support proc expressions" do
763
- @dataset.exclude{:id.sql_number < 6}.sql.should ==
764
- 'SELECT * FROM test WHERE (id >= 6)'
725
+ @dataset.exclude{id < 6}.sql.should == 'SELECT * FROM test WHERE (id >= 6)'
765
726
  end
766
727
 
767
728
  specify "should allow the use of blocks and arguments simultaneously" do
768
- @dataset.exclude(:id => (7..11)){:id.sql_number < 6}.sql.should ==
769
- 'SELECT * FROM test WHERE ((id < 7) OR (id > 11) OR (id >= 6))'
770
- @dataset.exclude([:id, 1], [:x, 3]){:id.sql_number < 6}.sql.should ==
771
- 'SELECT * FROM test WHERE ((id != 1) OR (x != 3) OR (id >= 6))'
729
+ @dataset.exclude(:id => (7..11)){id < 6}.sql.should == 'SELECT * FROM test WHERE ((id < 7) OR (id > 11) OR (id >= 6))'
730
+ @dataset.exclude([:id, 1], [:x, 3]){id < 6}.sql.should == 'SELECT * FROM test WHERE ((id != 1) OR (x != 3) OR (id >= 6))'
731
+ end
732
+ end
733
+
734
+ describe "Dataset#exclude_where" do
735
+ before do
736
+ @dataset = Sequel::Dataset.new(nil).from(:test)
737
+ end
738
+
739
+ specify "should correctly negate the expression and add it to the where clause" do
740
+ @dataset.exclude_where(:region=>'Asia').sql.should == "SELECT * FROM test WHERE (region != 'Asia')"
741
+ @dataset.exclude_where(:region=>'Asia').exclude_where(:region=>'NA').sql.should == "SELECT * FROM test WHERE ((region != 'Asia') AND (region != 'NA'))"
742
+ end
743
+
744
+ specify "should affect the where clause even if having clause is already used" do
745
+ @dataset.group_and_count(:name).having{count > 2}.exclude_where(:region=>'Asia').sql.should ==
746
+ "SELECT name, count(*) AS count FROM test WHERE (region != 'Asia') GROUP BY name HAVING (count > 2)"
747
+ end
748
+ end
749
+
750
+ describe "Dataset#exclude_having" do
751
+ specify "should correctly negate the expression and add it to the having clause" do
752
+ Sequel::Dataset.new(nil).from(:test).exclude_having{count > 2}.exclude_having{count < 0}.sql.should == "SELECT * FROM test HAVING ((count <= 2) AND (count >= 0))"
772
753
  end
773
754
  end
774
755
 
@@ -793,10 +774,7 @@ end
793
774
  describe "Dataset#having" do
794
775
  before do
795
776
  @dataset = Sequel::Dataset.new(nil).from(:test)
796
- @grouped = @dataset.group(:region).select(:region, :sum.sql_function(:population), :avg.sql_function(:gdp))
797
- @d1 = @grouped.having('sum(population) > 10')
798
- @d2 = @grouped.having(:region => 'Asia')
799
- @columns = "region, sum(population), avg(gdp)"
777
+ @grouped = @dataset.group(:region).select(:region, Sequel.function(:sum, :population), Sequel.function(:avg, :gdp))
800
778
  end
801
779
 
802
780
  specify "should just clone if given an empty argument" do
@@ -806,18 +784,15 @@ describe "Dataset#having" do
806
784
  end
807
785
 
808
786
  specify "should affect select statements" do
809
- @d1.select_sql.should ==
810
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
787
+ @grouped.having('sum(population) > 10').select_sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
811
788
  end
812
789
 
813
790
  specify "should support proc expressions" do
814
- @grouped.having {:sum.sql_function(:population) > 10}.sql.should ==
815
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
791
+ @grouped.having{Sequel.function(:sum, :population) > 10}.sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
816
792
  end
817
793
 
818
794
  specify "should work with and on the having clause" do
819
- @grouped.having( :a.sql_number > 1 ).and( :b.sql_number < 2 ).sql.should ==
820
- "SELECT #{@columns} FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
795
+ @grouped.having(Sequel.expr(:a) > 1).and(Sequel.expr(:b) < 2).sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
821
796
  end
822
797
  end
823
798
 
@@ -848,7 +823,7 @@ describe "a grouped dataset" do
848
823
  end
849
824
 
850
825
  specify "should format the right statement for counting (as a subquery)" do
851
- db = MockDatabase.new
826
+ db = Sequel.mock
852
827
  db[:test].select(:name).group(:name).count
853
828
  db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT name FROM test GROUP BY name) AS t1 LIMIT 1"]
854
829
  end
@@ -856,7 +831,7 @@ end
856
831
 
857
832
  describe "Dataset#group_by" do
858
833
  before do
859
- @dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
834
+ @dataset = Sequel.mock[:test].group_by(:type_id)
860
835
  end
861
836
 
862
837
  specify "should raise when trying to generate an update statement" do
@@ -868,45 +843,102 @@ describe "Dataset#group_by" do
868
843
  end
869
844
 
870
845
  specify "should specify the grouping in generated select statement" do
871
- @dataset.select_sql.should ==
872
- "SELECT * FROM test GROUP BY type_id"
873
- @dataset.group_by(:a, :b).select_sql.should ==
874
- "SELECT * FROM test GROUP BY a, b"
875
- @dataset.group_by(:type_id=>nil).select_sql.should ==
876
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
846
+ @dataset.select_sql.should == "SELECT * FROM test GROUP BY type_id"
847
+ @dataset.group_by(:a, :b).select_sql.should == "SELECT * FROM test GROUP BY a, b"
848
+ @dataset.group_by(:type_id=>nil).select_sql.should == "SELECT * FROM test GROUP BY (type_id IS NULL)"
877
849
  end
878
850
 
879
- specify "should ungroup when passed nil, empty, or no arguments" do
880
- @dataset.group_by.select_sql.should ==
881
- "SELECT * FROM test"
882
- @dataset.group_by(nil).select_sql.should ==
883
- "SELECT * FROM test"
851
+ specify "should ungroup when passed nil or no arguments" do
852
+ @dataset.group_by.select_sql.should == "SELECT * FROM test"
853
+ @dataset.group_by(nil).select_sql.should == "SELECT * FROM test"
884
854
  end
885
855
 
886
856
  specify "should undo previous grouping" do
887
- @dataset.group_by(:a).group_by(:b).select_sql.should ==
888
- "SELECT * FROM test GROUP BY b"
889
- @dataset.group_by(:a, :b).group_by.select_sql.should ==
890
- "SELECT * FROM test"
857
+ @dataset.group_by(:a).group_by(:b).select_sql.should == "SELECT * FROM test GROUP BY b"
858
+ @dataset.group_by(:a, :b).group_by.select_sql.should == "SELECT * FROM test"
891
859
  end
892
860
 
893
861
  specify "should be aliased as #group" do
894
- @dataset.group(:type_id=>nil).select_sql.should ==
895
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
862
+ @dataset.group(:type_id=>nil).select_sql.should == "SELECT * FROM test GROUP BY (type_id IS NULL)"
863
+ end
864
+
865
+ specify "should take a virtual row block" do
866
+ @dataset.group{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
867
+ @dataset.group_by{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
868
+ @dataset.group{[type_id > 1, type_id < 2]}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1), (type_id < 2)"
869
+ @dataset.group(:foo){type_id > 1}.sql.should == "SELECT * FROM test GROUP BY foo, (type_id > 1)"
870
+ end
871
+
872
+ specify "should support a #group_rollup method if the database supports it" do
873
+ @dataset.meta_def(:supports_group_rollup?){true}
874
+ @dataset.group(:type_id).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id)"
875
+ @dataset.group(:type_id, :b).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id, b)"
876
+ @dataset.meta_def(:uses_with_rollup?){true}
877
+ @dataset.group(:type_id).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY type_id WITH ROLLUP"
878
+ @dataset.group(:type_id, :b).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY type_id, b WITH ROLLUP"
879
+ end
880
+
881
+ specify "should support a #group_cube method if the database supports it" do
882
+ @dataset.meta_def(:supports_group_cube?){true}
883
+ @dataset.group(:type_id).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id)"
884
+ @dataset.group(:type_id, :b).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id, b)"
885
+ @dataset.meta_def(:uses_with_rollup?){true}
886
+ @dataset.group(:type_id).group_cube.select_sql.should == "SELECT * FROM test GROUP BY type_id WITH CUBE"
887
+ @dataset.group(:type_id, :b).group_cube.select_sql.should == "SELECT * FROM test GROUP BY type_id, b WITH CUBE"
888
+ end
889
+
890
+ specify "should have #group_cube and #group_rollup methods raise an Error if not supported it" do
891
+ proc{@dataset.group(:type_id).group_rollup}.should raise_error(Sequel::Error)
892
+ proc{@dataset.group(:type_id).group_cube}.should raise_error(Sequel::Error)
896
893
  end
897
894
  end
898
895
 
899
896
  describe "Dataset#as" do
900
897
  specify "should set up an alias" do
901
898
  dataset = Sequel::Dataset.new(nil).from(:test)
902
- dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == \
903
- 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
899
+ dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
904
900
  end
905
901
  end
906
902
 
907
903
  describe "Dataset#literal" do
908
904
  before do
909
- @dataset = Sequel::Dataset.new(nil).from(:test)
905
+ @ds = Sequel::Database.new.dataset
906
+ end
907
+
908
+ specify "should convert qualified symbol notation into dot notation" do
909
+ @ds.literal(:abc__def).should == 'abc.def'
910
+ end
911
+
912
+ specify "should convert AS symbol notation into SQL AS notation" do
913
+ @ds.literal(:xyz___x).should == 'xyz AS x'
914
+ @ds.literal(:abc__def___x).should == 'abc.def AS x'
915
+ end
916
+
917
+ specify "should support names with digits" do
918
+ @ds.literal(:abc2).should == 'abc2'
919
+ @ds.literal(:xx__yy3).should == 'xx.yy3'
920
+ @ds.literal(:ab34__temp3_4ax).should == 'ab34.temp3_4ax'
921
+ @ds.literal(:x1___y2).should == 'x1 AS y2'
922
+ @ds.literal(:abc2__def3___ggg4).should == 'abc2.def3 AS ggg4'
923
+ end
924
+
925
+ specify "should support upper case and lower case" do
926
+ @ds.literal(:ABC).should == 'ABC'
927
+ @ds.literal(:Zvashtoy__aBcD).should == 'Zvashtoy.aBcD'
928
+ end
929
+
930
+ specify "should support spaces inside column names" do
931
+ @ds.quote_identifiers = true
932
+ @ds.literal(:"AB C").should == '"AB C"'
933
+ @ds.literal(:"Zvas htoy__aB cD").should == '"Zvas htoy"."aB cD"'
934
+ @ds.literal(:"aB cD___XX XX").should == '"aB cD" AS "XX XX"'
935
+ @ds.literal(:"Zva shtoy__aB cD___XX XX").should == '"Zva shtoy"."aB cD" AS "XX XX"'
936
+ end
937
+ end
938
+
939
+ describe "Dataset#literal" do
940
+ before do
941
+ @dataset = Sequel::Database.new.from(:test)
910
942
  end
911
943
 
912
944
  specify "should escape strings properly" do
@@ -914,13 +946,13 @@ describe "Dataset#literal" do
914
946
  @dataset.literal('a"x"bc').should == "'a\"x\"bc'"
915
947
  @dataset.literal("a'bc").should == "'a''bc'"
916
948
  @dataset.literal("a''bc").should == "'a''''bc'"
917
- @dataset.literal("a\\bc").should == "'a\\\\bc'"
918
- @dataset.literal("a\\\\bc").should == "'a\\\\\\\\bc'"
919
- @dataset.literal("a\\'bc").should == "'a\\\\''bc'"
949
+ @dataset.literal("a\\bc").should == "'a\\bc'"
950
+ @dataset.literal("a\\\\bc").should == "'a\\\\bc'"
951
+ @dataset.literal("a\\'bc").should == "'a\\''bc'"
920
952
  end
921
953
 
922
954
  specify "should escape blobs as strings by default" do
923
- @dataset.literal('abc'.to_sequel_blob).should == "'abc'"
955
+ @dataset.literal(Sequel.blob('abc')).should == "'abc'"
924
956
  end
925
957
 
926
958
  specify "should literalize numbers properly" do
@@ -944,6 +976,21 @@ describe "Dataset#literal" do
944
976
  @dataset.literal(:"items__na#m$e").should == "items.na#m$e"
945
977
  end
946
978
 
979
+ specify "should call sql_literal_append with dataset and sql on type if not natively supported and the object responds to it" do
980
+ @a = Class.new do
981
+ def sql_literal_append(ds, sql)
982
+ sql << "called #{ds.blah}"
983
+ end
984
+ def sql_literal(ds)
985
+ "not called #{ds.blah}"
986
+ end
987
+ end
988
+ def @dataset.blah
989
+ "ds"
990
+ end
991
+ @dataset.literal(@a.new).should == "called ds"
992
+ end
993
+
947
994
  specify "should call sql_literal with dataset on type if not natively supported and the object responds to it" do
948
995
  @a = Class.new do
949
996
  def sql_literal(ds)
@@ -965,6 +1012,12 @@ describe "Dataset#literal" do
965
1012
  d.literal(d).should == "(#{d.sql})"
966
1013
  end
967
1014
 
1015
+ specify "should literalize Sequel::SQLTime properly" do
1016
+ t = Sequel::SQLTime.now
1017
+ s = t.strftime("'%H:%M:%S")
1018
+ @dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}'"
1019
+ end
1020
+
968
1021
  specify "should literalize Time properly" do
969
1022
  t = Time.now
970
1023
  s = t.strftime("'%Y-%m-%d %H:%M:%S")
@@ -1041,10 +1094,9 @@ describe "Dataset#literal" do
1041
1094
  end
1042
1095
 
1043
1096
  specify "should not modify literal strings" do
1044
- @dataset.literal('col1 + 2'.lit).should == 'col1 + 2'
1045
-
1046
- @dataset.update_sql(:a => 'a + 2'.lit).should ==
1047
- 'UPDATE test SET a = a + 2'
1097
+ @dataset.quote_identifiers = true
1098
+ @dataset.literal(Sequel.lit('col1 + 2')).should == 'col1 + 2'
1099
+ @dataset.update_sql(Sequel::SQL::Identifier.new(Sequel.lit('a')) => Sequel.lit('a + 2')).should == 'UPDATE "test" SET a = a + 2'
1048
1100
  end
1049
1101
 
1050
1102
  specify "should literalize BigDecimal instances correctly" do
@@ -1054,6 +1106,17 @@ describe "Dataset#literal" do
1054
1106
  @dataset.literal(BigDecimal.new("-Infinity")).should == "'-Infinity'"
1055
1107
  end
1056
1108
 
1109
+ specify "should literalize PlaceholderLiteralStrings correctly" do
1110
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new('? = ?', [1, 2])).should == '1 = 2'
1111
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new('? = ?', [1, 2], true)).should == '(1 = 2)'
1112
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(':a = :b', :a=>1, :b=>2)).should == '1 = 2'
1113
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(':a = :b', {:a=>1, :b=>2}, true)).should == '(1 = 2)'
1114
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(['', ' = ', ''], [1, 2])).should == '1 = 2'
1115
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(['', ' = ', ''], [1, 2], true)).should == '(1 = 2)'
1116
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(['', ' = '], [1, 2])).should == '1 = 2'
1117
+ @dataset.literal(Sequel::SQL::PlaceholderLiteralString.new(['', ' = '], [1, 2], true)).should == '(1 = 2)'
1118
+ end
1119
+
1057
1120
  specify "should raise an Error if the object can't be literalized" do
1058
1121
  proc{@dataset.literal(Object.new)}.should raise_error(Sequel::Error)
1059
1122
  end
@@ -1069,35 +1132,36 @@ describe "Dataset#from" do
1069
1132
  end
1070
1133
 
1071
1134
  specify "should format a Dataset as a subquery if it has had options set" do
1072
- @dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should ==
1073
- "SELECT * FROM (SELECT * FROM a WHERE (a = 1)) AS t1"
1135
+ @dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should == "SELECT * FROM (SELECT * FROM a WHERE (a = 1)) AS t1"
1074
1136
  end
1075
1137
 
1076
1138
  specify "should automatically alias sub-queries" do
1077
- @dataset.from(@dataset.from(:a).group(:b)).select_sql.should ==
1078
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1"
1139
+ @dataset.from(@dataset.from(:a).group(:b)).select_sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1"
1079
1140
 
1080
1141
  d1 = @dataset.from(:a).group(:b)
1081
1142
  d2 = @dataset.from(:c).group(:d)
1082
-
1083
- @dataset.from(d1, d2).sql.should ==
1084
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1, (SELECT * FROM c GROUP BY d) AS t2"
1143
+ @dataset.from(d1, d2).sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1, (SELECT * FROM c GROUP BY d) AS t2"
1085
1144
  end
1086
1145
 
1087
1146
  specify "should accept a hash for aliasing" do
1088
- @dataset.from(:a => :b).sql.should ==
1089
- "SELECT * FROM a AS b"
1090
-
1091
- @dataset.from(:a => 'b').sql.should ==
1092
- "SELECT * FROM a AS b"
1093
-
1094
- @dataset.from(@dataset.from(:a).group(:b) => :c).sql.should ==
1095
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
1147
+ @dataset.from(:a => :b).sql.should == "SELECT * FROM a AS b"
1148
+ @dataset.from(:a => 'b').sql.should == "SELECT * FROM a AS b"
1149
+ @dataset.from(@dataset.from(:a).group(:b) => :c).sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
1096
1150
  end
1097
1151
 
1098
1152
  specify "should always use a subquery if given a dataset" do
1099
- @dataset.from(@dataset.from(:a)).select_sql.should ==
1100
- "SELECT * FROM (SELECT * FROM a) AS t1"
1153
+ @dataset.from(@dataset.from(:a)).select_sql.should == "SELECT * FROM (SELECT * FROM a) AS t1"
1154
+ end
1155
+
1156
+ specify "should treat string arguments as identifiers" do
1157
+ @dataset.quote_identifiers = true
1158
+ @dataset.from('a').select_sql.should == "SELECT * FROM \"a\""
1159
+ end
1160
+
1161
+ specify "should not treat literal strings or blobs as identifiers" do
1162
+ @dataset.quote_identifiers = true
1163
+ @dataset.from(Sequel.lit('a')).select_sql.should == "SELECT * FROM a"
1164
+ @dataset.from(Sequel.blob('a')).select_sql.should == "SELECT * FROM 'a'"
1101
1165
  end
1102
1166
 
1103
1167
  specify "should remove all FROM tables if called with no arguments" do
@@ -1105,32 +1169,31 @@ describe "Dataset#from" do
1105
1169
  end
1106
1170
 
1107
1171
  specify "should accept sql functions" do
1108
- @dataset.from(:abc.sql_function(:def)).select_sql.should ==
1109
- "SELECT * FROM abc(def)"
1110
-
1111
- @dataset.from(:a.sql_function(:i)).select_sql.should ==
1112
- "SELECT * FROM a(i)"
1172
+ @dataset.from(Sequel.function(:abc, :def)).select_sql.should == "SELECT * FROM abc(def)"
1173
+ @dataset.from(Sequel.function(:a, :i)).select_sql.should == "SELECT * FROM a(i)"
1113
1174
  end
1114
1175
 
1115
1176
  specify "should accept :schema__table___alias symbol format" do
1116
- @dataset.from(:abc__def).select_sql.should ==
1117
- "SELECT * FROM abc.def"
1118
- @dataset.from(:a_b__c).select_sql.should ==
1119
- "SELECT * FROM a_b.c"
1120
- @dataset.from(:'#__#').select_sql.should ==
1121
- 'SELECT * FROM #.#'
1122
- @dataset.from(:abc__def___d).select_sql.should ==
1123
- "SELECT * FROM abc.def AS d"
1124
- @dataset.from(:a_b__d_e___f_g).select_sql.should ==
1125
- "SELECT * FROM a_b.d_e AS f_g"
1126
- @dataset.from(:'#__#___#').select_sql.should ==
1127
- 'SELECT * FROM #.# AS #'
1128
- @dataset.from(:abc___def).select_sql.should ==
1129
- "SELECT * FROM abc AS def"
1130
- @dataset.from(:a_b___c_d).select_sql.should ==
1131
- "SELECT * FROM a_b AS c_d"
1132
- @dataset.from(:'#___#').select_sql.should ==
1133
- 'SELECT * FROM # AS #'
1177
+ @dataset.from(:abc__def).select_sql.should == "SELECT * FROM abc.def"
1178
+ @dataset.from(:a_b__c).select_sql.should == "SELECT * FROM a_b.c"
1179
+ @dataset.from(:'#__#').select_sql.should == 'SELECT * FROM #.#'
1180
+ @dataset.from(:abc__def___d).select_sql.should == "SELECT * FROM abc.def AS d"
1181
+ @dataset.from(:a_b__d_e___f_g).select_sql.should == "SELECT * FROM a_b.d_e AS f_g"
1182
+ @dataset.from(:'#__#___#').select_sql.should == 'SELECT * FROM #.# AS #'
1183
+ @dataset.from(:abc___def).select_sql.should == "SELECT * FROM abc AS def"
1184
+ @dataset.from(:a_b___c_d).select_sql.should == "SELECT * FROM a_b AS c_d"
1185
+ @dataset.from(:'#___#').select_sql.should == 'SELECT * FROM # AS #'
1186
+ end
1187
+
1188
+ specify "should not handle :foo__schema__table___alias specially" do
1189
+ @dataset.from(:foo__schema__table___alias).select_sql.should == "SELECT * FROM foo.schema__table AS alias"
1190
+ end
1191
+
1192
+ specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
1193
+ @dataset.meta_def(:supports_cte?){true}
1194
+ @dataset.meta_def(:supports_cte_in_subselect?){false}
1195
+ @dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b))).sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
1196
+ @dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b)), @dataset.from(:c).with(:c, @dataset.from(:d))).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a) AS t1, (SELECT * FROM c) AS t2'
1134
1197
  end
1135
1198
  end
1136
1199
 
@@ -1145,41 +1208,41 @@ describe "Dataset#select" do
1145
1208
  end
1146
1209
 
1147
1210
  specify "should accept symbols and literal strings" do
1148
- @d.select('aaa'.lit).sql.should == 'SELECT aaa FROM test'
1149
- @d.select(:a, 'b'.lit).sql.should == 'SELECT a, b FROM test'
1150
- @d.select(:test__cc, 'test.d AS e'.lit).sql.should ==
1151
- 'SELECT test.cc, test.d AS e FROM test'
1152
- @d.select('test.d AS e'.lit, :test__cc).sql.should ==
1153
- 'SELECT test.d AS e, test.cc FROM test'
1154
-
1155
- # symbol helpers
1156
- @d.select(:test.*).sql.should ==
1157
- 'SELECT test.* FROM test'
1158
- @d.select(:test__name.as(:n)).sql.should ==
1159
- 'SELECT test.name AS n FROM test'
1160
- @d.select(:test__name___n).sql.should ==
1161
- 'SELECT test.name AS n FROM test'
1211
+ @d.select(Sequel.lit('aaa')).sql.should == 'SELECT aaa FROM test'
1212
+ @d.select(:a, Sequel.lit('b')).sql.should == 'SELECT a, b FROM test'
1213
+ @d.select(:test__cc, Sequel.lit('test.d AS e')).sql.should == 'SELECT test.cc, test.d AS e FROM test'
1214
+ @d.select(Sequel.lit('test.d AS e'), :test__cc).sql.should == 'SELECT test.d AS e, test.cc FROM test'
1215
+ @d.select(:test__name___n).sql.should == 'SELECT test.name AS n FROM test'
1216
+ end
1217
+
1218
+ specify "should accept ColumnAlls" do
1219
+ @d.select(Sequel::SQL::ColumnAll.new(:test)).sql.should == 'SELECT test.* FROM test'
1220
+ end
1221
+
1222
+ specify "should accept QualifiedIdentifiers" do
1223
+ @d.select(Sequel.expr(:test__name).as(:n)).sql.should == 'SELECT test.name AS n FROM test'
1162
1224
  end
1163
1225
 
1164
1226
  specify "should use the wildcard if no arguments are given" do
1165
1227
  @d.select.sql.should == 'SELECT * FROM test'
1166
1228
  end
1167
1229
 
1230
+ specify "should handle array condition specifiers that are aliased" do
1231
+ @d.select(Sequel.as([[:b, :c]], :n)).sql.should == 'SELECT (b = c) AS n FROM test'
1232
+ end
1233
+
1168
1234
  specify "should accept a hash for AS values" do
1169
- @d.select(:name => 'n', :__ggh => 'age').sql.should =~
1170
- /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
1235
+ @d.select(:name => 'n', :__ggh => 'age').sql.should =~ /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
1171
1236
  end
1172
1237
 
1173
- specify "should overrun the previous select option" do
1238
+ specify "should override the previous select option" do
1174
1239
  @d.select!(:a, :b, :c).select.sql.should == 'SELECT * FROM test'
1175
1240
  @d.select!(:price).select(:name).sql.should == 'SELECT name FROM test'
1176
1241
  end
1177
1242
 
1178
1243
  specify "should accept arbitrary objects and literalize them correctly" do
1179
1244
  @d.select(1, :a, 't').sql.should == "SELECT 1, a, 't' FROM test"
1180
-
1181
- @d.select(nil, :sum.sql_function(:t), :x___y).sql.should == "SELECT NULL, sum(t), x AS y FROM test"
1182
-
1245
+ @d.select(nil, Sequel.function(:sum, :t), :x___y).sql.should == "SELECT NULL, sum(t), x AS y FROM test"
1183
1246
  @d.select(nil, 1, :x => :y).sql.should == "SELECT NULL, 1, x AS y FROM test"
1184
1247
  end
1185
1248
 
@@ -1198,6 +1261,27 @@ describe "Dataset#select" do
1198
1261
  end
1199
1262
  end
1200
1263
 
1264
+ describe "Dataset#select_group" do
1265
+ before do
1266
+ @d = Sequel::Dataset.new(nil).from(:test)
1267
+ end
1268
+
1269
+ specify "should set both SELECT and GROUP" do
1270
+ @d.select_group(:name).sql.should == 'SELECT name FROM test GROUP BY name'
1271
+ @d.select_group(:a, :b__c, :d___e).sql.should == 'SELECT a, b.c, d AS e FROM test GROUP BY a, b.c, d'
1272
+ end
1273
+
1274
+ specify "should remove from both SELECT and GROUP if no arguments" do
1275
+ @d.select_group(:name).select_group.sql.should == 'SELECT * FROM test'
1276
+ end
1277
+
1278
+ specify "should accept virtual row blocks" do
1279
+ @d.select_group{name}.sql.should == 'SELECT name FROM test GROUP BY name'
1280
+ @d.select_group{[name, f(v).as(a)]}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
1281
+ @d.select_group(:name){f(v).as(a)}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
1282
+ end
1283
+ end
1284
+
1201
1285
  describe "Dataset#select_all" do
1202
1286
  before do
1203
1287
  @d = Sequel::Dataset.new(nil).from(:test)
@@ -1207,9 +1291,43 @@ describe "Dataset#select_all" do
1207
1291
  @d.select_all.sql.should == 'SELECT * FROM test'
1208
1292
  end
1209
1293
 
1210
- specify "should overrun the previous select option" do
1294
+ specify "should override the previous select option" do
1211
1295
  @d.select!(:a, :b, :c).select_all.sql.should == 'SELECT * FROM test'
1212
1296
  end
1297
+
1298
+ specify "should select all columns in a table if given an argument" do
1299
+ @d.select_all(:test).sql.should == 'SELECT test.* FROM test'
1300
+ end
1301
+
1302
+ specify "should select all columns all tables if given a multiple arguments" do
1303
+ @d.select_all(:test, :foo).sql.should == 'SELECT test.*, foo.* FROM test'
1304
+ end
1305
+
1306
+ specify "should work correctly with qualified symbols" do
1307
+ @d.select_all(:sch__test).sql.should == 'SELECT sch.test.* FROM test'
1308
+ end
1309
+
1310
+ specify "should work correctly with aliased symbols" do
1311
+ @d.select_all(:test___al).sql.should == 'SELECT al.* FROM test'
1312
+ @d.select_all(:sch__test___al).sql.should == 'SELECT al.* FROM test'
1313
+ end
1314
+
1315
+ specify "should work correctly with SQL::Identifiers" do
1316
+ @d.select_all(Sequel.identifier(:test)).sql.should == 'SELECT test.* FROM test'
1317
+ end
1318
+
1319
+ specify "should work correctly with SQL::QualifiedIdentifier" do
1320
+ @d.select_all(Sequel.qualify(:sch, :test)).sql.should == 'SELECT sch.test.* FROM test'
1321
+ end
1322
+
1323
+ specify "should work correctly with SQL::AliasedExpressions" do
1324
+ @d.select_all(Sequel.expr(:test).as(:al)).sql.should == 'SELECT al.* FROM test'
1325
+ end
1326
+
1327
+ specify "should work correctly with SQL::JoinClauses" do
1328
+ d = @d.cross_join(:foo).cross_join(:test___al)
1329
+ @d.select_all(*d.opts[:join]).sql.should == 'SELECT foo.*, al.* FROM test'
1330
+ end
1213
1331
  end
1214
1332
 
1215
1333
  describe "Dataset#select_more" do
@@ -1225,12 +1343,12 @@ describe "Dataset#select_more" do
1225
1343
 
1226
1344
  specify "should add to the currently selected columns" do
1227
1345
  @d.select(:a).select_more(:b).sql.should == 'SELECT a, b FROM test'
1228
- @d.select(:a.*).select_more(:b.*).sql.should == 'SELECT a.*, b.* FROM test'
1346
+ @d.select(Sequel::SQL::ColumnAll.new(:a)).select_more(Sequel::SQL::ColumnAll.new(:b)).sql.should == 'SELECT a.*, b.* FROM test'
1229
1347
  end
1230
1348
 
1231
1349
  specify "should accept a block that yields a virtual row" do
1232
1350
  @d.select(:a).select_more{|o| o.b}.sql.should == 'SELECT a, b FROM test'
1233
- @d.select(:a.*).select_more(:b.*){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1351
+ @d.select(Sequel::SQL::ColumnAll.new(:a)).select_more(Sequel::SQL::ColumnAll.new(:b)){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1234
1352
  end
1235
1353
  end
1236
1354
 
@@ -1247,12 +1365,20 @@ describe "Dataset#select_append" do
1247
1365
 
1248
1366
  specify "should add to the currently selected columns" do
1249
1367
  @d.select(:a).select_append(:b).sql.should == 'SELECT a, b FROM test'
1250
- @d.select(:a.*).select_append(:b.*).sql.should == 'SELECT a.*, b.* FROM test'
1368
+ @d.select(Sequel::SQL::ColumnAll.new(:a)).select_append(Sequel::SQL::ColumnAll.new(:b)).sql.should == 'SELECT a.*, b.* FROM test'
1251
1369
  end
1252
1370
 
1253
1371
  specify "should accept a block that yields a virtual row" do
1254
1372
  @d.select(:a).select_append{|o| o.b}.sql.should == 'SELECT a, b FROM test'
1255
- @d.select(:a.*).select_append(:b.*){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1373
+ @d.select(Sequel::SQL::ColumnAll.new(:a)).select_append(Sequel::SQL::ColumnAll.new(:b)){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1374
+ end
1375
+
1376
+ specify "should select from all from and join tables if SELECT *, column not supported" do
1377
+ @d.meta_def(:supports_select_all_and_column?){false}
1378
+ @d.select_append(:b).sql.should == 'SELECT test.*, b FROM test'
1379
+ @d.from(:test, :c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test, c'
1380
+ @d.cross_join(:c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test CROSS JOIN c'
1381
+ @d.cross_join(:c).cross_join(:d).select_append(:b).sql.should == 'SELECT test.*, c.*, d.*, b FROM test CROSS JOIN c CROSS JOIN d'
1256
1382
  end
1257
1383
  end
1258
1384
 
@@ -1262,38 +1388,31 @@ describe "Dataset#order" do
1262
1388
  end
1263
1389
 
1264
1390
  specify "should include an ORDER BY clause in the select statement" do
1265
- @dataset.order(:name).sql.should ==
1266
- 'SELECT * FROM test ORDER BY name'
1391
+ @dataset.order(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1267
1392
  end
1268
1393
 
1269
1394
  specify "should accept multiple arguments" do
1270
- @dataset.order(:name, :price.desc).sql.should ==
1271
- 'SELECT * FROM test ORDER BY name, price DESC'
1395
+ @dataset.order(:name, Sequel.desc(:price)).sql.should == 'SELECT * FROM test ORDER BY name, price DESC'
1272
1396
  end
1273
1397
 
1274
1398
  specify "should accept :nulls options for asc and desc" do
1275
- @dataset.order(:name.asc(:nulls=>:last), :price.desc(:nulls=>:first)).sql.should ==
1276
- 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
1399
+ @dataset.order(Sequel.asc(:name, :nulls=>:last), Sequel.desc(:price, :nulls=>:first)).sql.should == 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
1277
1400
  end
1278
1401
 
1279
- specify "should overrun a previous ordering" do
1280
- @dataset.order(:name).order(:stamp).sql.should ==
1281
- 'SELECT * FROM test ORDER BY stamp'
1402
+ specify "should override a previous ordering" do
1403
+ @dataset.order(:name).order(:stamp).sql.should == 'SELECT * FROM test ORDER BY stamp'
1282
1404
  end
1283
1405
 
1284
1406
  specify "should accept a literal string" do
1285
- @dataset.order('dada ASC'.lit).sql.should ==
1286
- 'SELECT * FROM test ORDER BY dada ASC'
1407
+ @dataset.order(Sequel.lit('dada ASC')).sql.should == 'SELECT * FROM test ORDER BY dada ASC'
1287
1408
  end
1288
1409
 
1289
1410
  specify "should accept a hash as an expression" do
1290
- @dataset.order(:name=>nil).sql.should ==
1291
- 'SELECT * FROM test ORDER BY (name IS NULL)'
1411
+ @dataset.order(:name=>nil).sql.should == 'SELECT * FROM test ORDER BY (name IS NULL)'
1292
1412
  end
1293
1413
 
1294
1414
  specify "should accept a nil to remove ordering" do
1295
- @dataset.order(:bah).order(nil).sql.should ==
1296
- 'SELECT * FROM test'
1415
+ @dataset.order(:bah).order(nil).sql.should == 'SELECT * FROM test'
1297
1416
  end
1298
1417
 
1299
1418
  specify "should accept a block that yields a virtual row" do
@@ -1355,6 +1474,13 @@ describe "Dataset#with_sql" do
1355
1474
  specify "should keep row_proc" do
1356
1475
  @dataset.with_sql('SELECT 1 FROM test').row_proc.should == @dataset.row_proc
1357
1476
  end
1477
+
1478
+ specify "should work with method symbols and arguments" do
1479
+ @dataset.with_sql(:delete_sql).sql.should == 'DELETE FROM test'
1480
+ @dataset.with_sql(:insert_sql, :b=>1).sql.should == 'INSERT INTO test (b) VALUES (1)'
1481
+ @dataset.with_sql(:update_sql, :b=>1).sql.should == 'UPDATE test SET b = 1'
1482
+ end
1483
+
1358
1484
  end
1359
1485
 
1360
1486
  describe "Dataset#order_by" do
@@ -1363,28 +1489,23 @@ describe "Dataset#order_by" do
1363
1489
  end
1364
1490
 
1365
1491
  specify "should include an ORDER BY clause in the select statement" do
1366
- @dataset.order_by(:name).sql.should ==
1367
- 'SELECT * FROM test ORDER BY name'
1492
+ @dataset.order_by(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1368
1493
  end
1369
1494
 
1370
1495
  specify "should accept multiple arguments" do
1371
- @dataset.order_by(:name, :price.desc).sql.should ==
1372
- 'SELECT * FROM test ORDER BY name, price DESC'
1496
+ @dataset.order_by(:name, Sequel.desc(:price)).sql.should == 'SELECT * FROM test ORDER BY name, price DESC'
1373
1497
  end
1374
1498
 
1375
- specify "should overrun a previous ordering" do
1376
- @dataset.order_by(:name).order(:stamp).sql.should ==
1377
- 'SELECT * FROM test ORDER BY stamp'
1499
+ specify "should override a previous ordering" do
1500
+ @dataset.order_by(:name).order(:stamp).sql.should == 'SELECT * FROM test ORDER BY stamp'
1378
1501
  end
1379
1502
 
1380
1503
  specify "should accept a string" do
1381
- @dataset.order_by('dada ASC'.lit).sql.should ==
1382
- 'SELECT * FROM test ORDER BY dada ASC'
1504
+ @dataset.order_by(Sequel.lit('dada ASC')).sql.should == 'SELECT * FROM test ORDER BY dada ASC'
1383
1505
  end
1384
1506
 
1385
1507
  specify "should accept a nil to remove ordering" do
1386
- @dataset.order_by(:bah).order_by(nil).sql.should ==
1387
- 'SELECT * FROM test'
1508
+ @dataset.order_by(:bah).order_by(nil).sql.should == 'SELECT * FROM test'
1388
1509
  end
1389
1510
  end
1390
1511
 
@@ -1399,8 +1520,8 @@ describe "Dataset#order_more and order_append" do
1399
1520
  end
1400
1521
 
1401
1522
  specify "should add to the end of a previous ordering" do
1402
- @dataset.order(:name).order_more(:stamp.desc).sql.should == 'SELECT * FROM test ORDER BY name, stamp DESC'
1403
- @dataset.order(:name).order_append(:stamp.desc).sql.should == 'SELECT * FROM test ORDER BY name, stamp DESC'
1523
+ @dataset.order(:name).order_more(Sequel.desc(:stamp)).sql.should == 'SELECT * FROM test ORDER BY name, stamp DESC'
1524
+ @dataset.order(:name).order_append(Sequel.desc(:stamp)).sql.should == 'SELECT * FROM test ORDER BY name, stamp DESC'
1404
1525
  end
1405
1526
 
1406
1527
  specify "should accept a block that yields a virtual row" do
@@ -1417,13 +1538,11 @@ describe "Dataset#order_prepend" do
1417
1538
  end
1418
1539
 
1419
1540
  specify "should include an ORDER BY clause in the select statement" do
1420
- @dataset.order_prepend(:name).sql.should ==
1421
- 'SELECT * FROM test ORDER BY name'
1541
+ @dataset.order_prepend(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1422
1542
  end
1423
1543
 
1424
1544
  specify "should add to the beginning of a previous ordering" do
1425
- @dataset.order(:name).order_prepend(:stamp.desc).sql.should ==
1426
- 'SELECT * FROM test ORDER BY stamp DESC, name'
1545
+ @dataset.order(:name).order_prepend(Sequel.desc(:stamp)).sql.should == 'SELECT * FROM test ORDER BY stamp DESC, name'
1427
1546
  end
1428
1547
 
1429
1548
  specify "should accept a block that yields a virtual row" do
@@ -1438,45 +1557,36 @@ describe "Dataset#reverse_order" do
1438
1557
  end
1439
1558
 
1440
1559
  specify "should use DESC as default order" do
1441
- @dataset.reverse_order(:name).sql.should ==
1442
- 'SELECT * FROM test ORDER BY name DESC'
1560
+ @dataset.reverse_order(:name).sql.should == 'SELECT * FROM test ORDER BY name DESC'
1443
1561
  end
1444
1562
 
1445
1563
  specify "should invert the order given" do
1446
- @dataset.reverse_order(:name.desc).sql.should ==
1447
- 'SELECT * FROM test ORDER BY name ASC'
1564
+ @dataset.reverse_order(Sequel.desc(:name)).sql.should == 'SELECT * FROM test ORDER BY name ASC'
1448
1565
  end
1449
1566
 
1450
1567
  specify "should invert the order for ASC expressions" do
1451
- @dataset.reverse_order(:name.asc).sql.should ==
1452
- 'SELECT * FROM test ORDER BY name DESC'
1568
+ @dataset.reverse_order(Sequel.asc(:name)).sql.should == 'SELECT * FROM test ORDER BY name DESC'
1453
1569
  end
1454
1570
 
1455
1571
  specify "should accept multiple arguments" do
1456
- @dataset.reverse_order(:name, :price.desc).sql.should ==
1457
- 'SELECT * FROM test ORDER BY name DESC, price ASC'
1572
+ @dataset.reverse_order(:name, Sequel.desc(:price)).sql.should == 'SELECT * FROM test ORDER BY name DESC, price ASC'
1458
1573
  end
1459
1574
 
1460
1575
  specify "should handles NULLS ordering correctly when reversing" do
1461
- @dataset.reverse_order(:name.asc(:nulls=>:first), :price.desc(:nulls=>:last)).sql.should ==
1462
- 'SELECT * FROM test ORDER BY name DESC NULLS LAST, price ASC NULLS FIRST'
1576
+ @dataset.reverse_order(Sequel.asc(:name, :nulls=>:first), Sequel.desc(:price, :nulls=>:last)).sql.should == 'SELECT * FROM test ORDER BY name DESC NULLS LAST, price ASC NULLS FIRST'
1463
1577
  end
1464
1578
 
1465
1579
  specify "should reverse a previous ordering if no arguments are given" do
1466
- @dataset.order(:name).reverse_order.sql.should ==
1467
- 'SELECT * FROM test ORDER BY name DESC'
1468
- @dataset.order(:clumsy.desc, :fool).reverse_order.sql.should ==
1469
- 'SELECT * FROM test ORDER BY clumsy ASC, fool DESC'
1580
+ @dataset.order(:name).reverse_order.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1581
+ @dataset.order(Sequel.desc(:clumsy), :fool).reverse_order.sql.should == 'SELECT * FROM test ORDER BY clumsy ASC, fool DESC'
1470
1582
  end
1471
1583
 
1472
1584
  specify "should return an unordered dataset for a dataset with no order" do
1473
- @dataset.unordered.reverse_order.sql.should ==
1474
- 'SELECT * FROM test'
1585
+ @dataset.unordered.reverse_order.sql.should == 'SELECT * FROM test'
1475
1586
  end
1476
1587
 
1477
1588
  specify "should have #reverse alias" do
1478
- @dataset.order(:name).reverse.sql.should ==
1479
- 'SELECT * FROM test ORDER BY name DESC'
1589
+ @dataset.order(:name).reverse.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1480
1590
  end
1481
1591
  end
1482
1592
 
@@ -1486,42 +1596,33 @@ describe "Dataset#limit" do
1486
1596
  end
1487
1597
 
1488
1598
  specify "should include a LIMIT clause in the select statement" do
1489
- @dataset.limit(10).sql.should ==
1490
- 'SELECT * FROM test LIMIT 10'
1599
+ @dataset.limit(10).sql.should == 'SELECT * FROM test LIMIT 10'
1491
1600
  end
1492
1601
 
1493
1602
  specify "should accept ranges" do
1494
- @dataset.limit(3..7).sql.should ==
1495
- 'SELECT * FROM test LIMIT 5 OFFSET 3'
1496
-
1497
- @dataset.limit(3...7).sql.should ==
1498
- 'SELECT * FROM test LIMIT 4 OFFSET 3'
1603
+ @dataset.limit(3..7).sql.should == 'SELECT * FROM test LIMIT 5 OFFSET 3'
1604
+ @dataset.limit(3...7).sql.should == 'SELECT * FROM test LIMIT 4 OFFSET 3'
1499
1605
  end
1500
1606
 
1501
1607
  specify "should include an offset if a second argument is given" do
1502
- @dataset.limit(6, 10).sql.should ==
1503
- 'SELECT * FROM test LIMIT 6 OFFSET 10'
1504
- end
1608
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 10'
1609
+ end
1505
1610
 
1506
1611
  specify "should convert regular strings to integers" do
1507
- @dataset.limit('6', 'a() - 1').sql.should ==
1508
- 'SELECT * FROM test LIMIT 6 OFFSET 0'
1612
+ @dataset.limit('6', 'a() - 1').sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 0'
1509
1613
  end
1510
1614
 
1511
1615
  specify "should not convert literal strings to integers" do
1512
- @dataset.limit('6'.lit, 'a() - 1'.lit).sql.should ==
1513
- 'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
1616
+ @dataset.limit(Sequel.lit('6'), Sequel.lit('a() - 1')).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
1514
1617
  end
1515
1618
 
1516
1619
  specify "should not convert other objects" do
1517
- @dataset.limit(6, :a.sql_function - 1).sql.should ==
1518
- 'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
1620
+ @dataset.limit(6, Sequel.function(:a) - 1).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
1519
1621
  end
1520
1622
 
1521
1623
  specify "should work with fixed sql datasets" do
1522
1624
  @dataset.opts[:sql] = 'select * from cccc'
1523
- @dataset.limit(6, 10).sql.should ==
1524
- 'SELECT * FROM (select * from cccc) AS t1 LIMIT 6 OFFSET 10'
1625
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM (select * from cccc) AS t1 LIMIT 6 OFFSET 10'
1525
1626
  end
1526
1627
 
1527
1628
  specify "should raise an error if an invalid limit or offset is used" do
@@ -1535,15 +1636,20 @@ describe "Dataset#limit" do
1535
1636
  end
1536
1637
 
1537
1638
  describe "Dataset#naked" do
1538
- before do
1539
- @d1 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4})
1540
- @d2 = @d1.clone
1541
- @d2.row_proc = Proc.new{|r| r}
1639
+ specify "should returned clone dataset without row_proc" do
1640
+ d = Sequel::Dataset.new(nil)
1641
+ d.row_proc = Proc.new{|r| r}
1642
+ d.naked.row_proc.should be_nil
1643
+ d.row_proc.should_not be_nil
1542
1644
  end
1543
-
1645
+ end
1646
+
1647
+ describe "Dataset#naked!" do
1544
1648
  specify "should remove any existing row_proc" do
1545
- naked = @d2.naked
1546
- naked.row_proc.should be_nil
1649
+ d = Sequel::Dataset.new(nil)
1650
+ d.row_proc = Proc.new{|r| r}
1651
+ d.naked!.row_proc.should be_nil
1652
+ d.row_proc.should be_nil
1547
1653
  end
1548
1654
  end
1549
1655
 
@@ -1555,7 +1661,7 @@ describe "Dataset#qualified_column_name" do
1555
1661
  specify "should return the literal value if not given a symbol" do
1556
1662
  @dataset.literal(@dataset.send(:qualified_column_name, 'ccc__b', :items)).should == "'ccc__b'"
1557
1663
  @dataset.literal(@dataset.send(:qualified_column_name, 3, :items)).should == '3'
1558
- @dataset.literal(@dataset.send(:qualified_column_name, 'a'.lit, :items)).should == 'a'
1664
+ @dataset.literal(@dataset.send(:qualified_column_name, Sequel.lit('a'), :items)).should == 'a'
1559
1665
  end
1560
1666
 
1561
1667
  specify "should qualify the column with the supplied table name if given an unqualified symbol" do
@@ -1567,42 +1673,46 @@ describe "Dataset#qualified_column_name" do
1567
1673
  end
1568
1674
 
1569
1675
  specify "should handle an aliased identifier" do
1570
- @dataset.literal(@dataset.send(:qualified_column_name, :ccc, :items.as(:i))).should == 'i.ccc'
1571
- end
1572
- end
1573
-
1574
- class DummyDataset < Sequel::Dataset
1575
- VALUES = [
1576
- {:a => 1, :b => 2},
1577
- {:a => 3, :b => 4},
1578
- {:a => 5, :b => 6}
1579
- ]
1580
- def fetch_rows(sql, &block)
1581
- VALUES.each(&block)
1676
+ @dataset.literal(@dataset.send(:qualified_column_name, :ccc, Sequel.expr(:items).as(:i))).should == 'i.ccc'
1582
1677
  end
1583
1678
  end
1584
1679
 
1585
1680
  describe "Dataset#map" do
1586
1681
  before do
1587
- @d = DummyDataset.new(nil).from(:items)
1682
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1588
1683
  end
1589
1684
 
1590
1685
  specify "should provide the usual functionality if no argument is given" do
1591
- @d.map {|n| n[:a] + n[:b]}.should == [3, 7, 11]
1686
+ @d.map{|n| n[:a] + n[:b]}.should == [3, 7, 11]
1592
1687
  end
1593
1688
 
1594
1689
  specify "should map using #[column name] if column name is given" do
1595
1690
  @d.map(:a).should == [1, 3, 5]
1596
1691
  end
1597
1692
 
1693
+ specify "should support multiple column names if an array of column names is given" do
1694
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1695
+ end
1696
+
1697
+ specify "should not call the row_proc if an argument is given" do
1698
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1699
+ @d.map(:a).should == [1, 3, 5]
1700
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1701
+ end
1702
+
1703
+ specify "should call the row_proc if no argument is given" do
1704
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1705
+ @d.map{|n| n[:a] + n[:b]}.should == [6, 14, 22]
1706
+ end
1707
+
1598
1708
  specify "should return the complete dataset values if nothing is given" do
1599
- @d.map.to_a.should == DummyDataset::VALUES
1709
+ @d.map.to_a.should == [{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}]
1600
1710
  end
1601
1711
  end
1602
1712
 
1603
1713
  describe "Dataset#to_hash" do
1604
1714
  before do
1605
- @d = DummyDataset.new(nil).from(:items)
1715
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1606
1716
  end
1607
1717
 
1608
1718
  specify "should provide a hash with the first column as key and the second as value" do
@@ -1614,11 +1724,73 @@ describe "Dataset#to_hash" do
1614
1724
  @d.to_hash(:a).should == {1 => {:a => 1, :b => 2}, 3 => {:a => 3, :b => 4}, 5 => {:a => 5, :b => 6}}
1615
1725
  @d.to_hash(:b).should == {2 => {:a => 1, :b => 2}, 4 => {:a => 3, :b => 4}, 6 => {:a => 5, :b => 6}}
1616
1726
  end
1727
+
1728
+ specify "should support using an array of columns as either the key or the value" do
1729
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1730
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1731
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1732
+ @d.to_hash([:a, :b]).should == {[1, 2] => {:a => 1, :b => 2}, [3, 4] => {:a => 3, :b => 4}, [5, 6] => {:a => 5, :b => 6}}
1733
+ end
1734
+
1735
+ specify "should not call the row_proc if two arguments are given" do
1736
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1737
+ @d.to_hash(:a, :b).should == {1 => 2, 3 => 4, 5 => 6}
1738
+ @d.to_hash(:b, :a).should == {2 => 1, 4 => 3, 6 => 5}
1739
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1740
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1741
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1742
+ end
1743
+
1744
+ specify "should call the row_proc if only a single argument is given" do
1745
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1746
+ @d.to_hash(:a).should == {2 => {:a => 2, :b => 4}, 6 => {:a => 6, :b => 8}, 10 => {:a => 10, :b => 12}}
1747
+ @d.to_hash(:b).should == {4 => {:a => 2, :b => 4}, 8 => {:a => 6, :b => 8}, 12 => {:a => 10, :b => 12}}
1748
+ @d.to_hash([:a, :b]).should == {[2, 4] => {:a => 2, :b => 4}, [6, 8] => {:a => 6, :b => 8}, [10, 12] => {:a => 10, :b => 12}}
1749
+ end
1750
+ end
1751
+
1752
+ describe "Dataset#to_hash_groups" do
1753
+ before do
1754
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 1, :b => 6}, {:a => 7, :b => 4}])[:items]
1755
+ end
1756
+
1757
+ specify "should provide a hash with the first column as key and the second as value" do
1758
+ @d.to_hash_groups(:a, :b).should == {1 => [2, 6], 3 => [4], 7 => [4]}
1759
+ @d.to_hash_groups(:b, :a).should == {2 => [1], 4=>[3, 7], 6=>[1]}
1760
+ end
1761
+
1762
+ specify "should provide a hash with the first column as key and the entire hash as value if the value column is blank or nil" do
1763
+ @d.to_hash_groups(:a).should == {1 => [{:a => 1, :b => 2}, {:a => 1, :b => 6}], 3 => [{:a => 3, :b => 4}], 7 => [{:a => 7, :b => 4}]}
1764
+ @d.to_hash_groups(:b).should == {2 => [{:a => 1, :b => 2}], 4 => [{:a => 3, :b => 4}, {:a => 7, :b => 4}], 6 => [{:a => 1, :b => 6}]}
1765
+ end
1766
+
1767
+ specify "should support using an array of columns as either the key or the value" do
1768
+ @d.to_hash_groups([:a, :b], :b).should == {[1, 2] => [2], [3, 4] => [4], [1, 6] => [6], [7, 4]=>[4]}
1769
+ @d.to_hash_groups(:b, [:a, :b]).should == {2 => [[1, 2]], 4 => [[3, 4], [7, 4]], 6 => [[1, 6]]}
1770
+ @d.to_hash_groups([:b, :a], [:a, :b]).should == {[2, 1] => [[1, 2]], [4, 3] => [[3, 4]], [6, 1] => [[1, 6]], [4, 7]=>[[7, 4]]}
1771
+ @d.to_hash_groups([:a, :b]).should == {[1, 2] => [{:a => 1, :b => 2}], [3, 4] => [{:a => 3, :b => 4}], [1, 6] => [{:a => 1, :b => 6}], [7, 4] => [{:a => 7, :b => 4}]}
1772
+ end
1773
+
1774
+ specify "should not call the row_proc if two arguments are given" do
1775
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1776
+ @d.to_hash_groups(:a, :b).should == {1 => [2, 6], 3 => [4], 7 => [4]}
1777
+ @d.to_hash_groups(:b, :a).should == {2 => [1], 4=>[3, 7], 6=>[1]}
1778
+ @d.to_hash_groups([:a, :b], :b).should == {[1, 2] => [2], [3, 4] => [4], [1, 6] => [6], [7, 4]=>[4]}
1779
+ @d.to_hash_groups(:b, [:a, :b]).should == {2 => [[1, 2]], 4 => [[3, 4], [7, 4]], 6 => [[1, 6]]}
1780
+ @d.to_hash_groups([:b, :a], [:a, :b]).should == {[2, 1] => [[1, 2]], [4, 3] => [[3, 4]], [6, 1] => [[1, 6]], [4, 7]=>[[7, 4]]}
1781
+ end
1782
+
1783
+ specify "should call the row_proc if only a single argument is given" do
1784
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1785
+ @d.to_hash_groups(:a).should == {2 => [{:a => 2, :b => 4}, {:a => 2, :b => 12}], 6 => [{:a => 6, :b => 8}], 14 => [{:a => 14, :b => 8}]}
1786
+ @d.to_hash_groups(:b).should == {4 => [{:a => 2, :b => 4}], 8 => [{:a => 6, :b => 8}, {:a => 14, :b => 8}], 12 => [{:a => 2, :b => 12}]}
1787
+ @d.to_hash_groups([:a, :b]).should == {[2, 4] => [{:a => 2, :b => 4}], [6, 8] => [{:a => 6, :b => 8}], [2, 12] => [{:a => 2, :b => 12}], [14, 8] => [{:a => 14, :b => 8}]}
1788
+ end
1617
1789
  end
1618
1790
 
1619
1791
  describe "Dataset#distinct" do
1620
1792
  before do
1621
- @db = MockDatabase.new
1793
+ @db = Sequel.mock
1622
1794
  @dataset = @db[:test].select(:name)
1623
1795
  end
1624
1796
 
@@ -1634,7 +1806,7 @@ describe "Dataset#distinct" do
1634
1806
  specify "should use DISTINCT ON if columns are given and DISTINCT ON is supported" do
1635
1807
  @dataset.meta_def(:supports_distinct_on?){true}
1636
1808
  @dataset.distinct(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
1637
- @dataset.distinct(:stamp.cast(:integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
1809
+ @dataset.distinct(Sequel.cast(:stamp, :integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
1638
1810
  end
1639
1811
 
1640
1812
  specify "should do a subselect for count" do
@@ -1645,132 +1817,119 @@ end
1645
1817
 
1646
1818
  describe "Dataset#count" do
1647
1819
  before do
1648
- @c = Class.new(Sequel::Dataset) do
1649
- def self.sql
1650
- @@sql
1651
- end
1652
-
1653
- def fetch_rows(sql)
1654
- @columns = [sql =~ /SELECT COUNT/i ? :count : :a]
1655
- @@sql = sql
1656
- yield({@columns.first=>1})
1657
- end
1658
- end
1659
- @dataset = @c.new(nil).from(:test)
1820
+ @db = Sequel.mock(:fetch=>{:count=>1})
1821
+ @dataset = @db.from(:test).columns(:count)
1660
1822
  end
1661
1823
 
1662
1824
  specify "should format SQL properly" do
1663
1825
  @dataset.count.should == 1
1664
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test LIMIT 1'
1826
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LIMIT 1']
1827
+ end
1828
+
1829
+ specify "should accept an argument" do
1830
+ @dataset.count(:foo).should == 1
1831
+ @db.sqls.should == ['SELECT COUNT(foo) AS count FROM test LIMIT 1']
1832
+ end
1833
+
1834
+ specify "should work with a nil argument" do
1835
+ @dataset.count(nil).should == 1
1836
+ @db.sqls.should == ['SELECT COUNT(NULL) AS count FROM test LIMIT 1']
1837
+ end
1838
+
1839
+ specify "should accept a virtual row block" do
1840
+ @dataset.count{foo(bar)}.should == 1
1841
+ @db.sqls.should == ['SELECT COUNT(foo(bar)) AS count FROM test LIMIT 1']
1842
+ end
1843
+
1844
+ specify "should raise an Error if given an argument and a block" do
1845
+ proc{@dataset.count(:foo){foo(bar)}}.should raise_error(Sequel::Error)
1665
1846
  end
1666
1847
 
1667
1848
  specify "should include the where clause if it's there" do
1668
- @dataset.filter(:abc.sql_number < 30).count.should == 1
1669
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1'
1849
+ @dataset.filter(Sequel.expr(:abc) < 30).count.should == 1
1850
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1']
1670
1851
  end
1671
1852
 
1672
1853
  specify "should count properly for datasets with fixed sql" do
1673
1854
  @dataset.opts[:sql] = "select abc from xyz"
1674
1855
  @dataset.count.should == 1
1675
- @c.sql.should == "SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"
1856
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"]
1676
1857
  end
1677
1858
 
1678
1859
  specify "should count properly when using UNION, INTERSECT, or EXCEPT" do
1679
1860
  @dataset.union(@dataset).count.should == 1
1680
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 1"
1861
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 1"]
1681
1862
  @dataset.intersect(@dataset).count.should == 1
1682
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test INTERSECT SELECT * FROM test) AS t1 LIMIT 1"
1863
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test INTERSECT SELECT * FROM test) AS t1 LIMIT 1"]
1683
1864
  @dataset.except(@dataset).count.should == 1
1684
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test EXCEPT SELECT * FROM test) AS t1 LIMIT 1"
1865
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test EXCEPT SELECT * FROM test) AS t1 LIMIT 1"]
1685
1866
  end
1686
1867
 
1687
1868
  specify "should return limit if count is greater than it" do
1688
1869
  @dataset.limit(5).count.should == 1
1689
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"
1870
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"]
1690
1871
  end
1691
1872
 
1692
1873
  it "should work on a graphed_dataset" do
1693
1874
  @dataset.should_receive(:columns).twice.and_return([:a])
1694
1875
  @dataset.graph(@dataset, [:a], :table_alias=>:test2).count.should == 1
1695
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1'
1876
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
1696
1877
  end
1697
1878
 
1698
1879
  specify "should not cache the columns value" do
1699
- ds = @dataset.from(:blah)
1880
+ ds = @dataset.from(:blah).columns(:a)
1700
1881
  ds.columns.should == [:a]
1701
1882
  ds.count.should == 1
1702
- @c.sql.should == 'SELECT COUNT(*) AS count FROM blah LIMIT 1'
1883
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM blah LIMIT 1']
1703
1884
  ds.columns.should == [:a]
1704
1885
  end
1705
1886
  end
1706
1887
 
1707
-
1708
1888
  describe "Dataset#group_and_count" do
1709
1889
  before do
1710
- @c = Class.new(Sequel::Dataset) do
1711
- def self.sql
1712
- @@sql
1713
- end
1714
-
1715
- def fetch_rows(sql)
1716
- @@sql = sql
1717
- yield({1 => 1})
1718
- end
1719
- end
1720
- @ds = @c.new(nil).from(:test)
1890
+ @ds = Sequel::Dataset.new(nil).from(:test)
1721
1891
  end
1722
1892
 
1723
1893
  specify "should format SQL properly" do
1724
- @ds.group_and_count(:name).sql.should ==
1725
- "SELECT name, count(*) AS count FROM test GROUP BY name"
1894
+ @ds.group_and_count(:name).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1726
1895
  end
1727
1896
 
1728
1897
  specify "should accept multiple columns for grouping" do
1729
- @ds.group_and_count(:a, :b).sql.should ==
1730
- "SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
1898
+ @ds.group_and_count(:a, :b).sql.should == "SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
1731
1899
  end
1732
1900
 
1733
1901
  specify "should format column aliases in the select clause but not in the group clause" do
1734
- @ds.group_and_count(:name___n).sql.should ==
1735
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1736
- @ds.group_and_count(:name__n).sql.should ==
1737
- "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
1902
+ @ds.group_and_count(:name___n).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1903
+ @ds.group_and_count(:name__n).sql.should == "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
1738
1904
  end
1739
1905
 
1740
1906
  specify "should handle identifiers" do
1741
- @ds.group_and_count(:name___n.identifier).sql.should ==
1742
- "SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
1907
+ @ds.group_and_count(Sequel.identifier(:name___n)).sql.should == "SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
1743
1908
  end
1744
1909
 
1745
1910
  specify "should handle literal strings" do
1746
- @ds.group_and_count("name".lit).sql.should ==
1747
- "SELECT name, count(*) AS count FROM test GROUP BY name"
1911
+ @ds.group_and_count(Sequel.lit("name")).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1748
1912
  end
1749
1913
 
1750
1914
  specify "should handle aliased expressions" do
1751
- @ds.group_and_count(:name.as(:n)).sql.should ==
1752
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1753
- @ds.group_and_count(:name.identifier.as(:n)).sql.should ==
1754
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1915
+ @ds.group_and_count(Sequel.expr(:name).as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1916
+ @ds.group_and_count(Sequel.identifier(:name).as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1917
+ end
1918
+
1919
+ specify "should take a virtual row block" do
1920
+ @ds.group_and_count{(type_id > 1).as(t)}.sql.should == "SELECT (type_id > 1) AS t, count(*) AS count FROM test GROUP BY (type_id > 1)"
1921
+ @ds.group_and_count{[(type_id > 1).as(t), type_id < 2]}.sql.should == "SELECT (type_id > 1) AS t, (type_id < 2), count(*) AS count FROM test GROUP BY (type_id > 1), (type_id < 2)"
1922
+ @ds.group_and_count(:foo){type_id > 1}.sql.should == "SELECT foo, (type_id > 1), count(*) AS count FROM test GROUP BY foo, (type_id > 1)"
1755
1923
  end
1756
1924
  end
1757
1925
 
1758
1926
  describe "Dataset#empty?" do
1759
1927
  specify "should return true if records exist in the dataset" do
1760
- @c = Class.new(Sequel::Dataset) do
1761
- def self.sql
1762
- @@sql
1763
- end
1764
-
1765
- def fetch_rows(sql)
1766
- @@sql = sql
1767
- yield({1 => 1}) unless sql =~ /WHERE 'f'/
1768
- end
1769
- end
1770
- @c.new(nil).from(:test).should_not be_empty
1771
- @c.sql.should == 'SELECT 1 FROM test LIMIT 1'
1772
- @c.new(nil).from(:test).filter(false).should be_empty
1773
- @c.sql.should == "SELECT 1 FROM test WHERE 'f' LIMIT 1"
1928
+ db = Sequel.mock(:fetch=>proc{|sql| {1=>1} unless sql =~ /WHERE 'f'/})
1929
+ db.from(:test).should_not be_empty
1930
+ db.sqls.should == ['SELECT 1 AS one FROM test LIMIT 1']
1931
+ db.from(:test).filter(false).should be_empty
1932
+ db.sqls.should == ["SELECT 1 AS one FROM test WHERE 'f' LIMIT 1"]
1774
1933
  end
1775
1934
  end
1776
1935
 
@@ -1781,22 +1940,22 @@ describe "Dataset#first_source_alias" do
1781
1940
 
1782
1941
  specify "should be the entire first source if not aliased" do
1783
1942
  @ds.from(:t).first_source_alias.should == :t
1784
- @ds.from(:t__a.identifier).first_source_alias.should == :t__a.identifier
1943
+ @ds.from(Sequel.identifier(:t__a)).first_source_alias.should == Sequel.identifier(:t__a)
1785
1944
  @ds.from(:s__t).first_source_alias.should == :s__t
1786
- @ds.from(:t.qualify(:s)).first_source_alias.should == :t.qualify(:s)
1945
+ @ds.from(Sequel.qualify(:s, :t)).first_source_alias.should == Sequel.qualify(:s, :t)
1787
1946
  end
1788
1947
 
1789
1948
  specify "should be the alias if aliased" do
1790
1949
  @ds.from(:t___a).first_source_alias.should == :a
1791
1950
  @ds.from(:s__t___a).first_source_alias.should == :a
1792
- @ds.from(:t.as(:a)).first_source_alias.should == :a
1951
+ @ds.from(Sequel.expr(:t).as(:a)).first_source_alias.should == :a
1793
1952
  end
1794
1953
 
1795
1954
  specify "should be aliased as first_source" do
1796
1955
  @ds.from(:t).first_source.should == :t
1797
- @ds.from(:t__a.identifier).first_source.should == :t__a.identifier
1956
+ @ds.from(Sequel.identifier(:t__a)).first_source.should == Sequel.identifier(:t__a)
1798
1957
  @ds.from(:s__t___a).first_source.should == :a
1799
- @ds.from(:t.as(:a)).first_source.should == :a
1958
+ @ds.from(Sequel.expr(:t).as(:a)).first_source.should == :a
1800
1959
  end
1801
1960
 
1802
1961
  specify "should raise exception if table doesn't have a source" do
@@ -1811,15 +1970,15 @@ describe "Dataset#first_source_table" do
1811
1970
 
1812
1971
  specify "should be the entire first source if not aliased" do
1813
1972
  @ds.from(:t).first_source_table.should == :t
1814
- @ds.from(:t__a.identifier).first_source_table.should == :t__a.identifier
1973
+ @ds.from(Sequel.identifier(:t__a)).first_source_table.should == Sequel.identifier(:t__a)
1815
1974
  @ds.from(:s__t).first_source_table.should == :s__t
1816
- @ds.from(:t.qualify(:s)).first_source_table.should == :t.qualify(:s)
1975
+ @ds.from(Sequel.qualify(:s, :t)).first_source_table.should == Sequel.qualify(:s, :t)
1817
1976
  end
1818
1977
 
1819
1978
  specify "should be the unaliased part if aliased" do
1820
- @ds.from(:t___a).first_source_table.should == :t.identifier
1821
- @ds.from(:s__t___a).first_source_table.should == :t.qualify(:s)
1822
- @ds.from(:t.as(:a)).first_source_table.should == :t
1979
+ @ds.literal(@ds.from(:t___a).first_source_table).should == "t"
1980
+ @ds.literal(@ds.from(:s__t___a).first_source_table).should == "s.t"
1981
+ @ds.literal(@ds.from(Sequel.expr(:t).as(:a)).first_source_table).should == "t"
1823
1982
  end
1824
1983
 
1825
1984
  specify "should raise exception if table doesn't have a source" do
@@ -1849,73 +2008,64 @@ describe "Dataset#from_self" do
1849
2008
  'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name INNER JOIN posts ON (posts.alias = some_name.name)'
1850
2009
  end
1851
2010
 
1852
- specify "should not options such as server" do
2011
+ specify "should not remove non-SQL options such as :server" do
1853
2012
  @ds.server(:blah).from_self(:alias=>:some_name).opts[:server].should == :blah
1854
2013
  end
1855
2014
 
2015
+ specify "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
2016
+ ds = Sequel::Dataset.new(nil)
2017
+ ds.meta_def(:supports_cte?){true}
2018
+ ds.meta_def(:supports_cte_in_subselect?){false}
2019
+ ds.from(:a).with(:a, ds.from(:b)).from_self.sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
2020
+ ds.from(:a, :c).with(:a, ds.from(:b)).with(:c, ds.from(:d)).from_self.sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a, c) AS t1'
2021
+ end
1856
2022
  end
1857
2023
 
1858
2024
  describe "Dataset#join_table" do
1859
2025
  before do
1860
- @d = MockDataset.new(nil).from(:items)
2026
+ @d = Sequel::Dataset.new(nil).from(:items)
1861
2027
  @d.quote_identifiers = true
1862
2028
  end
1863
2029
 
1864
2030
  specify "should format the JOIN clause properly" do
1865
- @d.join_table(:left_outer, :categories, :category_id => :id).sql.should ==
1866
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2031
+ @d.join_table(:left_outer, :categories, :category_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1867
2032
  end
1868
2033
 
1869
2034
  specify "should handle multiple conditions on the same join table column" do
1870
- @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
2035
+ @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1871
2036
  'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND ("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100))'
1872
2037
  end
1873
2038
 
1874
2039
  specify "should include WHERE clause if applicable" do
1875
- @d.filter(:price.sql_number < 100).join_table(:right_outer, :categories, :category_id => :id).sql.should ==
2040
+ @d.filter(Sequel.expr(:price) < 100).join_table(:right_outer, :categories, :category_id => :id).sql.should ==
1876
2041
  'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") WHERE ("price" < 100)'
1877
2042
  end
1878
2043
 
1879
2044
  specify "should include ORDER BY clause if applicable" do
1880
- @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should ==
1881
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
2045
+ @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should == 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
1882
2046
  end
1883
2047
 
1884
2048
  specify "should support multiple joins" do
1885
- @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.should ==
1886
- 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
2049
+ @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.should == 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1887
2050
  end
1888
2051
 
1889
2052
  specify "should support arbitrary join types" do
1890
- @d.join_table(:magic, :categories, :category_id=>:id).sql.should ==
1891
- 'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2053
+ @d.join_table(:magic, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1892
2054
  end
1893
2055
 
1894
2056
  specify "should support many join methods" do
1895
- @d.left_outer_join(:categories, :category_id=>:id).sql.should ==
1896
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1897
- @d.right_outer_join(:categories, :category_id=>:id).sql.should ==
1898
- 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1899
- @d.full_outer_join(:categories, :category_id=>:id).sql.should ==
1900
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1901
- @d.inner_join(:categories, :category_id=>:id).sql.should ==
1902
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1903
- @d.left_join(:categories, :category_id=>:id).sql.should ==
1904
- 'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1905
- @d.right_join(:categories, :category_id=>:id).sql.should ==
1906
- 'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1907
- @d.full_join(:categories, :category_id=>:id).sql.should ==
1908
- 'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1909
- @d.natural_join(:categories).sql.should ==
1910
- 'SELECT * FROM "items" NATURAL JOIN "categories"'
1911
- @d.natural_left_join(:categories).sql.should ==
1912
- 'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
1913
- @d.natural_right_join(:categories).sql.should ==
1914
- 'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
1915
- @d.natural_full_join(:categories).sql.should ==
1916
- 'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
1917
- @d.cross_join(:categories).sql.should ==
1918
- 'SELECT * FROM "items" CROSS JOIN "categories"'
2057
+ @d.left_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2058
+ @d.right_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2059
+ @d.full_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2060
+ @d.inner_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2061
+ @d.left_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2062
+ @d.right_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2063
+ @d.full_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2064
+ @d.natural_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
2065
+ @d.natural_left_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
2066
+ @d.natural_right_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
2067
+ @d.natural_full_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
2068
+ @d.cross_join(:categories).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
1919
2069
  end
1920
2070
 
1921
2071
  specify "should raise an error if additional arguments are provided to join methods that don't take conditions" do
@@ -1935,95 +2085,76 @@ describe "Dataset#join_table" do
1935
2085
  end
1936
2086
 
1937
2087
  specify "should default to a plain join if nil is used for the type" do
1938
- @d.join_table(nil, :categories, :category_id=>:id).sql.should ==
1939
- 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2088
+ @d.join_table(nil, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1940
2089
  end
1941
2090
 
1942
2091
  specify "should use an inner join for Dataset#join" do
1943
- @d.join(:categories, :category_id=>:id).sql.should ==
1944
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2092
+ @d.join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1945
2093
  end
1946
2094
 
1947
2095
  specify "should support aliased tables using the deprecated argument" do
1948
- @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should ==
1949
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
2096
+ @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1950
2097
  end
1951
2098
 
1952
2099
  specify "should support aliased tables using the :table_alias option" do
1953
- @d.from('stats').join('players', {:id => :player_id}, :table_alias=>:p).sql.should ==
1954
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
2100
+ @d.from('stats').join('players', {:id => :player_id}, :table_alias=>:p).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1955
2101
  end
1956
2102
 
1957
2103
  specify "should support aliased tables using an implicit alias" do
1958
- @d.from('stats').join(:players.as(:p), {:id => :player_id}).sql.should ==
1959
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
2104
+ @d.from('stats').join(Sequel.expr(:players).as(:p), {:id => :player_id}).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1960
2105
  end
1961
2106
 
1962
2107
  specify "should support using an alias for the FROM when doing the first join with unqualified condition columns" do
1963
- ds = MockDataset.new(nil).from(:foo => :f)
1964
- ds.quote_identifiers = true
1965
- ds.join_table(:inner, :bar, :id => :bar_id).sql.should ==
1966
- 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
2108
+ @d.from(:foo=>:f).join_table(:inner, :bar, :id => :bar_id).sql.should == 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
1967
2109
  end
1968
2110
 
1969
2111
  specify "should support implicit schemas in from table symbols" do
1970
- @d.from(:s__t).join(:u__v, {:id => :player_id}).sql.should ==
1971
- 'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
2112
+ @d.from(:s__t).join(:u__v, {:id => :player_id}).sql.should == 'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
1972
2113
  end
1973
2114
 
1974
2115
  specify "should support implicit aliases in from table symbols" do
1975
- @d.from(:t___z).join(:v___y, {:id => :player_id}).sql.should ==
1976
- 'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
1977
- @d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.should ==
1978
- 'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
2116
+ @d.from(:t___z).join(:v___y, {:id => :player_id}).sql.should == 'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
2117
+ @d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.should == 'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
1979
2118
  end
1980
2119
 
1981
2120
  specify "should support AliasedExpressions" do
1982
- @d.from(:s.as(:t)).join(:u.as(:v), {:id => :player_id}).sql.should ==
1983
- 'SELECT * FROM "s" AS "t" INNER JOIN "u" AS "v" ON ("v"."id" = "t"."player_id")'
2121
+ @d.from(Sequel.expr(:s).as(:t)).join(Sequel.expr(:u).as(:v), {:id => :player_id}).sql.should == 'SELECT * FROM "s" AS "t" INNER JOIN "u" AS "v" ON ("v"."id" = "t"."player_id")'
1984
2122
  end
1985
2123
 
1986
2124
  specify "should support the :implicit_qualifier option" do
1987
- @d.from('stats').join('players', {:id => :player_id}, :implicit_qualifier=>:p).sql.should ==
1988
- 'SELECT * FROM "stats" INNER JOIN "players" ON ("players"."id" = "p"."player_id")'
2125
+ @d.from('stats').join('players', {:id => :player_id}, :implicit_qualifier=>:p).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" ON ("players"."id" = "p"."player_id")'
2126
+ end
2127
+
2128
+ specify "should not qualify if :qualify=>false option is given" do
2129
+ @d.from('stats').join(:players, {:id => :player_id}, :qualify=>false).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" ON ("id" = "player_id")'
2130
+ end
2131
+
2132
+ specify "should do deep qualification if :qualify=>:deep option is given" do
2133
+ @d.from('stats').join(:players, {Sequel.function(:f, :id) => Sequel.subscript(:player_id, 0)}, :qualify=>:deep).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" ON (f("players"."id") = "stats"."player_id"[0])'
1989
2134
  end
1990
2135
 
1991
2136
  specify "should allow for arbitrary conditions in the JOIN clause" do
1992
- @d.join_table(:left_outer, :categories, :status => 0).sql.should ==
1993
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
1994
- @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
1995
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
1996
- @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1997
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
1998
- @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
1999
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
2137
+ @d.join_table(:left_outer, :categories, :status => 0).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
2138
+ @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
2139
+ @d.join_table(:left_outer, :categories, :timestamp => Sequel::CURRENT_TIMESTAMP).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
2140
+ @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
2000
2141
  end
2001
2142
 
2002
2143
  specify "should raise error for a table without a source" do
2003
- proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}. \
2004
- should raise_error(Sequel::Error)
2144
+ proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}.should raise_error(Sequel::Error)
2005
2145
  end
2006
2146
 
2007
2147
  specify "should support joining datasets" do
2008
2148
  ds = Sequel::Dataset.new(nil).from(:categories)
2009
-
2010
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2011
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id")'
2012
-
2149
+ @d.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id")'
2013
2150
  ds.filter!(:active => true)
2014
-
2015
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2016
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t1" ON ("t1"."item_id" = "items"."id")'
2017
-
2018
- @d.from_self.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2019
- 'SELECT * FROM (SELECT * FROM "items") AS "t1" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t2" ON ("t2"."item_id" = "t1"."id")'
2151
+ @d.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t1" ON ("t1"."item_id" = "items"."id")'
2152
+ @d.from_self.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM (SELECT * FROM "items") AS "t1" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t2" ON ("t2"."item_id" = "t1"."id")'
2020
2153
  end
2021
2154
 
2022
2155
  specify "should support joining datasets and aliasing the join" do
2023
2156
  ds = Sequel::Dataset.new(nil).from(:categories)
2024
-
2025
- @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :ds).sql.should ==
2026
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
2157
+ @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :ds).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
2027
2158
  end
2028
2159
 
2029
2160
  specify "should support joining multiple datasets" do
@@ -2037,43 +2168,27 @@ describe "Dataset#join_table" do
2037
2168
  'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) AS "t3" ON ("t3"."attribute_id" = "t2"."id")'
2038
2169
  end
2039
2170
 
2040
- specify "should support joining objects that respond to :table_name" do
2041
- ds = Object.new
2042
- def ds.table_name; :categories end
2043
-
2044
- @d.join(ds, :item_id => :id).sql.should ==
2045
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
2046
- end
2047
-
2048
- specify "should support using a SQL String as the join condition" do
2049
- @d.join(:categories, %{c.item_id = items.id}, :c).sql.should ==
2050
- 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2171
+ specify "should support using an SQL String as the join condition" do
2172
+ @d.join(:categories, "c.item_id = items.id", :c).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2051
2173
  end
2052
2174
 
2053
2175
  specify "should support using a boolean column as the join condition" do
2054
- @d.join(:categories, :active).sql.should ==
2055
- 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2176
+ @d.join(:categories, :active).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2056
2177
  end
2057
2178
 
2058
2179
  specify "should support using an expression as the join condition" do
2059
- @d.join(:categories, :number.sql_number > 10).sql.should ==
2060
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
2180
+ @d.join(:categories, Sequel.expr(:number) > 10).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
2061
2181
  end
2062
2182
 
2063
- specify "should support natural and cross joins using nil" do
2064
- @d.join_table(:natural, :categories).sql.should ==
2065
- 'SELECT * FROM "items" NATURAL JOIN "categories"'
2066
- @d.join_table(:cross, :categories, nil).sql.should ==
2067
- 'SELECT * FROM "items" CROSS JOIN "categories"'
2068
- @d.join_table(:natural, :categories, nil, :c).sql.should ==
2069
- 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
2183
+ specify "should support natural and cross joins" do
2184
+ @d.join_table(:natural, :categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
2185
+ @d.join_table(:cross, :categories, nil).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
2186
+ @d.join_table(:natural, :categories, nil, :c).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
2070
2187
  end
2071
2188
 
2072
2189
  specify "should support joins with a USING clause if an array of symbols is used" do
2073
- @d.join(:categories, [:id]).sql.should ==
2074
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
2075
- @d.join(:categories, [:id1, :id2]).sql.should ==
2076
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
2190
+ @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
2191
+ @d.join(:categories, [:id1, :id2]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
2077
2192
  end
2078
2193
 
2079
2194
  specify "should emulate JOIN USING (poorly) if the dataset doesn't support it" do
@@ -2081,6 +2196,12 @@ describe "Dataset#join_table" do
2081
2196
  @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
2082
2197
  end
2083
2198
 
2199
+ specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
2200
+ @d.meta_def(:supports_cte?){true}
2201
+ @d.meta_def(:supports_cte_in_subselect?){false}
2202
+ @d.join(Sequel::Dataset.new(nil).from(:categories).with(:a, Sequel::Dataset.new(nil).from(:b)), [:id]).sql.should == 'WITH "a" AS (SELECT * FROM b) SELECT * FROM "items" INNER JOIN (SELECT * FROM categories) AS "t1" USING ("id")'
2203
+ end
2204
+
2084
2205
  specify "should raise an error if using an array of symbols with a block" do
2085
2206
  proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
2086
2207
  end
@@ -2135,27 +2256,21 @@ describe "Dataset#join_table" do
2135
2256
  end
2136
2257
 
2137
2258
  specify "should use the block result as the only condition if no condition is given" do
2138
- @d.join(:categories){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
2139
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" = "items"."c")'
2140
- @d.join(:categories){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
2141
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" > "items"."c")'
2259
+ @d.join(:categories){|j,lj,js| {Sequel.qualify(j, :b)=>Sequel.qualify(lj, :c)}}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" = "items"."c")'
2260
+ @d.join(:categories){|j,lj,js| Sequel.qualify(j, :b) > Sequel.qualify(lj, :c)}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" > "items"."c")'
2142
2261
  end
2143
2262
 
2144
2263
  specify "should combine the block conditions and argument conditions if both given" do
2145
- @d.join(:categories, :a=>:d){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
2146
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" = "items"."c"))'
2147
- @d.join(:categories, :a=>:d){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
2148
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" > "items"."c"))'
2264
+ @d.join(:categories, :a=>:d){|j,lj,js| {Sequel.qualify(j, :b)=>Sequel.qualify(lj, :c)}}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" = "items"."c"))'
2265
+ @d.join(:categories, :a=>:d){|j,lj,js| Sequel.qualify(j, :b) > Sequel.qualify(lj, :c)}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" > "items"."c"))'
2149
2266
  end
2150
2267
 
2151
2268
  specify "should prefer explicit aliases over implicit" do
2152
- @d.from(:items___i).join(:categories___c, {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should ==
2153
- 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2154
- @d.from(:items.as(:i)).join(:categories.as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should ==
2269
+ @d.from(:items___i).join(:categories___c, {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should == 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2270
+ @d.from(Sequel.expr(:items).as(:i)).join(Sequel.expr(:categories).as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should ==
2155
2271
  'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2156
2272
  end
2157
2273
 
2158
-
2159
2274
  specify "should not allow insert, update, delete, or truncate" do
2160
2275
  proc{@d.join(:categories, :a=>:d).insert_sql}.should raise_error(Sequel::InvalidOperation)
2161
2276
  proc{@d.join(:categories, :a=>:d).update_sql(:a=>1)}.should raise_error(Sequel::InvalidOperation)
@@ -2165,86 +2280,64 @@ describe "Dataset#join_table" do
2165
2280
 
2166
2281
  specify "should raise an error if an invalid option is passed" do
2167
2282
  proc{@d.join(:c, [:id], nil)}.should raise_error(Sequel::Error)
2168
- proc{@d.join(:c, [:id], :c.qualify(:d))}.should raise_error(Sequel::Error)
2283
+ proc{@d.join(:c, [:id], Sequel.qualify(:d, :c))}.should raise_error(Sequel::Error)
2169
2284
  end
2170
2285
  end
2171
2286
 
2172
2287
  describe "Dataset#[]=" do
2173
- before do
2174
- c = Class.new(Sequel::Dataset) do
2175
- def last_sql
2176
- @@last_sql
2177
- end
2178
-
2179
- def update(*args)
2180
- @@last_sql = update_sql(*args)
2181
- end
2182
- end
2183
-
2184
- @d = c.new(nil).from(:items)
2185
- end
2186
-
2187
2288
  specify "should perform an update on the specified filter" do
2188
- @d[:a => 1] = {:x => 3}
2189
- @d.last_sql.should == 'UPDATE items SET x = 3 WHERE (a = 1)'
2289
+ db = Sequel.mock
2290
+ ds = db[:items]
2291
+ ds[:a => 1] = {:x => 3}
2292
+ db.sqls.should == ['UPDATE items SET x = 3 WHERE (a = 1)']
2190
2293
  end
2191
2294
  end
2192
2295
 
2193
2296
  describe "Dataset#set" do
2194
- before do
2195
- c = Class.new(Sequel::Dataset) do
2196
- def last_sql
2197
- @@last_sql
2198
- end
2199
-
2200
- def update(*args, &block)
2201
- @@last_sql = update_sql(*args, &block)
2202
- end
2203
- end
2204
-
2205
- @d = c.new(nil).from(:items)
2206
- end
2207
-
2208
2297
  specify "should act as alias to #update" do
2209
- @d.set({:x => 3})
2210
- @d.last_sql.should == 'UPDATE items SET x = 3'
2298
+ db = Sequel.mock
2299
+ ds = db[:items]
2300
+ ds.set({:x => 3})
2301
+ db.sqls.should == ['UPDATE items SET x = 3']
2211
2302
  end
2212
2303
  end
2213
2304
 
2214
-
2215
2305
  describe "Dataset#insert_multiple" do
2216
2306
  before do
2217
- c = Class.new(Sequel::Dataset) do
2218
- attr_reader :inserts
2219
- def insert(arg)
2220
- @inserts ||= []
2221
- @inserts << arg
2222
- end
2223
- end
2224
-
2225
- @d = c.new(nil)
2307
+ @db = Sequel.mock(:autoid=>2)
2308
+ @ds = @db[:items]
2226
2309
  end
2227
2310
 
2228
2311
  specify "should insert all items in the supplied array" do
2229
- @d.insert_multiple [:aa, 5, 3, {1 => 2}]
2230
- @d.inserts.should == [:aa, 5, 3, {1 => 2}]
2312
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}])
2313
+ @db.sqls.should == ["INSERT INTO items VALUES ('aa')",
2314
+ "INSERT INTO items VALUES (5)",
2315
+ "INSERT INTO items VALUES (3)",
2316
+ "INSERT INTO items (a) VALUES (2)"]
2231
2317
  end
2232
2318
 
2233
2319
  specify "should pass array items through the supplied block if given" do
2234
- a = ["inevitable", "hello", "the ticking clock"]
2235
- @d.insert_multiple(a) {|i| i.gsub('l', 'r')}
2236
- @d.inserts.should == ["inevitabre", "herro", "the ticking crock"]
2320
+ @ds.insert_multiple(["inevitable", "hello", "the ticking clock"]){|i| i.gsub('l', 'r')}
2321
+ @db.sqls.should == ["INSERT INTO items VALUES ('inevitabre')",
2322
+ "INSERT INTO items VALUES ('herro')",
2323
+ "INSERT INTO items VALUES ('the ticking crock')"]
2324
+ end
2325
+
2326
+ specify "should return array of inserted ids" do
2327
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}]).should == [2, 3, 4, 5]
2328
+ end
2329
+
2330
+ specify "should work exactly like in metioned in the example" do
2331
+ @ds.insert_multiple([{:x=>1}, {:x=>2}]){|row| row[:y] = row[:x] * 2 ; row }
2332
+ sqls = @db.sqls
2333
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls[0])
2334
+ ["INSERT INTO items (x, y) VALUES (2, 4)", "INSERT INTO items (y, x) VALUES (4, 2)"].should include(sqls[1])
2237
2335
  end
2238
2336
  end
2239
2337
 
2240
2338
  describe "Dataset aggregate methods" do
2241
2339
  before do
2242
- c = Class.new(Sequel::Dataset) do
2243
- def fetch_rows(sql)
2244
- yield({1 => sql})
2245
- end
2246
- end
2247
- @d = c.new(nil).from(:test)
2340
+ @d = Sequel.mock(:fetch=>proc{|s| {1=>s}})[:test]
2248
2341
  end
2249
2342
 
2250
2343
  specify "should include min" do
@@ -2278,116 +2371,88 @@ end
2278
2371
 
2279
2372
  describe "Dataset#range" do
2280
2373
  before do
2281
- c = Class.new(Sequel::Dataset) do
2282
- class_variable_set(:@@sql, nil)
2283
-
2284
- def last_sql; self.class.send(:class_variable_get, :@@sql); end
2285
-
2286
- def fetch_rows(sql)
2287
- self.class.send(:class_variable_set, :@@sql, sql)
2288
- yield(:v1 => 1, :v2 => 10)
2289
- end
2290
- end
2291
- @d = c.new(nil).from(:test)
2374
+ @db = Sequel.mock(:fetch=>{:v1 => 1, :v2 => 10})
2375
+ @ds = @db[:test]
2292
2376
  end
2293
2377
 
2294
2378
  specify "should generate a correct SQL statement" do
2295
- @d.range(:stamp)
2296
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"
2379
+ @ds.range(:stamp)
2380
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"]
2297
2381
 
2298
- @d.filter(:price.sql_number > 100).range(:stamp)
2299
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"
2382
+ @ds.filter(Sequel.expr(:price) > 100).range(:stamp)
2383
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"]
2300
2384
  end
2301
2385
 
2302
2386
  specify "should return a range object" do
2303
- @d.range(:tryme).should == (1..10)
2387
+ @ds.range(:tryme).should == (1..10)
2304
2388
  end
2305
2389
 
2306
2390
  specify "should use a subselect for the same conditions as count" do
2307
- @d.order(:stamp).limit(5).range(:stamp).should == (1..10)
2308
- @d.last_sql.should == 'SELECT min(stamp) AS v1, max(stamp) AS v2 FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1'
2391
+ @ds.order(:stamp).limit(5).range(:stamp).should == (1..10)
2392
+ @db.sqls.should == ['SELECT min(stamp) AS v1, max(stamp) AS v2 FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1']
2309
2393
  end
2310
2394
  end
2311
2395
 
2312
2396
  describe "Dataset#interval" do
2313
2397
  before do
2314
- c = Class.new(Sequel::Dataset) do
2315
- class_variable_set(:@@sql, nil)
2316
-
2317
- def last_sql; self.class.send(:class_variable_get, :@@sql); end
2318
-
2319
- def fetch_rows(sql)
2320
- self.class.send(:class_variable_set, :@@sql, sql)
2321
- yield(:v => 1234)
2322
- end
2323
- end
2324
- @d = c.new(nil).from(:test)
2398
+ @db = Sequel.mock(:fetch=>{:v => 1234})
2399
+ @ds = @db[:test]
2325
2400
  end
2326
2401
 
2327
- specify "should generate a correct SQL statement" do
2328
- @d.interval(:stamp)
2329
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"
2402
+ specify "should generate the correct SQL statement" do
2403
+ @ds.interval(:stamp)
2404
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"]
2330
2405
 
2331
- @d.filter(:price.sql_number > 100).interval(:stamp)
2332
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"
2333
- end
2334
-
2335
- specify "should return an integer" do
2336
- @d.interval(:tryme).should == 1234
2406
+ @ds.filter(Sequel.expr(:price) > 100).interval(:stamp)
2407
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"]
2337
2408
  end
2338
2409
 
2339
2410
  specify "should use a subselect for the same conditions as count" do
2340
- @d.order(:stamp).limit(5).interval(:stamp).should == 1234
2341
- @d.last_sql.should == 'SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1'
2411
+ @ds.order(:stamp).limit(5).interval(:stamp).should == 1234
2412
+ @db.sqls.should == ['SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1']
2342
2413
  end
2343
2414
  end
2344
2415
 
2345
2416
  describe "Dataset #first and #last" do
2346
2417
  before do
2347
- @c = Class.new(Sequel::Dataset) do
2348
- def each(&block)
2349
- s = select_sql
2350
- x = [:a,1,:b,2,s]
2351
- i = /LIMIT (\d+)/.match(s)[1].to_i.times{yield x}
2352
- end
2353
- end
2354
- @d = @c.new(nil).from(:test)
2418
+ @db = Sequel.mock(:fetch=>proc{|s| {:s=>s}})
2419
+ @d = @db[:test]
2355
2420
  end
2356
2421
 
2357
2422
  specify "should return a single record if no argument is given" do
2358
- @d.order(:a).first.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a LIMIT 1']
2359
- @d.order(:a).last.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a DESC LIMIT 1']
2423
+ @d.order(:a).first.should == {:s=>'SELECT * FROM test ORDER BY a LIMIT 1'}
2424
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2360
2425
  end
2361
2426
 
2362
2427
  specify "should return the first/last matching record if argument is not an Integer" do
2363
- @d.order(:a).first(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1']
2364
- @d.order(:a).first('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1']
2365
- @d.order(:a).last(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1']
2366
- @d.order(:a).last('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1']
2428
+ @d.order(:a).first(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1'}
2429
+ @d.order(:a).first('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1'}
2430
+ @d.order(:a).last(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1'}
2431
+ @d.order(:a).last('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1'}
2367
2432
  end
2368
2433
 
2369
2434
  specify "should set the limit and return an array of records if the given number is > 1" do
2370
2435
  i = rand(10) + 10
2371
- r = @d.order(:a).first(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a LIMIT #{i}"]] * i
2436
+ r = @d.order(:a).first(i).should == [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
2372
2437
  i = rand(10) + 10
2373
- r = @d.order(:a).last(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a DESC LIMIT #{i}"]] * i
2438
+ r = @d.order(:a).last(i).should == [{:s=>"SELECT * FROM test ORDER BY a DESC LIMIT #{i}"}]
2374
2439
  end
2375
2440
 
2376
2441
  specify "should return the first matching record if a block is given without an argument" do
2377
- @d.first{:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) LIMIT 1']
2378
- @d.order(:name).last{:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1']
2442
+ @d.first{z > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) LIMIT 1'}
2443
+ @d.order(:name).last{z > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1'}
2379
2444
  end
2380
2445
 
2381
2446
  specify "should combine block and standard argument filters if argument is not an Integer" do
2382
- @d.first(:y=>25){:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1']
2383
- @d.order(:name).last('y = ?', 16){:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 16)) ORDER BY name DESC LIMIT 1']
2447
+ @d.first(:y=>25){z > 26}.should == {:s=>'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1'}
2448
+ @d.order(:name).last('y = ?', 16){z > 26}.should == {:s=>'SELECT * FROM test WHERE ((z > 26) AND (y = 16)) ORDER BY name DESC LIMIT 1'}
2384
2449
  end
2385
2450
 
2386
2451
  specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
2387
2452
  i = rand(10) + 10
2388
- r = @d.order(:a).first(i){:z.sql_number > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"]] * i
2453
+ r = @d.order(:a).first(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
2389
2454
  i = rand(10) + 10
2390
- r = @d.order(:a).last(i){:z.sql_number > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"]] * i
2455
+ r = @d.order(:a).last(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"}]
2391
2456
  end
2392
2457
 
2393
2458
  specify "#last should raise if no order is given" do
@@ -2398,10 +2463,10 @@ describe "Dataset #first and #last" do
2398
2463
  end
2399
2464
 
2400
2465
  specify "#last should invert the order" do
2401
- @d.order(:a).last.pop.should == 'SELECT * FROM test ORDER BY a DESC LIMIT 1'
2402
- @d.order(:b.desc).last.pop.should == 'SELECT * FROM test ORDER BY b ASC LIMIT 1'
2403
- @d.order(:c, :d).last.pop.should == 'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'
2404
- @d.order(:e.desc, :f).last.pop.should == 'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'
2466
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2467
+ @d.order(Sequel.desc(:b)).last.should == {:s=>'SELECT * FROM test ORDER BY b ASC LIMIT 1'}
2468
+ @d.order(:c, :d).last.should == {:s=>'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'}
2469
+ @d.order(Sequel.desc(:e), :f).last.should == {:s=>'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'}
2405
2470
  end
2406
2471
  end
2407
2472
 
@@ -2412,55 +2477,37 @@ describe "Dataset compound operations" do
2412
2477
  end
2413
2478
 
2414
2479
  specify "should support UNION and UNION ALL" do
2415
- @a.union(@b).sql.should == \
2416
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
2417
- @b.union(@a, true).sql.should == \
2418
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2419
- @b.union(@a, :all=>true).sql.should == \
2420
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2480
+ @a.union(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
2481
+ @b.union(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2482
+ @b.union(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2421
2483
  end
2422
2484
 
2423
2485
  specify "should support INTERSECT and INTERSECT ALL" do
2424
- @a.intersect(@b).sql.should == \
2425
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
2426
- @b.intersect(@a, true).sql.should == \
2427
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2428
- @b.intersect(@a, :all=>true).sql.should == \
2429
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2486
+ @a.intersect(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
2487
+ @b.intersect(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2488
+ @b.intersect(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2430
2489
  end
2431
2490
 
2432
2491
  specify "should support EXCEPT and EXCEPT ALL" do
2433
- @a.except(@b).sql.should == \
2434
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
2435
- @b.except(@a, true).sql.should == \
2436
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2437
- @b.except(@a, :all=>true).sql.should == \
2438
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2492
+ @a.except(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
2493
+ @b.except(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2494
+ @b.except(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2439
2495
  end
2440
2496
 
2441
2497
  specify "should support :alias option for specifying identifier" do
2442
- @a.union(@b, :alias=>:xx).sql.should == \
2443
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS xx"
2444
- @a.intersect(@b, :alias=>:xx).sql.should == \
2445
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS xx"
2446
- @a.except(@b, :alias=>:xx).sql.should == \
2447
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS xx"
2498
+ @a.union(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS xx"
2499
+ @a.intersect(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS xx"
2500
+ @a.except(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS xx"
2448
2501
  end
2449
2502
 
2450
2503
  specify "should support :from_self=>false option to not wrap the compound in a SELECT * FROM (...)" do
2451
- @b.union(@a, :from_self=>false).sql.should == \
2452
- "SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
2453
- @b.intersect(@a, :from_self=>false).sql.should == \
2454
- "SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
2455
- @b.except(@a, :from_self=>false).sql.should == \
2456
- "SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
2504
+ @b.union(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
2505
+ @b.intersect(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
2506
+ @b.except(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
2457
2507
 
2458
- @b.union(@a, :from_self=>false, :all=>true).sql.should == \
2459
- "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
2460
- @b.intersect(@a, :from_self=>false, :all=>true).sql.should == \
2461
- "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
2462
- @b.except(@a, :from_self=>false, :all=>true).sql.should == \
2463
- "SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
2508
+ @b.union(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
2509
+ @b.intersect(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
2510
+ @b.except(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
2464
2511
  end
2465
2512
 
2466
2513
  specify "should raise an InvalidOperation if INTERSECT or EXCEPT is used and they are not supported" do
@@ -2480,141 +2527,105 @@ describe "Dataset compound operations" do
2480
2527
  end
2481
2528
 
2482
2529
  specify "should handle chained compound operations" do
2483
- @a.union(@b).union(@a, true).sql.should == \
2484
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1 UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2485
- @a.intersect(@b, true).intersect(@a).sql.should == \
2486
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2)) AS t1 INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1"
2487
- @a.except(@b).except(@a, true).sql.should == \
2488
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1 EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2530
+ @a.union(@b).union(@a, true).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1 UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2531
+ @a.intersect(@b, true).intersect(@a).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2)) AS t1 INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1"
2532
+ @a.except(@b).except(@a, true).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1 EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2489
2533
  end
2490
2534
 
2491
2535
  specify "should use a subselect when using a compound operation with a dataset that already has a compound operation" do
2492
- @a.union(@b.union(@a, true)).sql.should == \
2493
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2494
- @a.intersect(@b.intersect(@a), true).sql.should == \
2495
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2496
- @a.except(@b.except(@a, true)).sql.should == \
2497
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2536
+ @a.union(@b.union(@a, true)).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2537
+ @a.intersect(@b.intersect(@a), true).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2538
+ @a.except(@b.except(@a, true)).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2498
2539
  end
2499
2540
 
2500
2541
  specify "should order and limit properly when using UNION, INTERSECT, or EXCEPT" do
2501
2542
  @dataset = Sequel::Dataset.new(nil).from(:test)
2502
- @dataset.union(@dataset).limit(2).sql.should ==
2503
- "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
2504
- @dataset.limit(2).intersect(@dataset).sql.should ==
2505
- "SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
2506
- @dataset.except(@dataset.limit(2)).sql.should ==
2507
- "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
2508
-
2509
- @dataset.union(@dataset).order(:num).sql.should ==
2510
- "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
2511
- @dataset.order(:num).intersect(@dataset).sql.should ==
2512
- "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1 INTERSECT SELECT * FROM test) AS t1"
2513
- @dataset.except(@dataset.order(:num)).sql.should ==
2514
- "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1) AS t1"
2543
+ @dataset.union(@dataset).limit(2).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
2544
+ @dataset.limit(2).intersect(@dataset).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
2545
+ @dataset.except(@dataset.limit(2)).sql.should == "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
2546
+
2547
+ @dataset.union(@dataset).order(:num).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
2548
+ @dataset.order(:num).intersect(@dataset).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1 INTERSECT SELECT * FROM test) AS t1"
2549
+ @dataset.except(@dataset.order(:num)).sql.should == "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1) AS t1"
2515
2550
 
2516
2551
  @dataset.limit(2).order(:a).union(@dataset.limit(3).order(:b)).order(:c).limit(4).sql.should ==
2517
2552
  "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY a LIMIT 2) AS t1 UNION SELECT * FROM (SELECT * FROM test ORDER BY b LIMIT 3) AS t1) AS t1 ORDER BY c LIMIT 4"
2518
2553
  end
2519
2554
 
2555
+ specify "should hoist WITH clauses in given dataset if dataset doesn't support WITH in subselect" do
2556
+ ds = Sequel::Dataset.new(nil)
2557
+ ds.meta_def(:supports_cte?){true}
2558
+ ds.meta_def(:supports_cte_in_subselect?){false}
2559
+ ds.from(:a).union(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM a UNION SELECT * FROM c'
2560
+ ds.from(:a).except(ds.from(:c).with(:c, ds.from(:d))).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a EXCEPT SELECT * FROM c) AS t1'
2561
+ ds.from(:a).with(:a, ds.from(:b)).intersect(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM a INTERSECT SELECT * FROM c'
2562
+ end
2520
2563
  end
2521
2564
 
2522
2565
  describe "Dataset#[]" do
2523
2566
  before do
2524
- @c = Class.new(Sequel::Dataset) do
2525
- class_variable_set(:@@last_dataset, nil)
2526
-
2527
- def self.last_dataset
2528
- class_variable_get(:@@last_dataset)
2529
- end
2530
-
2531
- def single_record
2532
- self.class.send(:class_variable_set, :@@last_dataset, opts ? clone(opts) : self)
2533
- {1 => 2, 3 => 4}
2534
- end
2535
- end
2536
- @d = @c.new(nil).from(:test)
2567
+ @db = Sequel.mock(:fetch=>{1 => 2, 3 => 4})
2568
+ @d = @db[:items]
2537
2569
  end
2538
2570
 
2539
2571
  specify "should return a single record filtered according to the given conditions" do
2540
2572
  @d[:name => 'didi'].should == {1 => 2, 3 => 4}
2541
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "(name = 'didi')"
2573
+ @db.sqls.should == ["SELECT * FROM items WHERE (name = 'didi') LIMIT 1"]
2542
2574
 
2543
2575
  @d[:id => 5..45].should == {1 => 2, 3 => 4}
2544
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "((id >= 5) AND (id <= 45))"
2576
+ @db.sqls.should == ["SELECT * FROM items WHERE ((id >= 5) AND (id <= 45)) LIMIT 1"]
2545
2577
  end
2546
2578
  end
2547
2579
 
2548
2580
  describe "Dataset#single_record" do
2549
2581
  before do
2550
- @c = Class.new(Sequel::Dataset) do
2551
- def fetch_rows(sql)
2552
- yield sql
2553
- end
2554
- end
2555
- @cc = Class.new(@c) do
2556
- def fetch_rows(sql); end
2557
- end
2558
-
2559
- @d = @c.new(nil).from(:test)
2560
- @e = @cc.new(nil).from(:test)
2582
+ @db = Sequel.mock
2561
2583
  end
2562
2584
 
2563
2585
  specify "should call each with a limit of 1 and return the record" do
2564
- @d.single_record.should == 'SELECT * FROM test LIMIT 1'
2586
+ @db.fetch = {:a=>1}
2587
+ @db[:test].single_record.should == {:a=>1}
2588
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2565
2589
  end
2566
2590
 
2567
2591
  specify "should return nil if no record is present" do
2568
- @e.single_record.should be_nil
2592
+ @db[:test].single_record.should be_nil
2593
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2569
2594
  end
2570
2595
  end
2571
2596
 
2572
2597
  describe "Dataset#single_value" do
2573
2598
  before do
2574
- @c = Class.new(Sequel::Dataset) do
2575
- def fetch_rows(sql)
2576
- yield({1 => sql})
2577
- end
2578
- end
2579
- @cc = Class.new(@c) do
2580
- def fetch_rows(sql); end
2581
- end
2582
-
2583
- @d = @c.new(nil).from(:test)
2584
- @e = @cc.new(nil).from(:test)
2599
+ @db = Sequel.mock
2585
2600
  end
2586
2601
 
2587
2602
  specify "should call each and return the first value of the first record" do
2588
- @d.single_value.should == 'SELECT * FROM test LIMIT 1'
2603
+ @db.fetch = {:a=>1}
2604
+ @db[:test].single_value.should == 1
2605
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2589
2606
  end
2590
2607
 
2591
2608
  specify "should return nil if no records" do
2592
- @e.single_value.should be_nil
2609
+ @db[:test].single_value.should be_nil
2610
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2593
2611
  end
2594
2612
 
2595
2613
  it "should work on a graphed_dataset" do
2596
- @d.should_receive(:columns).twice.and_return([:a])
2597
- @d.graph(@d, [:a], :table_alias=>:test2).single_value.should == 'SELECT test.a, test2.a AS test2_a FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1'
2614
+ @db.fetch = {:a=>1}
2615
+ ds = @db[:test].columns(:a)
2616
+ ds.graph(ds, [:a], :table_alias=>:test2).single_value.should == 1
2617
+ @db.sqls.should == ['SELECT test.a, test2.a AS test2_a FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
2598
2618
  end
2599
2619
  end
2600
2620
 
2601
2621
  describe "Dataset#get" do
2602
2622
  before do
2603
- @c = Class.new(Sequel::Dataset) do
2604
- attr_reader :last_sql
2605
-
2606
- def fetch_rows(sql)
2607
- @last_sql = sql
2608
- yield(:name => sql)
2609
- end
2610
- end
2611
-
2612
- @d = @c.new(nil).from(:test)
2623
+ @d = Sequel.mock(:fetch=>proc{|s| {:name=>s}})[:test]
2613
2624
  end
2614
2625
 
2615
2626
  specify "should select the specified column and fetch its value" do
2616
2627
  @d.get(:name).should == "SELECT name FROM test LIMIT 1"
2617
- @d.get(:abc).should == "SELECT abc FROM test LIMIT 1" # the first available value is returned always
2628
+ @d.get(:abc).should == "SELECT abc FROM test LIMIT 1"
2618
2629
  end
2619
2630
 
2620
2631
  specify "should work with filters" do
@@ -2622,7 +2633,7 @@ describe "Dataset#get" do
2622
2633
  end
2623
2634
 
2624
2635
  specify "should work with aliased fields" do
2625
- @d.get(:x__b.as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
2636
+ @d.get(Sequel.expr(:x__b).as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
2626
2637
  end
2627
2638
 
2628
2639
  specify "should accept a block that yields a virtual row" do
@@ -2633,243 +2644,253 @@ describe "Dataset#get" do
2633
2644
  specify "should raise an error if both a regular argument and block argument are used" do
2634
2645
  proc{@d.get(:name){|o| o.x__b.as(:name)}}.should raise_error(Sequel::Error)
2635
2646
  end
2647
+
2648
+ specify "should support false and nil values" do
2649
+ @d.get(false).should == "SELECT 'f' FROM test LIMIT 1"
2650
+ @d.get(nil).should == "SELECT NULL FROM test LIMIT 1"
2651
+ end
2636
2652
  end
2637
2653
 
2638
2654
  describe "Dataset#set_row_proc" do
2639
2655
  before do
2640
- @c = Class.new(Sequel::Dataset) do
2641
- def fetch_rows(sql, &block)
2642
- # yield a hash with kind as the 1 bit of a number
2643
- (1..10).each {|i| block.call({:kind => i[0]})}
2644
- end
2645
- end
2646
- @dataset = @c.new(nil).from(:items)
2656
+ @db = Sequel.mock(:fetch=>[{:a=>1}, {:a=>2}])
2657
+ @dataset = @db[:items]
2658
+ @dataset.row_proc = proc{|h| h[:der] = h[:a] + 2; h}
2647
2659
  end
2648
2660
 
2649
2661
  specify "should cause dataset to pass all rows through the filter" do
2650
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
2651
-
2652
2662
  rows = @dataset.all
2653
- rows.size.should == 10
2654
-
2655
- rows.each {|r| r[:der].should == (r[:kind] + 2)}
2663
+ rows.map{|h| h[:der]}.should == [3, 4]
2664
+ @db.sqls.should == ['SELECT * FROM items']
2656
2665
  end
2657
2666
 
2658
2667
  specify "should be copied over when dataset is cloned" do
2659
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
2660
-
2661
- @dataset.filter(:a => 1).first.should == {:kind => 1, :der => 3}
2668
+ @dataset.filter(:a => 1).all.should == [{:a=>1, :der=>3}, {:a=>2, :der=>4}]
2662
2669
  end
2663
2670
  end
2664
2671
 
2665
2672
  describe "Dataset#<<" do
2666
2673
  before do
2667
- @d = Sequel::Dataset.new(nil)
2668
- @d.meta_def(:insert) do |*args|
2669
- 1234567890
2670
- end
2674
+ @db = Sequel.mock
2671
2675
  end
2672
-
2676
+
2673
2677
  specify "should call #insert" do
2674
- (@d << {:name => 1}).should == 1234567890
2678
+ @db[:items] << {:name => 1}
2679
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)']
2680
+ end
2681
+
2682
+ specify "should be chainable" do
2683
+ @db[:items] << {:name => 1} << @db[:old_items].select(:name)
2684
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)', 'INSERT INTO items SELECT name FROM old_items']
2675
2685
  end
2676
2686
  end
2677
2687
 
2678
2688
  describe "Dataset#columns" do
2679
2689
  before do
2680
- @dataset = DummyDataset.new(nil).from(:items)
2681
- @dataset.meta_def(:columns=) {|c| @columns = c}
2682
- i = 'a'
2683
- @dataset.meta_def(:each){@columns = select_sql + i; i = i.next}
2690
+ @dataset = Sequel.mock[:items]
2684
2691
  end
2685
2692
 
2686
2693
  specify "should return the value of @columns if @columns is not nil" do
2687
- @dataset.columns = [:a, :b, :c]
2688
- @dataset.columns.should == [:a, :b, :c]
2694
+ @dataset.columns(:a, :b, :c).columns.should == [:a, :b, :c]
2695
+ @dataset.db.sqls.should == []
2689
2696
  end
2690
2697
 
2691
2698
  specify "should attempt to get a single record and return @columns if @columns is nil" do
2692
- @dataset.columns = nil
2693
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2694
- @dataset.opts[:from] = [:nana]
2695
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2699
+ @dataset.db.columns = [:a]
2700
+ @dataset.columns.should == [:a]
2701
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2702
+ end
2703
+
2704
+ specify "should be cleared if you change the selected columns" do
2705
+ @dataset.db.columns = [[:a], [:b]]
2706
+ @dataset.columns.should == [:a]
2707
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2708
+ @dataset.columns.should == [:a]
2709
+ @dataset.db.sqls.should == []
2710
+ ds = @dataset.select{foo{}}
2711
+ ds.columns.should == [:b]
2712
+ @dataset.db.sqls.should == ['SELECT foo() FROM items LIMIT 1']
2713
+ end
2714
+
2715
+ specify "should be cleared if you change the FROM table" do
2716
+ @dataset.db.columns = [[:a], [:b]]
2717
+ @dataset.columns.should == [:a]
2718
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2719
+ ds = @dataset.from(:foo)
2720
+ ds.columns.should == [:b]
2721
+ @dataset.db.sqls.should == ['SELECT * FROM foo LIMIT 1']
2722
+ end
2723
+
2724
+ specify "should be cleared if you join a table to the dataset" do
2725
+ @dataset.db.columns = [[:a], [:a, :b]]
2726
+ @dataset.columns.should == [:a]
2727
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2728
+ ds = @dataset.cross_join(:foo)
2729
+ ds.columns.should == [:a, :b]
2730
+ @dataset.db.sqls.should == ['SELECT * FROM items CROSS JOIN foo LIMIT 1']
2731
+ end
2732
+
2733
+ specify "should be cleared if you set custom SQL for the dataset" do
2734
+ @dataset.db.columns = [[:a], [:b]]
2735
+ @dataset.columns.should == [:a]
2736
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2737
+ ds = @dataset.with_sql('SELECT b FROM foo')
2738
+ ds.columns.should == [:b]
2739
+ @dataset.db.sqls.should == ['SELECT b FROM foo']
2696
2740
  end
2697
2741
 
2698
2742
  specify "should ignore any filters, orders, or DISTINCT clauses" do
2743
+ @dataset.db.columns = [:a]
2699
2744
  @dataset.filter!(:b=>100).order!(:b).distinct!
2700
- @dataset.columns = nil
2701
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2745
+ @dataset.columns.should == [:a]
2746
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2702
2747
  end
2703
2748
  end
2704
2749
 
2705
2750
  describe "Dataset#columns!" do
2706
- before do
2707
- @dataset = DummyDataset.new(nil).from(:items)
2708
- i = 'a'
2709
- @dataset.meta_def(:each){@columns = select_sql + i; i = i.next}
2710
- end
2711
-
2712
2751
  specify "should always attempt to get a record and return @columns" do
2713
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1a'
2714
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1b'
2715
- @dataset.opts[:from] = [:nana]
2716
- @dataset.columns!.should == 'SELECT * FROM nana LIMIT 1c'
2752
+ ds = Sequel.mock(:columns=>[[:a, :b, :c], [:d, :e, :f]])[:items]
2753
+ ds.columns!.should == [:a, :b, :c]
2754
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2755
+ ds.columns!.should == [:d, :e, :f]
2756
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2717
2757
  end
2718
2758
  end
2719
2759
 
2720
2760
  describe "Dataset#import" do
2721
2761
  before do
2722
- @dbc = Class.new(Sequel::Database) do
2723
- attr_reader :sqls
2724
-
2725
- def execute(sql, opts={})
2726
- @sqls ||= []
2727
- @sqls << sql
2728
- end
2729
- alias execute_dui execute
2730
-
2731
- def transaction(opts={})
2732
- @sqls ||= []
2733
- @sqls << 'BEGIN'
2734
- yield
2735
- @sqls << 'COMMIT'
2736
- end
2737
- end
2738
- @db = @dbc.new
2739
-
2740
- @ds = Sequel::Dataset.new(@db).from(:items)
2741
-
2742
- @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2762
+ @db = Sequel.mock
2763
+ @ds = @db[:items]
2743
2764
  end
2744
2765
 
2766
+ specify "should return nil without a query if no values" do
2767
+ @ds.import(['x', 'y'], []).should == nil
2768
+ @db.sqls.should == []
2769
+ end
2770
+
2745
2771
  specify "should accept string keys as column names" do
2746
2772
  @ds.import(['x', 'y'], [[1, 2], [3, 4]])
2747
- @db.sqls.should == [
2748
- 'BEGIN',
2773
+ @db.sqls.should == ['BEGIN',
2749
2774
  "INSERT INTO items (x, y) VALUES (1, 2)",
2750
2775
  "INSERT INTO items (x, y) VALUES (3, 4)",
2751
- 'COMMIT'
2752
- ]
2776
+ 'COMMIT']
2753
2777
  end
2754
2778
 
2755
2779
  specify "should accept a columns array and a values array" do
2756
2780
  @ds.import([:x, :y], [[1, 2], [3, 4]])
2757
- @db.sqls.should == [
2758
- 'BEGIN',
2781
+ @db.sqls.should == ['BEGIN',
2759
2782
  "INSERT INTO items (x, y) VALUES (1, 2)",
2760
2783
  "INSERT INTO items (x, y) VALUES (3, 4)",
2761
- 'COMMIT'
2762
- ]
2784
+ 'COMMIT']
2763
2785
  end
2764
2786
 
2765
2787
  specify "should accept a columns array and a dataset" do
2766
- @ds2 = Sequel::Dataset.new(@db).from(:cats).filter(:purr => true).select(:a, :b)
2788
+ @ds2 = @ds.from(:cats).filter(:purr => true).select(:a, :b)
2767
2789
 
2768
2790
  @ds.import([:x, :y], @ds2)
2769
- @db.sqls.should == [
2770
- 'BEGIN',
2791
+ @db.sqls.should == ['BEGIN',
2771
2792
  "INSERT INTO items (x, y) SELECT a, b FROM cats WHERE (purr IS TRUE)",
2772
- 'COMMIT'
2773
- ]
2793
+ 'COMMIT']
2774
2794
  end
2775
2795
 
2776
2796
  specify "should accept a columns array and a values array with :commit_every option" do
2777
2797
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :commit_every => 3)
2778
- @db.sqls.should == [
2779
- 'BEGIN',
2798
+ @db.sqls.should == ['BEGIN',
2780
2799
  "INSERT INTO items (x, y) VALUES (1, 2)",
2781
2800
  "INSERT INTO items (x, y) VALUES (3, 4)",
2782
2801
  "INSERT INTO items (x, y) VALUES (5, 6)",
2783
- 'COMMIT',
2784
- ]
2802
+ 'COMMIT']
2785
2803
  end
2786
- specify "should accept a columns array and a values array with slice option" do
2804
+ specify "should accept a columns array and a values array with :slice option" do
2787
2805
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2788
- @db.sqls.should == [
2789
- 'BEGIN',
2806
+ @db.sqls.should == ['BEGIN',
2790
2807
  "INSERT INTO items (x, y) VALUES (1, 2)",
2791
2808
  "INSERT INTO items (x, y) VALUES (3, 4)",
2792
2809
  'COMMIT',
2793
2810
  'BEGIN',
2794
2811
  "INSERT INTO items (x, y) VALUES (5, 6)",
2795
- 'COMMIT'
2796
- ]
2812
+ 'COMMIT']
2797
2813
  end
2798
2814
  end
2799
2815
 
2800
2816
  describe "Dataset#multi_insert" do
2801
2817
  before do
2802
- @dbc = Class.new do
2803
- attr_reader :sqls
2804
-
2805
- def execute(sql, opts={})
2806
- @sqls ||= []
2807
- @sqls << sql
2808
- end
2809
- alias execute_dui execute
2810
-
2811
- def transaction(opts={})
2812
- @sqls ||= []
2813
- @sqls << 'BEGIN'
2814
- yield
2815
- @sqls << 'COMMIT'
2816
- end
2817
- end
2818
- @db = @dbc.new
2819
-
2820
- @ds = Sequel::Dataset.new(@db).from(:items)
2821
-
2818
+ @db = Sequel.mock(:servers=>{:s1=>{}})
2819
+ @ds = @db[:items]
2822
2820
  @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2823
2821
  end
2824
2822
 
2823
+ specify "should return nil without a query if no values" do
2824
+ @ds.multi_insert([]).should == nil
2825
+ @db.sqls.should == []
2826
+ end
2827
+
2825
2828
  specify "should issue multiple insert statements inside a transaction" do
2826
2829
  @ds.multi_insert(@list)
2827
- @db.sqls.should == [
2828
- 'BEGIN',
2830
+ @db.sqls.should == ['BEGIN',
2829
2831
  "INSERT INTO items (name) VALUES ('abc')",
2830
2832
  "INSERT INTO items (name) VALUES ('def')",
2831
2833
  "INSERT INTO items (name) VALUES ('ghi')",
2832
- 'COMMIT'
2833
- ]
2834
+ 'COMMIT']
2835
+ end
2836
+
2837
+ specify "should respect :server option" do
2838
+ @ds.multi_insert(@list, :server=>:s1)
2839
+ @db.sqls.should == ['BEGIN -- s1',
2840
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2841
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2842
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2843
+ 'COMMIT -- s1']
2844
+ end
2845
+
2846
+ specify "should respect existing :server option on dataset" do
2847
+ @ds.server(:s1).multi_insert(@list)
2848
+ @db.sqls.should == ['BEGIN -- s1',
2849
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2850
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2851
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2852
+ 'COMMIT -- s1']
2853
+ end
2854
+
2855
+ specify "should respect :return=>:primary_key option" do
2856
+ @db.autoid = 1
2857
+ @ds.multi_insert(@list, :return=>:primary_key).should == [1, 2, 3]
2858
+ @db.sqls.should == ['BEGIN',
2859
+ "INSERT INTO items (name) VALUES ('abc')",
2860
+ "INSERT INTO items (name) VALUES ('def')",
2861
+ "INSERT INTO items (name) VALUES ('ghi')",
2862
+ 'COMMIT']
2834
2863
  end
2835
2864
 
2836
2865
  specify "should handle different formats for tables" do
2837
2866
  @ds = @ds.from(:sch__tab)
2838
2867
  @ds.multi_insert(@list)
2839
- @db.sqls.should == [
2840
- 'BEGIN',
2868
+ @db.sqls.should == ['BEGIN',
2841
2869
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2842
2870
  "INSERT INTO sch.tab (name) VALUES ('def')",
2843
2871
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2844
- 'COMMIT'
2845
- ]
2846
- @db.sqls.clear
2872
+ 'COMMIT']
2847
2873
 
2848
- @ds = @ds.from(:tab.qualify(:sch))
2874
+ @ds = @ds.from(Sequel.qualify(:sch, :tab))
2849
2875
  @ds.multi_insert(@list)
2850
- @db.sqls.should == [
2851
- 'BEGIN',
2876
+ @db.sqls.should == ['BEGIN',
2852
2877
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2853
2878
  "INSERT INTO sch.tab (name) VALUES ('def')",
2854
2879
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2855
- 'COMMIT'
2856
- ]
2857
- @db.sqls.clear
2858
- @ds = @ds.from(:sch__tab.identifier)
2880
+ 'COMMIT']
2881
+
2882
+ @ds = @ds.from(Sequel.identifier(:sch__tab))
2859
2883
  @ds.multi_insert(@list)
2860
- @db.sqls.should == [
2861
- 'BEGIN',
2884
+ @db.sqls.should == ['BEGIN',
2862
2885
  "INSERT INTO sch__tab (name) VALUES ('abc')",
2863
2886
  "INSERT INTO sch__tab (name) VALUES ('def')",
2864
2887
  "INSERT INTO sch__tab (name) VALUES ('ghi')",
2865
- 'COMMIT'
2866
- ]
2888
+ 'COMMIT']
2867
2889
  end
2868
2890
 
2869
2891
  specify "should accept the :commit_every option for committing every x records" do
2870
2892
  @ds.multi_insert(@list, :commit_every => 1)
2871
- @db.sqls.should == [
2872
- 'BEGIN',
2893
+ @db.sqls.should == ['BEGIN',
2873
2894
  "INSERT INTO items (name) VALUES ('abc')",
2874
2895
  'COMMIT',
2875
2896
  'BEGIN',
@@ -2877,36 +2898,31 @@ describe "Dataset#multi_insert" do
2877
2898
  'COMMIT',
2878
2899
  'BEGIN',
2879
2900
  "INSERT INTO items (name) VALUES ('ghi')",
2880
- 'COMMIT'
2881
- ]
2901
+ 'COMMIT']
2882
2902
  end
2883
2903
 
2884
2904
  specify "should accept the :slice option for committing every x records" do
2885
2905
  @ds.multi_insert(@list, :slice => 2)
2886
- @db.sqls.should == [
2887
- 'BEGIN',
2906
+ @db.sqls.should == ['BEGIN',
2888
2907
  "INSERT INTO items (name) VALUES ('abc')",
2889
2908
  "INSERT INTO items (name) VALUES ('def')",
2890
2909
  'COMMIT',
2891
2910
  'BEGIN',
2892
2911
  "INSERT INTO items (name) VALUES ('ghi')",
2893
- 'COMMIT'
2894
- ]
2912
+ 'COMMIT']
2895
2913
  end
2896
2914
 
2897
2915
  specify "should accept string keys as column names" do
2898
2916
  @ds.multi_insert([{'x'=>1, 'y'=>2}, {'x'=>3, 'y'=>4}])
2899
- @db.sqls.should == [
2900
- 'BEGIN',
2901
- "INSERT INTO items (x, y) VALUES (1, 2)",
2902
- "INSERT INTO items (x, y) VALUES (3, 4)",
2903
- 'COMMIT'
2904
- ]
2917
+ sqls = @db.sqls
2918
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls.slice!(1))
2919
+ ["INSERT INTO items (x, y) VALUES (3, 4)", "INSERT INTO items (y, x) VALUES (4, 3)"].should include(sqls.slice!(1))
2920
+ sqls.should == ['BEGIN', 'COMMIT']
2905
2921
  end
2906
2922
 
2907
2923
  specify "should not do anything if no hashes are provided" do
2908
2924
  @ds.multi_insert([])
2909
- @db.sqls.should be_nil
2925
+ @db.sqls.should == []
2910
2926
  end
2911
2927
  end
2912
2928
 
@@ -2936,7 +2952,7 @@ describe "Dataset" do
2936
2952
  end
2937
2953
 
2938
2954
  specify "should support self-changing filter! with block" do
2939
- @d.filter!{:y.sql_number < 2}
2955
+ @d.filter!{y < 2}
2940
2956
  @d.sql.should == "SELECT * FROM x WHERE (y < 2)"
2941
2957
  end
2942
2958
 
@@ -2951,41 +2967,21 @@ describe "Dataset" do
2951
2967
  end
2952
2968
 
2953
2969
  specify "should support chaining of bang methods" do
2954
- @d.order!(:y)
2955
- @d.filter!(:y => 1)
2956
- @d.sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2970
+ @d.order!(:y).filter!(:y => 1).sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2957
2971
  end
2958
2972
  end
2959
2973
 
2960
2974
  describe "Dataset#to_csv" do
2961
2975
  before do
2962
- @c = Class.new(Sequel::Dataset) do
2963
- attr_accessor :data
2964
- attr_accessor :columns
2965
-
2966
- def fetch_rows(sql, &block)
2967
- @data.each(&block)
2968
- end
2969
-
2970
- # naked should return self here because to_csv wants a naked result set.
2971
- def naked
2972
- self
2973
- end
2974
- end
2975
-
2976
- @ds = @c.new(nil).from(:items)
2977
- @ds.columns = [:a, :b, :c]
2978
- @ds.data = [ {:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>9} ]
2976
+ @ds = Sequel.mock(:fetch=>[{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>9}])[:items].columns(:a, :b, :c)
2979
2977
  end
2980
2978
 
2981
2979
  specify "should format a CSV representation of the records" do
2982
- @ds.to_csv.should ==
2983
- "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2980
+ @ds.to_csv.should == "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2984
2981
  end
2985
2982
 
2986
2983
  specify "should exclude column titles if so specified" do
2987
- @ds.to_csv(false).should ==
2988
- "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2984
+ @ds.to_csv(false).should == "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2989
2985
  end
2990
2986
  end
2991
2987
 
@@ -3007,7 +3003,7 @@ describe "Dataset#update_sql" do
3007
3003
  end
3008
3004
 
3009
3005
  specify "should accept array subscript references" do
3010
- @ds.update_sql((:day.sql_subscript(1)) => 'd').should == "UPDATE items SET day[1] = 'd'"
3006
+ @ds.update_sql((Sequel.subscript(:day, 1)) => 'd').should == "UPDATE items SET day[1] = 'd'"
3011
3007
  end
3012
3008
  end
3013
3009
 
@@ -3025,7 +3021,7 @@ describe "Dataset#insert_sql" do
3025
3021
  end
3026
3022
 
3027
3023
  specify "should accept array subscript references" do
3028
- @ds.insert_sql((:day.sql_subscript(1)) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
3024
+ @ds.insert_sql((Sequel.subscript(:day, 1)) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
3029
3025
  end
3030
3026
 
3031
3027
  specify "should raise an Error if the dataset has no sources" do
@@ -3063,164 +3059,117 @@ describe "Dataset#insert_sql" do
3063
3059
  end
3064
3060
 
3065
3061
  specify "should accept a single LiteralString" do
3066
- @ds.insert_sql('VALUES (1, 2, 3)'.lit).should == "INSERT INTO items VALUES (1, 2, 3)"
3062
+ @ds.insert_sql(Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items VALUES (1, 2, 3)"
3067
3063
  end
3068
3064
 
3069
3065
  specify "should accept an array of columns and an LiteralString" do
3070
- @ds.insert_sql([:a, :b, :c], 'VALUES (1, 2, 3)'.lit).should == "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
3071
- end
3072
-
3073
- specify "should accept an object that responds to values and returns a hash by using that hash as the columns and values" do
3074
- o = Object.new
3075
- def o.values; {:c=>'d'}; end
3076
- @ds.insert_sql(o).should == "INSERT INTO items (c) VALUES ('d')"
3077
- end
3078
-
3079
- specify "should accept an object that responds to values and returns something other than a hash by using the object itself as a single value" do
3080
- o = Date.civil(2000, 1, 1)
3081
- def o.values; self; end
3082
- @ds.insert_sql(o).should == "INSERT INTO items VALUES ('2000-01-01')"
3066
+ @ds.insert_sql([:a, :b, :c], Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
3083
3067
  end
3084
3068
  end
3085
3069
 
3086
- class DummyMummyDataset < Sequel::Dataset
3087
- def first
3088
- raise if @opts[:from] == [:a]
3089
- true
3070
+ describe "Dataset#inspect" do
3071
+ before do
3072
+ class ::InspectDataset < Sequel::Dataset; end
3090
3073
  end
3091
- end
3092
-
3093
- class DummyMummyDatabase < Sequel::Database
3094
- attr_reader :sqls
3095
-
3096
- def execute(sql)
3097
- @sqls ||= []
3098
- @sqls << sql
3074
+ after do
3075
+ Object.send(:remove_const, :InspectDataset) if defined?(::InspectDataset)
3099
3076
  end
3100
-
3101
- def transaction; yield; end
3102
3077
 
3103
- def dataset
3104
- DummyMummyDataset.new(self)
3078
+ specify "should include the class name and the corresponding SQL statement" do
3079
+ Sequel::Dataset.new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
3080
+ InspectDataset.new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3105
3081
  end
3106
- end
3107
3082
 
3108
- describe "Dataset#inspect" do
3109
- before do
3110
- @ds = Sequel::Dataset.new(nil).from(:blah)
3111
- end
3112
-
3113
- specify "should include the class name and the corresponding SQL statement" do
3114
- @ds.inspect.should == '#<%s: %s>' % [@ds.class.to_s, @ds.sql.inspect]
3083
+ specify "should skip anonymous classes" do
3084
+ Class.new(Class.new(Sequel::Dataset)).new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
3085
+ Class.new(InspectDataset).new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3115
3086
  end
3116
3087
  end
3117
3088
 
3118
3089
  describe "Dataset#all" do
3119
3090
  before do
3120
- @c = Class.new(Sequel::Dataset) do
3121
- def fetch_rows(sql, &block)
3122
- block.call({:x => 1, :y => 2})
3123
- block.call({:x => 3, :y => 4})
3124
- block.call(sql)
3125
- end
3126
- end
3127
- @dataset = @c.new(nil).from(:items)
3091
+ @dataset = Sequel.mock(:fetch=>[{:x => 1, :y => 2}, {:x => 3, :y => 4}])[:items]
3128
3092
  end
3129
3093
 
3130
3094
  specify "should return an array with all records" do
3131
- @dataset.all.should == [
3132
- {:x => 1, :y => 2},
3133
- {:x => 3, :y => 4},
3134
- "SELECT * FROM items"
3135
- ]
3095
+ @dataset.all.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
3096
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3136
3097
  end
3137
3098
 
3138
3099
  specify "should iterate over the array if a block is given" do
3139
3100
  a = []
3140
-
3141
- @dataset.all do |r|
3142
- a << (r.is_a?(Hash) ? r[:x] : r)
3143
- end
3144
-
3145
- a.should == [1, 3, "SELECT * FROM items"]
3101
+ @dataset.all{|r| a << r.values_at(:x, :y)}.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
3102
+ a.should == [[1, 2], [3, 4]]
3103
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3146
3104
  end
3147
3105
  end
3148
3106
 
3149
3107
  describe "Dataset#grep" do
3150
3108
  before do
3151
- @ds = Sequel::Dataset.new(nil).from(:posts)
3109
+ @ds = Sequel.mock[:posts]
3152
3110
  end
3153
3111
 
3154
3112
  specify "should format a SQL filter correctly" do
3155
- @ds.grep(:title, 'ruby').sql.should ==
3156
- "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
3113
+ @ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
3157
3114
  end
3158
3115
 
3159
3116
  specify "should support multiple columns" do
3160
- @ds.grep([:title, :body], 'ruby').sql.should ==
3161
- "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
3117
+ @ds.grep([:title, :body], 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
3162
3118
  end
3163
3119
 
3164
3120
  specify "should support multiple search terms" do
3165
- @ds.grep(:title, ['abc', 'def']).sql.should ==
3166
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
3121
+ @ds.grep(:title, ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
3167
3122
  end
3168
3123
 
3169
3124
  specify "should support multiple columns and search terms" do
3170
- @ds.grep([:title, :body], ['abc', 'def']).sql.should ==
3171
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
3125
+ @ds.grep([:title, :body], ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
3172
3126
  end
3173
3127
 
3174
3128
  specify "should support the :all_patterns option" do
3175
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should ==
3176
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
3129
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
3177
3130
  end
3178
3131
 
3179
3132
  specify "should support the :all_columns option" do
3180
- @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should ==
3181
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
3133
+ @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
3182
3134
  end
3183
3135
 
3184
3136
  specify "should support the :case_insensitive option" do
3185
- @ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should ==
3186
- "SELECT * FROM posts WHERE ((title ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
3137
+ @ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
3187
3138
  end
3188
3139
 
3189
3140
  specify "should support the :all_patterns and :all_columns options together" do
3190
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should ==
3191
- "SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
3141
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
3192
3142
  end
3193
3143
 
3194
3144
  specify "should support the :all_patterns and :case_insensitive options together" do
3195
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should ==
3196
- "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
3145
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
3197
3146
  end
3198
3147
 
3199
3148
  specify "should support the :all_columns and :case_insensitive options together" do
3200
- @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should ==
3201
- "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
3149
+ @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
3202
3150
  end
3203
3151
 
3204
3152
  specify "should support the :all_patterns, :all_columns, and :case_insensitive options together" do
3205
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should ==
3206
- "SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
3153
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
3207
3154
  end
3208
3155
 
3209
- specify "should support regexps though the database may not support it" do
3210
- @ds.grep(:title, /ruby/).sql.should ==
3211
- "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
3156
+ specify "should not support regexps if the database doesn't supports it" do
3157
+ proc{@ds.grep(:title, /ruby/).sql}.should raise_error(Sequel::InvalidOperation)
3158
+ proc{@ds.grep(:title, [/^ruby/, 'ruby']).sql}.should raise_error(Sequel::InvalidOperation)
3159
+ end
3212
3160
 
3213
- @ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
3214
- "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3161
+ specify "should support regexps if the database supports it" do
3162
+ def @ds.supports_regexp?; true end
3163
+ @ds.grep(:title, /ruby/).sql.should == "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
3164
+ @ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3215
3165
  end
3216
3166
 
3217
3167
  specify "should support searching against other columns" do
3218
- @ds.grep(:title, :body).sql.should ==
3219
- "SELECT * FROM posts WHERE ((title LIKE body))"
3168
+ @ds.grep(:title, :body).sql.should == "SELECT * FROM posts WHERE ((title LIKE body))"
3220
3169
  end
3221
3170
  end
3222
3171
 
3223
- describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #execute" do
3172
+ describe "Dataset default #fetch_rows, #insert, #update, #delete, #with_sql_delete, #truncate, #execute" do
3224
3173
  before do
3225
3174
  @db = Sequel::Database.new
3226
3175
  @ds = @db[:items]
@@ -3237,6 +3186,14 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
3237
3186
  @ds.delete
3238
3187
  end
3239
3188
 
3189
+ specify "#with_sql_delete should execute delete SQL" do
3190
+ sql = 'DELETE FROM foo'
3191
+ @db.should_receive(:execute).once.with(sql, :server=>:default)
3192
+ @ds.with_sql_delete(sql)
3193
+ @db.should_receive(:execute_dui).once.with(sql, :server=>:default)
3194
+ @ds.with_sql_delete(sql)
3195
+ end
3196
+
3240
3197
  specify "#insert should execute insert SQL" do
3241
3198
  @db.should_receive(:execute).once.with('INSERT INTO items DEFAULT VALUES', :server=>:default)
3242
3199
  @ds.insert([])
@@ -3260,6 +3217,7 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
3260
3217
 
3261
3218
  specify "#truncate should raise an InvalidOperation exception if the dataset is filtered" do
3262
3219
  proc{@ds.filter(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3220
+ proc{@ds.having(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3263
3221
  end
3264
3222
 
3265
3223
  specify "#execute should execute the SQL on the database" do
@@ -3270,58 +3228,80 @@ end
3270
3228
 
3271
3229
  describe "Dataset prepared statements and bound variables " do
3272
3230
  before do
3273
- @db = Sequel::Database.new
3274
- @db.meta_def(:sqls){@sqls||=[]}
3275
- def @db.execute(sql, opts={})
3276
- sqls << sql
3277
- end
3278
- def @db.dataset
3279
- ds = super()
3280
- def ds.fetch_rows(sql, &block)
3281
- execute(sql)
3282
- end
3283
- ds
3284
- end
3231
+ @db = Sequel.mock
3285
3232
  @ds = @db[:items]
3233
+ @ds.meta_def(:insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
3286
3234
  end
3287
3235
 
3288
3236
  specify "#call should take a type and bind hash and interpolate it" do
3237
+ @ds.filter(:num=>:$n).call(:each, :n=>1)
3289
3238
  @ds.filter(:num=>:$n).call(:select, :n=>1)
3239
+ @ds.filter(:num=>:$n).call([:map, :a], :n=>1)
3240
+ @ds.filter(:num=>:$n).call([:to_hash, :a, :b], :n=>1)
3241
+ @ds.filter(:num=>:$n).call([:to_hash_groups, :a, :b], :n=>1)
3290
3242
  @ds.filter(:num=>:$n).call(:first, :n=>1)
3291
3243
  @ds.filter(:num=>:$n).call(:delete, :n=>1)
3292
3244
  @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3293
3245
  @ds.call(:insert, {:n=>1}, :num=>:$n)
3294
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3246
+ @ds.call(:insert_select, {:n=>1}, :num=>:$n)
3247
+ @db.sqls.should == [
3248
+ 'SELECT * FROM items WHERE (num = 1)',
3249
+ 'SELECT * FROM items WHERE (num = 1)',
3250
+ 'SELECT * FROM items WHERE (num = 1)',
3251
+ 'SELECT * FROM items WHERE (num = 1)',
3252
+ 'SELECT * FROM items WHERE (num = 1)',
3295
3253
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3296
3254
  'DELETE FROM items WHERE (num = 1)',
3297
3255
  'UPDATE items SET num = 2 WHERE (num = 1)',
3298
- 'INSERT INTO items (num) VALUES (1)']
3256
+ 'INSERT INTO items (num) VALUES (1)',
3257
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3299
3258
  end
3300
3259
 
3301
3260
  specify "#prepare should take a type and name and store it in the database for later use with call" do
3302
3261
  pss = []
3262
+ pss << @ds.filter(:num=>:$n).prepare(:each, :en)
3303
3263
  pss << @ds.filter(:num=>:$n).prepare(:select, :sn)
3264
+ pss << @ds.filter(:num=>:$n).prepare([:map, :a], :sm)
3265
+ pss << @ds.filter(:num=>:$n).prepare([:to_hash, :a, :b], :sh)
3266
+ pss << @ds.filter(:num=>:$n).prepare([:to_hash_groups, :a, :b], :shg)
3304
3267
  pss << @ds.filter(:num=>:$n).prepare(:first, :fn)
3305
3268
  pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
3306
3269
  pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
3307
3270
  pss << @ds.prepare(:insert, :in, :num=>:$n)
3308
- @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :sn, :un]
3309
- [:sn, :fn, :dn, :un, :in].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3271
+ pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
3272
+ @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :en, :fn, :in, :ins, :sh, :shg, :sm, :sn, :un]
3273
+ [:en, :sn, :sm, :sh, :shg, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3274
+ @db.call(:en, :n=>1){}
3310
3275
  @db.call(:sn, :n=>1)
3276
+ @db.call(:sm, :n=>1)
3277
+ @db.call(:sh, :n=>1)
3278
+ @db.call(:shg, :n=>1)
3311
3279
  @db.call(:fn, :n=>1)
3312
3280
  @db.call(:dn, :n=>1)
3313
3281
  @db.call(:un, :n=>1, :n2=>2)
3314
3282
  @db.call(:in, :n=>1)
3315
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3283
+ @db.call(:ins, :n=>1)
3284
+ @db.sqls.should == [
3285
+ 'SELECT * FROM items WHERE (num = 1)',
3286
+ 'SELECT * FROM items WHERE (num = 1)',
3287
+ 'SELECT * FROM items WHERE (num = 1)',
3288
+ 'SELECT * FROM items WHERE (num = 1)',
3289
+ 'SELECT * FROM items WHERE (num = 1)',
3316
3290
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3317
3291
  'DELETE FROM items WHERE (num = 1)',
3318
3292
  'UPDATE items SET num = 2 WHERE (num = 1)',
3319
- 'INSERT INTO items (num) VALUES (1)']
3293
+ 'INSERT INTO items (num) VALUES (1)',
3294
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3320
3295
  end
3321
3296
 
3297
+ specify "#call should default to using :all if an invalid type is given" do
3298
+ @ds.filter(:num=>:$n).call(:select_all, :n=>1)
3299
+ @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
3300
+ end
3301
+
3322
3302
  specify "#inspect should indicate it is a prepared statement with the prepared SQL" do
3323
3303
  @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.should == \
3324
- '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3304
+ '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3325
3305
  end
3326
3306
 
3327
3307
  specify "should handle literal strings" do
@@ -3329,6 +3309,13 @@ describe "Dataset prepared statements and bound variables " do
3329
3309
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
3330
3310
  end
3331
3311
 
3312
+ specify "should handle columns on prepared statements correctly" do
3313
+ @db.columns = [:num]
3314
+ @ds.meta_def(:select_where_sql){|sql| super(sql); sql << " OR #{columns.first} = 1" if opts[:where]}
3315
+ @ds.filter(:num=>:$n).prepare(:select, :sn).sql.should == 'SELECT * FROM items WHERE (num = $n) OR num = 1'
3316
+ @db.sqls.should == ['SELECT * FROM items LIMIT 1']
3317
+ end
3318
+
3332
3319
  specify "should handle datasets using static sql and placeholders" do
3333
3320
  @db["SELECT * FROM items WHERE (num = ?)", :$n].call(:select, :n=>1)
3334
3321
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
@@ -3357,23 +3344,16 @@ end
3357
3344
 
3358
3345
  describe Sequel::Dataset::UnnumberedArgumentMapper do
3359
3346
  before do
3360
- @db = Sequel::Database.new
3361
- @db.meta_def(:sqls){@sqls||=[]}
3362
- def @db.execute(sql, opts={})
3363
- sqls << [sql, *opts[:arguments]]
3364
- end
3347
+ @db = Sequel.mock
3365
3348
  @ds = @db[:items].filter(:num=>:$n)
3366
- def @ds.fetch_rows(sql, &block)
3367
- execute(sql)
3368
- end
3369
3349
  def @ds.execute(sql, opts={}, &block)
3370
- @db.execute(sql, {:arguments=>bind_arguments}.merge(opts))
3350
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3371
3351
  end
3372
- def @ds.execute_dui(*args, &block)
3373
- execute(*args, &block)
3352
+ def @ds.execute_dui(sql, opts={}, &block)
3353
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3374
3354
  end
3375
- def @ds.execute_insert(*args, &block)
3376
- execute(*args, &block)
3355
+ def @ds.execute_insert(sql, opts={}, &block)
3356
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3377
3357
  end
3378
3358
  @ps = []
3379
3359
  @ps << @ds.prepare(:select, :s)
@@ -3386,64 +3366,51 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
3386
3366
  end
3387
3367
 
3388
3368
  specify "#inspect should show the actual SQL submitted to the database" do
3389
- @ps.first.inspect.should == '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3369
+ @ps.first.inspect.should == '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3390
3370
  end
3391
3371
 
3392
- specify "should submitted the SQL to the database with placeholders and bind variables" do
3372
+ specify "should submit the SQL to the database with placeholders and bind variables" do
3393
3373
  @ps.each{|p| p.prepared_sql; p.call(:n=>1)}
3394
- @db.sqls.should == [["SELECT * FROM items WHERE (num = ?)", 1],
3395
- ["SELECT * FROM items WHERE (num = ?)", 1],
3396
- ["SELECT * FROM items WHERE (num = ?) LIMIT 1", 1],
3397
- ["DELETE FROM items WHERE (num = ?)", 1],
3398
- ["INSERT INTO items (num) VALUES (?)", 1],
3399
- ["UPDATE items SET num = ? WHERE (num = ?)", 1, 1],
3400
- ]
3374
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]",
3375
+ "SELECT * FROM items WHERE (num = ?) -- args: [1]",
3376
+ "SELECT * FROM items WHERE (num = ?) LIMIT 1 -- args: [1]",
3377
+ "DELETE FROM items WHERE (num = ?) -- args: [1]",
3378
+ "INSERT INTO items (num) VALUES (?) -- args: [1]",
3379
+ "UPDATE items SET num = ? WHERE (num = ?) -- args: [1, 1]"]
3380
+ end
3381
+
3382
+ specify "should handle unrecognized statement types as :all" do
3383
+ ps = @ds.prepare(:select_all, :s)
3384
+ ps.extend(Sequel::Dataset::UnnumberedArgumentMapper)
3385
+ ps.prepared_sql
3386
+ ps.call(:n=>1)
3387
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]"]
3401
3388
  end
3402
3389
  end
3403
3390
 
3404
3391
  describe "Sequel::Dataset#server" do
3405
3392
  specify "should set the server to use for the dataset" do
3406
- @db = Sequel::Database.new
3393
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}, :d=>{}, :u=>{}})
3407
3394
  @ds = @db[:items].server(:s)
3408
- sqls = []
3409
- @db.meta_def(:execute) do |sql, opts|
3410
- sqls << [sql, opts[:server]]
3411
- end
3412
- def @ds.fetch_rows(sql, &block)
3413
- execute(sql)
3414
- end
3415
3395
  @ds.all
3416
3396
  @ds.server(:i).insert(:a=>1)
3417
3397
  @ds.server(:d).delete
3418
- @ds.server(:u).update(:a=>:a+1)
3419
- sqls.should == [['SELECT * FROM items', :s],
3420
- ['INSERT INTO items (a) VALUES (1)', :i],
3421
- ['DELETE FROM items', :d],
3422
- ['UPDATE items SET a = (a + 1)', :u]]
3398
+ @ds.server(:u).update(:a=>Sequel.expr(:a)+1)
3399
+ @db.sqls.should == ['SELECT * FROM items -- s', 'INSERT INTO items (a) VALUES (1) -- i', 'DELETE FROM items -- d', 'UPDATE items SET a = (a + 1) -- u']
3423
3400
  end
3424
3401
  end
3425
3402
 
3426
3403
  describe "Sequel::Dataset#each_server" do
3427
- before do
3428
- @db = Sequel::Database.new(:servers=>{:s=>{}, :i=>{}})
3429
- @ds = @db[:items]
3430
- sqls = @sqls = []
3431
- @db.meta_def(:execute) do |sql, opts|
3432
- sqls << [sql, opts[:server]]
3433
- end
3434
- def @ds.fetch_rows(sql, &block)
3435
- execute(sql)
3436
- end
3437
- end
3438
-
3439
3404
  specify "should yield a dataset for each server" do
3405
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}})
3406
+ @ds = @db[:items]
3440
3407
  @ds.each_server do |ds|
3441
3408
  ds.should be_a_kind_of(Sequel::Dataset)
3442
3409
  ds.should_not == @ds
3443
3410
  ds.sql.should == @ds.sql
3444
3411
  ds.all
3445
3412
  end
3446
- @sqls.sort_by{|sql, s| s.to_s}.should == [['SELECT * FROM items', :default], ['SELECT * FROM items', :i], ['SELECT * FROM items', :s]]
3413
+ @db.sqls.sort.should == ['SELECT * FROM items', 'SELECT * FROM items -- i', 'SELECT * FROM items -- s']
3447
3414
  end
3448
3415
  end
3449
3416
 
@@ -3493,23 +3460,23 @@ end
3493
3460
 
3494
3461
  describe "Sequel::Dataset#qualify" do
3495
3462
  specify "should qualify to the given table" do
3496
- MockDatabase.new[:t].filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3463
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3497
3464
  end
3498
3465
 
3499
3466
  specify "should qualify to the first source if no table if given" do
3500
- MockDatabase.new[:t].filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
3467
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
3501
3468
  end
3502
3469
  end
3503
3470
 
3504
3471
  describe "Sequel::Dataset#qualify_to" do
3505
3472
  specify "should qualify to the given table" do
3506
- MockDatabase.new[:t].filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3473
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3507
3474
  end
3508
3475
  end
3509
3476
 
3510
3477
  describe "Sequel::Dataset#qualify_to_first_source" do
3511
3478
  before do
3512
- @ds = MockDatabase.new[:t]
3479
+ @ds = Sequel::Database.new[:t]
3513
3480
  end
3514
3481
 
3515
3482
  specify "should qualify_to the first source" do
@@ -3519,13 +3486,11 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3519
3486
  end
3520
3487
 
3521
3488
  specify "should handle the select, order, where, having, and group options/clauses" do
3522
- @ds.select(:a).filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
3523
- 'SELECT t.a FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3489
+ @ds.select(:a).filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == 'SELECT t.a FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3524
3490
  end
3525
3491
 
3526
3492
  specify "should handle the select using a table.* if all columns are currently selected" do
3527
- @ds.filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
3528
- 'SELECT t.* FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3493
+ @ds.filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3529
3494
  end
3530
3495
 
3531
3496
  specify "should handle hashes in select option" do
@@ -3541,7 +3506,7 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3541
3506
  end
3542
3507
 
3543
3508
  specify "should handle hashes" do
3544
- @ds.select({:b=>{:c=>1}}.case(false)).qualify_to_first_source.sql.should == "SELECT (CASE WHEN t.b THEN (t.c = 1) ELSE 'f' END) FROM t"
3509
+ @ds.select(Sequel.case({:b=>{:c=>1}}, false)).qualify_to_first_source.sql.should == "SELECT (CASE WHEN t.b THEN (t.c = 1) ELSE 'f' END) FROM t"
3545
3510
  end
3546
3511
 
3547
3512
  specify "should handle SQL::Identifiers" do
@@ -3549,15 +3514,15 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3549
3514
  end
3550
3515
 
3551
3516
  specify "should handle SQL::OrderedExpressions" do
3552
- @ds.order(:a.desc, :b.asc).qualify_to_first_source.sql.should == 'SELECT t.* FROM t ORDER BY t.a DESC, t.b ASC'
3517
+ @ds.order(Sequel.desc(:a), Sequel.asc(:b)).qualify_to_first_source.sql.should == 'SELECT t.* FROM t ORDER BY t.a DESC, t.b ASC'
3553
3518
  end
3554
3519
 
3555
3520
  specify "should handle SQL::AliasedExpressions" do
3556
- @ds.select(:a.as(:b)).qualify_to_first_source.sql.should == 'SELECT t.a AS b FROM t'
3521
+ @ds.select(Sequel.expr(:a).as(:b)).qualify_to_first_source.sql.should == 'SELECT t.a AS b FROM t'
3557
3522
  end
3558
3523
 
3559
3524
  specify "should handle SQL::CaseExpressions" do
3560
- @ds.filter{{a=>b}.case(c, d)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (CASE t.d WHEN t.a THEN t.b ELSE t.c END)'
3525
+ @ds.filter{Sequel.case({a=>b}, c, d)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (CASE t.d WHEN t.a THEN t.b ELSE t.c END)'
3561
3526
  end
3562
3527
 
3563
3528
  specify "should handle SQL:Casts" do
@@ -3573,7 +3538,7 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3573
3538
  end
3574
3539
 
3575
3540
  specify "should handle SQL::ValueLists" do
3576
- @ds.filter(:a=>[:b, :c].sql_value_list).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.c))'
3541
+ @ds.filter(:a=>Sequel.value_list([:b, :c])).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.c))'
3577
3542
  end
3578
3543
 
3579
3544
  specify "should handle SQL::Subscripts" do
@@ -3588,20 +3553,97 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3588
3553
  @ds.filter(':a > :b', :a=>:c, :b=>1).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.c > 1)'
3589
3554
  end
3590
3555
 
3556
+ specify "should handle SQL::Wrappers" do
3557
+ @ds.filter(Sequel::SQL::Wrapper.new(:a)).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE t.a'
3558
+ end
3559
+
3591
3560
  specify "should handle SQL::WindowFunctions" do
3592
3561
  @ds.meta_def(:supports_window_functions?){true}
3593
3562
  @ds.select{sum(:over, :args=>:a, :partition=>:b, :order=>:c){}}.qualify_to_first_source.sql.should == 'SELECT sum(t.a) OVER (PARTITION BY t.b ORDER BY t.c) FROM t'
3594
3563
  end
3595
3564
 
3596
3565
  specify "should handle all other objects by returning them unchanged" do
3597
- @ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('a > ?'.lit(1)).having(false).qualify_to_first_source.sql.should == \
3598
- "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
3566
+ @ds.select("a").filter{a(3)}.filter('blah').order(Sequel.lit('true')).group(Sequel.lit('a > ?', 1)).having(false).qualify_to_first_source.sql.should == "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
3567
+ end
3568
+ end
3569
+
3570
+ describe "Sequel::Dataset#unbind" do
3571
+ before do
3572
+ @ds = Sequel::Database.new[:t]
3573
+ @u = proc{|ds| ds, bv = ds.unbind; [ds.sql, bv]}
3574
+ end
3575
+
3576
+ specify "should unbind values assigned to equality and inequality statements" do
3577
+ @ds.filter(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo = $foo)"
3578
+ @ds.exclude(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo != $foo)"
3579
+ @ds.filter{foo > 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo > $foo)"
3580
+ @ds.filter{foo >= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo >= $foo)"
3581
+ @ds.filter{foo < 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo < $foo)"
3582
+ @ds.filter{foo <= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo <= $foo)"
3583
+ end
3584
+
3585
+ specify "should return variables that could be used bound to recreate the previous query" do
3586
+ @ds.filter(:foo=>1).unbind.last.should == {:foo=>1}
3587
+ @ds.exclude(:foo=>1).unbind.last.should == {:foo=>1}
3588
+ end
3589
+
3590
+ specify "should handle numerics, strings, dates, times, and datetimes" do
3591
+ @u[@ds.filter(:foo=>1)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1}]
3592
+ @u[@ds.filter(:foo=>1.0)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1.0}]
3593
+ @u[@ds.filter(:foo=>BigDecimal.new('1.0'))].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>BigDecimal.new('1.0')}]
3594
+ @u[@ds.filter(:foo=>'a')].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>'a'}]
3595
+ @u[@ds.filter(:foo=>Date.today)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>Date.today}]
3596
+ t = Time.now
3597
+ @u[@ds.filter(:foo=>t)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>t}]
3598
+ dt = DateTime.now
3599
+ @u[@ds.filter(:foo=>dt)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>dt}]
3600
+ end
3601
+
3602
+ specify "should not unbind literal strings" do
3603
+ @u[@ds.filter(:foo=>Sequel.lit('a'))].should == ["SELECT * FROM t WHERE (foo = a)", {}]
3604
+ end
3605
+
3606
+ specify "should not unbind Identifiers, QualifiedIdentifiers, or Symbols used as booleans" do
3607
+ @u[@ds.filter(:foo).filter{bar}.filter{foo__bar}].should == ["SELECT * FROM t WHERE (foo AND bar AND foo.bar)", {}]
3608
+ end
3609
+
3610
+ specify "should not unbind for values it doesn't understand" do
3611
+ @u[@ds.filter(:foo=>Class.new{def sql_literal(ds) 'bar' end}.new)].should == ["SELECT * FROM t WHERE (foo = bar)", {}]
3612
+ end
3613
+
3614
+ specify "should handle QualifiedIdentifiers" do
3615
+ @u[@ds.filter{foo__bar > 1}].should == ["SELECT * FROM t WHERE (foo.bar > $foo.bar)", {:"foo.bar"=>1}]
3616
+ end
3617
+
3618
+ specify "should handle wrapped objects" do
3619
+ @u[@ds.filter{Sequel::SQL::Wrapper.new(foo__bar) > Sequel::SQL::Wrapper.new(1)}].should == ["SELECT * FROM t WHERE (foo.bar > $foo.bar)", {:"foo.bar"=>1}]
3620
+ end
3621
+
3622
+ specify "should handle deep nesting" do
3623
+ @u[@ds.filter{foo > 1}.and{bar < 2}.or(:baz=>3).and(Sequel.case({~Sequel.expr(:x=>4)=>true}, false))].should == ["SELECT * FROM t WHERE ((((foo > $foo) AND (bar < $bar)) OR (baz = $baz)) AND (CASE WHEN (x != $x) THEN 't' ELSE 'f' END))", {:foo=>1, :bar=>2, :baz=>3, :x=>4}]
3624
+ end
3625
+
3626
+ specify "should handle JOIN ON" do
3627
+ @u[@ds.cross_join(:x).join(:a, [:u]).join(:b, [[:c, :d], [:e,1]])].should == ["SELECT * FROM t CROSS JOIN x INNER JOIN a USING (u) INNER JOIN b ON ((b.c = a.d) AND (b.e = $b.e))", {:"b.e"=>1}]
3628
+ end
3629
+
3630
+ specify "should raise an UnbindDuplicate exception if same variable is used with multiple different values" do
3631
+ proc{@ds.filter(:foo=>1).or(:foo=>2).unbind}.should raise_error(Sequel::UnbindDuplicate)
3632
+ end
3633
+
3634
+ specify "should handle case where the same variable has the same value in multiple places " do
3635
+ @u[@ds.filter(:foo=>1).or(:foo=>1)].should == ["SELECT * FROM t WHERE ((foo = $foo) OR (foo = $foo))", {:foo=>1}]
3636
+ end
3637
+
3638
+ specify "should raise Error for unhandled objects inside Identifiers and QualifiedIndentifiers" do
3639
+ proc{@ds.filter(Sequel::SQL::Identifier.new([]) > 1).unbind}.should raise_error(Sequel::Error)
3640
+ proc{@ds.filter{foo.qualify({}) > 1}.unbind}.should raise_error(Sequel::Error)
3599
3641
  end
3600
3642
  end
3601
3643
 
3602
3644
  describe "Sequel::Dataset #with and #with_recursive" do
3603
3645
  before do
3604
- @db = MockDatabase.new
3646
+ @db = Sequel::Database.new
3605
3647
  @ds = @db[:t]
3606
3648
  end
3607
3649
 
@@ -3633,56 +3675,72 @@ describe "Sequel::Dataset #with and #with_recursive" do
3633
3675
  proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
3634
3676
  proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
3635
3677
  end
3678
+
3679
+ specify "#with should work on insert, update, and delete statements if they support it" do
3680
+ [:insert, :update, :delete].each do |m|
3681
+ @ds.meta_def(:"#{m}_clause_methods"){[:"#{m}_with_sql"] + super()}
3682
+ end
3683
+ @ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
3684
+ @ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
3685
+ @ds.with(:t, @db[:x]).delete_sql.should == 'WITH t AS (SELECT * FROM x) DELETE FROM t'
3686
+ end
3687
+
3688
+ specify "should hoist WITH clauses in given dataset(s) if dataset doesn't support WITH in subselect" do
3689
+ @ds.meta_def(:supports_cte?){true}
3690
+ @ds.meta_def(:supports_cte_in_subselect?){false}
3691
+ @ds.with(:t, @ds.from(:s).with(:s, @ds.from(:r))).sql.should == 'WITH s AS (SELECT * FROM r), t AS (SELECT * FROM s) SELECT * FROM t'
3692
+ @ds.with_recursive(:t, @ds.from(:s).with(:s, @ds.from(:r)), @ds.from(:q).with(:q, @ds.from(:p))).sql.should == 'WITH s AS (SELECT * FROM r), q AS (SELECT * FROM p), t AS (SELECT * FROM s UNION ALL SELECT * FROM q) SELECT * FROM t'
3693
+ end
3636
3694
  end
3637
3695
 
3638
3696
  describe Sequel::SQL::Constants do
3639
3697
  before do
3640
- @db = MockDatabase.new
3698
+ @db = Sequel::Database.new
3641
3699
  end
3642
3700
 
3643
3701
  it "should have CURRENT_DATE" do
3644
- @db.literal(Sequel::SQL::Constants::CURRENT_DATE) == 'CURRENT_DATE'
3645
- @db.literal(Sequel::CURRENT_DATE) == 'CURRENT_DATE'
3702
+ @db.literal(Sequel::SQL::Constants::CURRENT_DATE).should == 'CURRENT_DATE'
3703
+ @db.literal(Sequel::CURRENT_DATE).should == 'CURRENT_DATE'
3646
3704
  end
3647
3705
 
3648
3706
  it "should have CURRENT_TIME" do
3649
- @db.literal(Sequel::SQL::Constants::CURRENT_TIME) == 'CURRENT_TIME'
3650
- @db.literal(Sequel::CURRENT_TIME) == 'CURRENT_TIME'
3707
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIME).should == 'CURRENT_TIME'
3708
+ @db.literal(Sequel::CURRENT_TIME).should == 'CURRENT_TIME'
3651
3709
  end
3652
3710
 
3653
3711
  it "should have CURRENT_TIMESTAMP" do
3654
- @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3655
- @db.literal(Sequel::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3712
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3713
+ @db.literal(Sequel::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3656
3714
  end
3657
3715
 
3658
3716
  it "should have NULL" do
3659
- @db.literal(Sequel::SQL::Constants::NULL) == 'NULL'
3660
- @db.literal(Sequel::NULL) == 'NULL'
3717
+ @db.literal(Sequel::SQL::Constants::NULL).should == 'NULL'
3718
+ @db.literal(Sequel::NULL).should == 'NULL'
3661
3719
  end
3662
3720
 
3663
3721
  it "should have NOTNULL" do
3664
- @db.literal(Sequel::SQL::Constants::NOTNULL) == 'NOT NULL'
3665
- @db.literal(Sequel::NOTNULL) == 'NOT NULL'
3722
+ @db.literal(Sequel::SQL::Constants::NOTNULL).should == 'NOT NULL'
3723
+ @db.literal(Sequel::NOTNULL).should == 'NOT NULL'
3666
3724
  end
3667
3725
 
3668
3726
  it "should have TRUE and SQLTRUE" do
3669
- @db.literal(Sequel::SQL::Constants::TRUE) == '1'
3670
- @db.literal(Sequel::TRUE) == '1'
3671
- @db.literal(Sequel::SQL::Constants::SQLTRUE) == '1'
3672
- @db.literal(Sequel::SQLTRUE) == '1'
3727
+ @db.literal(Sequel::SQL::Constants::TRUE).should == "'t'"
3728
+ @db.literal(Sequel::TRUE).should == "'t'"
3729
+ @db.literal(Sequel::SQL::Constants::SQLTRUE).should == "'t'"
3730
+ @db.literal(Sequel::SQLTRUE).should == "'t'"
3673
3731
  end
3674
3732
 
3675
3733
  it "should have FALSE and SQLFALSE" do
3676
- @db.literal(Sequel::SQL::Constants::FALSE) == '0'
3677
- @db.literal(Sequel::FALSE) == '0'
3678
- @db.literal(Sequel::SQL::Constants::SQLFALSE) == '0'
3679
- @db.literal(Sequel::SQLFALSE) == '0'
3734
+ @db.literal(Sequel::SQL::Constants::FALSE).should == "'f'"
3735
+ @db.literal(Sequel::FALSE).should == "'f'"
3736
+ @db.literal(Sequel::SQL::Constants::SQLFALSE).should == "'f'"
3737
+ @db.literal(Sequel::SQLFALSE).should == "'f'"
3680
3738
  end
3681
3739
  end
3682
3740
 
3683
3741
  describe "Sequel timezone support" do
3684
3742
  before do
3685
- @db = MockDatabase.new
3743
+ @db = Sequel::Database.new
3686
3744
  @dataset = @db.dataset
3687
3745
  @dataset.meta_def(:supports_timestamp_timezones?){true}
3688
3746
  @dataset.meta_def(:supports_timestamp_usecs?){false}
@@ -3717,6 +3775,30 @@ describe "Sequel timezone support" do
3717
3775
  @dataset.literal(t).should == "#{s}#{@offset}'"
3718
3776
  end
3719
3777
 
3778
+ specify "should have Database#timezone override Sequel.database_timezone" do
3779
+ Sequel.database_timezone = :local
3780
+ @db.timezone = :utc
3781
+
3782
+ t = Time.now
3783
+ s = t.getutc.strftime("'%Y-%m-%d %H:%M:%S")
3784
+ @dataset.literal(t).should == "#{s}+0000'"
3785
+
3786
+ t = DateTime.now
3787
+ s = t.new_offset(0).strftime("'%Y-%m-%d %H:%M:%S")
3788
+ @dataset.literal(t).should == "#{s}+0000'"
3789
+
3790
+ Sequel.database_timezone = :utc
3791
+ @db.timezone = :local
3792
+
3793
+ t = Time.now.utc
3794
+ s = t.getlocal.strftime("'%Y-%m-%d %H:%M:%S")
3795
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3796
+
3797
+ t = DateTime.now.new_offset(0)
3798
+ s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
3799
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3800
+ end
3801
+
3720
3802
  specify "should handle converting database timestamps into application timestamps" do
3721
3803
  Sequel.database_timezone = :utc
3722
3804
  Sequel.application_timezone = :local
@@ -3827,13 +3909,7 @@ end
3827
3909
 
3828
3910
  describe "Sequel::Dataset#select_map" do
3829
3911
  before do
3830
- @ds = MockDatabase.new[:t]
3831
- def @ds.fetch_rows(sql)
3832
- db << sql
3833
- yield({:c=>1})
3834
- yield({:c=>2})
3835
- end
3836
- @ds.db.reset
3912
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3837
3913
  end
3838
3914
 
3839
3915
  specify "should do select and map in one step" do
@@ -3846,31 +3922,63 @@ describe "Sequel::Dataset#select_map" do
3846
3922
  @ds.db.sqls.should == ['SELECT a.b FROM t']
3847
3923
  end
3848
3924
 
3925
+ specify "should raise if multiple arguments and can't determine alias" do
3926
+ proc{@ds.select_map([Sequel.function(:a), :b])}.should raise_error(Sequel::Error)
3927
+ proc{@ds.select_map(Sequel.function(:a)){b}}.should raise_error(Sequel::Error)
3928
+ proc{@ds.select_map{[a{}, b]}}.should raise_error(Sequel::Error)
3929
+ end
3930
+
3849
3931
  specify "should handle implicit aliases in arguments" do
3850
3932
  @ds.select_map(:a___b).should == [1, 2]
3851
3933
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
3852
3934
  end
3853
3935
 
3854
3936
  specify "should handle other objects" do
3855
- @ds.select_map("a".lit.as(:b)).should == [1, 2]
3937
+ @ds.select_map(Sequel.lit("a").as(:b)).should == [1, 2]
3856
3938
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
3857
3939
  end
3858
3940
 
3941
+ specify "should handle identifiers with strings" do
3942
+ @ds.select_map([Sequel::SQL::Identifier.new('c'), :c]).should == [[1, 1], [2, 2]]
3943
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3944
+ end
3945
+
3859
3946
  specify "should accept a block" do
3860
3947
  @ds.select_map{a(t__c)}.should == [1, 2]
3861
3948
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t']
3862
3949
  end
3950
+
3951
+ specify "should accept a block with an array of columns" do
3952
+ @ds.select_map{[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
3953
+ @ds.db.sqls.should == ['SELECT a(t.c) AS c, a(t.c) AS c FROM t']
3954
+ end
3955
+
3956
+ specify "should accept a block with a column" do
3957
+ @ds.select_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
3958
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t']
3959
+ end
3960
+
3961
+ specify "should accept a block and array of arguments" do
3962
+ @ds.select_map([:c, :c]){[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
3963
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, a(t.c) AS c FROM t']
3964
+ end
3965
+
3966
+ specify "should handle an array of columns" do
3967
+ @ds.select_map([:c, :c]).should == [[1, 1], [2, 2]]
3968
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3969
+ @ds.select_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b), :a__c, :a__d___c]).should == [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2]]
3970
+ @ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, a.c, a.d AS c FROM t']
3971
+ end
3972
+
3973
+ specify "should handle an array with a single element" do
3974
+ @ds.select_map([:c]).should == [[1], [2]]
3975
+ @ds.db.sqls.should == ['SELECT c FROM t']
3976
+ end
3863
3977
  end
3864
3978
 
3865
3979
  describe "Sequel::Dataset#select_order_map" do
3866
3980
  before do
3867
- @ds = MockDatabase.new[:t]
3868
- def @ds.fetch_rows(sql)
3869
- db << sql
3870
- yield({:c=>1})
3871
- yield({:c=>2})
3872
- end
3873
- @ds.db.reset
3981
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3874
3982
  end
3875
3983
 
3876
3984
  specify "should do select and map in one step" do
@@ -3883,6 +3991,12 @@ describe "Sequel::Dataset#select_order_map" do
3883
3991
  @ds.db.sqls.should == ['SELECT a.b FROM t ORDER BY a.b']
3884
3992
  end
3885
3993
 
3994
+ specify "should raise if multiple arguments and can't determine alias" do
3995
+ proc{@ds.select_order_map([Sequel.function(:a), :b])}.should raise_error(Sequel::Error)
3996
+ proc{@ds.select_order_map(Sequel.function(:a)){b}}.should raise_error(Sequel::Error)
3997
+ proc{@ds.select_order_map{[a{}, b]}}.should raise_error(Sequel::Error)
3998
+ end
3999
+
3886
4000
  specify "should handle implicit aliases in arguments" do
3887
4001
  @ds.select_order_map(:a___b).should == [1, 2]
3888
4002
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
@@ -3894,59 +4008,164 @@ describe "Sequel::Dataset#select_order_map" do
3894
4008
  end
3895
4009
 
3896
4010
  specify "should handle AliasedExpressions" do
3897
- @ds.select_order_map("a".lit.as(:b)).should == [1, 2]
4011
+ @ds.select_order_map(Sequel.lit("a").as(:b)).should == [1, 2]
3898
4012
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
3899
4013
  end
3900
4014
 
4015
+ specify "should handle OrderedExpressions" do
4016
+ @ds.select_order_map(Sequel.desc(:a)).should == [1, 2]
4017
+ @ds.db.sqls.should == ['SELECT a FROM t ORDER BY a DESC']
4018
+ end
4019
+
3901
4020
  specify "should accept a block" do
3902
4021
  @ds.select_order_map{a(t__c)}.should == [1, 2]
3903
4022
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t ORDER BY a(t.c)']
3904
4023
  end
4024
+
4025
+ specify "should accept a block with an array of columns" do
4026
+ @ds.select_order_map{[c.desc, a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
4027
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c DESC, a(t.c)']
4028
+ end
4029
+
4030
+ specify "should accept a block with a column" do
4031
+ @ds.select_order_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
4032
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c, a(t.c)']
4033
+ end
4034
+
4035
+ specify "should accept a block and array of arguments" do
4036
+ @ds.select_order_map([:c, :c]){[a(t__c).as(c), c.desc]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
4037
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, c FROM t ORDER BY c, c, a(t.c), c DESC']
4038
+ end
4039
+
4040
+ specify "should handle an array of columns" do
4041
+ @ds.select_order_map([:c, :c]).should == [[1, 1], [2, 2]]
4042
+ @ds.db.sqls.should == ['SELECT c, c FROM t ORDER BY c, c']
4043
+ @ds.select_order_map([Sequel.expr(:d).as(:c), Sequel.qualify(:b, :c), Sequel.identifier(:c), Sequel.identifier(:c).qualify(:b), Sequel.identifier(:c).qualify(:b).desc, :a__c, Sequel.desc(:a__d___c), Sequel.desc(Sequel.expr(:a__d___c))]).should == [[1, 1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2, 2]]
4044
+ @ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, b.c, a.c, a.d AS c, a.d AS c FROM t ORDER BY d, b.c, c, b.c, b.c DESC, a.c, a.d DESC, a.d DESC']
4045
+ end
4046
+
4047
+ specify "should handle an array with a single element" do
4048
+ @ds.select_order_map([:c]).should == [[1], [2]]
4049
+ @ds.db.sqls.should == ['SELECT c FROM t ORDER BY c']
4050
+ end
3905
4051
  end
3906
4052
 
3907
4053
  describe "Sequel::Dataset#select_hash" do
3908
4054
  before do
3909
- @ds = MockDatabase.new[:t]
3910
- def @ds.set_fr_yield(hs)
3911
- @hs = hs
3912
- end
3913
- def @ds.fetch_rows(sql)
3914
- db << sql
3915
- @hs.each{|h| yield h}
3916
- end
3917
- @ds.db.reset
4055
+ @db = Sequel.mock(:fetch=>[{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4056
+ @ds = @db[:t]
3918
4057
  end
3919
4058
 
3920
- specify "should do select and map in one step" do
3921
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4059
+ specify "should do select and to_hash in one step" do
3922
4060
  @ds.select_hash(:a, :b).should == {1=>2, 3=>4}
3923
4061
  @ds.db.sqls.should == ['SELECT a, b FROM t']
3924
4062
  end
3925
4063
 
3926
4064
  specify "should handle implicit qualifiers in arguments" do
3927
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3928
4065
  @ds.select_hash(:t__a, :t__b).should == {1=>2, 3=>4}
3929
4066
  @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
3930
4067
  end
3931
4068
 
3932
4069
  specify "should handle implicit aliases in arguments" do
3933
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3934
4070
  @ds.select_hash(:c___a, :d___b).should == {1=>2, 3=>4}
3935
4071
  @ds.db.sqls.should == ['SELECT c AS a, d AS b FROM t']
3936
4072
  end
3937
4073
 
3938
4074
  specify "should handle implicit qualifiers and aliases in arguments" do
3939
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3940
4075
  @ds.select_hash(:t__c___a, :t__d___b).should == {1=>2, 3=>4}
3941
4076
  @ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
3942
4077
  end
4078
+
4079
+ specify "should handle SQL::Identifiers in arguments" do
4080
+ @ds.select_hash(Sequel.identifier(:a), Sequel.identifier(:b)).should == {1=>2, 3=>4}
4081
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4082
+ end
4083
+
4084
+ specify "should handle SQL::QualifiedIdentifiers in arguments" do
4085
+ @ds.select_hash(Sequel.qualify(:t, :a), Sequel.identifier(:b).qualify(:t)).should == {1=>2, 3=>4}
4086
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4087
+ end
4088
+
4089
+ specify "should handle SQL::AliasedExpressions in arguments" do
4090
+ @ds.select_hash(Sequel.expr(:c).as(:a), Sequel.expr(:t).as(:b)).should == {1=>2, 3=>4}
4091
+ @ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
4092
+ end
4093
+
4094
+ specify "should work with arrays of columns" do
4095
+ @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
4096
+ @ds.select_hash([:a, :c], :b).should == {[1, 3]=>2, [4, 6]=>5}
4097
+ @ds.db.sqls.should == ['SELECT a, c, b FROM t']
4098
+ @ds.select_hash(:a, [:b, :c]).should == {1=>[2, 3], 4=>[5, 6]}
4099
+ @ds.db.sqls.should == ['SELECT a, b, c FROM t']
4100
+ @ds.select_hash([:a, :b], [:b, :c]).should == {[1, 2]=>[2, 3], [4, 5]=>[5, 6]}
4101
+ @ds.db.sqls.should == ['SELECT a, b, b, c FROM t']
4102
+ end
4103
+
4104
+ specify "should raise an error if the resulting symbol cannot be determined" do
4105
+ proc{@ds.select_hash(Sequel.expr(:c).as(:a), Sequel.function(:b))}.should raise_error(Sequel::Error)
4106
+ end
4107
+ end
4108
+
4109
+ describe "Sequel::Dataset#select_hash_groups" do
4110
+ before do
4111
+ @db = Sequel.mock(:fetch=>[{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4112
+ @ds = @db[:t]
4113
+ end
4114
+
4115
+ specify "should do select and to_hash in one step" do
4116
+ @ds.select_hash_groups(:a, :b).should == {1=>[2], 3=>[4]}
4117
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4118
+ end
4119
+
4120
+ specify "should handle implicit qualifiers in arguments" do
4121
+ @ds.select_hash_groups(:t__a, :t__b).should == {1=>[2], 3=>[4]}
4122
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4123
+ end
4124
+
4125
+ specify "should handle implicit aliases in arguments" do
4126
+ @ds.select_hash_groups(:c___a, :d___b).should == {1=>[2], 3=>[4]}
4127
+ @ds.db.sqls.should == ['SELECT c AS a, d AS b FROM t']
4128
+ end
4129
+
4130
+ specify "should handle implicit qualifiers and aliases in arguments" do
4131
+ @ds.select_hash_groups(:t__c___a, :t__d___b).should == {1=>[2], 3=>[4]}
4132
+ @ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
4133
+ end
4134
+
4135
+ specify "should handle SQL::Identifiers in arguments" do
4136
+ @ds.select_hash_groups(Sequel.identifier(:a), Sequel.identifier(:b)).should == {1=>[2], 3=>[4]}
4137
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4138
+ end
4139
+
4140
+ specify "should handle SQL::QualifiedIdentifiers in arguments" do
4141
+ @ds.select_hash_groups(Sequel.qualify(:t, :a), Sequel.identifier(:b).qualify(:t)).should == {1=>[2], 3=>[4]}
4142
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4143
+ end
4144
+
4145
+ specify "should handle SQL::AliasedExpressions in arguments" do
4146
+ @ds.select_hash_groups(Sequel.expr(:c).as(:a), Sequel.expr(:t).as(:b)).should == {1=>[2], 3=>[4]}
4147
+ @ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
4148
+ end
4149
+
4150
+ specify "should work with arrays of columns" do
4151
+ @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
4152
+ @ds.select_hash_groups([:a, :c], :b).should == {[1, 3]=>[2], [4, 6]=>[5]}
4153
+ @ds.db.sqls.should == ['SELECT a, c, b FROM t']
4154
+ @ds.select_hash_groups(:a, [:b, :c]).should == {1=>[[2, 3]], 4=>[[5, 6]]}
4155
+ @ds.db.sqls.should == ['SELECT a, b, c FROM t']
4156
+ @ds.select_hash_groups([:a, :b], [:b, :c]).should == {[1, 2]=>[[2, 3]], [4, 5]=>[[5, 6]]}
4157
+ @ds.db.sqls.should == ['SELECT a, b, b, c FROM t']
4158
+ end
4159
+
4160
+ specify "should raise an error if the resulting symbol cannot be determined" do
4161
+ proc{@ds.select_hash_groups(Sequel.expr(:c).as(:a), Sequel.function(:b))}.should raise_error(Sequel::Error)
4162
+ end
3943
4163
  end
3944
4164
 
3945
4165
  describe "Modifying joined datasets" do
3946
4166
  before do
3947
- @ds = MockDatabase.new.from(:b, :c).join(:d, [:id]).where(:id => 2)
4167
+ @ds = Sequel.mock.from(:b, :c).join(:d, [:id]).where(:id => 2)
3948
4168
  @ds.meta_def(:supports_modifying_joins?){true}
3949
- @ds.db.reset
3950
4169
  end
3951
4170
 
3952
4171
  specify "should allow deleting from joined datasets" do
@@ -3962,7 +4181,7 @@ end
3962
4181
 
3963
4182
  describe "Dataset#lock_style and for_update" do
3964
4183
  before do
3965
- @ds = MockDatabase.new[:t]
4184
+ @ds = Sequel::Dataset.new(nil).from(:t)
3966
4185
  end
3967
4186
 
3968
4187
  specify "#for_update should use FOR UPDATE" do
@@ -3977,3 +4196,173 @@ describe "Dataset#lock_style and for_update" do
3977
4196
  @ds.lock_style("FOR SHARE").sql.should == "SELECT * FROM t FOR SHARE"
3978
4197
  end
3979
4198
  end
4199
+
4200
+ describe "Custom ASTTransformer" do
4201
+ specify "should transform given objects" do
4202
+ c = Class.new(Sequel::ASTTransformer) do
4203
+ def v(s)
4204
+ (s.is_a?(Symbol) || s.is_a?(String)) ? :"#{s}#{s}" : super
4205
+ end
4206
+ end.new
4207
+ ds = Sequel::Dataset.new(nil).from(:t).cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
4208
+ ds.sql.should == 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
4209
+ ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.should == 'SELECT * FROM tt CROSS JOIN aa AS gg INNER JOIN bb AS hh USING (cc) INNER JOIN dd AS ii ON (ii.ee = hh.ff)'
4210
+ end
4211
+ end
4212
+
4213
+ describe "Dataset#returning" do
4214
+ before do
4215
+ @ds = Sequel.mock(:fetch=>proc{|s| {:foo=>s}})[:t].returning(:foo)
4216
+ @pr = proc do
4217
+ [:insert, :update, :delete].each do |m|
4218
+ @ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
4219
+ end
4220
+ end
4221
+ end
4222
+
4223
+ specify "should use RETURNING clause in the SQL if the dataset supports it" do
4224
+ @pr.call
4225
+ @ds.delete_sql.should == "DELETE FROM t RETURNING foo"
4226
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1) RETURNING foo"
4227
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1 RETURNING foo"
4228
+ end
4229
+
4230
+ specify "should not use RETURNING clause in the SQL if the dataset does not support it" do
4231
+ @ds.delete_sql.should == "DELETE FROM t"
4232
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1)"
4233
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1"
4234
+ end
4235
+
4236
+ specify "should have insert, update, and delete yield to blocks if RETURNING is used" do
4237
+ @pr.call
4238
+ h = {}
4239
+ @ds.delete{|r| h = r}
4240
+ h.should == {:foo=>"DELETE FROM t RETURNING foo"}
4241
+ @ds.insert(1){|r| h = r}
4242
+ h.should == {:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}
4243
+ @ds.update(:foo=>1){|r| h = r}
4244
+ h.should == {:foo=>"UPDATE t SET foo = 1 RETURNING foo"}
4245
+ end
4246
+
4247
+ specify "should have insert, update, and delete return arrays of hashes if RETURNING is used and a block is not given" do
4248
+ @pr.call
4249
+ h = {}
4250
+ @ds.delete.should == [{:foo=>"DELETE FROM t RETURNING foo"}]
4251
+ @ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
4252
+ @ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
4253
+ end
4254
+ end
4255
+
4256
+ describe "Dataset emulating bitwise operator support" do
4257
+ before do
4258
+ @ds = Sequel::Database.new.dataset
4259
+ @ds.quote_identifiers = true
4260
+ def @ds.complex_expression_sql_append(sql, op, args)
4261
+ sql << complex_expression_arg_pairs(args){|a, b| "bitand(#{literal(a)}, #{literal(b)})"}
4262
+ end
4263
+ end
4264
+
4265
+ it "should work with any numbers of arguments for operators" do
4266
+ @ds.select(Sequel::SQL::ComplexExpression.new(:&, :x)).sql.should == 'SELECT "x"'
4267
+ @ds.select(Sequel.expr(:x) & 1).sql.should == 'SELECT bitand("x", 1)'
4268
+ @ds.select(Sequel.expr(:x) & 1 & 2).sql.should == 'SELECT bitand(bitand("x", 1), 2)'
4269
+ end
4270
+ end
4271
+
4272
+ describe "Dataset feature defaults" do
4273
+ it "should not require aliases for recursive CTEs by default" do
4274
+ Sequel::Database.new.dataset.recursive_cte_requires_column_aliases?.should be_false
4275
+ end
4276
+
4277
+ it "should not require placeholder type specifiers by default" do
4278
+ Sequel::Database.new.dataset.requires_placeholder_type_specifiers?.should be_false
4279
+ end
4280
+
4281
+ it "offset use should be returning a separate row number column by default" do
4282
+ Sequel::Database.new.dataset.send(:offset_returns_row_number_column?).should be_false
4283
+ end
4284
+ end
4285
+
4286
+ describe "Dataset extensions" do
4287
+ before(:all) do
4288
+ class << Sequel
4289
+ alias _extension extension
4290
+ def extension(*)
4291
+ end
4292
+ end
4293
+ end
4294
+ after(:all) do
4295
+ class << Sequel
4296
+ alias extension _extension
4297
+ end
4298
+ end
4299
+ before do
4300
+ @ds = Sequel::Dataset.new(nil)
4301
+ end
4302
+
4303
+ specify "should be able to register an extension with a module Database#extension extend the module" do
4304
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4305
+ @ds.extension(:foo).a.should == 1
4306
+ end
4307
+
4308
+ specify "should be able to register an extension with a block and Database#extension call the block" do
4309
+ @ds.quote_identifiers = false
4310
+ Sequel::Dataset.register_extension(:foo){|db| db.quote_identifiers = true}
4311
+ @ds.extension(:foo).quote_identifiers?.should be_true
4312
+ end
4313
+
4314
+ specify "should be able to register an extension with a callable and Database#extension call the callable" do
4315
+ @ds.quote_identifiers = false
4316
+ Sequel::Dataset.register_extension(:foo, proc{|db| db.quote_identifiers = true})
4317
+ @ds.extension(:foo).quote_identifiers?.should be_true
4318
+ end
4319
+
4320
+ specify "should be able to load multiple extensions in the same call" do
4321
+ @ds.quote_identifiers = false
4322
+ @ds.identifier_input_method = :downcase
4323
+ Sequel::Dataset.register_extension(:foo, proc{|ds| ds.quote_identifiers = true})
4324
+ Sequel::Dataset.register_extension(:bar, proc{|ds| ds.identifier_input_method = nil})
4325
+ ds = @ds.extension(:foo, :bar)
4326
+ ds.quote_identifiers?.should be_true
4327
+ ds.identifier_input_method.should be_nil
4328
+ end
4329
+
4330
+ specify "should have #extension not modify the receiver" do
4331
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4332
+ @ds.extension(:foo)
4333
+ proc{@ds.a}.should raise_error(NoMethodError)
4334
+ end
4335
+
4336
+ specify "should have #extension not return a cloned dataset" do
4337
+ @ds.extend(Module.new{def b; 2; end})
4338
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4339
+ v = @ds.extension(:foo)
4340
+ v.should_not equal(@ds)
4341
+ v.should be_a_kind_of(Sequel::Dataset)
4342
+ v.b.should == 2
4343
+ end
4344
+
4345
+ specify "should have #extension! modify the receiver" do
4346
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4347
+ @ds.extension!(:foo)
4348
+ @ds.a.should == 1
4349
+ end
4350
+
4351
+ specify "should have #extension! return the receiver" do
4352
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4353
+ @ds.extension!(:foo).should equal(@ds)
4354
+ end
4355
+
4356
+ specify "should register a Database extension for modifying all datasets when registering with a module" do
4357
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4358
+ Sequel.mock.extension(:foo).dataset.a.should == 1
4359
+ end
4360
+
4361
+ specify "should raise an Error if registering with both a module and a block" do
4362
+ proc{Sequel::Dataset.register_extension(:foo, Module.new){}}.should raise_error(Sequel::Error)
4363
+ end
4364
+
4365
+ specify "should raise an Error if attempting to load an incompatible extension" do
4366
+ proc{@ds.extension(:foo2)}.should raise_error(Sequel::Error)
4367
+ end
4368
+ end